码迷,mamicode.com
首页 > 移动开发 > 详细

Android 消息机制

时间:2016-04-22 20:33:07      阅读:200      评论:0      收藏:0      [点我收藏+]

标签:


从一接触Android开始,就听说了消息机制,那时候对消息机制的理解也只是看看别人博客,了解一些概念和相关的类而已,并没有深入的去了解和探究。直到最近,重新过了一遍消息机制和相关源码,这里做简单的整理和记录,毕竟好记性不如烂笔头。如果有什么问题,还请大家指出。(注:源码版本 4.0)

基础使用

  • Android的消息机制,主要是指Handler的运行机制,通常情况下,我们使用Handler来更新UI,详细的说就是一些耗时操作(IO读取,网络请求等)运行结束后,对UI的状态进行更新,为了避免页面卡顿,耗时操作通常在子线程中处理,然而UI的更新操作必须在UI线程中进行更新,这个时候就需要使用Handler来进行线程间消息的传递
  • 基础示例:

    public class MainActivity extends AppCompatActivity {
    
        public static final String TAG = MainActivity.class.getSimpleName();
    
        //报内存泄露警告的handler
        private Handler handler = new Handler() {
            @Override
            public void handleMessage(Message msg) {//处理发送过来的消息
                if (msg.arg1 == 1) {
                    Log.i(TAG, "1消息已被接收");
                }
            }
        };
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            sendMessageToHandler();
        }
    
        /**
         * 向Handler发送消息
         */
        private void sendMessageToHandler() {
    
            new Thread(new Runnable() {
                @Override
                public void run() {
                    Message message = Message.obtain();
                    message.arg1 = 1;
                    handler.sendMessage(message);
                }
            }).start();
        }
    }
    
  • 使用过Handler的朋友都知道,上面的使用方式Android Lint会给出内存泄露警告,具体内容如下:

This Handler class should be static or leaks might occur (anonymous android.os.Handler) less...

Since this Handler is declared as an inner class, it may prevent the outer class from being garbage collected. If the Handler is using a Looper or MessageQueue for a thread other than the main thread, then there is no issue. If the Handler is using the Looper or MessageQueue of the main thread, you need to fix your Handler declaration, as follows: Declare the Handler as a static class; In the outer class, instantiate a WeakReference to the outer class and pass this object to your Handler when you instantiate the Handler; Make all references to members of the outer class using the WeakReference object.

  • 警告内容说的很详细,并且给出解决方案。此处先放一放,我们了解源码后,再回头解释此问题

对消息机制相关类的说明(源码细节,直接在源码中加注释说明)

  • Handler 该类负责Message的发送和处理

    • 发送消息的核心源码如下:

      public boolean sendMessageAtTime(Message msg, long uptimeMillis)
      {   
          boolean sent = false;
          MessageQueue queue = mQueue;
          if (queue != null) {
              msg.target = this;
              //关键代码。发送消息的实质,其实是向消息队列中插入一条消息
              sent = queue.enqueueMessage(msg, uptimeMillis);
          }
          else {
              RuntimeException e = new RuntimeException(
                  this + " sendMessageAtTime() called with no mQueue");
              Log.w("Looper", e.getMessage(), e);
          }
          return sent;
      }
      
    • 处理消息的核心源码如下:

      //该方法对Message进行了分发处理
      public void dispatchMessage(Message msg) {
          if (msg.callback != null) {
              handleCallback(msg);
          } else {
              if (mCallback != null) {
                  if (mCallback.handleMessage(msg)) {
                      return;
                  }
              }
              handleMessage(msg);
          }
      }
      
      ...
      
      private final void handleCallback(Message message) {
          message.callback.run();
      }
      
      ...
      
      public interface Callback {
          public boolean handleMessage(Message msg);
      }
      
      ...
      
      public void handleMessage(Message msg) {
      }
      
  • Message 该类负责Message对象的创建和释放

    • Message的创建obtain()

      //该方法有很多的重载方法,都是在此方法的基础上扩展而来,其他方法不再列举
      public static Message obtain() {
          synchronized (sPoolSync) {
              if (sPool != null) {
                  Message m = sPool;
                  sPool = m.next;
                  m.next = null;
                  sPoolSize--;
                  return m;
              }
          }
          return new Message();
      }
      
    • Message的释放

      public void recycle() {
          clearForRecycle();
      
          synchronized (sPoolSync) {
              if (sPoolSize < MAX_POOL_SIZE) {
                  next = sPool;
                  sPool = this;
                  sPoolSize++;
              }
          }
      }
      
      void clearForRecycle() {
          flags = 0;
          what = 0;
          arg1 = 0;
          arg2 = 0;
          obj = null;
          replyTo = null;
          when = 0;
          target = null;
          callback = null;
          data = null;
      }
      
  • ThreadLocal ThreadLocal的作用是提供线程内的局部变量,这种变量在线程的生命周期内起作用,减少同一个线程内多个函数或者组件之间一些公共变量的传递的复杂度。这里给出参考博客链接:http://qifuguang.me/2015/09/02/[Java%E5%B9%B6%E5%8F%91%E5%8C%85%E5%AD%A6%E4%B9%A0%E4%B8%83]%E8%A7%A3%E5%AF%86ThreadLocal/

    • 该类中定义了一个静态内部类Values,Values中有一个对象数组table,不同的线程访问着同一个ThreadLocal的set()和get()方法,本质是操作table数组,他们对ThreadLocal的操作是在各自线程的内部。结合源码我们能更好的理解ThreadLocal的实现
    • ThreadLocal中的set()

      //获取当前线程的Values对象,如果当前线程的Values不存在,则重新实例化一个
      //否则将value添加到table数组中
      public void set(T value) {
          Thread currentThread = Thread.currentThread();
          Values values = values(currentThread);
          if (values == null) {
              values = initializeValues(currentThread);
          }
          values.put(this, value);
      }
      
    • ThreadLocal中的get()

      public T get() {
          // Optimized for the fast path.
          Thread currentThread = Thread.currentThread();
          Values values = values(currentThread);
          if (values != null) {
              Object[] table = values.table;
              int index = hash & values.mask;
              if (this.reference == table[index]) {
                  //Values中的put()方法是将索引和值在相邻位置进行存储的,具体实现查看put()源码
                  return (T) table[index + 1];
              }
          } else {
              values = initializeValues(currentThread);
          }
      
          return (T) values.getAfterMiss(this);
      }
      
    • Values 中的put()

      void put(ThreadLocal<?> key, Object value) {
          cleanUp();
      
          // Keep track of first tombstone. That‘s where we want to go back
          // and add an entry if necessary.
          int firstTombstone = -1;
      
          for (int index = key.hash & mask;; index = next(index)) {
              Object k = table[index];
      
              if (k == key.reference) {
                  // Replace existing entry.
                  table[index + 1] = value;
                  return;
              }
      
              if (k == null) {
                  if (firstTombstone == -1) {
                      // Fill in null slot.
                      table[index] = key.reference;
                      table[index + 1] = value;
                      size++;
                      return;
                  }
                  //数组中相邻的位置存储索引和值
                  // Go back and replace first tombstone.
                  table[firstTombstone] = key.reference;
                  table[firstTombstone + 1] = value;
                  tombstones--;
                  size++;
                  return;
              }
      
              // Remember first tombstone.
              if (firstTombstone == -1 && k == TOMBSTONE) {
                  firstTombstone = index;
              }
          }
      }
      
  • Looper 该类是一个循环控制器

    • 该类是消息机制的重要组成部分,本类包含消息机制的准备工作,循环控制器的启动以及退出循环控制器功能
    • Looper的准备

      static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
      ...
      
      private Looper() {
          mQueue = new MessageQueue();//创建消息队列对象
          mRun = true;
          mThread = Thread.currentThread();//获取当前线程
      }
      ...
      
      public static void prepare() {
          if (sThreadLocal.get() != null) {
              throw new RuntimeException("Only one Looper may be created per thread");
          }
          //将当前线程的Looper对象传递给ThreadLocal
          sThreadLocal.set(new Looper());
      }
      
    • Looper循环控制器的启动

      public static Looper myLooper() {
          //从ThreadLocal中获取Looper对象,这里可看出他们操作的是同一个ThreadLocal对象
          return sThreadLocal.get();
      }
      
      ...
      
      public static void loop() {
          //启动loop方法必须有looper对象,否则会报下面的异常
          Looper me = myLooper();
          if (me == null) {
              throw new RuntimeException("No Looper; Looper.prepare() wasn‘t called on this thread.");
          }
          //获取Looper中的消息队列
          MessageQueue queue = me.mQueue;
      
          // Make sure the identity of this thread is that of the local process,
          // and keep track of what that identity token actually is.
          Binder.clearCallingIdentity();
          final long ident = Binder.clearCallingIdentity();
      
          //无限循环,从MessageQueue中不断的获取Message
          while (true) {
              Message msg = queue.next(); // might block
              if (msg != null) {
                  //唯一退出循环的条件
                  if (msg.target == null) {
                      // No target is a magic identifier for the quit message.
                      return;
                  }
      
                  long wallStart = 0;
                  long threadStart = 0;
      
                  // This must be in a local variable, in case a UI event sets the logger
                  Printer logging = me.mLogging;
                  if (logging != null) {
                      logging.println(">>>>> Dispatching to " + msg.target + " " +
                              msg.callback + ": " + msg.what);
                      wallStart = SystemClock.currentTimeMicro();
                      threadStart = SystemClock.currentThreadTimeMicro();
                  }
      
                  //分发Message到Handler中进行处理
                  msg.target.dispatchMessage(msg);
      
                  if (logging != null) {
                      long wallTime = SystemClock.currentTimeMicro() - wallStart;
                      long threadTime = SystemClock.currentThreadTimeMicro() - threadStart;
      
                      logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
                      if (logging instanceof Profiler) {
                          ((Profiler) logging).profile(msg, wallStart, wallTime,
                                  threadStart, threadTime);
                      }
                  }
      
                  // Make sure that during the course of dispatching the
                  // identity of the thread wasn‘t corrupted.
                  final long newIdent = Binder.clearCallingIdentity();
                  if (ident != newIdent) {
                      Log.wtf(TAG, "Thread identity changed from 0x"
                              + Long.toHexString(ident) + " to 0x"
                              + Long.toHexString(newIdent) + " while dispatching to "
                              + msg.target.getClass().getName() + " "
                              + msg.callback + " what=" + msg.what);
                  }
                  //释放Message
                  msg.recycle();
              }
          }
      }
      
    • 退出Looper quite()

      //向MessageQueue中插入一个target为null的Message,会退出Looper
      //退出loop()的唯一条件 if (msg.target == null){return;}
      public void quit() {
          Message msg = Message.obtain();
          // NOTE: By enqueueing directly into the message queue, the
          // message is left with a null target.  This is how we know it is
          // a quit message.
          mQueue.enqueueMessage(msg, 0);
      }
      
  • MessageQueue 该类负责Message的插入,取出,以及移除

    • 插入Message方法enqueueMessage()源码

      //Handler中的sendMessage()方法最终会调用此方法,所以说发送消息其实是向MessageQueue中插入一条消息
      final boolean enqueueMessage(Message msg, long when) {
          if (msg.isInUse()) {
              throw new AndroidRuntimeException(msg
                      + " This message is already in use.");
          }
          if (msg.target == null && !mQuitAllowed) {
              throw new RuntimeException("Main thread not allowed to quit");
          }
          final boolean needWake;
          synchronized (this) {
              if (mQuiting) {
                  RuntimeException e = new RuntimeException(
                      msg.target + " sending message to a Handler on a dead thread");
                  Log.w("MessageQueue", e.getMessage(), e);
                  return false;
              } else if (msg.target == null) {
                  mQuiting = true;
              }
      
              msg.when = when;
              //Log.d("MessageQueue", "Enqueing: " + msg);
              Message p = mMessages;
              if (p == null || when == 0 || when < p.when) {
                  msg.next = p;
                  mMessages = msg;
                  needWake = mBlocked; // new head, might need to wake up
              } else {
                  Message prev = null;
                  while (p != null && p.when <= when) {
                      prev = p;
                      p = p.next;
                  }
                  msg.next = prev.next;
                  prev.next = msg;
                  needWake = false; // still waiting on head, no need to wake up
              }
          }
          if (needWake) {
              nativeWake(mPtr);
          }
          return true;
      }
      
    • 取出和移除

      final Message next() {
          int pendingIdleHandlerCount = -1; // -1 only during first iteration
          int nextPollTimeoutMillis = 0;
      
          //又一个无限循环
          for (;;) {
              if (nextPollTimeoutMillis != 0) {
                  Binder.flushPendingCommands();
              }
              nativePollOnce(mPtr, nextPollTimeoutMillis);
      
              //当有Message时,取出Message,否则该方法将会一直堵塞
              synchronized (this) {
                  // Try to retrieve the next message.  Return if found.
                  final long now = SystemClock.uptimeMillis();
                  final Message msg = mMessages;
                  if (msg != null) {
                      final long when = msg.when;
                      if (now >= when) {
                          mBlocked = false;
                          mMessages = msg.next;
                          msg.next = null;
                          if (false) Log.v("MessageQueue", "Returning message: " + msg);
                          msg.markInUse();
                          return msg;
                      } else {
                          nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);
                      }
                  } else {
                      nextPollTimeoutMillis = -1;
                  }
      
                  // If first time, then get the number of idlers to run.
                  if (pendingIdleHandlerCount < 0) {
                      pendingIdleHandlerCount = mIdleHandlers.size();
                  }
                  if (pendingIdleHandlerCount == 0) {
                      // No idle handlers to run.  Loop and wait some more.
                      mBlocked = true;
                      continue;
                  }
      
                  if (mPendingIdleHandlers == null) {
                      mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                  }
                  mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
              }
      
              // Run the idle handlers.
              // We only ever reach this code block during the first iteration.
              for (int i = 0; i < pendingIdleHandlerCount; i++) {
                  final IdleHandler idler = mPendingIdleHandlers[i];
                  mPendingIdleHandlers[i] = null; // release the reference to the handler
      
                  boolean keep = false;
                  try {
                      keep = idler.queueIdle();
                  } catch (Throwable t) {
                      Log.wtf("MessageQueue", "IdleHandler threw exception", t);
                  }
      
                  //当有Message时,返回Message后,删除Message
                  if (!keep) {
                      synchronized (this) {
                          mIdleHandlers.remove(idler);
                      }
                  }
              }
      
              // Reset the idle handler count to 0 so we do not run them again.
              pendingIdleHandlerCount = 0;
      
              // While calling an idle handler, a new message could have been delivered
              // so go back and look again for a pending message without waiting.
              nextPollTimeoutMillis = 0;
          }
      }
      

消息机制关键类与核心方法图

技术分享

根据Handler的使用流程,逐步分析

  • 首先,使用Handler必须保证其所在的线程中具有Looper对象,但是有的朋友会说,我在Activity中并没有创建Looper对象,但是我也可以使用Handler啊。其实Activity所在的主线程(ActivityThread)也就是UI线程是特殊的线程,该线程在创建的时候就已经创建了Looper对象,看源码

    • ActivityThread的main()方法

      public static void main(String[] args) {
          SamplingProfilerIntegration.start();
      
          // CloseGuard defaults to true and can be quite spammy.  We
          // disable it here, but selectively enable it later (via
          // StrictMode) on debug builds, but using DropBox, not logs.
          CloseGuard.setEnabled(false);
      
          Process.setArgV0("<pre-initialized>");
      
          Looper.prepareMainLooper();//准备工作
          if (sMainThreadHandler == null) {
              sMainThreadHandler = new Handler();
          }
      
          ActivityThread thread = new ActivityThread();
          thread.attach(false);
      
          if (false) {
              Looper.myLooper().setMessageLogging(new
                      LogPrinter(Log.DEBUG, "ActivityThread"));
          }
      
          Looper.loop();//启动循环控制器
      
          throw new RuntimeException("Main thread loop unexpectedly exited");
      }
      
    • Looper中的prepareMainLooper()方法是专门用与主线程的

      public static void prepareMainLooper() {
          prepare();//最后还是调用了prepare(),从而创建了Looper对象,并设置给了ThreadLocal
          setMainLooper(myLooper());
          myLooper().mQueue.mQuitAllowed = false;
      }
      private synchronized static void setMainLooper(Looper looper) {
          mMainLooper = looper;
      }
      
  • 以上工作准备完成后,Looper,ThreadLocal已经创建完毕,并且建立了关联关系。接下来创建Handler对象,获取Message对象(Message.obtain()),使用Handler中的sendMessage()方法向MessageQueue中插入Message(enqueueMessage()),这时候Looper对象中处于堵塞状态的next()方法检测到Message后,将其从MessageQueue中取出,传递给Handler的dispatchMessage()方法后,清除该Message(msg.recycle();),最后dispatchMessage()方法,将Message分发到run()方法或者handlerMessage(Message msg)中进行处理。

  • 最后来一张消息机制的示意图

技术分享

分析Handler内存泄露问题

  1. App启动时,Android Framework 为主线程(ActivityThread)创建了一个Looper对象(main方法中),这个Looper将贯穿整个app的生命周期,它内部持有消息队列(MessageQueue),并且开启一个循环(loop())来处理Message,而Framework的主要事件都包含着内部Message对象,当这些事件被触发的时候,Message对象会被加到消息队列中执行。
  2. 当一个Handler被实例化时(如上面那样),它将和主线程Looper对象的消息队列相关联,被推到消息队列中的Message对象将持有一个Handler的引用以便于当Looper处理到这个Message的时候,Framework执行Handler的handleMessage(Message)方法。
  3. 在 Java 语言中,非静态匿名内部类将持有一个对外部类的隐式引用,而静态内部类则不会。
  4. 内存泄露会在什么情况下产生呢?这里给出一个示例

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                Log.i(TAG,"post delay");
            }
        },10*60*1000);
    
        finish();
    }
    

示例中,当Activity被finish()掉,Message 将存在于消息队列中长达10分钟的时间才会被执行到。这个Message持有一个对Handler的引用,Handler也会持有一个对于外部类(SampleActivity)的隐式引用,这些引用在Message被执行前将一直保持,这样会保证Activity的上下文不被垃圾回收机制回收,同时也会泄露应用程序的资源(views and resources)。

解决办法

Handler的警告提示中已经说明,因为静态匿名内部类不会持有外部类的隐式引用,因此我们创建一个静态的Handler子类。如果需要调用外部类(例如:Activity)的方法,就让Handler持有一个Activity的弱引用(WeakReference),这样就不会泄露Activity的上下文了。示例:

public class MainActivity extends AppCompatActivity {

    public static final String TAG = MainActivity.class.getSimpleName();

    private MyHandler myHandler = new MyHandler(this);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        sendMessageToHandler();

    }

    /**
     * 向Handler发送消息
     */
    private void sendMessageToHandler() {

        new Thread(new Runnable() {
            @Override
            public void run() {
                Message message = Message.obtain();
                message.arg2 = 2;
                myHandler.sendMessage(message);
            }
        }).start();

    }

    /**
     * MyHandler的处理方法
     *
     * @param msg 消息
     */
    private void handlerMessage(Message msg) {
        if (msg.arg2 == 2) {
            Log.i(MainActivity.TAG, "2已接收到消息");
        }
    }


    private static class MyHandler extends Handler {

        //弱引用,避免Handler持有外部类的引用,即MainActivity的引用,
        // 这样会导致MainActivity的上下文及资源无法被回收,引发内存泄露的情况发生
        private WeakReference<MainActivity> weakReference;

        public MyHandler(MainActivity mainActivity) {
            weakReference = new WeakReference<>(mainActivity);
        }

        @Override
        public void handleMessage(Message msg) {

            MainActivity mainActivity = weakReference.get();
            mainActivity.handlerMessage(msg);

        }
    }

}

参考:
http://qifuguang.me/2015/09/02/[Java%E5%B9%B6%E5%8F%91%E5%8C%85%E5%AD%A6%E4%B9%A0%E4%B8%83]%E8%A7%A3%E5%AF%86ThreadLocal/ http://developer.51cto.com/art/201204/332155.htm

Android 消息机制

标签:

原文地址:http://blog.csdn.net/a254830856/article/details/51199588

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!