面试宝典Android面试问题专题分享几大机制

Android开发你应该懂的:Handler

2017-07-15  本文已影响197人  606fd5f5448c
Android开发你应该懂的

1.什么是Handler?

Handler是Android给我们提供用来更新UI的一套机制,也是一套消息处理的机制,我们可以通过它发送消息,也可以通过它处理消息。

2.Handler的工作原理是什么?

Android中的异步消息处理涉及以下几个概

Handler
Looper
Message
MessageQueue

1.Looper

Looper主要有prepare()和loop()两个方法
一个线程中只有一个Looper实例
在线程中必须先调用Looper.prepare()方法,才能创建Handler对象loop()方法,不断从MessageQueue中去取消息,交给消息的target属性的dispatchMessage去处理

2.Message

Message是在线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程之间交换数据

3. MessageQueue

MessageQueue是由Looper创建的一个消息队列,它主要是用于存放所有的Handler发送的消息。这部分消息会一直存在于消息队列中,等待被处理。每个线程中只有一个MessageQueue对象。它被创建后,Looper进入一个无限循环体不断从该MessageQueue中读取消息。

4. Handler

当应用程序启动时,Android首先会开启一个主线程 (也就是UI线程) , 主线程管理界面中的UI控件, 进行事件分发, 比如说, 你要是点击一个 Button ,Android会分发事件到Button上,来响应你的操作。 如果此时需要一个耗时的操作,例如: 联网读取数据,或者读取本地较大的一个文件的时候,你不能把这些操作放在主线程中,如果你放在主线程中的话,界面会出现假死现象, 如果5秒钟还没有完成的话,会收到Android系统的一个错误提示 "强制关闭"。 这个时候我们需要把这些耗时的操作,放在一个子线程中,因为子线程涉及到UI更新,Android主线程是线程不安全的, 也就是说,更新UI只能在主线程中更新,子线程中操作是危险的。 这个时候,Handler就出现了。

5. 那么它们是怎么合作完成复杂的异步消息处理机制的呢
Handler.png

从图中我们可以清晰的看出Handler的工作原理,Handler是运行在主线程中(UI线程中), 它与子线程可以通过Message对象来传递数据。Handler可以分发Message对象和Runnable对象到主线程中, 每个Handler实例,都会绑定到创建他的线程中(一般是位于主线程),它有两个作用:

(1)安排消息或Runnable 在某个主线程中某个地方执行;
(2)安排一个动作在不同的线程中执行。

Handler中有以下一些分发消息的方法:

post(Runnable)
postAtTime(Runnable,long)
postDelayed(Runnable long)
sendEmptyMessage(int)
sendMessage(Message)
sendMessageAtTime(Message,long)
sendMessageDelayed(Message,long)

以上post类方法允许你排列一个Runnable对象到主线程队列中,
sendMessage类方法, 允许你安排一个带数据的Message对象到队列中,等待更新。
代码下发如下:

public class MyHandlerActivity extends Activity { 
    Button btnHandler; 
    MyHandler myHandler; 
 
    protected void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState); 
        setContentView(R.layout.myhandler); 
 
        btnHandler = (Button) findViewById(R.id.btnHandler); 
        myHandler = new MyHandler(); 
        // 当创建一个新的Handler实例时, 它会绑定到当前线程和消息的队列中,开始分发数据 
        // Handler有两个作用, (1) : 定时执行Message和Runnalbe 对象 
        // (2): 让一个动作,在不同的线程中执行。 
 
        // 它安排消息,用以下方法 
        // post(Runnable) 
        // postAtTime(Runnable,long) 
        // postDelayed(Runnable,long) 
        // sendEmptyMessage(int) 
        // sendMessage(Message); 
        // sendMessageAtTime(Message,long) 
        // sendMessageDelayed(Message,long) 
      
        // 以上方法以 post开头的允许你处理Runnable对象 
        //sendMessage()允许你处理Message对象(Message里可以包含数据,) 
 
        MyThread m = new MyThread(); 
        new Thread(m).start(); 
    } 
 
    /** 
    * 接受消息,处理消息 ,此Handler会与当前主线程一块运行 
    * */ 
 
    class MyHandler extends Handler { 
        public MyHandler() { 
        } 
 
        public MyHandler(Looper L) { 
            super(L); 
        } 
 
        // 子类必须重写此方法,接受数据 
        @Override 
        public void handleMessage(Message msg) { 
            // TODO Auto-generated method stub 
            Log。d("MyHandler", "handleMessage。。。。。。"); 
            super.handleMessage(msg); 
            // 此处可以更新UI 
            Bundle b = msg.getData(); 
            String color = b.getString("color"); 
            MyHandlerActivity.this.btnHandler.append(color); 
 
        } 
    } 
 
    class MyThread implements Runnable { 
        public void run() { 
 
            try { 
                Thread.sleep(10000); 
            } catch (InterruptedException e) { 
                // TODO Auto-generated catch block 
                e.printStackTrace(); 
            } 
 
             Message msg = new Message(); 
            Bundle b = new Bundle();// 存放数据 
            b.putString("color", "我的"); 
            msg.setData(b); 
 
            MyHandlerActivity.this.myHandler.sendMessage(msg); // 向Handler发送消息,更新UI 
 
        } 
    } 
} 


另外除了发送消息之外,我们还有以下几种方法可以在子线程中进行UI操作:

  1. Handler的post()方法
  1. View的post()方法
  2. Activity的runOnUiThread()方法

需要注意的是:

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);  
        handler1 = new Handler();  
        new Thread(new Runnable() {  
            @Override  
            public void run() {  
                handler2 = new Handler();  
            }  
        }).start();  
    }  
  
}

运行上面的代码,程序就会崩溃,提示的错误信息为

 Can't create handler inside thread that has not called Looper.prepare() 

崩溃的原因是在子线程创建了Handler,而没有调用Looper.prepare(), 上面我们也讲过是因为在线程中必须先调用Looper.prepare()方法,才能创建Handler对象loop()方法
Google文档写法如下:

class LooperThread extends Thread {
      public Handler mHandler;

      public void run() {
          Looper.prepare();

          mHandler = new Handler() {
              public void handleMessage(Message msg) {
                  // process incoming messages here
              }
          };

          Looper.loop();
      }
  }

那么问题就来了

为什么主线程中的Handler也没有调用Looper.prepare()方法,就没有崩溃呢
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,然后thread.getHandler(),保存了主线程的Handler,最后Looper.loop();进入消息循环。这样就可以解释我们上面的疑问了。

3.Handler和AsycnTask有什么关系呢

GoogleDeveloper解释如下:

AsyncTask is designed to be a helper class around Thread
 and Handler and does not constitute a generic threading framework. AsyncTasks should ideally be used for short operations (a few seconds at the most.) If you need to keep threads running for long periods of time

意思就是AsyncTask是围绕Thread和Handler设计的辅助类,理想情况下,AsyncTasks应用于短操作(最多几秒钟)
也就是AsyncTask会自动帮我们创建一个线程,执行一个耗时操作,并随时报告执行进度给UI线程,执行完成后将结果报告给UI线程

AsyncTask有四个重要方法,当一个异步任务被执行时,要经历四步:

1.onPreExecute(),在UI线程中执行,它会在异步任务开始前执行,一般用来设置任务参数;
2.doInBackground, 最重要的方法,在子线程中执行(事实上,只有它在子线程中执行,其他方法都在UI线程中执行)。当onPreExecute结束后,本方法立刻执行,它用来进行后台的耗时计算,异步任务的参数会被传给它,执行完成的结果会被送给第四步;执行途中,它还可以调用publishProgress 方法来通知UI线程当前执行的进度;
3.onProgressUpdate, 当publishProgress 被调用后,它在UI线程中执行,刷新任务进度,一般用来刷新进度条等UI部件;
4.onPostExecute, 当后台的异步任务完成后,它会在UI线程中被调用,并获取异步任务执行完成的结果。

如有错误和遗漏,欢迎指正

上一篇 下一篇

猜你喜欢

热点阅读