zoukankan      html  css  js  c++  java
  • android之handler机制深入解析

    一、android中需要另开线程处理耗时、网络的任务,但是有必须要在UI线程中修改组件。这样做是为了:

      ①只能在UI线程中修改组件,避免了多线程造成组件显示混乱

      ②不使用加锁策略是为了提高性能,因为android中经常使用多线程。

    handler就是为了解决在多个线程之间的通信问题

    二、基本使用:

      1 package com.dqxst.first.multithread;
      2 
      3 import android.app.Activity;
      4 import android.os.Bundle;
      5 import android.os.Handler;
      6 import android.os.Handler.Callback;
      7 import android.os.Message;
      8 import android.util.Log;
      9 import android.view.View;
     10 import android.widget.TextView;
     11 
     12 import com.dqxst.first.R;
     13 
     14 public class HandlerActivity extends Activity {
     15     private TextView tv,msg_tv;
     16     private Handler handler = new Handler();
     17     final Handler msgHandler=new Handler(new Callback() {
     18         @Override
     19         public boolean handleMessage(Message msg) {
     20             //!!!在这里可以进行一些操作,返回true则将msg进行拦截
     21             return false;
     22         }
     23     }){
     24         @Override
     25         public void handleMessage(Message msg) {
     26             switch(msg.what){
     27             case 200:
     28                 msg_tv.setText("网络请求成功.");
     29                 break;
     30             case 404:
     31                 msg_tv.setText("not found!");
     32                 break;
     33             }
     34             super.handleMessage(msg);
     35         }
     36         
     37     };
     38     private MyRunnable runnable = new MyRunnable();
     39 
     40     @Override
     41     protected void onCreate(Bundle savedInstanceState) {
     42         super.onCreate(savedInstanceState);
     43         setContentView(R.layout.activity_handler);
     44         init();
     45 
     46         Log.i("main", "主线程=" + Thread.currentThread().toString());
     47     }
     48 
     49     class MyRunnable implements Runnable {
     50         @Override
     51         public void run() {
     52             int time = Integer.parseInt(tv.getText().toString());
     53             int now = time + 1;
     54             Log.i("main", "处理UI更新线程=" + Thread.currentThread().toString());
     55             tv.setText(now + "");
     56             //这里是将Runnable间隔1秒重新发送到消息队列中,所以是定时操作的效果
     57             handler.postDelayed(runnable, 1000);
     58         }
     59 
     60     }
     61 
     62     //下面主要是在子线程中调用post将消息加入到消息队列中
     63     public void startCallback(View view) {
     64         new Thread() {
     65             public void run() {
     66                 try {
     67                     Thread.sleep(1000);
     68                 } catch (InterruptedException e) {
     69                     e.printStackTrace();
     70                 }
     71 
     72                 Log.i("main", "新线程=" + Thread.currentThread().toString());
     73 
     74                 handler.post(runnable);
     75             }
     76         }.start();
     77     }
     78 
     79     //这里是移除消息中的执行体部分,就是那个Runnable
     80     public void stopCallback(View view){
     81         handler.removeCallbacks(runnable);
     82     }
     83 
     84     public void msgChange(View view){
     85         new Thread(new Runnable() {
     86             @Override
     87             public void run() {
     88                 Message message=Message.obtain();
     89                 if(Math.random()>0.3){
     90                     message.what=200;
     91                 }else{
     92                     message.what=404;
     93                 }
     94                 msgHandler.sendMessage(message);
     95             }
     96         }).start();
     97     }
     98     
     99     private void init() {
    100         tv = (TextView) findViewById(R.id.handler_tv);
    101         tv.setText("0");
    102         msg_tv=(TextView) findViewById(R.id.handler_tv_message);
    103     }
    104 }
    handler基本使用

      由上例可以看到,handler使用有两种形式:

      ①post/postDelayed:这时传递的是一个Runnable对象,会封装到一个Message对象中发送到消息队列中

      ②sendMessage:这时需要重写handler的handleMessage或者在构造时传入一个实现Callback接口的对象(该对象的方法也是前面重写的方法名一样,但是先执行,可以用来对消息进行拦截)

    三、源码分析:与handler机制有关的类有Looper、MessageQueue、Message以及Handler本身。

      1、Looper:主要有两个作用

        ①创建一个与当前线程相关的Looper实例,通过调用prepare()

     1    public static void prepare() {
     2         prepare(true);
     3     }
     4 
     5     private static void prepare(boolean quitAllowed) {
     6         if (sThreadLocal.get() != null) {
     7             throw new RuntimeException("Only one Looper may be created per thread");
     8         }
     9         sThreadLocal.set(new Looper(quitAllowed));
    10     }
    prepare

        ②不断的读取MessageQueue中的消息,通过调用loop(),MessageQueue是和Looper相关的,由其创建和管理。注意:这个过程是阻塞的。

     1     public static void loop() {
     2         final Looper me = myLooper();
     3         if (me == null) {
     4             throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
     5         }
     6         final MessageQueue queue = me.mQueue;
     7 
     8         // Make sure the identity of this thread is that of the local process,
     9         // and keep track of what that identity token actually is.
    10         Binder.clearCallingIdentity();
    11         final long ident = Binder.clearCallingIdentity();
    12 
    13         //这里是一个死循环,读取message
    14         for (;;) {
    15             Message msg = queue.next(); // might block
    16             //!!!这里注意:通常不会执行这一句,除非使用quit退出Looper
    17             if (msg == null) {
    18                 // No message indicates that the message queue is quitting.
    19                 return;
    20             }
    21 
    22             // This must be in a local variable, in case a UI event sets the logger
    23             Printer logging = me.mLogging;
    24             if (logging != null) {
    25                 logging.println(">>>>> Dispatching to " + msg.target + " " +
    26                         msg.callback + ": " + msg.what);
    27             }
    28             
    29             //这里是一个重点,说明了消息的具体执行方法
    30             msg.target.dispatchMessage(msg);
    31 
    32             if (logging != null) {
    33                 logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
    34             }
    35 
    36             // Make sure that during the course of dispatching the
    37             // identity of the thread wasn't corrupted.
    38             final long newIdent = Binder.clearCallingIdentity();
    39             if (ident != newIdent) {
    40                 Log.wtf(TAG, "Thread identity changed from 0x"
    41                         + Long.toHexString(ident) + " to 0x"
    42                         + Long.toHexString(newIdent) + " while dispatching to "
    43                         + msg.target.getClass().getName() + " "
    44                         + msg.callback + " what=" + msg.what);
    45             }
    46 
    47             msg.recycleUnchecked();
    48         }
    49     }
    loop

      2、Message:就是消息对象,一般使用都只是用来传递数据(从网络获取的)等,常用属性有

        ①what    :int类型,用于标识不同的消息,在handler中使用

        ②arg1,arg2  :int类型,用于传递int型的数据

        ③obj     :Object类型,用于传递对象

        ④data       :Bundle类型,

        创建Message有一些需要注意的点:

        ①使用Message.obtain()或者Handler.obtainMessage()进行创建而不是new,可以利用公共池中对象避免重新分配空间。

        ②一个Message是和一个handler关联的,表现为Message中的target属性,但是通常这个属性是由系统自动赋值的,就是那个发送该消Message到MessageQueue的handler

      3、Handler:主要作用有以下三点

        ①关联所在线程的Looper对象和其所管理的MessageQueue对象,这是在构造函数中体现的,最终都会调用以下两个构造函数

     1     public Handler(Callback callback, boolean async) {
     2         if (FIND_POTENTIAL_LEAKS) {
     3             final Class<? extends Handler> klass = getClass();
     4             if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
     5                     (klass.getModifiers() & Modifier.STATIC) == 0) {
     6                 Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
     7                     klass.getCanonicalName());
     8             }
     9         }
    10 
    11         mLooper = Looper.myLooper();
    12         if (mLooper == null) {
    13             throw new RuntimeException(
    14                 "Can't create handler inside thread that has not called Looper.prepare()");
    15         }
    16         mQueue = mLooper.mQueue;
    17         mCallback = callback;
    18         mAsynchronous = async;
    19     }
    20 
    21 
    22     public Handler(Looper looper, Callback callback, boolean async) {
    23         mLooper = looper;
    24         mQueue = looper.mQueue;
    25         mCallback = callback;
    26         mAsynchronous = async;
    27     }
    Handler构造函数

         !!!注意:在构造函数中,必须有一个Looper对象与其绑定,因为handler要将Message发送到MessageQueue中,而MessageQueue是由Looper创建的

        ②发送消息到上面关联的消息队列,可以使用post和sendMessage两种类型方法。可以看到他们内部的调用过程是一样的。最终都是调用MessageQueue中的方法将消息绑定到一个链表上。  

     1     public final boolean post(Runnable r)
     2     {
     3        return  sendMessageDelayed(getPostMessage(r), 0);
     4     }
     5 
     6     public final boolean postDelayed(Runnable r, long delayMillis)
     7     {
     8         return sendMessageDelayed(getPostMessage(r), delayMillis);
     9     }
    10     //这是上面用到的将Runnable封装到Message中的方法
    11     private static Message getPostMessage(Runnable r) {
    12         Message m = Message.obtain();
    13         m.callback = r;
    14         return m;
    15     }
    16 
    17 
    18     public final boolean sendMessage(Message msg)
    19     {
    20         return sendMessageDelayed(msg, 0);
    21     }
    发送消息
     1     public final boolean sendMessageDelayed(Message msg, long delayMillis)
     2     {
     3         if (delayMillis < 0) {
     4             delayMillis = 0;
     5         }
     6         return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
     7     }
     8 
     9 
    10     public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    11         MessageQueue queue = mQueue;
    12         if (queue == null) {
    13             RuntimeException e = new RuntimeException(
    14                     this + " sendMessageAtTime() called with no mQueue");
    15             Log.w("Looper", e.getMessage(), e);
    16             return false;
    17         }
    18         return enqueueMessage(queue, msg, uptimeMillis);
    19     }
    20 
    21 
    22     private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    23         msg.target = this;
    24         if (mAsynchronous) {
    25             msg.setAsynchronous(true);
    26         }
    27         return queue.enqueueMessage(msg, uptimeMillis);
    28     }
    进一步的调用过程

        ③在Looper.loop()的循环读取过程中,读取的Message将通过msg.target.dispatchMessage(msg);交由Handler中的dispatchMessage()进行处理。

     1     public void dispatchMessage(Message msg) {
     2         if (msg.callback != null) {
     3             handleCallback(msg);
     4         } else {
     5             if (mCallback != null) {
     6                 if (mCallback.handleMessage(msg)) {
     7                     return;
     8                 }
     9             }
    10             handleMessage(msg);
    11         }
    12     }
    dispatchMessage

        这里就说明了Handler处理消息有三种方式,需要说明的是:

          a、msg.callback就是通过post/postDelayed或者构造函数传入的Runnable对象

          b、mCallback就是通过构造函数传入的实现其内部接口的对象,该方法返回true则退出,否则向下执行

    四、扩展:

      0、HandlerThread:在上面的基本使用中,是创建一个子线程,然后在子线程中通过主线程中的handler实例向主线程的MessageQueue中发送消息。而该类的实现是在子线程中开启一个Looper,然后在使用时将handler与该子线程中的Looper/MessageQueue进行绑定,即可通过handler将消息发送到子线程中进行处理,避免了主线程的阻塞。通过上述的描述,该类的使用只需简单的两步即可:

        ①实例化一个线程对象并开启该线程:HandlerThread downThread=new HandlerThread("ThreadName");downThread.start();

        ②将Handler与该线程的Looper绑定:Handler handler=new Handler(downThread.getLooper());

      1、在子线程中使用Handler(这里指的是在子线程中创建Handler实例来接受和Message)。通过上面的说明我们知道,Handler创建之前必须有一个该线程的Looper对象被创建。在主线程中直接使用是因为已经由系统进行创建了,此时在创建则会出错。下面是android中给出的标准使用方法:

     1 class LooperThread extends Thread {
     2         public Handler mHandler;
     3   
     4         public void run() {
     5             Looper.prepare();
     6   
     7             mHandler = new Handler() {
     8                 public void handleMessage(Message msg) {
     9                     // process incoming messages here
    10                 }
    11             };
    12 
    13             Looper.loop();
    14         }
    15     }
    LooperThread

      2、上面说主线程不能再创建Looper,这是因为主线程被初始化是就是一个Looper线程,各种消息都是通过Handler机制处理的。

      3、AsyncTask:异步消息处理机制,在内部封装了Handler处理流程。

        ①首先是android推荐的标准使用方式:创建一个子类,然后调用execute()即可

     1 private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
     2      protected Long doInBackground(URL... urls) {
     3          int count = urls.length;
     4          long totalSize = 0;
     5          for (int i = 0; i < count; i++) {
     6              totalSize += Downloader.downloadFile(urls[i]);
     7              publishProgress((int) ((i / (float) count) * 100));
     8              // Escape early if cancel() is called
     9              if (isCancelled()) break;
    10          }
    11          return totalSize;
    12      }
    13 
    14      protected void onProgressUpdate(Integer... progress) {
    15          setProgressPercent(progress[0]);
    16      }
    17 
    18      protected void onPostExecute(Long result) {
    19          showDialog("Downloaded " + result + " bytes");
    20      }
    21  }
    DownloadFilesTask

        这里简要说明三个泛型参数的作用:

          a、Params:就是后台执行需要的参数,例如下载时需要url

          b、Progress:就是任务执行的进度,在onProgressUpdate(Progress...)中会被使用,

            c、Result:就是任务完成返回的结果,在onPostExecute(Result)中进行处理。

           ②源码解析:首先说明,Handler机制就是在子线程中处理任务,通过Handler将需要处理的消息传递到与其绑定的Looper的消息队列中,再由Looper取出交由Handler处理。

          a、任务在子线程中处理,这里就是通过线程池处理,而任务就是doInBackground()中的部分,该函数被放在线程池的工作任务之中

     1    public AsyncTask() {
     2         mWorker = new WorkerRunnable<Params, Result>() {
     3             public Result call() throws Exception {
     4                 mTaskInvoked.set(true);
     5 
     6                 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
     7                 //noinspection unchecked
     8 //!!!重点:doInBackground()在这里被封装
     9                 Result result = doInBackground(mParams);
    10                 Binder.flushPendingCommands();
    11                 return postResult(result);
    12             }
    13         };
    14 
    15         mFuture = new FutureTask<Result>(mWorker) {
    16             @Override
    17             protected void done() {
    18                 try {
    19                     postResultIfNotInvoked(get());
    20                 } catch (InterruptedException e) {
    21                     android.util.Log.w(LOG_TAG, e);
    22                 } catch (ExecutionException e) {
    23                     throw new RuntimeException("An error occurred while executing doInBackground()",
    24                             e.getCause());
    25                 } catch (CancellationException e) {
    26                     postResultIfNotInvoked(null);
    27                 }
    28             }
    29         };
    30     }
    AsyncTask构造函数

            调用execute()就是用默认线程池来进行处理任务

     1     @MainThread
     2     public final AsyncTask<Params, Progress, Result> execute(Params... params) {
     3         return executeOnExecutor(sDefaultExecutor, params);
     4     }
     5 
     6 
     7     public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
     8             Params... params) {
     9         if (mStatus != Status.PENDING) {
    10             switch (mStatus) {
    11                 case RUNNING:
    12                     throw new IllegalStateException("Cannot execute task:"
    13                             + " the task is already running.");
    14                 case FINISHED:
    15                     throw new IllegalStateException("Cannot execute task:"
    16                             + " the task has already been executed "
    17                             + "(a task can be executed only once)");
    18             }
    19         }
    20 
    21         mStatus = Status.RUNNING;
    22 
    23         onPreExecute();
    24 
    25         mWorker.mParams = params;
    26         exec.execute(mFuture);
    27 
    28         return this;
    29     }
    线程池处理

            下面是这个线程池的本身部分,但是要注意:这是默认的情况,即调用execute()时的默认处理,默认处理是指:1、通过执行器模仿单一线程池的效果,即多个任务(一个AsyncTask实例的一次execute()创建一个任务)时只能有一个在线程中执行,2、而且线程中只能有128个任务(这是由阻塞队列的大小限制的)

                       其实这是升级之后的效果,我们可以通过执行executeOnExecutor(Executor exec, Params... params)而不是execute()传入定制的线程池来解决上面问题

     1 //这是一个执行器,负责添加任务到一个ArrayDeque队列中,并且将第一个任务分配给线程池处理。注意下面的execute是synchronized的,说明一次只能有一个线程在处理一个任务。
     2     private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
     3     public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
     4     private static class SerialExecutor implements Executor {
     5         final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
     6         Runnable mActive;
     7 
     8         public synchronized void execute(final Runnable r) {
     9             mTasks.offer(new Runnable() {
    10                 public void run() {
    11                     try {
    12                         r.run();
    13                     } finally {
    14                         scheduleNext();
    15                     }
    16                 }
    17             });
    18             if (mActive == null) {
    19                 scheduleNext();
    20             }
    21         }
    22 
    23         protected synchronized void scheduleNext() {
    24             if ((mActive = mTasks.poll()) != null) {
    25                 THREAD_POOL_EXECUTOR.execute(mActive);
    26             }
    27         }
    28     }
    29 
    30 //这是上面用到的线程池的设置
    31     private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
    32     private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
    33     private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
    34     private static final int KEEP_ALIVE = 1;
    35 
    36     private static final ThreadFactory sThreadFactory = new ThreadFactory() {
    37         private final AtomicInteger mCount = new AtomicInteger(1);
    38 
    39         public Thread newThread(Runnable r) {
    40             return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
    41         }
    42     };
    43 
    44     private static final BlockingQueue<Runnable> sPoolWorkQueue =
    45             new LinkedBlockingQueue<Runnable>(128);
    46 
    47     public static final Executor THREAD_POOL_EXECUTOR
    48             = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
    49                     TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
    执行器和线程池

            可以看到构造函数的任务对象中的最后一句是postResult(result),而该方法就是向AsyncTask内部的Handler发送消息。

           b、下面就到了Handler的部分,

     1     //获取内部Handler
     2     private static Handler getHandler() {
     3         synchronized (AsyncTask.class) {
     4             if (sHandler == null) {
     5                 sHandler = new InternalHandler();
     6             }
     7             return sHandler;
     8         }
     9     }
    10 
    11     private static class InternalHandler extends Handler {
    12         public InternalHandler() {
    13             super(Looper.getMainLooper());
    14         }
    15 
    16         @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
    17         @Override
    18         public void handleMessage(Message msg) {
    19             AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
    20             switch (msg.what) {
    21                 case MESSAGE_POST_RESULT:
    22                     // There is only one result
    23                     result.mTask.finish(result.mData[0]);
    24                     break;
    25                 case MESSAGE_POST_PROGRESS:
    26                     result.mTask.onProgressUpdate(result.mData);
    27                     break;
    28             }
    29         }
    30     }
    AsyncTask内部Handler

            可以看到,这个handler处理两种类型的消息,一种是MESSAGE_POST_PROGRESS,由publishProgress发送,触发;一种是MESSAGE_POST_RESULT,由postResult发送。

     1 //发送MESSAGE_POST_PROGRESS消息,触发onProgressUpdate()
     2     protected final void publishProgress(Progress... values) {
     3         if (!isCancelled()) {
     4             getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
     5                     new AsyncTaskResult<Progress>(this, values)).sendToTarget();
     6         }
     7     }
     8     //所以重写这个方法可以在主UI中更新界面
     9     protected void onProgressUpdate(Progress... values) {
    10     }
    11 
    12 
    13     //发送MESSAGE_POST_RESULT消息,触发finish()
    14     private Result postResult(Result result) {
    15         @SuppressWarnings("unchecked")
    16         Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
    17                 new AsyncTaskResult<Result>(this, result));
    18         message.sendToTarget();
    19         return result;
    20     }
    21     //!!!这里的结束有两种情况,一种是被取消,一种是执行完成。
    22     private void finish(Result result) {
    23         if (isCancelled()) {
    24             onCancelled(result);
    25         } else {
    26             onPostExecute(result);
    27         }
    28         mStatus = Status.FINISHED;
    29     }
    消息的发送和处理

      有关AsyncTask的使用还有一个问题就是如果在执行耗时任务时用户退出Activity,此时该任务线程可能没有执行完所以不会退出,这是可能造成内存泄露,需要在onPause中进行判断。还有其他问题参见http://droidyue.com/blog/2014/11/08/bad-smell-of-asynctask-in-android/index.html

    参考:

    http://threezj.com/2016/01/23/Android%20Handler%20Looper%20Message%20%20%E8%AF%A6%E7%BB%86%E5%88%86%E6%9E%90/

    http://blog.csdn.net/guolin_blog/article/details/11711405

  • 相关阅读:
    Devexpress GridView 数据格式化显示
    贝叶斯算法
    贝叶斯算法
    科普 | 7 个步骤入门区块链
    科普 | 7 个步骤入门区块链
    漫画:Spring Boot、Java多线程、分布式、Java架构的高级架构师进阶之路
    漫画:Spring Boot、Java多线程、分布式、Java架构的高级架构师进阶之路
    ARP协议工作原理[同网段及跨网段]
    ARP协议工作原理[同网段及跨网段]
    过滤DataTable数据的Select()方法
  • 原文地址:https://www.cnblogs.com/songfeilong2325/p/5412424.html
Copyright © 2011-2022 走看看