Handler 机制和原理

2019-12-19  本文已影响0人  wervy

Handler我们在项目中经常用到,因为Android中避免在主线程中处理耗时操作,我们就会借助Handler来更新数据,使用方式就在主线程中创建Handler对象,在例如请求网络数据时的子线程中发送消息,在主线程中收到消息,更新ui。

Handler的消息处理由以下几个部分组成

Looper, Message, MessageQueue,ThreadLocal

我们先看一下各个名词的源码描述部分:

Meeage

image.png

翻译一下:定义一个消息,该消息包含描述和任意的数据对象,用来发送给Handler,Message包含2个int的字段和一个额外的对象字段。
可以通过obtain Message.obtain()或Handler.obtainMessage()来的到Message

/**
     * User-defined message code so that the recipient can identify
     * what this message is about. Each {@link Handler} has its own name-space
     * for message codes, so you do not need to worry about yours conflicting
     * with other handlers.
     */
    public int what;

    /**
     * arg1 and arg2 are lower-cost alternatives to using
     * {@link #setData(Bundle) setData()} if you only need to store a
     * few integer values.
     */
    public int arg1;

    /**
     * arg1 and arg2 are lower-cost alternatives to using
     * {@link #setData(Bundle) setData()} if you only need to store a
     * few integer values.
     */
    public int arg2;

    /**
     * An arbitrary object to send to the recipient.  When using
     * {@link Messenger} to send the message across processes this can only
     * be non-null if it contains a Parcelable of a framework class (not one
     * implemented by the application).   For other data transfer use
     * {@link #setData}.
     *
     * <p>Note that Parcelable objects here are not supported prior to
     * the {@link android.os.Build.VERSION_CODES#FROYO} release.
     */
    public Object obj;

what用来命名的,区别其他的Handler
arg1arg2用来存储int数据的
what是用来携带对象数据的

MessageQueue

/**
 * Low-level class holding the list of messages to be dispatched by a
 * {@link Looper}.  Messages are not added directly to a MessageQueue,
 * but rather through {@link Handler} objects associated with the Looper.
 *
 * <p>You can retrieve the MessageQueue for the current thread with
 * {@link Looper#myQueue() Looper.myQueue()}.
 */

被Looper分配的消息列表,Messages不是直接添加到MessageQueue中的,而是通过Looper相关联的Hander添加的
你可以通过Looper.myQueue来检索当前的MessageQueue

Looper

/**
  * Class used to run a message loop for a thread.  Threads by default do
  * not have a message loop associated with them; to create one, call
  * {@link #prepare} in the thread that is to run the loop, and then
  * {@link #loop} to have it process messages until the loop is stopped.
  *
  * <p>Most interaction with a message loop is through the
  * {@link Handler} class.
  *
  * <p>This is a typical example of the implementation of a Looper thread,
  * using the separation of {@link #prepare} and {@link #loop} to create an
  * initial Handler to communicate with the Looper.
  *
  * <pre>
  *  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();
  *      }
  *  }</pre>
  */

Looper用于为线程运行消息循环的,线程默认是没有消息循环的,要创建一个,请调用Looper.prepare在执行循环的线程中,然后在调用Looper.loop让它处理消息,直到循环停止。
示例如下:

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的使用情况,但是我们在主线程中使用Handler并没有使用Looper.prepare()和 Looper.loop()
类似这种写法:

public class TestHandlerActivity extends AppCompatActivity {


        private static final String TAG = "TestHandlerActivity";

        private Handler mHandler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                //获得刚才发送的Message对象,然后在这里进行UI操作
                Log.e(TAG,"------------> msg.what = " + msg.what);
            }
        };


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

        private void initData() {

            //开启一个线程模拟处理耗时的操作
            new Thread(new Runnable() {
                @Override
                public void run() {

                    SystemClock.sleep(2000);
                    //通过Handler发送一个消息切换回主线程(mHandler所在的线程)
                    mHandler.sendEmptyMessage(0);
                }
            }).start();

        }   

很奇怪是不是

我们看一下ActivityThread的启动函数Main函数

public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");

        // Install selective syscall interception
        AndroidOs.install();

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

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

        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();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

原来是在main方法中调用Looper.prepareMainLooper()和Looper.loop()

我们来看一下prepareMainLooper这个方法

/**
     * Initialize the current thread as a looper, marking it as an
     * application's main looper. The main looper for your application
     * is created by the Android environment, so you should never need
     * to call this function yourself.  See also: {@link #prepare()}
     */
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

main looper是Android应用启动时就创建了,你用不着来调用这个方法。
在子线程中创建Handler,示例如下:

//开启一个线程模拟处理耗时的操作
            new Thread(new Runnable() {
                @Override
                public void run() {
                    mHandler.sendEmptyMessage(0);

                    //调用Looper.prepare()方法
                    Looper.prepare();

                    mHandlerThread = new Handler(){
                        @Override
                        public void handleMessage(Message msg) {
                            super.handleMessage(msg);
                            Log.e("sub thread","---------> msg.what = " + msg.what);
                        }
                    };

                    mHandlerThread.sendEmptyMessage(1);

                    //调用Looper.loop()方法
                    Looper.loop();
                }
            }).start();

ThreadLocal

我们来看一下ThreadLocal的文字描述部分

/**
 * This class provides thread-local variables.  These variables differ from
 * their normal counterparts in that each thread that accesses one (via its
 * {@code get} or {@code set} method) has its own, independently initialized
 * copy of the variable.  {@code ThreadLocal} instances are typically private
 * static fields in classes that wish to associate state with a thread (e.g.,
 * a user ID or Transaction ID).
 *
 * <p>For example, the class below generates unique identifiers local to each
 * thread.
 * A thread's id is assigned the first time it invokes {@code ThreadId.get()}
 * and remains unchanged on subsequent calls.
 * <pre>
 * import java.util.concurrent.atomic.AtomicInteger;
 *
 * public class ThreadId {
 *     // Atomic integer containing the next thread ID to be assigned
 *     private static final AtomicInteger nextId = new AtomicInteger(0);
 *
 *     // Thread local variable containing each thread's ID
 *     private static final ThreadLocal&lt;Integer&gt; threadId =
 *         new ThreadLocal&lt;Integer&gt;() {
 *             &#64;Override protected Integer initialValue() {
 *                 return nextId.getAndIncrement();
 *         }
 *     };
 *
 *     // Returns the current thread's unique ID, assigning it if necessary
 *     public static int get() {
 *         return threadId.get();
 *     }
 * }
 * </pre>
 * <p>Each thread holds an implicit reference to its copy of a thread-local
 * variable as long as the thread is alive and the {@code ThreadLocal}
 * instance is accessible; after a thread goes away, all of its copies of
 * thread-local instances are subject to garbage collection (unless other
 * references to these copies exist).
 *
 * @author  Josh Bloch and Doug Lea
 * @since   1.2
 */

ThreadLocal 不是 Thread,是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,对数据存储后,只有在线程中才可以获取到存储的数据,对于其他线程来说是无法获取到数据

上一篇下一篇

猜你喜欢

热点阅读