1. 概述
在子线程更新UI程序会崩溃,解决方案就是:创建 Message对象,然后用handler发送出去,然后调用handler的handleMessage(Message msg),从中取出msg对象,在这里更新UI即可,这种方式叫做 异步消息处理线程。
2. 创建handler对象
下边分别在主线程和子线程中创建handler对象:
public class MainActivity extends AppCompatActivity {
private Handler handler1 ;
private Handler handler2 ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 方式 1:在主线程中创建handler
handler1 = new Handler() ;
// 方式 2:在子线程中创建handler
new Thread(new Runnable() {
@Override
public void run() {
handler2 = new Handler() ;
}
}).start();
}
}
运行结果:
在子线程中创建的handler崩溃,报错信息为Can't create handler inside thread that has not called Looper.prepare() :意思是 没有调用Looper.prepare()禁止创建handler,也就是说在子线程中创建handler对象,先调用 Looper.prepare()方法:就可以
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
handler2 = new Handler() ;
}
}).start();
3. handler源码分析
1>:为什么在子线程中 不调用 Looper.prepare() ,创建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());
}
}
// 获取looper对象,如果 looper 对象为空,则抛出异常,异常就是上边程序报错的错误
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 = callback;
mAsynchronous = async;
}
什么时候 looper对象为空,可以看下 Looper.myLooper():
public static @Nullable Looper myLooper() {
// 从 sThreadLocal 中取出 looper 对象,如果 sThreadLocal 中存在 looper对象,就返回,
// 如果不存在,就返回空
return sThreadLocal.get();
}
到这里应该可以想到,是在 Looper.prepare()方法中获取的 looper 对象,就是在子线程中创建 handler 对象之前 调用的 Looper.prepare()方法:
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中是否存在 looper,如果为null,就 new Looper对象设置进去
sThreadLocal.set(new Looper(quitAllowed));
}
到这里就解释了:
第一:在子线程中创建 handler 之前,必须要先调用 Looper.prepare()来获取 looper 对象,如果 looper 对象为null,那么在 handler 构造方法 就会报错:Can't create handler inside thread that has not called Looper.prepare();
第二:一个线程中只有一个 looper 对象;
2>:为什么在 主线程中 不用调用 Looper.prepare(),不会崩溃?
原因是:在程序一启动的时候,系统已经自动帮我们调用了 Looper.prepare()方法了,在 ActivityThread中的 main() 方法:
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
SamplingProfilerIntegration.start();
CloseGuard.setEnabled(false);
// 这个方法调用 Looper.prepare() 方法,如下
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
Looper.loop();
}
public static void prepareMainLooper() {
// 这个就是 Looper.prepare()方法
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
可以看到,应用程序主程序中始终存在 looper 对象,不用手动调用 Looper.prepare()方法;
4. 总结
1>:创建handler有2种方式:主线程和子线程;
主线程中可以直接创建handler对象,不用调用 Looper.prepare()方法,因为程序一启动系统就帮我们调用了该方法;
子线程中必须先调用 Looper.prepare(),然后才能创建 handler 对象,否则直接报错;
网友评论