1.子线程创建handler对象
public class MainActivity extends AppCompatActivity {
private Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
}
private void init() {
new Thread(){
@Override
public void run() {
handler=new Handler();
}
}.start();
}
}
抛出异常:
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.administrator.handlertest.MainActivity$1.run(MainActivity.java:20)
也就是说在子线程创建handler对象必须先执行Looper.prepare方法,那就从源码上看看这个方法做了什么
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));
}
这个方法中,有两个重要的步骤。
1.创建了一个Looper对象
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
可以看到这个构造方法中,初始化了一个消息队列MessageQueue和当前Threade对象。
2.将Looper对象set进sThreadLocal对象当中。那首先看一下sThreadLocal对象的作用是什么。
ThreadLocal从字面意义上理解是“本地线程”,但实际上并不是用来操作什么本地线程而是用于实现不同线程的数据副本,当使用ThreadLocal维护变量时,它会为每个使用该变量的线程提供独立的变量副本;每一个线程都可以独立地改变自己的副本并且不会影响其他线程所持有的对应的副本。sThreadLocal对象是存在Looper类中的static final对象:
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
也就是这个方法的作用是当在某一个Thread中中执行Looper.prepare()方法的时候系统就会将与该Thread所对应的Looper保存到sThreadLocal中,不同的线程对应着不同的Looper,
,但他们均由系统保存在sThreadLocal中,并且互不影响,相互独立,并且可以通过sThreadLocal.get()方法获取不同线程所对应的Looper。
小结:一个线程对应一个Looper对象,一个Looper对象对应一个消息队列,三者一一对应。在子线程创建handler对象的正确步骤:
new Thread(){ @Override public void run() { Looper.prepare(); handler=new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); } }; Looper.loop();//开始轮询消息队列 } }.start();