Android面试系列文章2018之Android部分HandlerThread机制篇
Android面试系列文章2018之Android部分HandlerThread机制篇
1.HandlerThread是什么?
产生的背景:开启子线程进行耗时操作,多次创建和销毁子线程是很耗费资源的,但是木有关系,谷歌考虑了这点为我们专门开发出了HandlerThread机制,那么它有什么特点呢?请看下面。
本质:Handler + Thread + Looper,是一个Thread内部有Looper。当你被面试官问道HandlerThread是什么,有何特点,那么你应该这么回答:
a.HandlerThread本质上是一个线程类,它继承了Thread。
b.HandlerThread有自己内部的Looper对象,可以进行Looper循环。
c.通过获取HandlerThread的Looper对象传递给Handler对象,可以在handlerMessage方法中执行异步任务。
d.优点是不会有堵塞,减少对性能的消耗,缺点是不能进行多任务的处理,需要等待进行处理,处理效率较低。
e.与线程池注重并发不同,HandlerThread是一个串行队列,HandlerThread背后只有一个线程。
2.HandlerThread的使用
其实HandlerThread的使用就是为了主线程通知子线程,再由子线程通知主线程,这里面建立一套完整的通信体系,并不像传统的开启子线程,使用HandlerThread的好处就是主线程可以发送信息告诉子线程要干什么,而且可以一直发送信息,无论在主线程的何处,都可以发送合适的信息告诉子线程,它要干什么,子线程做完了信息中交代的事情,然后再通知主线程更新等操作。说白了HandlerThread提供了主线程向子线程的通信。
public class MainActivity extends AppCompatActivity { private ImageView imageView; private HandlerThread handlerThread; /** * 图片地址数组 */ private String url[]={ "图片地址1","图片地址2","图片地址3","图片地址4","图片地址5" }; private Handler uiHandler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); //加载图片 imageView.setImageBitmap((Bitmap) msg.obj); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); imageView = findViewById(R.id.iv1); handlerThread = new HandlerThread("downloadImage"); //必须开启HandlerThread线程 handlerThread.start(); //获取子Handler Handler childHandler = new Handler(handlerThread.getLooper(),new ChildCallback()); for(int i=0;i
在上面代码中,创建了两个Handler,一个用于更新UI线程的uiHandler,一个是用于异步下载图片的childHandler。最终的结果是childHandler会每个隔1秒钟通过sendEmptyMessageDelayed方法去通知ChildCallback的回调函数handleMessage方法去下载图片并告诉mUIHandler去更新UI界面,以上便是HandlerThread常规使用。
3.HandlerTread机制原理
下面我们通过源码来分析一下HandlerThread:
public class HandlerThread extends Thread { int mPriority; int mTid = -1; Looper mLooper; public HandlerThread(String name) { super(name); mPriority = Process.THREAD_PRIORITY_DEFAULT; } public HandlerThread(String name, int priority) { super(name); mPriority = priority; } protected void onLooperPrepared() { } @Override public void run() { mTid = Process.myTid(); Looper.prepare(); synchronized (this) { mLooper = Looper.myLooper(); notifyAll(); //唤醒其他线程的等待锁,这边是为了唤醒getLooper()方法中的等待锁 } Process.setThreadPriority(mPriority); onLooperPrepared(); Looper.loop(); mTid = -1; } public Looper getLooper() { if (!isAlive()) { return null; } // 线程开始,该方法会一直等待Looper对象创建完成才会执行, synchronized (this) { while (isAlive() && mLooper == null) { try { wait(); } catch (InterruptedException e) { } } } return mLooper; } public boolean quit() { Looper looper = getLooper(); if (looper != null) { looper.quit(); return true; } return false; } public boolean quitSafely() { Looper looper = getLooper(); if (looper != null) { looper.quitSafely(); return true; } return false; } public int getThreadId() { return mTid; }}
首先我们看一下它的run()方法,可以发现该方法中调用Looper.myLooper()创建了一个Looper对象mLooper,并把该对象放到线程变量sThreadLocal中,然后通过调用Looper.loop()开启消息循环,Looper.loop()方法会不断循环从MessageQueue中取出消息处理消息,没有消息是则会阻塞。getLooper()方法是获取线程中的Looper对象,可以用来初始化Handler对象。
quit()和quitSafely()的区别在于,quit()会清空所有消息(无论延时或非延时),quitSafely()只清空延时消息,无论是调用了quit()方法还是quitSafely()方法,Looper就不再接收新的消息。即在调用了Looper的quit()或quitSafely()方法之后,消息循环就终结了,这时候再通过Handler调用sendMessage或post等方法发送消息时均返回false,线程也就结束了。
更多相关文章
- eclipse adb 启动失败,无法匹配android设备 的解决方案
- Android的两种数据存储方式分析(二)
- Instrumentation学习基础(一)
- android 异步获取图片
- ADB连接Android设备的三种方法
- 实战技巧:Android异步指南
- android 常用控件的使用 TextView EditTextView
- 浅谈Java中Collections.sort对List排序的两种方法
- Python list sort方法的具体使用