众所周知,在android中,非ui线程中是不能更新ui,如果在子线程中做ui相关操作,可能会出现程序崩溃。一般的做法是,创建一个Message对象,然后Handler发送该message,然后在Handler的handleMessage()方法中做ui相关操作,这样就成功实现了子线程切换到主线程。
初始化Handler有两个地方,一个在主线程中,一个在子线程中,具体有什么区别呢,接下来从源码角度来讲解。
1)主线程中初始化handler
handler1= new Handler(){ @Override public void handleMessage(Message msg) { if (msg.arg1==1) { Toast.makeText(MainActivity.this,"hanlder1",Toast.LENGTH_SHORT).show(); } super.handleMessage(msg); } }; new Thread(new Runnable() { @Override public void run() { Message message = handler1.obtainMessage(); message.arg1 = 1; handler1.sendMessage(message); } }).start();
这个是大家用得很多的场景,这边不再多说了。
2)子线程中初始化handler
new Thread(new Runnable() { @Override public void run() { handler2 = new Handler(){ @Override public void handleMessage(Message msg) { if (msg.arg1==1) { Toast.makeText(MainActivity.this,"hanlder2",Toast.LENGTH_SHORT).show(); } super.handleMessage(msg); } }; Message message = handler2.obtainMessage(); message.arg1 = 1; handler2.sendMessage(message); } }).start();
如果是上面的代码,运行程序后,程序会crash,崩溃信息如下所示:
E/AndroidRuntime: FATAL EXCEPTION: Thread-50003 Process: com.example.cyf.myapplication, PID: 2223 java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare() at android.os.Handler.<init>(Handler.java:200) at android.os.Handler.<init>(Handler.java:114) at com.example.cyf.myapplication.MainActivity$3$1.<init>(MainActivity.java:0) at com.example.cyf.myapplication.MainActivity$3.run(MainActivity.java:56) at java.lang.Thread.run(Thread.java:818)
从错误的解释可以看出:没有调用Looper.prepare(),不能创建handler。所以很简单,我们在创建handler前面加上Looper.prepare(),再运行程序,果然没有错误了。
代码如下所示:
new Thread(new Runnable() { @Override public void run() { Looper.prepare(); handler2 = new Handler(){ @Override public void handleMessage(Message msg) { if (msg.arg1==1) { Toast.makeText(MainActivity.this,"hanlder2",Toast.LENGTH_SHORT).show(); } super.handleMessage(msg); } }; Message message = handler2.obtainMessage(); message.arg1 = 1; handler2.sendMessage(message); Looper.loop(); } }).start();
这种调用方式相信很多人也明白,面试的时候,也经常提到,但是为什么这样写就可以呢?
3.源码讲解1)我们先看下handler的源码中的构造函数。
public Handler() { 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 that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = null; }
可以看到第12行出现了刚刚上述的错误信息,很明显mLooper为空的时候,就会抛出如下异常。
Can't create handler inside thread that has not called Looper.prepare()
2)Looper对象什么时候为空,我们看看Looper.myLooper()中的代码了,如下所示:
public static final Looper myLooper() { return (Looper)sThreadLocal.get(); }
代码非常少,很容易理解,就是从sThreadLocal对象中取出Looper。sThreadLocal源码其实就是个数组,源码不贴了,把他想成数组就好了。sThreadLocal什么时候存在Looper对象呢,及什么时候会set一个Looper到该数组中呢,不用想,肯定是Looper.prepare()方法,我们来看下它的源码:
3)Looper.prepare()源码
public static final void prepare() { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper()); }
从上面的代码可以看出,sThreadLocal如果没有Looper,则新建Looper进去,如果存在,则抛出异常,一个线程最多只能创建一个Looper对象。
4)所以一开始调用Looper.prepare()方法,其实相当于为线程新建了一个Looper放到sThreadLocal中,这样mLooper = Looper.myLooper();则可以从sThreadLocal中获取刚刚创建的Looper,不会导致程序崩溃。
4.其他: 可能会有人说,为什么我在主线程中初始化handler的时候,没有new Looper,为什么没有报异常,相信很多人会听到别人说,主线程默认给我们创建了Looper对象,没有错。
我们看下ActivityThread的源码中的main()方法
public static void main(String[] args) { SamplingProfilerIntegration.start(); CloseGuard.setEnabled(false); Environment.initForCurrentUser(); EventLogger.setReporter(new EventLoggingReporter()); Process.setArgV0("<pre-initialized>"); Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } AsyncTask.init(); if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); } Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }
我们可以看到调用Looper.prepareMainLooper();继续看Looper.prepareMainLooper();源码
public static final void prepareMainLooper() { prepare(); setMainLooper(myLooper()); if (Process.supportsProcesses()) { myLooper().mQueue.mQuitAllowed = false; } }
从上面代码可以看到,会有prepare()方法,及主线程中会默认为我们初始化一个Looper对象,从而不需要再手动去调用Looper.prepare()方法了。
5.结论: 1)主线程中可以直接创建Handler对象。
2)子线程中需要先调用Looper.prepare(),然后创建Handler对象。
package com.example.cyf.myapplication; import android.app.Activity; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.widget.Toast; public class MainActivity extends Activity { private Handler handler1; private Handler handler2; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initHandler1(); initHandler2(); } /** * 初始化handler(主线程) */ private void initHandler1() { handler1= new Handler(){ @Override public void handleMessage(Message msg) { if (msg.arg1==1) { Toast.makeText(MainActivity.this,"hanlder1",Toast.LENGTH_SHORT).show(); } super.handleMessage(msg); } }; new Thread(new Runnable() { @Override public void run() { Message message = handler1.obtainMessage(); message.arg1 = 1; handler1.sendMessage(message); } }).start(); } /** * 初始化handler(子线程) */ private void initHandler2() { new Thread(new Runnable() { @Override public void run() { Looper.prepare(); handler2 = new Handler(){ @Override public void handleMessage(Message msg) { if (msg.arg1==1) { Toast.makeText(MainActivity.this,"hanlder2",Toast.LENGTH_SHORT).show(); } super.handleMessage(msg); } }; Message message = handler2.obtainMessage(); message.arg1 = 1; handler2.sendMessage(message); Looper.loop(); } }).start(); } }