Android开发你应该懂的:Handler

作者: 606fd5f5448c | 来源:发表于2017-07-15 14:16 被阅读197次
    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线程中被调用,并获取异步任务执行完成的结果。

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

    相关文章

      网友评论

        本文标题:Android开发你应该懂的:Handler

        本文链接:https://www.haomeiwen.com/subject/mwnnhxtx.html