Android下,对于耗时的操作要放到子线程中,要不然会残生ANR,本次我们就来学习一下Android多线程更新UI的方式。

  首先我们来认识一下anr:

anr:application not reponse:应用程序无响应
  主线程:UI线程
  anr产生的原因:主线程需要做很多重要的事情,响应点击事件,更新ui,如果在主线程里面阻塞时间过久,应用程序就会无响应,为了避免应用程序出现anr,所有的耗时的操作,都应该放在子线程中执行。

  认识了anr后,我们就来学习一下怎样在Android下开启多线程,并更新ui了。一共有两种方式:

第一种采用:Handler。

  Handler获取当前线程中的looper对象,looper用来从存有Message的Message Queue里取出message,再由Handler进行message的分发和处理。

handler进制的原理:

android提供了handler和looper来满足线程间的通信。Handler先进先出原则。looper用来管理特定线程内对象之间的消息交换(message Exchange).

     1)looper:一个线程可以产生一个looper对象,由它来管理此线程里的message queue(消息队列)

      2)handler:你可以构造一个handler对象来与looper沟通,以便push新消息到messagequeue里;或者接收looper(从messagequeue里取出)所送来的消息。

我们使用的时候呢,需要在子线程中定义message,并把要返回的obj对象,传递给msg,同时由于handler不止要处理这一种类型,所以我们还要定义what的类型,以便让handler分类处理。

Message msg = new Message();
	msg.what = CHANGE_UI;
	msg.obj = result;
	handler.sendMessage(msg);

好的接下来我们就实例来使用handler。


new Thread(){
			   public void run() {
				   try {
					URL url = new URL(path);
					HttpURLConnection conn = (HttpURLConnection) url.openConnection();
					conn.setRequestMethod("GET");
					conn.setConnectTimeout(5000);
					conn.setRequestProperty("User-Agent", "");
					
					int code = conn.getResponseCode();
					if(code==200){
						InputStream is = conn.getInputStream();
						String result = StreamTools.readInputStream(is);
						
						//tv_content.setText(result);
						Message msg = new Message();
						msg.what = CHANGE_UI;
						msg.obj = result;
						handler.sendMessage(msg);
						
						
					}else{
						Message msg = new Message();
						msg.what = ERROR;
						handler.sendMessage(msg);
					}
					
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
					
					Message msg = new Message();
					msg.what = ERROR;
					handler.sendMessage(msg);
					
					
				}
				   
				   
			   };
		   }.start();


StreamTools.readInputStream的实现是这样的,目的是把输入流转化为字符串。


/**
	 * 把输入流转化为字符串
	 * @param is
	 * @return
	 */
	public static String readInputStream(InputStream is){
		try {
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			int len = 0;
			byte[] buffer = new byte[1024];
			while((len = is.read(buffer))!=-1){
				baos.write(buffer,0,len);
			}
			is.close();
			baos.close();
			byte[] result = baos.toByteArray();
			
			//试着解析网址返回的字符串,
			String temp = new String(result);
			if(temp.contains("utf-8")){
				return temp;
			}else if(temp.contains("gbk")){
				return new String(result,"gbk");
			}
			
			return temp;
			//return new String(result);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return "获取失败";
		}
		
	}

 

其中handler的实现是这样的。

private Handler handler = new Handler(){
		public void handleMessage(android.os.Message msg) {
			switch (msg.what) {
			case ERROR:
				Toast.makeText(MainActivity.this, "获取数据失败", 0).show();
				break;
			case CHANGE_UI:
				tv_content.setText(msg.obj+"");
				Toast.makeText(MainActivity.this, "获取数据失败", 0).show();
				break;

			}
		};
	};

 这样呢,我们便可以用handler,也即消息处理机制来更新UI了,

 

第二种方式。

runOnUiThread(new Runnable()),这要实现Runnable借口,我们可以直接在这个线程中进行UI的更新。是api提供的方法,较为便捷。


new Thread(){
    		@Override
    		public void run() {
    			final String result = LoginServices.loginByGet(username, password);
    			if(result != null){
    				//成功
    				runOnUiThread(new Runnable() {
						
						@Override
						public void run() {
							// TODO Auto-generated method stub
							Toast.makeText(MainActivity.this, result, 0).show();
						}
					});
    			}else{
    				//请求失败
    				runOnUiThread(new Runnable() {
						
						@Override
						public void run() {
							// TODO Auto-generated method stub
							Toast.makeText(MainActivity.this, "请求失败", 0).show();
						}
					});
    			}
    			
    		};
    	}.start();

 

源码:https://github.com/darren90/ChengeUI

作者:Darren