文章较长,阅读时刻大概3~6分钟,如有不对,欢迎评论讨论

Handler是Android中的音讯处理机制,是一种线程间通信的解决方案,一起你也能够理解为它天然的为咱们在主线程创立一个行列,行列中的音讯次序便是咱们设置的推迟的时刻,假如你想在Android中完结一个行列的功用,无妨第一时刻考虑一下它。本文分为三部分:

Handler的源码和常见问题的回答

  • 一个线程中最多有多少个Handler,Looper,MessageQueue?
  • Looper死循环为什么不会导致运用卡死,会耗费很多资源吗?
  • 子线程的怎么更新UI,比方Dialog,Toast等?体系为什么不建议子线程中更新UI?
  • 主线程怎么拜访网络?
  • 怎么处理Handler运用不当构成的内存走漏?
  • Handler的音讯优先级,有什么运用场景?
  • 主线程的Looper何时退出?能否手动退出?
  • 怎么判别当前线程是安卓主线程?
  • 正确创立Message实例的办法?

Handler深层次问题回答

  • ThreadLocal
  • epoll机制
  • Handle同步屏障机制
  • Handler的锁相关问题
  • Handler中的同步办法
  • Handler在体系以及第三方结构的一些运用
  • HandlerThread
  • IntentService
  • 怎么打造一个不崩溃的APP
  • Glide中的运用

Handler的源码和常见问题的回答

下面来看一下官方对其的定义:

A Handler allows you to send and process Message and Runnable objects associated with a thread's MessageQueue. Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler it is bound to a Looper. It will deliver messages and runnables to that Looper's message queue and execute them on that Looper's thread.

大意便是Handler答应你发送Message/Runnable到线程的音讯行列(MessageQueue)中,每个Handler实例和一个线程以及那个线程的音讯行列相相关。当你创立一个Handler时应该和一个Looper进行绑定(主线程默许现已创立Looper了,子线程需求自己创立Looper),它向Looper的对应的音讯行列传送Message/Runnable一起在那个Looper地点线程处理对应的Message/Runnable。下面这张图便是Handler的作业流程。
Handler作业流程图

想实现Android队列功能?Handler内功心法,你值得拥有!——Handler源码和常见问题的解答

能够看到在Thread中,Looper的这个传送带其实就一个死循环,它不断的从音讯行列MessageQueue中不断的取音讯,终究交给Handler.dispatchMessage进行音讯的分发,而Handler.sendXXX,Handler.postXXX这些办法把音讯发送到音讯行列中MessageQueue,整个形式其实便是一个出产者-消费者形式,连绵不断的出产音讯,处理音讯,没有音讯时进行休眠。MessageQueue是一个由单链表构成的优先级行列(取的都是头部,所以说是行列)。

//ActivityThread.java
public static void main(String[] args) {
    
    Looper.prepareMainLooper();
        
    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");
}

能够看到在ActivityThread中的main办法中,咱们先调用了Looper.prepareMainLooper()办法,然后获取当前线程的Handler,终究调用Looper.loop()。先来看一下Looper.prepareMainLooper()办法。

//Looper.java  
/**
* 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();
     }
}
//prepare
private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
}

能够看到在Looper.prepareMainLooper()办法中创立了当前线程的Looper,一起将Looper实例存放到线程局部变量sThreadLocal(ThreadLocal)中,也便是每个线程有自己的Looper。在创立Looper的时分也创立了该线程的音讯行列,能够看到prepareMainLooper会判别sMainLooper是否有值,假如调用多次,就会抛出反常,所以也便是说主线程的Looper和MessageQueue只会有一个。同理子线程中调用Looper.prepare()时,会调用prepare(true)办法,假如多次调用,也会抛出每个线程只能由一个Looper的反常,总结起来便是每个线程中只要一个Looper和MessageQueue。

//Looper.java
private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

再来看看主线程sMainThreadHandler = thread.getHandler(),getHandler获取到的实际上便是mH这个Handler。

//ActivityThread.java
final H mH = new H(); 
@UnsupportedAppUsage
    final Handler getHandler() {
      return mH;
}

mH这个Handler是ActivityThread的内部类,经过检查handMessage办法,能够看到这个Handler处理四大组件,Application等的一些音讯,比方创立Service,绑定Service的一些音讯。

//ActivityThread.java
class H extends Handler {
    
    public void handleMessage(Message msg) {
        if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
        switch (msg.what) {
            case BIND_APPLICATION:
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
                AppBindData data = (AppBindData)msg.obj;
                handleBindApplication(data);
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                break;
            case EXIT_APPLICATION:
                if (mInitialApplication != null) {
                    mInitialApplication.onTerminate();
                }
                Looper.myLooper().quit();
                break;
            case RECEIVER:
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "broadcastReceiveComp");
                handleReceiver((ReceiverData)msg.obj);
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                break;
            case CREATE_SERVICE:
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, ("serviceCreate: " + String.valueOf(msg.obj)));
                handleCreateService((CreateServiceData)msg.obj);
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                break;
            case BIND_SERVICE:
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceBind");
                handleBindService((BindServiceData)msg.obj);
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                break;
            case UNBIND_SERVICE:
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceUnbind");
                handleUnbindService((BindServiceData)msg.obj);
                schedulePurgeIdler();
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                break;
            case SERVICE_ARGS:
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, ("serviceStart: " + String.valueOf(msg.obj)));
                handleServiceArgs((ServiceArgsData)msg.obj);
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                break;
            case STOP_SERVICE:
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceStop");
                handleStopService((IBinder)msg.obj);
                schedulePurgeIdler();
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                break;
            
            case APPLICATION_INFO_CHANGED:
                mUpdatingSystemConfig = true;
                try {
                    handleApplicationInfoChanged((ApplicationInfo) msg.obj);
                } finally {
                    mUpdatingSystemConfig = false;
                }
                break;
            case RUN_ISOLATED_ENTRY_POINT:
                handleRunIsolatedEntryPoint((String) ((SomeArgs) msg.obj).arg1,
                        (String[]) ((SomeArgs) msg.obj).arg2);
                break;
            case EXECUTE_TRANSACTION:
                final ClientTransaction transaction = (ClientTransaction) msg.obj;
                mTransactionExecutor.execute(transaction);
                if (isSystem()) {
                    // Client transactions inside system process are recycled on the client side
                    // instead of ClientLifecycleManager to avoid being cleared before this
                    // message is handled.
                    transaction.recycle();
                }
                // TODO(lifecycler): Recycle locally scheduled transactions.
                break;
            case RELAUNCH_ACTIVITY:
                handleRelaunchActivityLocally((IBinder) msg.obj);
                break;
            case PURGE_RESOURCES:
                schedulePurgeIdler();
                break;
        }
        Object obj = msg.obj;
        if (obj instanceof SomeArgs) {
            ((SomeArgs) obj).recycle();
        }
        if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));
    }
}

终究咱们检查Looper.loop()办法:

//Looper.java
public static void loop() {
      //获取ThreadLocal中的Looper
    final Looper me = myLooper();
        
    final MessageQueue queue = me.mQueue;
    
    for (;;) { //死循环
          //获取音讯
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }
                
        msg.target.dispatchMessage(msg);
                
        //收回复用  
        msg.recycleUnchecked();
    }
}

在loop办法中是一个死循环,在这儿从音讯行列中不断的获取音讯queue.next(),然后经过Handler(msg.target)进行音讯的分发,其实并没有什么详细的绑定,由于Handler在每个线程中对应只要一个Looper和音讯行列MessageQueue,自然要靠它来处理,也便是是调用Looper.loop()办法。在Looper.loop()的死循环中不断的取音讯,终究收回复用。

这儿要强调一下Message中的参数target(Handler),正是这个变量,每个Message才能找到对应的Handler进行音讯分发,让多个Handler一起作业。

再来看看子线程中是怎么处理的,首先在子线程中创立一个Handler并发送Runnable。

@Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_three);
        new Thread(new Runnable() {
            @Override
            public void run() {
                new Handler().post(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(HandlerActivity.this,"toast",Toast.LENGTH_LONG).show();
                    }
                });
            }
        }).start();
    }

运转后能够看到错误日志,能够看到提示咱们需求在子线程中调用Looper.prepare()办法,实际上便是要创立一个Looper和你的Handler进行“相关”。

 --------- beginning of crash
2020-11-09 15:51:03.938 21122-21181/com.jackie.testdialog E/AndroidRuntime: FATAL EXCEPTION: Thread-2
    Process: com.jackie.testdialog, PID: 21122
    java.lang.RuntimeException: Can't create handler inside thread Thread[Thread-2,5,main] that has not called Looper.prepare()
        at android.os.Handler.<init>(Handler.java:207)
        at android.os.Handler.<init>(Handler.java:119)
        at com.jackie.testdialog.HandlerActivity$1.run(HandlerActivity.java:31)
        at java.lang.Thread.run(Thread.java:919)

增加Looper.prepare()创立Looper,一起调用Looper.loop()办法开端处理音讯。

 @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_three);
        new Thread(new Runnable() {
            @Override
            public void run() {
                //创立Looper,MessageQueue
                Looper.prepare();
                new Handler().post(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(HandlerActivity.this,"toast",Toast.LENGTH_LONG).show();
                    }
                });
                //开端处理音讯
                Looper.loop();
            }
        }).start();
    }

这儿需求留意在一切作业处理完结后应该调用quit办法来停止音讯循环,不然这个子线程就会一向处于循环等候的状况,因而不需求的时分停止Looper,调用Looper.myLooper().quit()。

看完上面的代码或许你会有一个疑问,在子线程中更新UI(进行Toast)不会有问题吗,咱们Android不是不答应在子线程更新UI吗,实际上并不是这样的,在ViewRootImpl中的checkThread办法会校验mThread != Thread.currentThread(),mThread的初始化是在ViewRootImpl的的结构器中,也便是说一个创立ViewRootImpl线程必须和调用checkThread地点的线程一致,UI的更新并非只能在主线程才能进行。

void checkThread() {
    if (mThread != Thread.currentThread()) {
        throw new CalledFromWrongThreadException(
                "Only the original thread that created a view hierarchy can touch its views.");
    }
}

这儿需求引入一些概念,Window是Android中的窗口,每个Activity和Dialog,Toast分别对应一个详细的Window,Window是一个抽象的概念,每一个Window都对应着一个View和一个ViewRootImpl,Window和View经过ViewRootImpl来树立联络,因而,它是以View的形式存在的。咱们来看一下Toast中的ViewRootImpl的创立进程,调用toast的show办法终究会调用到其handleShow办法。

//Toast.java
public void handleShow(IBinder windowToken) {
        
    if (mView != mNextView) {
        // Since the notification manager service cancels the token right
        // after it notifies us to cancel the toast there is an inherent
        // race and we may attempt to add a window after the token has been
        // invalidated. Let us hedge against that.
        try {
            mWM.addView(mView, mParams); //进行ViewRootImpl的创立
            trySendAccessibilityEvent();
        } catch (WindowManager.BadTokenException e) {
            /* ignore */
        }
    }
}

这个mWM(WindowManager)的终究完结者是WindowManagerGlobal,其的addView办法中会创立ViewRootImpl,然后进行root.setView(view, wparams, panelParentView),经过ViewRootImpl来更新界面并完结Window的增加进程。

//WindowManagerGlobal.java
root = new ViewRootImpl(view.getContext(), display); //创立ViewRootImpl
    view.setLayoutParams(wparams);
    mViews.add(view);
    mRoots.add(root);
    mParams.add(wparams);
    // do this last because it fires off messages to start doing things
    try {
        //ViewRootImpl
        root.setView(view, wparams, panelParentView);
    } catch (RuntimeException e) {
        // BadTokenException or InvalidDisplayException, clean up.
        if (index >= 0) {
            removeViewLocked(index, true);
        }
        throw e;
    }
}

setView内部会经过requestLayout来完结异步刷新请求,一起也会调用checkThread办法来查验线程的合法性。

@Override
public void requestLayout() {
    if (!mHandlingLayoutInLayoutRequest) {
        checkThread();
        mLayoutRequested = true;
        scheduleTraversals();
    }
}

因而,咱们的ViewRootImpl的创立是在子线程,所以mThread的值也是子线程,一起咱们的更新也是在子线程,所以不会产生反常,一起也能够参考这篇文章分析,写的非常详细。同理下面的代码也能够验证这个状况。

//子线程中调用    
public void showDialog(){
        new Thread(new Runnable() {
            @Override
            public void run() {
                //创立Looper,MessageQueue
                Looper.prepare();
                new Handler().post(new Runnable() {
                    @Override
                    public void run() {
                        builder = new AlertDialog.Builder(HandlerActivity.this);
                        builder.setTitle("jackie");
                        alertDialog = builder.create();
                        alertDialog.show();
                        alertDialog.hide();
                    }
                });
                //开端处理音讯
                Looper.loop();
            }
        }).start();
    }

在子线程中调用showDialog办法,先调用alertDialog.show()办法,再调用alertDialog.hide()办法,hide办法只是将Dialog隐藏,并没有做其他任何操作(没有移除Window),然后再在主线程调用alertDialog.show();便会抛出Only the original thread that created a view hierarchy can touch its views反常了。

2020-11-09 18:35:39.874 24819-24819/com.jackie.testdialog E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.jackie.testdialog, PID: 24819
    android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
        at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:8191)
        at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:1420)
        at android.view.View.requestLayout(View.java:24454)
        at android.view.View.setFlags(View.java:15187)
        at android.view.View.setVisibility(View.java:10836)
        at android.app.Dialog.show(Dialog.java:307)
        at com.jackie.testdialog.HandlerActivity$2.onClick(HandlerActivity.java:41)
        at android.view.View.performClick(View.java:7125)
        at android.view.View.performClickInternal(View.java:7102)

所以在线程中更新UI的重点是创立它的ViewRootImpl和checkThread地点的线程是否一致。

怎么在主线程中拜访网络
在网络请求之前增加如下代码:

StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitNetwork().build();
StrictMode.setThreadPolicy(policy);

StrictMode(苛刻形式)Android2.3引入,用于检测两大问题:ThreadPolicy(线程战略)和VmPolicy(VM战略),这儿把苛刻形式的网络检测关了,就能够在主线程中履行网络操作了,一般是不建议这么做的。

体系为什么不建议在子线程中拜访UI?
这是由于 Android 的UI控件不是线程安全的,假如在多线程中并发拜访或许会导致UI控件处于不可预期的状况,那么为什么体系不对UI控件的拜访加上锁机制呢?缺点有两个:

首先加上锁机制会让UI拜访的逻辑变得复杂
锁机制会降低UI拜访的效率,由于锁机制会阻塞某些线程的履行。
所以最简略且高效的办法便是采用单线程模型来处理UI操作。(安卓开发艺术探究)

子线程怎么告诉主线程更新UI(都是经过Handle发送音讯到主线程操作UI的)
主线程中定义 Handler,子线程经过 mHandler 发送音讯,主线程 Handler 的 handleMessage 更新UI。
用 Activity 目标的 runOnUiThread 办法。
创立 Handler,传入 getMainLooper。
View.post(Runnable r) 。
Looper死循环为什么不会导致运用卡死,会耗费很多资源吗?
早年面的主线程、子线程的分析能够看出,Looper会在线程中不断的检索音讯,假如是子线程的Looper死循环,一旦任务完结,用户应该手动退出,而不是让其一向休眠等候。(引证自Gityuan)线程其实便是一段可履行的代码,当可履行的代码履行完结后,线程的生命周期便该停止了,线程退出。而关于主线程,咱们是绝不期望会被运转一段时刻,自己就退出,那么怎么保证能一向存活呢?简略做法便是可履行代码是能一向履行下去的,死循环便能保证不会被退出,例如,binder 线程也是采用死循环的办法,经过循环办法不同与 Binder 驱动进行读写操作,当然并非简略地死循环,无音讯时会休眠。Android是基于音讯处理机制的,用户的行为都在这个Looper循环中,咱们在休眠时点击屏幕,便唤醒主线程继续进行作业。

主线程的死循环一向运转是不是特别耗费 CPU 资源呢?其实不然,这儿就涉及到 Linux pipe/epoll机制,简略说便是在主线程的 MessageQueue 没有音讯时,便阻塞在 loop 的 queue.next() 中的 nativePollOnce() 办法里,此刻主线程会开释 CPU 资源进入休眠状况,直到下个音讯抵达或者有事务产生,经过往 pipe 管道写端写入数据来唤醒主线程作业。这儿采用的 epoll 机制,是一种IO多路复用机制,能够一起监控多个描述符,当某个描述符安排妥当(读或写安排妥当),则马上告诉相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。所以说,主线程大多数时分都是处于休眠状况,并不会耗费很多CPU资源。

主线程的Looper何时退出
在App退出时,ActivityThread中的mH(Handler)收到音讯后,履行退出。

//ActivityThread.java
case EXIT_APPLICATION:
    if (mInitialApplication != null) {
        mInitialApplication.onTerminate();
    }
    Looper.myLooper().quit();
    break;

假如你尝试手动退出主线程Looper,便会抛出如下反常。

Caused by: java.lang.IllegalStateException: Main thread not allowed to quit.
    at android.os.MessageQueue.quit(MessageQueue.java:428)
    at android.os.Looper.quit(Looper.java:354)
    at com.jackie.testdialog.Test2Activity.onCreate(Test2Activity.java:29)
    at android.app.Activity.performCreate(Activity.java:7802)
    at android.app.Activity.performCreate(Activity.java:7791)
    at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1299)
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3245)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3409) 
    at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83) 
    at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) 
    at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95) 
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2016) 
    at android.os.Handler.dispatchMessage(Handler.java:107) 
    at android.os.Looper.loop(Looper.java:214) 
    at android.app.ActivityThread.main(ActivityThread.java:7356) 
    at java.lang.reflect.Method.invoke(Native Method) 
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492) 
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)

为什么不答应退出呢,由于主线程不答应退出,一旦退出就意味着程序挂了,退出也不应该用这种办法退出。
Handler的音讯处理次序
在Looper履行音讯循环loop()时会履行下面这行代码,msg.targe便是这个Handler目标

msg.target.dispatchMessage(msg);

咱们来看看dispatchMessage的源码:

 public void dispatchMessage(@NonNull Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            //假如 callback 处理了该 msg 并且回来 true, 就不会再回调 handleMessage
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

1.假如Message这个目标有CallBack回调的话,这个CallBack实际上是个Runnable,就只履行这个回调,然后就完毕了,创立该Message的CallBack代码如下:

Message msgCallBack = Message.obtain(handler, new Runnable() {
    @Override
    public void run() {
    }
});

而handleCallback办法中调用的是Runnable的run办法。

private static void handleCallback(Message message) {
    message.callback.run();
}

2.假如Message目标没有CallBack回调,进入else分支判别Handler的CallBack是否为空,不为空履行CallBack的handleMessage办法,然后return,构建Handler的CallBack代码如下:

Handler.Callback callback = new Handler.Callback() {
    @Override
    public boolean handleMessage(@NonNull Message msg) {
          //retrun true,就不履行下面的逻辑了,能够用于做优先级的处理
        return false;
    }
};

3.终究才调用到Handler的handleMessage()函数,也便是咱们经常去重写的函数,在该办法中做音讯的处理。

运用场景
能够看到Handler.Callback 有优先处理音讯的权力 ,当一条音讯被 Callback 处理并阻拦(回来 true),那么 Handler 的 handleMessage(msg) 办法就不会被调用了;假如 Callback 处理了音讯,可是并没有阻拦,那么就意味着一个音讯能够一起被 Callback 以及 Handler 处理。咱们能够运用CallBack这个阻拦来阻拦Handler的音讯。

场景:Hook ActivityThread.mH , 在 ActivityThread 中有个成员变量 mH ,它是个 Handler,又是个极其重要的类,简直一切的插件化结构都运用了这个办法。

Handler.post(Runnable r)办法的履行逻辑
咱们需求分析平时常用的Handler.post(Runnable r)办法是怎么履行的,是否新创立了一个线程了呢,实际上并没有,这个Runnable目标只是被调用了它的run办法,底子并没有发动一个线程,源码如下:

//Handler.java
public final boolean post(@NonNull Runnable r) {
   return  sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

终究该Runnable目标被包装成一个Message目标,也便是这个Runnable目标便是该Message的CallBack目标了,有优先履行的权力了。

Handler是怎么进行线程切换的

原理很简略,线程间是共享资源的,子线程经过handler.sendXXX,handler.postXXX等办法发送音讯,然后经过Looper.loop()在音讯行列中不断的循环检索音讯,终究交给handle.dispatchMessage办法进行音讯的分发处理。

怎么处理Handler运用不当构成的内存走漏?

有延时音讯,在界面关闭后及时移除Message/Runnable,调用handler.removeCallbacksAndMessages(null)
内部类导致的内存走漏改为静态内部类,并对上下文或者Activity/Fragment运用弱引证。
一起还有一个很要害的点,假如有个延时音讯,当界面关闭时,该Handler中的音讯还没有处理完毕,那么终究这个音讯是怎么处理的?经过测验,比方我翻开界面后推迟10s发送音讯,关闭界面,终究在Handler(匿名内部类创立的)的handMessage办法中仍是会收到音讯(打印日志)。由于会有一条MessageQueue -> Message -> Handler -> Activity的引证链,所以Handler不会被毁掉,Activity也不会被毁掉。

正确创立Message实例

经过 Message 的静态办法 Message.obtain() 获取;
经过 Handler 的公有办法 handler.obtainMessage()
一切的音讯会被收回,放入sPool中,运用享元设计形式。

今日共享到此完毕,对你有协助的话,点个赞再走呗,每日一个面试小技巧

关注公众号:Android老皮
解锁 《Android十大板块文档》 ,让学习更靠近未来实战。已构成PDF版

内容如下

1.Android车载运用开发体系学习指南(附项目实战)
2.Android Framework学习指南,助力成为体系级开发高手
3.2023最新Android中高级面试题汇总+解析,告别零offer
4.企业级Android音视频开发学习道路+项目实战(附源码)
5.Android Jetpack从入门到通晓,构建高质量UI界面
6.Flutter技术解析与实战,跨平台首要之选
7.Kotlin从入门到实战,全方面提升架构根底
8.高级Android插件化与组件化(含实战教程和源码)
9.Android 功能优化实战+360全方面功能调优
10.Android零根底入门到通晓,高手进阶之路