很多时候在项目中我们会用到一些独立的类,用来处理通用的逻辑,也有可能会牵扯到切换到UI线程进行比如Toast提示,不经意间会在当前类中new Handler,比如像下面这个, 假设A是一个单例模式,在整个应用生命周期都存在着:
public class A {
private static A sInstance;
Handler mHandler = new Handler();
private A(){}
public static A getsInstance() {
if (sInstance == null) {
synchronized (A.class) {
if (sInstance == null) {
sInstance = new A();
}
}
}
return sInstance;
}
}
这段代码看上去没有什么特别,如果A这个类在主线程进行实例化是不会有什么问题,但如果在一个子线程进行实例化就会报错提示Can't create handler inside thread that has not called Looper.prepare() 说的是不能在主线程来创建Handler,咱们根据这个异常提示看一下Handler源码
public class Handler {
/**
* Default constructor associates this handler with the {@link Looper} for the
* current thread.
*
* If this thread does not have a looper, this handler won't be able to receive messages
* so an exception is thrown.
*/
public Handler() {
this(null, false);
}
/**
* Constructor associates this handler with the {@link Looper} for the
* current thread and takes a callback interface in which you can handle
* messages.
*
* If this thread does not have a looper, this handler won't be able to receive messages
* so an exception is thrown.
*
* @param callback The callback interface in which to handle messages, or null.
*/
public Handler(Callback callback) {
this(callback, false);
}
/**
* Use the provided {@link Looper} instead of the default one.
*
* @param looper The looper, must not be null.
*/
public Handler(Looper looper) {
this(looper, null, false);
}
/**
* Use the provided {@link Looper} instead of the default one and take a callback
* interface in which to handle messages.
*
* @param looper The looper, must not be null.
* @param callback The callback interface in which to handle messages, or null.
*/
public Handler(Looper looper, Callback callback) {
this(looper, callback, false);
}
/**
* Use the {@link Looper} for the current thread
* and set whether the handler should be asynchronous.
*
* Handlers are synchronous by default unless this constructor is used to make
* one that is strictly asynchronous.
*
* Asynchronous messages represent interrupts or events that do not require global ordering
* with represent to synchronous messages. Asynchronous messages are not subject to
* the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
*
* @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
* each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
*
* @hide
*/
public Handler(boolean async) {
this(null, async);
}
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 that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
}
我们可以清楚地看到,它有几个构造方法,最终的调用会到一起,问题就出现在if (mLooper == null)之处,由于我们在子线程来构造Handler实例,所以Looper.myLooper()返回为空,如果在主线程,那么返回的则Looper.myLooper()不会为空,那么我们有没有其它办法可以使得我们Handler实例化时不会报错呢,通过刚才的几个构造方法中我们已经查觉到有一种构造方法是接收Looper参数,那么我们能不能给它传入一个Looper对象呢,答案是可以的,如果查阅Looper的API会发现它可以返回主线程的Looper对象Looper.getMainLooper(), 接下来我们这么改动
public class A {
Handler mHandler = new Handler(Looper.getMainLooper());
}
public class LogoActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
new Thread() {
public void run() {
A a = new A();
}
}.start();
}
}
这样就不会再报错了,当然还有其它方式来初始化这个Handler,需要读者自己去看