前言
Handler消息处理机制在Android开发中起着举足轻重的作用,我们有必要好好理解下其原理,下面我们先从一个简单的例子出发
一、日常使用
假设我们有这么一个需要,请求网络然后将图片展示出来,我们知道网络请求是不允许在主线程执行的,而UI是不能在子线程(具体是不允许在非创建UI的原始线程)更新的,因此我们需要在子线程请求网络获得了数据以后再切换回主线程更新UI,这个例子中Handler就是起着切换线程的作用,下面的代码演示了这个例子
class MainActivity : AppCompatActivity() {
private lateinit var mImageView: ImageView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mImageView = findViewById(R.id.iv)
loadImage()
}
private fun loadImage() {
Thread {
val url = URL("https://img-my.youkuaiyun.com/uploads/201309/01/1378037235_7476.jpg")
val conn = url.openConnection()
val bitmap = BitmapFactory.decodeStream(conn.inputStream)
runOnUiThread {
mImageView.setImageBitmap(bitmap)
}
}.start()
}
}
咦!,说好的Handler去哪了?这里的runOnUIThread方法内部实现其实就是利用了Handler,我们来看看它的源码
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
该方法首先判断了当前线程是否是主线程,如果不是主线程就调用mHandler.post()
,如果当前线程就是主线程就直接运行,下面我们来分析看看Handler的原理
二、Handler原理
要想分析Handler的原理,我们先从Handler的创建过程开始分析
Handler的创建
Activity的这个mHandler是怎么来的呢?原来mHandler是Activity的成员变量,在Activity实例创建的时候就创建了
final Handler mHandler = new Handler();
接着看看Handler的构造方法
public Handler() {
this(null, false);
}
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
// 代表发送的消息是否是异步的
mAsynchronous = async;
}
首先判断了该Handler派生类是否是非静态内部类,如果是的话就打印出日志提示可能导致内存泄露,然后调用了Looper.myLooper
获取到当前线程的Looper对象,如果当前线程没有Looper就会抛出异常,最后将Looper中的MessageQueue对象赋值给mQueue,callback赋值给mCallback,aync赋值给mAsynchronous,我们来看看Looper.myLooper
做了些什么
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
从ThreadLocal里面去Looper,那么是在哪里把Looper设置到ThreadLocal里面去的呢?其实Looper提供了prepare
方法来创建当前线程的Looper,我们来看看代码
public static void prepare() {
// 表示允许退出循环
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
只有在当前线程拿不到Looper的时候才会去创建Looper对象并将其设置到ThreadLocal中去,不然就抛出异常说一个线程只能拥有一个Looper,继续看看Looper的构造方法</