Android学习五 网络编程与handler消息机制

      Android中许多应用需要从网络上获取内容,而如何从网络上获取内容,这就需要用到网络编程的知识。下面是Android中关于网络编程的一些知识。

一、使用httpURLConnection对象访问网络

    使用UrlConnection请求一个url地址获取内容的一般步骤如下:

//1.创建一个Url对象
       URL url = new URL(url_str);

//2.获取一个UrlConnection对象
        HttpURLConnection connection = (HttpURLConnection)url.openConnection();
//3.为UrlConnection对象设置一些请求的参数,请求方式,连接的超时时间
        connection.setRequestMethod("GET");//设置请求方式
        connection.setConnectTimeout(1000*10);//设置超时时间

//4.在获取url请求的数据前需要判断响应码,200 :成功,206:访问部分数据成功   300:跳转或重定向  400:错误 500:服务器异常
        int code = connection.getResponseCode();

//5.获取有效数据,并将获取的流数据解析成String
         InputStream inputStream = connection.getInputStream();
         String result = StreamUtils.streamToString(inputStream);

注意访问网络需要设置android.permission.INTERNET  这个权限。

       如果直接在主线程中做网络请求的话,可能会ANR:application not response 应用无响应的错误,这是因为Android中的耗时操作(请求网络,大文件的拷贝,数据库的操作等)需要在子线程中去做,但是在子线程中不能够更新控件(如Toast的展示及其他界面的修改操作)。这是可能就会产生问题,当即需要请求网络有需要更新UI时该怎么办呢?

这是后就需要handler了。

二、handler的使用

     handler可以在子线程和主线程之间传递消息,当子线程中需要更新UI时,只需要把要更新的内容用handler传递到主线程中,在主线程中更新即可。下面来看一下handler的用法。

        1.主线程中创建一个Handler
        private Handler handler = new Handler(){
                public void handleMessage(android.os.Message msg) {
                };
        };
        2.重写handler的handlermessage方法
        3.子线程中创建一个Message对象,将获取的数据绑定给msg
                Message msg = new Message();
                //另一种方式:Message msg = Messge.obtain;
                msg.obj = result;
        4.主线程中的handler对象在子线程中将message发送给主线程
                handler.sendMessage(msg);
        5.主线程中handlermessage方法接受子线程发来的数据,就可以做更新UI的操作。

下面来看一段示例代码:

 private EditText et_url;
    private TextView tv_source;
    private Context mContext;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mContext = this;
        //一、获取控件
        et_url = (EditText) findViewById(R.id.et_url);
        Button bt_looksource = (Button) findViewById(R.id.bt_looksource);
        tv_source = (TextView) findViewById(R.id.tv_source);
        //二、绑定点击事件
        bt_looksource.setOnClickListener(this);
        System.out.println("oncreate方法线程:"+Thread.currentThread().getName());
    }

    //使用handler在子线程和主线程之间传递消息的步骤
    //a.在主线程中创建一个handler对象
    private Handler handler = new Handler(){
        //b重写handler对象中的handlerMessage方法,用来接收子线程中发来的消息
        @Override
        public void handleMessage(Message msg) {
            //e.接收子线程发来的消息,处理消息
            String result = (String)msg.obj;
            System.out.println("获取到信息");
            //五、更新UI
            tv_source.setText(result);
        }
    };

    @Override
    public void onClick(View view) {
        //ctrl+alt+t快速添加try catch块
        try {
            //三、获取输入的url地址
            final String url_str = et_url.getText().toString().trim();
            if(TextUtils.isEmpty(url_str)){
                //???
                Toast.makeText(mContext,"url不能为空",Toast.LENGTH_SHORT).show();
                return ;
            }

            System.out.println("onclick方法线程:"+Thread.currentThread().getName());

            //四、创建一个子线程做网络请求
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        System.out.println("oclick方法runnable线程:"+Thread.currentThread().getName());
                        //使用UrlConnection请求一个url地址获取内容
                        //1.根据url地址创建一个URL对象
                        URL url = new URL(url_str);
                        //2.获取一个URLConnection对象
                        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                        connection.setRequestMethod("GET");
                        connection.setConnectTimeout(1000*10);
                        System.out.println("第一次请求的请求码"+connection.getResponseCode());
                        //3.获取头部信息,获取到重定向后的地址(为了解决返回码为302的问题)
                        String location = connection.getHeaderField("Location");
                        System.out.println("location "+location+"");
                        //4.为URLConnection设置请求参数(请求方式,连接的超时时间等)
                        url  = new URL(location);
                        connection = (HttpURLConnection) url.openConnection();
                        connection.setRequestMethod("GET");
                        connection.setConnectTimeout(1000*10);
                        //4.判断响应码
                        int code = connection.getResponseCode();
                        System.out.println(code);
                        if(code == 200){
                            //5.获取有效数据,并将数据解析为字符串
                            InputStream inputStream = connection.getInputStream();
                            String result = StreamUtils.streamToString(inputStream);
                            //c.子线程中创建一个Message对象,用于将子线程中的数传递给主线程
                            Message message = new Message();
                            message.obj = result;
                            //d.使用handler将数据从子线程传递到主线程
                            handler.sendMessage(message);
                        }else{
                            System.out.println("请求url失败");
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        } catch (Exception e) {
            e.printStackTrace();
        }


    }
public class StreamUtils {
    public static String streamToString(InputStream in){
        String result = "";
        try {
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int length = 0;
            while((length = in.read(buffer)) != -1){
                out.write(buffer,0,length);
                out.flush();
            }
            result = out.toString();
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;


    }
}
在子线程中更新UI的其他集中方法:

        1.使用activity的runOnUiThread方法更新ui,无论当前线程是否是主线程,都将在主线程执行。注意runOnUiThread不能在静态类中使用
                runOnUiThread(new Runnable() {              
                    @Override
                    public void run() {
                        tv_simple.setText("我被更新了");
                    }
                });
        2.使用handler直接post到主线程,handler需要在主线程创建
                    //延迟多少毫米执行runnable。
                    mHandler.postDelayed(new Runnable() {      
                    @Override
                    public void run() {
                        tv_simple.setText("我被更新了");
                    }
                }, 1000*5);

还有需要注意的是不能在子线程中更新UI并不是绝对的。以下集中情形可以在子线程中更新UI:SurfaceView :多媒体视频播放 ,可以在子线程中更新UI; Progress(进度)相关的控件:也是可以在子线程中更新Ui;审计机制:activity完全显示的时候审计机制才会去检测子线程有没有更新Ui。

三、get和post请求

     在请求网络时需要设置请求方式,常用的请求方式有两种post和get。get的请求内容是跟在URL地址后面(不安全且大小有限制但是简单),post的请求内容是放在请求体当中(大小无限制,安全,但是繁琐)。使用post请求相比get请求还需要设置一下内容:

请求头的设置(不是必需的)
            openConnection.setRequestProperty("Content-Length", body.length()+"");
            openConnection.setRequestProperty("Cache-Control", "max-age=0");
            openConnection.setRequestProperty("Origin", "http://192.168.13.83:8080");
请求内容的设置(必需)
             //设置UrlConnection可以写请求的内容
            openConnection.setDoOutput(true);
            //获取一个outputstream,并将内容写入该流
            openConnection.getOutputStream().write(body.getBytes());

使用post请求方式时还要注意编码要一致,否则可能产生乱码。可以使用URLEncode对数据进行编码。

四、用httpClient做网络请求

       直接上代码,get方式:

try{
				String path = "http://192.168.13.83:8080/itheima74/servlet/LoginServlet?username="+URLEncoder.encode(username,"utf-8")+"&pwd="+URLEncoder.encode(password,"utf-8");
				//1.创建一个httpClient对象
				HttpClient httpclient = new DefaultHttpClient();
				
				//2.设置请求的方式
				HttpGet httpget = new HttpGet(path);
				//3.执行一个http请求
				HttpResponse response = httpclient.execute(httpget);
				//4.获取请求的状态码,
				StatusLine statusLine = response.getStatusLine();
				int code = statusLine.getStatusCode();
				
				//5.判断状态码后获取内容
				if(code == 200){
					HttpEntity entity = response.getEntity();//获取实体内容,中封装的有http请求返回的流信息
					InputStream inputStream = entity.getContent();
					//将流信息转换成字符串
					String result = StreamUtils.streamToString(inputStream);
					
					Message msg = Message.obtain();
					msg.what = 1;
					msg.obj = result;
					handler.sendMessage(msg);
				}
				
				}catch (Exception e) {
					e.printStackTrace();
				}
post方式:

String path = "http://192.168.13.83:8080/itheima74/servlet/LoginServlet";
	
			AsyncHttpClient asyncHttpClient = new AsyncHttpClient();
			RequestParams params = new RequestParams();
			params.put("username", username);
			params.put("pwd", password);
			
			//url:   parmas:请求时携带的参数信息   responseHandler:是一个匿名内部类接受成功过失败
			asyncHttpClient.post(path, params, new AsyncHttpResponseHandler() {
				
				@Override
				public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
					//statusCode:状态码    headers:头信息  responseBody:返回的内容,返回的实体
					
					//判断状态码
					if(statusCode == 200){
						//获取结果
						try {
							String result = new String(responseBody,"utf-8");
							Toast.makeText(context, result, 0).show();
						} catch (UnsupportedEncodingException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
					
				}
				
				@Override
				public void onFailure(int statusCode, Header[] headers,
						byte[] responseBody, Throwable error) {
					
				}
			});


 


你可能感兴趣的:(Android)