201709013工作日记--Android消息机制HandlerThread
1.首先来看一个常规的handler用法:
在主线程中建立一个handler:
private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what) { case 0: mTestTV.setText("This is handleMessage");//更新UI break; } } };
在子线程中进行耗时操作,结束后发送消息,主线程收到消息后进行更新UI操作。
new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(1000);//在子线程有一段耗时操作,比如请求网络 mHandler.sendEmptyMessage(0); } catch (InterruptedException e) { e.printStackTrace(); } } }).start();
2.现在来看看handler.post()的版本:
private Handler mHandler;//全局变量 @Override protected void onCreate(Bundle savedInstanceState) { mHandler = new Handler(); new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(1000);//在子线程有一段耗时操作,比如请求网络 mHandler.post(new Runnable() { @Override public void run() { mTestTV.setText("This is post");//更新UI } }); } catch (InterruptedException e) { e.printStackTrace(); } } }).start();
耗时操作完成之后,直接在handler开启的子线程中进行了更新UI的操作。post和sendMessage原理都是封装成Message,并且最终都调用了enqueueMessage()一个无限循环将消息加入到消息队列中(链表的形式)。翻看MessageQueue的方法,我们找到了next(),代码太长,不赘述,我们知道它是用来把消息取出来的就行了。不过这个方法是在什么地方调用的呢,不是在Handler中,我们找到了Looper这个关键人物,我叫他环形使者,专门负责从消息队列中拿消息。参考博客:http://blog.csdn.net/ly502541243/article/details/52062179/
3.HandlerThread用法
(1)创建HandlerThread并启动
mThread = new HandlerThread("handler_thread"); mThread.start();
(2)创建处理任务的handler和在主线程中更新UI的handler
一句话来总结就是:通过handler与HandlerThread进行绑定相当于开启了一个子线程,在这个子线程中处理任务,处理后的任务形成消息或者其他数据形式。再通过主线程的handler.sendMessage(子线程结果),发送给主线程的handler,进行UI操作。
其中处理任务的mWorkHandler与handlerThread关联起来,那么这个Handler对象就是与HandlerThread这个线程绑定了(这时就不再是与UI线程绑定了,这样它处理耗时操作将不会阻塞UI),它将在handleMessage(Message msg)中处理耗时任务,之后在mUIHandler对UI进行刷新,如果在handleMessage()执行完成后,如果想要更新UI,可以用UI线程的Handler发消息给UI线程来更新。
mWorkHandler = new Handler(mThread.getLooper()); mUIHandler = new Handler();
(3)在合适的时机退出HandlerThread,比如activity中的onDestroy(),方法有quit()和quitSafely()
下面我们看一个比较典型的handlerThread的使用过程:参考http://blog.csdn.net/javazejian/article/details/52426353
public class HandlerThreadActivity extends Activity { /** * 图片地址集合 */ private String url[]={ "//img-blog.csdn.net/20160903083245762", "//img-blog.csdn.net/20160903083252184", "//img-blog.csdn.net/20160903083257871", "//img-blog.csdn.net/20160903083257871", "//img-blog.csdn.net/20160903083311972", "//img-blog.csdn.net/20160903083319668", "//img-blog.csdn.net/20160903083326871" }; private ImageView imageView; private Handler mUIHandler = new Handler(){ @Override public void handleMessage(Message msg) { LogUtils.e("次数:"+msg.what); ImageModel model = (ImageModel) msg.obj; imageView.setImageBitmap(model.bitmap); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_handler_thread); imageView= (ImageView) findViewById(R.id.image); //创建异步HandlerThread HandlerThread handlerThread = new HandlerThread("downloadImage"); //必须先开启线程 handlerThread.start(); //子线程Handler Handler childHandler = new Handler(handlerThread.getLooper(),new ChildCallback()); for(int i=0;i<7;i++){ //每个1秒去更新图片 childHandler.sendEmptyMessageDelayed(i,1000*i); } } /** * 该callback运行于子线程 */ class ChildCallback implements Handler.Callback { @Override public boolean handleMessage(Message msg) { //在子线程中进行网络请求 Bitmap bitmap=downloadUrlBitmap(url[msg.what]); ImageModel imageModel=new ImageModel(); imageModel.bitmap=bitmap; imageModel.url=url[msg.what]; Message msg1 = new Message(); msg1.what = msg.what; msg1.obj =imageModel; //通知主线程去更新UI mUIHandler.sendMessage(msg1); return false; } } private Bitmap downloadUrlBitmap(String urlString) { HttpURLConnection urlConnection = null; BufferedInputStream in = null; Bitmap bitmap=null; try { final URL url = new URL(urlString); urlConnection = (HttpURLConnection) url.openConnection(); in = new BufferedInputStream(urlConnection.getInputStream(), 8 * 1024); bitmap=BitmapFactory.decodeStream(in); } catch (final IOException e) { e.printStackTrace(); } finally { if (urlConnection != null) { urlConnection.disconnect(); } try { if (in != null) { in.close(); } } catch (final IOException e) { e.printStackTrace(); } } return bitmap; } }
在这个案例中,我们创建了两个Handler,一个用于更新UI线程的mUIHandler和一个用于异步下载图片的childHandler。最终的结果是childHandler会每个隔1秒钟通过sendEmptyMessageDelayed方法去通知ChildCallback的回调函数handleMessage方法去下载图片并告诉mUIHandler去更新UI界面,以上便是HandlerThread常规使用。
下面来看另外一个例子,使用post方式通信:
public class MainActivity extends AppCompatActivity { private TextView mTvServiceInfo; private HandlerThread mCheckMsgThread; private Handler mCheckMsgHandler; private boolean isUpdateInfo; private static final int MSG_UPDATE_INFO = 0x110; //与UI线程管理的handler private Handler mHandler = new Handler(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.i("~~~~~~~~~~~~~~~now is ",Thread.currentThread().getName()); setContentView(R.layout.activity_main); //创建后台线程 mCheckMsgThread = new HandlerThread("check-message-coming"); mCheckMsgThread.start(); mCheckMsgHandler = new Handler(mCheckMsgThread.getLooper()){ @Override public void handleMessage(Message msg) { try { //模拟耗时,处理任务(模拟实时获取大盘数据),处理完成之后,通知mHandler完成界面更新 Thread.sleep(1000); mHandler.post(new Runnable() { @Override public void run() { String result = "实时更新中,当前大盘指数:<font color='red'>%d</font>"; result = String.format(result, (int) (Math.random() * 3000 + 1000)); mTvServiceInfo.setText(Html.fromHtml(result)); } }); } catch (InterruptedException e) { e.printStackTrace(); } if (isUpdateInfo) { mCheckMsgHandler.sendEmptyMessageDelayed(MSG_UPDATE_INFO, 1000); } } }; } }
4.handlerThread的一些细节补充学习:
HandlerThread本质上是一个线程类,继承自Thread类,但是HandlerThread有自己的Looper对象,可以进行looper循环,不断从MessageQueue中取消息。
HandlerThread的特点
- HandlerThread将loop转到子线程中处理,说白了就是将分担MainLooper的工作量,降低了主线程的压力,使主界面更流畅。
- 开启一个线程起到多个线程的作用。处理任务是串行执行,按消息发送顺序进行处理。
相比多次使用new Thread(){…}.start()这样的方式节省系统资源。
但是由于每一个任务都将以队列的方式逐个被执行到,一旦队列中有某个任务执行时间过长,那么就会导致后续的任务都会被延迟处理。 - HandlerThread拥有自己的消息队列,它不会干扰或阻塞UI线程。
- 通过设置优先级就可以同步工作顺序的执行,而又不影响UI的初始化;
- HandlerThread本质上就是一个普通Thread,只不过内部建立了Looper
总结
HandlerThread比较适用于单线程+异步队列的场景,比如IO读写操作,耗时不多而且也不会产生较大的阻塞。对于网络IO操作,HandlerThread并不适合,因为它只有一个线程,还得排队一个一个等着