美文网首页
Handler 机制和原理

Handler 机制和原理

作者: wervy | 来源:发表于2019-12-19 16:10 被阅读0次

    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,是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,对数据存储后,只有在线程中才可以获取到存储的数据,对于其他线程来说是无法获取到数据

    相关文章

      网友评论

          本文标题:Handler 机制和原理

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