提出问题
现在这里列出几个问题:
- Handler的作用
- 为什么Android中要设计为只能在UI线程中去更新UI呢?
- Handler、Looper MessageQueue之间的关系
- 主线程如何让往子线程发消息
- 子线程中可以使用Handler吗?
- 可以在非主线程中更新UI吗?
- 使用Handle要注意什么(会引发什么问题)?
问题分析
以上几个问题如果都能回答的上来,说明关于Handle你已经基本掌握了,如果还有点模糊的话可以先看下以下两篇文章:
《Android消息处理机制》
《View的绘制流程》
ok,现在先看第一个问题:
Handler的作用
- 接收消息:在非UI线程中完成耗时操作,在UI线程中去更新UI。
- 发送消息:可以在主线程中发送延时消息。为什么Android中要设计为只能在UI线程中去更新UI呢?
- 从源码的角度来回答: 
 我们知道既然是要更新UI,那么一定会涉及到View的绘制,既然要绘制View,那么一定会调用ViewRootImpl. ()方法,requestLayout()方法,里面都会调用checkThread();里面就对当前是否是主线程进行判断。如果不是主线程,就会抛异常。- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 public void requestLayout() {//我们在自定义ViewGroup的时候,如果要更新布局就会调用这个方法刷新界面,其实就是调用了View的重新绘制流程。
 if (!mHandlingLayoutInLayoutRequest) {
 checkThread();
 mLayoutRequested = true;
 scheduleTraversals();
 }
 }
 void checkThread() {
 if (mThread != Thread.currentThread()) {//非主线程抛异常
 throw new CalledFromWrongThreadException(
 "Only the original thread that created a view hierarchy can touch its views.");
 }
 }
- 从设计思想的角度来回答: 
- 解决多线程并发问题(根本原因)在子线程中加锁也可以解决并发的问题,可是会造成UI卡顿的新问题,性能低下,所以不能加锁。
- 提高界面更新的性能问题
- 架构设计的简单说说Handler、Looper MessageQueue之间的关系 
 这里的话我举个例子来回答:在一个流水线生产车间中,整个大的机器就是Looper(一直在工作状态),而传送带就是MessageQueue,有货物(Massage)时,就会一直传送(Loop循环),机器处理这些货物(Massage)。没有货物(Massage)时就机器就关闭(主线程进入休眠状态),因为节省成本要省电(释放CPU资源),而工人(Handle)负责把货物(Massage)放到传送带上(Handler.sendMessage(Message))。
 这个例子不知道贴不贴切。。。主线程往子线程发消息我们用的最多的就是子线程向主线程发送消息,那如何让主线程给子线程发送消息呢?1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47public class MainActivity extends Activity implements OnClickListener { 
 public static final int UPDATE_TEXT = 1;
 private TextView tv;
 private Button btn;
 private Handler handler;
 
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 tv = (TextView) findViewById(R.id.tv);
 btn = (Button) findViewById(R.id.btn);
 btn.setOnClickListener(this);
 //固定写法
 new Thread(new Runnable() {
 
 public void run() {
 //1、准备Looper对象
 Looper.prepare();
 //2、在子线程中创建Handler
 handler = new Handler() {
 
 public void handleMessage(Message msg) {
 super.handleMessage(msg);
 Log.i("当前线程2", Thread.currentThread().getName());
 }
 };
 //3、调用Looper的loop()方法,取出消息对象
 Looper.loop();
 }
 }).start();
 }
 
 public void onClick(View v) {
 Log.i("当前线程1", Thread.currentThread().getName());
 switch (v.getId()) {
 case R.id.btn:
 Message msg = handler.obtainMessage();
 handler.sendMessage(msg);
 break;
 default:
 break;
 }
 }
 }
由此可见可以在子线程中创建使用Handle的,但是以下写法是不对的:1
2
3
4
5
6
7
8
9
10
11
12
13
14
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tv = (TextView) findViewById(R.id.tv);
        //尝试在子线程中去创建Handler
        new Thread(new Runnable() {
            
            public void run() {
                new Handler();
            }
        }).start();
    }
Handle.class:1
2
3
4
5mLooper = Looper.myLooper();
       if (mLooper == null) {
           throw new RuntimeException(
               "Can't create handler inside thread that has not called Looper.prepare()");
       }
以上会有 “Can’t create handler inside thread that has not called Looper.prepare()”的错误。
所以要在子线程中创建Handle的话必须要执行Looper的prepare()方法,生成Looper对象,把Looper对象和当前线程对象形成键值对(线程为键),存放在ThreadLocal当中,然后生成handler对象,调用Looper的myLooper()方法,mLooper就不会为null了,得到与Handler所对应的Looper对象。handler、looper 、消息队列就形成了一一对应的关系
可以在非主线程中更新UI吗?
可以吗?答案是可以的。要从源码的角度来分析就很好理解。
还是说回View的绘制吧,刚刚也说了View的绘制肯定会调用CheckThread()方法,不是主线程的话肯定会执行不下去的。那好,我们想想,当我们开启一个Activiy的时候,在哪个生命周期方法中开始View的绘制的,是在onCreate()吗?我们看了源码就会知道是在onRsume()中才开始绘制View的:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22// H();
 case RESUME_ACTIVITY://Acticity生命周期Resume
     Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityResume");
     SomeArgs args = (SomeArgs) msg.obj;
     handleResumeActivity((IBinder) args.arg1, true, args.argi1 != 0, true,
             args.argi3, "RESUME_ACTIVITY");
     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
     break;
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
    // 调用onResume方法
    r = performResumeActivity(token, clearHide, reason);
    // 将decorView添加到屏幕中
    View decor = r.window.getDecorView();
    decor.setVisibility(View.INVISIBLE);
    ViewManager wm = a.getWindowManager();
    WindowManager.LayoutParams l = r.window.getAttributes();
    a.mDecor = decor;
	//wm其实就是WindowManagerImpl,而在WindowManagerImpl中又调用了WindowManagerGlobal的addView方法。
	//将DecorView添加到Window中
    wm.addView(decor, l);//调用AddView,开始View的绘制
}
所以,我们只要在onResume方法之前,比如说在onCreate方法中:1
2
3
4
5
6
7
8
9
10
11
12
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);
       tv = (TextView) this.findViewById(R.id.startBtn);
           new Thread(new Runnable() {
               
               public void run() {
               tv.setText("子线程更细UI");
           }
       }).start();
   }
以上代码可直接运行。