Android消息机制的分析

Android 的消息机制主要涉及到这几个类 Handler,Looper,Message和MessageQueue

讲解下这几个类的作用以及之间的关系
  • Handler:是将一个任务切换到某个特定线程中去执行。在Android中规定访问UI必须在主线程中进行,如果在子线程中进行UI的更新就会抛出异常。

  • Looper:从MessageQueue中循环取出Message,然后将消息发送到Handler中的handleMessage()方法中。就是不断循环调用(loop方法),按照分发的机制将消息分发给目标的处理者。(能发送到handleMessage方法中的前提是 handler调用了sendMessge()系列方法来进行消息的发送,其实还有另外一个类别是调用post(),其实两者的目的都是将子线程切换到主线程中,来进行UI的更新)

  • MessageQueue:是一个存储Message的队列,Message的底层实现就是一个单链表,单链表对于增删改查都是比较友好的(很方便操作的)。在Android消息机制中,MessageQueue是一个将消息存储(enqueueMessage()方法)和取出(next()方法)消息队列。

  • Message:即消息,是作为数据的载体,在子线程到UI线程中进行传递。在Android的体系中,消息分为物理消息(触摸和点击),还有就是软件消息。

  • 下面讲解下这个Message 从创建到通过Handler发送到MessageQueue中存储,然后通过Looper从MessageQueue中取出,发送到Handler的handleMessage方法中的整个过程。

先看个小demo
  public class MainActivity extends AppCompatActivity {

  private Button sendMessage;
  private Button post;
  private static TextView tvMessage;


  private static Handler mHandler = new Handler() {
      @Override
      public void handleMessage(Message msg) {
          super.handleMessage(msg);
          switch (msg.what) {
              case 1:
                  tvMessage.setText(msg.obj.toString());
                  break;
          }
      }
  };


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

  private void initView() {
      sendMessage = findViewById(R.id.sendMessage);
      post = findViewById(R.id.post);
      tvMessage = findViewById(R.id.tvMessage);

      sendMessage.setOnClickListener(new View.OnClickListener() {
          @Override
          public void onClick(View v) {

              new Thread(new Runnable() {
                  @Override
                  public void run() {
                      //在子线程中进行消息的发送
                      Message msg = Message.obtain();
                      msg.what = 1;
                      msg.obj = "Hello World";
                      mHandler.sendMessage(msg);
                  }
              }).start();

          }
      });

      post.setOnClickListener(new View.OnClickListener() {
          @Override
          public void onClick(View v) {
              new Thread(new Runnable() {
                  @Override
                  public void run() {
                      mHandler.post(new Runnable() {
                          @Override
                          public void run() {
                              //这也说明了 post更新UI只需在run中进行,不用在handleMessage中进行
                              tvMessage.setText("Hello World2");
                          }
                      });
                  }
              }).start();
          }
      });

  }
}
  • 在主线程中创建了Handler,然后重写了handleMessage方法
  • 在子线程中调用了sendMessage和post方法来进行消息的发送,进而更新了主线程中的UI。
UI线程(主线程)
  • 这个UI线程是怎么一回事呢?
  • 都在强调不能在子线程中更新UI,只能在UI线程中更新UI,这是为什么?

下面针对上面的两个问题,来一一解答

  • 在Android应用程序创建的时候,会默认创建一个线程,这个线程就是主线程,对应着ActivityThread,那么程序的启动都会有一个入口,这个入口就是ActivityThread类中的main方法,main方法作为Android应用程序启动的入口,那么我们来看看这个main方法中干了什么。
      public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
    
        // 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);
    
        Environment.initForCurrentUser();
    
        // Set the reporter for event logging in libcore
        EventLogger.setReporter(new EventLoggingReporter());
    
        // Make sure TrustedCertificateStore looks in the right place for CA certificates
        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
        TrustedCertificateStore.setDefaultUserDirectory(configDir);
    
        Process.setArgV0("<pre-initialized>");
        // 这里面调用了prepareMainLooper方法
        //分析1 
        Looper.prepareMainLooper();
    
        // Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
        // It will be in the format "seq=114"
        long startSeq = 0;
        if (args != null) {
            for (int i = args.length - 1; i >= 0; --i) {
                if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
                    startSeq = Long.parseLong(
                            args[i].substring(PROC_START_SEQ_IDENT.length()));
                }
            }
        }
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);
    
        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
    
        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }
    
        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        //调用了 Looper中的loop方法
        //分析二
        Looper.loop();
    
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }
    

这里面着重分析这两个方法,第一个方法影响着Handler在子线程中的创建,第二个方法是进行消息的取出操作。

  • 分析1
 //我们进入到Looper类中找到prepareMainLooper方法
 public static void prepareMainLooper() {
       //分析1.1 
       prepare(false);
       synchronized (Looper.class) {
           if (sMainLooper != null) {
               throw new IllegalStateException("The main Looper has already been prepared.");
           }
           sMainLooper = myLooper();
       }
   }

 //分析1.1
  private static void prepare(boolean quitAllowed) {
       //这里面的sThreadLocal 其实是 ThreadLocal sThreadLcoal = new ThreadLocal()  是sThreadLocal的一个实例
       // ThreadLocal:是一个线程的本地存储区,每个线程都有一个独立的本地存储区,各个线程的本地储存区是不能互相访问的。 
       // 分析1.1.1
       // 当在当前线程中去ThreadLocal中获取值,如果不为空那么抛出异常,意思就是一个线程中只能存在一个Looper对象。
       if (sThreadLocal.get() != null) {
           throw new RuntimeException("Only one Looper may be created per thread");
       }
       //如果ThreadLcoal中没有存储Looper,那么就创建一个新的Looper存储在ThreadLocal中。
       sThreadLocal.set(new Looper(quitAllowed));
   }
// 在Looper的构造方法中
 private Looper(boolean quitAllowed) {
       //1. 创建了一个MessageQueue的对象
       mQueue = new MessageQueue(quitAllowed);
       //获取到当前的线程。
       mThread = Thread.currentThread();
   }

 //总结说来:在Android程序启动的时候,默认会在UI线程中创建一个Looper对象,并且看源代码显示,一个线程中只能存在一个Looper对象,如果有第二个会抛出异常。

//分析1.1.1
 // ThreadLocal主要是将Looper对象存储在当前线程的本地存储区中,下面来看下ThreadLocal中的主要两个方法。
 
//本地存储区的存储数据方法
public void set(T value) {
       // 获取到当前的线程
       Thread t = Thread.currentThread();
       //获取到线程的本地存储区
       ThreadLocalMap map = getMap(t);
       //本地存储区已经初始化了,经将值存储进本地存储区中
       if (map != null)
           map.set(this, value);
       else
           // 创建一个本地存储区,将数据存储到本地存储区中。
           createMap(t, value);
   }
 //取数据方法
 
public T get() {
       //获取到当前的线程池
       Thread t = Thread.currentThread();
       //查找当前线程的本地存储区
       ThreadLocalMap map = getMap(t);
       //线程的本地存储区有数据时
       if (map != null) {
           //获取到本地存储区存着的数据
           ThreadLocalMap.Entry e = map.getEntry(this);
           if (e != null)
               return (T)e.value;
       }
       return setInitialValue();
   }
   private T setInitialValue() {
       //初始化值 为null
       T value = initialValue();
       //获取到当前的线程
       Thread t = Thread.currentThread();
       //根据线程查找到本地存储区
       ThreadLocalMap map = getMap(t);
       //当前本地存储区不为空的时候,将数据存储到本地存储区中,
       if (map != null)
           map.set(this, value);
       else
           //本地存储区为空的时候,初始化一个本地存储区,value为null
           createMap(t, value);
       return value;
   }

//在Android的消息机制中ThreadLocal操作的类型都是Looper类型。
  • 那么为什么Looper会影响到Handler的创建呢?
  //上面的demo中,我们在主线程中,创建了一个Handler,那么我们看下Handler的构造方法的代码,看看初始化的时候Handler都需要干什么?

public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }
        //这个地方,调用了Looper的myLooper方法,获取到当前线程存本地存储区中储的Looper对象。能在不同线程存储Looper对象,得益于ThreadLocal这个类。
        mLooper = Looper.myLooper();
        //如果mLooper为空,就会抛出异常
        //这个地方说下:开启一个子线程,是不会默认创建Looper对象,所以在子线程中创建Handler之前需要在子线程中创建一个Looper对象,这样Handler的创建才会成功。
        //可以通过Looper的preapre()或者getMainLooper()方法在子线程中创建一个Looper对象,存储在当前线程的ThreadLcoal中。
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        //这里把Looper的MessageQueue对象和Handler中的联系在一起了。
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
  //在Handler的构造方法中,我们可以看到,获取了当前线程的Looper对象,如果为空,说明Looper没有创建,没有创建就会报错。
  //那么Handler的创建失败了,当前线程的本地存储区中有没有Looper对象,直接影响着Handler的创建。
  • 分析二 ( Looper.Loop)
 //source
  public static void loop() {
        //获取到本地存储区中存储的Looper对象
        final Looper me = myLooper();
        if (me == null) {
            //主线程会默认创建一个Looper对象,如果在子线程中,没有调用prepare就会抛出异常。
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        //将Looper中的mQueue赋给queue变量
        final 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();

        //开启无限循环,取消息
        //为什么不使用 while(true)? 因为可以通过一些黑科技,来修改这个条件(比如通过反射)
        for (;;) {
            //当消息队列的next方法返回为null的时候才会退出这个循环
            //next方法什么时候退回的呢 ?当Looper调用了quit或者quitSafely 来通知消息队列退出。这时next方法就会返回null。
            //next是一个阻塞的方法,当MessageQueue中没有消息时,next方法就处于阻塞状态,那么Looper也处于阻塞的状态,直到消息队列中有消息
            //next方法取出消息,Looper中就会分发处理这条消息。
            Message msg = queue.next(); // might block 可能被阻塞
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            // This must be in a local variable, in case a UI event sets the logger
            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            final long traceTag = me.mTraceTag;
            if (traceTag != 0) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            try {
                //调用handler中的dispatchMessage()方法
                //dispatchMessage方法是在创建Handler时使用的Looper中调用
                //拿主线程来说吧,Handler创建是在UI线程,使用的Looper是存储在UI线程的本地存储区中,其中Looper对象的创建是在UI线程中,那么MessgeQueue也是在UI线程中创建啦,那么存储的Message也是在ui线程的MessageQueue中了,在Looper的loop方法中msg.target.dispatchMessage,其中的Message是存在UI线程的MessageQueue中的,所以保存的target(handler)变量也存在UI线程中,所以Handler的dispatchMessage方法是运行在UI线程中的。说白了就是,Looper.loop在那个线程中调用, handleMessage就运行在那个线程中。
                //在UI线程中执行,那么handleMessage中就是运行在UI线程中。
                msg.target.dispatchMessage(msg);
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            // 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);
            }

            //将用过的消息进行回收,放入消息池中,方便重复利用。
            msg.recycleUnchecked();
        }
    }

上述我们通过ActivityThread中的main方法延伸上述的分析,主要是Looper的创建和存储本地存储区中,然后Looper调用loop方法,该方法中是一个死循环,通过next方法不停的从MessageQueue中取出消息,如果没有消息next方法就会阻塞,这时Looper就处于阻塞状态,当MessageQueue中有消息的时候,这时会调用next方法来获取到消息,然后通过dispatchMessage方法进行消息的分发。该方法是Handler中的。

那么一个消息是怎么来的呢?怎么进入MessageQueue中的呢?

  • Handle中的消息发送方法

在Handler有一系列的send和一系列的post方法,来进行消息的发送,其中一系列的post方法会最终调用send系列的方法。我们知道最基本的一个消息发送方法是sendMessage(),那么我们来看看这个方法中究竟怎么运行的。

  //Handler的sendMessage方法。
  public final boolean sendMessage(Message msg)
    {
          //内部调用了sendMessageDelayed()方法,
          return sendMessageDelayed(msg, 0);
    }

  public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        //内部调用了sendMessageAtTime
        //其中 SystemClock.uptimeMIllis()是代表着系统的开机时间到运行这段代码的时间,
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        //这个mQueue 是Looper中的MessageQueue
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        //msg.target 就是发送消息的Handler
        //这里将handler对象赋值给msg.target
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        //将消息存储到MessageQueue队列中(先进先出)
        // MesageQueue调用了enqueueMessage 将Message存储到消息队列中
        //其中这个时间 uptimeMills 代表消息如入队列的顺序时间。
        return queue.enqueueMessage(msg, uptimeMillis);
    }

当发送一条消息到消息队列中的时候,通过next方法就取出个消息,在Looper的loop方法中通过调用
Handler中的dispatchMessage方法来进行消息的分发。

  • Handler的消息处理
      public void dispatchMessage(Message msg) {
          //此处的callback 就是 Message中Runnable的一个变量
          //那么什么时候不为空呢?
          //Handler发送消息的方式分为两种 sendMessage系列 和 post系列
          // post(Runnnable callback)  最终调用的是Handler中的 getPostMessage(),其中
            {
                 private static Message getPostMessage(Runnable r) {
                      //在这里将 传进去的 Runnable对象 赋值被 Message中的 callbak 当发消息的方式 是 post的时候                           callback不为空。
                      Message m = Message.obtain();
                      m.callback = r;
                      return m;
                   }
            }
          //所以我们得知 当调用post系列方法进行消息发送就msg.callback !=null 
          if (msg.callback != null) {
              handleCallback(msg);
          } else {
            //那么这个就是sendMessage系列方法走的
              if (mCallback != null) {
                  if (mCallback.handleMessage(msg)) {
                      return;
                  }
              }
              //执行handleMessage 这个是我们重写了这个方法。
              handleMessage(msg);
          }
      }
    
  • MessageQueue分析

对于MessageQueue来说 主要体现在Mesasge消息的存储和取出的操作上,MessageQueue说是一个队列,其实底层是一个单链表结构,单链表对于增加和查询非常的友好,很是方便。消息的存储就是将消息存储到消息队列中,消息的取出就是将消息从消息队列中取出,然后将消息从消息队列中移除,所以消息的取出的同时伴随着消息的删除。

  //对于MessageQueue,存入消息是调用了enqueueMessage方法,取出消息是调用了next方法。
  //enqueueMessage方法。
   boolean enqueueMessage(Message msg, long when) {
      //每个Message中都会定义一个Tartget,这个Terget类型就是Handler
      // 
      if (msg.target == null) {
          throw new IllegalArgumentException("Message must have a target.");
      }
      if (msg.isInUse()) {
          throw new IllegalStateException(msg + " This message is already in use.");
      }

      synchronized (this) {
          if (mQuitting) {
              IllegalStateException e = new IllegalStateException(
                      msg.target + " sending message to a Handler on a dead thread");
              Log.w(TAG, e.getMessage(), e);
              msg.recycle();
              return false;
          }

          msg.markInUse();
          msg.when = when;
          Message p = mMessages;
          boolean needWake;
          if (p == null || when == 0 || when < p.when) {
              // New head, wake up the event queue if blocked.
              msg.next = p;
              mMessages = msg;
              needWake = mBlocked;
          } else {
              // Inserted within the middle of the queue.  Usually we don't have to wake
              // up the event queue unless there is a barrier at the head of the queue
              // and the message is the earliest asynchronous message in the queue.
              needWake = mBlocked && p.target == null && msg.isAsynchronous();
              Message prev;
              for (;;) {
                  prev = p;
                  p = p.next;
                  if (p == null || when < p.when) {
                      break;
                  }
                  if (needWake && p.isAsynchronous()) {
                      needWake = false;
                  }
              }
              msg.next = p; // invariant: p == prev.next
              prev.next = msg;
          }

          // We can assume mPtr != 0 because mQuitting is false.
          if (needWake) {
              nativeWake(mPtr);
          }
      }
      return true;
  }

通过源码我们可以看出,当线程中没有处理它的Handler或者当前有正在处理的相同的消息,Meesage都不能进入到队列中。当线程处于死亡的状态下的时候,Message对象将会被回收,然后退出enqueueMessage方法,否则的话就进行单链表的插入操作,将消息放进消息队列中。

  //next方法
   Message next() {
        // Return here if the message loop has already quit and been disposed.
        // This can happen if the application tries to restart a looper after quit
        // which is not supported.
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
                        // Next message is not ready.  Set a timeout to wake up when it is ready.
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // Got a message.
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }

                // Process the quit message now that all pending messages have been handled.
                if (mQuitting) {
                    dispose();
                    return null;
                }

                // If first time idle, then get the number of idlers to run.
                // Idle handles only run if the queue is empty or if the first message
                // in the queue (possibly a barrier) is due to be handled in the future.
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    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(TAG, "IdleHandler threw exception", t);
                }

                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;
        }
    }

next方法中是一个死循环操作,进行着消息的不间断的取出操作。如果当前消息池中没有消息了,next方法就会处于阻塞的状态,如果消息池中出现消息了,会立刻将消息取出,并将该消息池的这个消息给移除。

  • 当Android应用程序启动的时候 ActivityThread 就是主线程,主线程的入口是main函数,主线程需要一个Handler,那么这个Handler就是内部定义的ActivityThread.H 来进行一系列系统级别的消息发送和处理。

到此我们消息机制的分析就完事了,接下来我用一张图来描述一下消息机制中Handler ,Message ,MessageQueue 和Looper的运作流程以及各个类之间的层级关系(这里只做Java层面的分析,native层间的日后在做分析)。

Android消息机制运行图.png

这里总结几个关于Android消息机制的问题吧。

  1. Handler引起的内存泄漏和解决的办法?
    • 内存泄漏的引起:如果我们通过handler.postDelay()发送一个消息,此时消息没有得到执行Activity就finish了,此时Message持有了Handler实例(Target),而Handler又持有了Activity的实例(内部类默认持有外部类的引用),那么就会导致GC不能回收这个Activity,就会发生了内存泄漏。
  • 解决方式:首先我们Activity内部声明Handler的时候声明成静态的,静态是属于类本身。 Handler内部将Activity声明成弱应用。在Activity的onDestroy()中调用handler. removeCallbacksAndMessages(null)。
  • Handler造成内存泄漏,是因为内部有没有执行完的消息。内存泄漏的解决办法中声明静态和调用removeCallBacksAndMessage()能立刻达到内存的释放,声明成弱应用能达到避免泄漏,但是得等到Handler中所有任务都执行完毕,才能释放内存,GC回收,有点慢,性能问题。
  1. 为什么在主线程中可以直接使用Handler,而不需要创建Looper对象?
  • 在Android的应用程序进程中,ActivityThread起到了主线程的角色作用,那么这个主线程的入口是ActivityThread内部中的main方法,在main方法中有一行代码 Looper.prepareMainLooper()调用了这个方法,创建了一个Looper对象,存在主线程的本地存储区中,所以在创建Handler之前,默认就已经创建了Looper对象。
  1. 主线程中的Looper不让退出!
  • 嗯是的,主线程的Looper不能退出,退出就意味着应用程序挂了!在主线程创建Looper的时候,在调用prepare(quitAllowed)时,quitAllowed赋值为false,设定了不让退出的标记。
  1. 创建Message的最佳方式
  • 在Android中,为节省内存,为Message设计了回收的机制,本次用完的Message,可以重复利用,不用重复创建Message的对象。有两种合适的创建Message方式:Message.obtain(); 和 handler.obtainMessage();
  1. 在子线程中用Toast弹出提示?
  • 首先在子线程可以使用Toast。
  • Toast的源码中有用到Handler,就是说在子线程中创建了Handler。
  • 在子线程中创建Handler,就的创建Looper对象,就得调用Looper.prepare()
  • 有了Looper可以将Toast加入到消息队列中,在子线程中调用Looper.loop(),完成整个消息的运转。
  1. Handler中的Callback 有什么用呢?
  • 在Handler中Callback是一个接口,内部有一个handleMessage方法。
  • 注释翻译:使用Callback接口可以初始化一个Handle实例,可以避免使用一个子类继承Handler 实现handlerMessage()的方式来创建Handler。
  • 如果这样实现的话,那么在dispatchMessage方法中,就调用不了Handler中的handleMessage了,而是调用了Callback接口中的handleMessage方法。这样就把消息拦截了,不再Handle 中的handleMessage方法中执行。
  • 是一种创建Handler的方式。
  1. Android中的Looper.loop()是一个无限循环的方法,为什么没有导致CPU占用率飙升呢?
  • 原因在于MessageQueue中有一个Jni的的方法nativeInit(),继续深究下去发现这个nativeInt方法内部创建了一个epoll,这个epoll是Linux中的一种多路IO复用方式,Java通过Jni调用到了底层,让无限循环处于阻塞睡眠状态,避免浪费CPU
  1. Handler 创建的所在的线程决定handleMessage或者 post(new Runnable(){run()})吗?
  • 不是的,取决于Handler绑定的Looper对象创建所在的线程。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,639评论 6 513
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,093评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,079评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,329评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,343评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,047评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,645评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,565评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,095评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,201评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,338评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,014评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,701评论 3 332
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,194评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,320评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,685评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,345评论 2 358

推荐阅读更多精彩内容