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();