• 深入理解Handler(上)


    1.二种创建方法及内存泄漏

    MainActivity

    package com.example.handler_sample;
    
    import androidx.annotation.NonNull;
    import androidx.appcompat.app.AppCompatActivity;
    
    import android.os.Bundle;
    import android.os.Handler;
    import android.os.Message;
    import android.view.View;
    import android.widget.TextView;
    
    public class MainActivity extends AppCompatActivity {
    
    
        public static final int MSG_CODE = 1001;
        private TextView textView;
        //handler创建方法一 使用Callback
        private Handler handler1=new Handler(new Handler.Callback() {
            @Override
            public boolean handleMessage(@NonNull Message msg) {
                return false;
            }
        });
        //handler创建方法二 重写handlerMessage()方法
        private Handler handler2=new Handler(){
            @Override
            public void handleMessage(@NonNull Message msg) {
                super.handleMessage(msg);
                textView.setText(msg.obj.toString());
            }
        };
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            textView = findViewById(R.id.tv);
            test();
        }
    
        //子线程创建发送消息给主线程,更新组件的显示,并且赋值消息
        private void test() {
            new Thread(()->{
                //常规写法
                Message message = new Message();
                message.obj="Next";
                message.what= MSG_CODE;
                handler2.sendMessage(message);
            }).start();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    activity_main.xml

    
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <TextView
            android:id="@+id/tv"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:text="Hello World!" />
    
    RelativeLayout>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    内存泄漏分析:

    添加测试代码

    package com.example.handler_sample;
    
    import androidx.annotation.NonNull;
    import androidx.appcompat.app.AppCompatActivity;
    
    import android.content.Intent;
    import android.os.Bundle;
    import android.os.Handler;
    import android.os.Message;
    import android.util.Log;
    import android.view.View;
    import android.widget.TextView;
    
    import java.util.concurrent.TimeUnit;
    
    public class MainActivity extends AppCompatActivity {
    
    
        public static final int MSG_CODE = 1001;
        private TextView textView;
        //handler创建方法一 使用Callback
        private Handler handler1=new Handler(new Handler.Callback() {
            @Override
            public boolean handleMessage(@NonNull Message msg) {
                startActivity(new Intent(MainActivity.this,SecondActivity.class));
                return false;
            }
        });
        //handler创建方法二 重写handlerMessage()方法
        private Handler handler2=new Handler(){
            @Override
            public void handleMessage(@NonNull Message msg) {
                super.handleMessage(msg);
                textView.setText(msg.obj.toString());
            }
        };
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            textView = findViewById(R.id.tv);
            test();
        }
    
        //子线程创建发送消息给主线程,更新组件的显示,并且赋值消息
        private void test() {
            new Thread(()->{
                //常规写法
               Message message = new Message();
              /*   message.obj="Next";
                message.what= MSG_CODE;
                handler2.sendMessage(message);*/
                try {
                    message.what=MSG_CODE;
                    TimeUnit.SECONDS.sleep(3);//休眠3秒 并且销毁Activity
                    handler1.sendMessage(message);//跳转到第二个界面
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            Log.e("TAG", "onDestroy: ");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69

    第二个Activity

    package com.example.handler_sample;
    
    import android.os.Bundle;
    
    import androidx.annotation.Nullable;
    import androidx.appcompat.app.AppCompatActivity;
    
    public class SecondActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_personal);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    activity_personal.xml

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:text="Hi,186"/>
    
    </RelativeLayout>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    让线程休眠三秒,发消息给handler1,然后跳转到第二个界面启动APP的时候,退出销毁掉Activity,但是三秒后还是会跳转到第二个界面,这里就出现了假销毁现象,也就是内存泄漏

    在Activity销毁时,移除message

    @Override
    protected void onDestroy() {
        super.onDestroy();
        handler1.removeMessages(MSG_CODE);
        Log.e("TAG", "onDestroy: ");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    也是无济于事,因为线程休眠的时候还未将消息放入到消息队列中,销毁时remove的是空

    使用sendMessageDelayed()延时发送

    private void test() {
        new Thread(()->{
            //常规写法
           Message message = new Message();
          /*   message.obj="Next";
            message.what= MSG_CODE;
            handler2.sendMessage(message);*/
    
                message.what=MSG_CODE;
    
                handler1.sendMessageDelayed(message,3000);
    
        }).start();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    此时销毁Activity即可终止发送

    针对第一种方式的解决方法

    private void test() {
        new Thread(()->{
            //常规写法
           Message message = new Message();
                message.what=MSG_CODE;
            try {
                TimeUnit.SECONDS.sleep(3);//休眠3秒 并且销毁Activity
               if (handler1!=null)handler1.sendMessage(message);//跳转到第二个界面
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        handler1=null;
        Log.e("TAG", "onDestroy: ");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    当activity销毁时直接给hander1赋为null,线程判断hander1不为空再发送消息,这时候休眠结束就不会发送消息了

    不推荐的写法

    private Message message;
    @Override
    protected void onDestroy() {
        super.onDestroy();
        //  handler1.removeMessages(MSG_CODE);
        // handler1=null;
        message.recycle();
        Log.e("TAG", "onDestroy: ");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    让Message设为全局变量,然后销毁的时候回收message,如果是发送延时消息没问题,如果是直接发送,则会抛出消息正在使用的异常

    2.不能在子线程创建Handler

        private void test() {
            new Thread(()->{
                new Handler();
            }).start();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    直接闪退

    java.lang.RuntimeException: Can't create handler inside thread Thread[Thread-2,5,main] that has not called Looper.prepare()
    
    • 1

    会抛出异常,因为应用启动时会调用ActivtyThead方法 其中的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);
        // Call per-process mainline module initialization.
        initializeMainlineModules();
        Process.setArgV0("");
        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");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    里边有关于looper的检测

    Looper.prepareMainLooper();
    
    • 1

    进去prepareMainLooper()发现

    @Deprecated
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    再进prepare(false)

    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));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    sThreadLocal.set(new Looper(quitAllowed));
    
    • 1

    它这里new了一个Looper 这个Looper是主线程的Looper,这个sThreadLocal是存在在ThreadLocalMap里的,查看其sThreadLocal.get()方法

    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    Thread t = Thread.currentThread();
    
    • 1

    从主线程的ThreadLocal去给get得到的是主线程做为key

    ThreadLocalMap map = getMap(t);
    
    • 1

    然后从getmap去找,找不到就抛出异常

    再看new Handler

    public Handler(@Nullable 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());
            }
        }
    
        mLooper = Looper.myLooper();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    mLooper = Looper.myLooper();
    
    • 1

    Looper从myLooper中去取

    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
    
    • 1
    • 2
    • 3

    结果取的looper 是sThreadLocal.get()去主线程拿 从子线程拿是找不到的 因为上边发现sThreadLocal.get()是从ThreadLocalMap去取值。key已经设置为主线程,value是looper,此时的new Handler()是从子线程进去的所以拿不到,最终抛出异常

    handler中

    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread " + Thread.currentThread()
                    + " that has not called Looper.prepare()");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    mLooper为null就抛出异常,此时已经很清晰了

    3.子线程修改UI

        private void test() {
            new Thread(()->{
            textView.setText("andy");
            }).start();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    发现子线程是可以修改控件的

    使用Toast

    private void test() {
        new Thread(()->{
            Toast.makeText(this, "andy", Toast.LENGTH_SHORT).show();
        }).start();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    发现闪退,并且抛出异常

    java.lang.RuntimeException: Can't toast on a thread that has not called Looper.prepare()
    
    • 1

    设置线程休眠后修改TextView

        private void test() {
            new Thread(()->{
                try {
                    TimeUnit.SECONDS.sleep(3);
                    textView.setText("andy");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    发现休眠后闪退,抛出异常

        android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
    
    • 1

    进入setText方法找到

    if (mLayout != null) {
        checkForRelayout();
    }
    
    • 1
    • 2
    • 3

    进入 checkForRelayout()方法

    private void checkForRelayout() {
        // If we have a fixed width, we can just swap in a new text layout
        // if the text height stays the same or if the view height is fixed.
    
        if ((mLayoutParams.width != LayoutParams.WRAP_CONTENT
                || (mMaxWidthMode == mMinWidthMode && mMaxWidth == mMinWidth))
                && (mHint == null || mHintLayout != null)
                && (mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight() > 0)) {
            // Static width, so try making a new text layout.
    
            int oldht = mLayout.getHeight();
            int want = mLayout.getWidth();
            int hintWant = mHintLayout == null ? 0 : mHintLayout.getWidth();
    
            /*
             * No need to bring the text into view, since the size is not
             * changing (unless we do the requestLayout(), in which case it
             * will happen at measure).
             */
            makeNewLayout(want, hintWant, UNKNOWN_BORING, UNKNOWN_BORING,
                          mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight(),
                          false);
    
            if (mEllipsize != TextUtils.TruncateAt.MARQUEE) {
                // In a fixed-height view, so use our new text layout.
                if (mLayoutParams.height != LayoutParams.WRAP_CONTENT
                        && mLayoutParams.height != LayoutParams.MATCH_PARENT) {
                    autoSizeText();
                    invalidate();
                    return;
                }
    
                // Dynamic height, but height has stayed the same,
                // so use our new text layout.
                if (mLayout.getHeight() == oldht
                        && (mHintLayout == null || mHintLayout.getHeight() == oldht)) {
                    autoSizeText();
                    invalidate();
                    return;
                }
            }
    
            // We lose: the height has changed and we have a dynamic height.
            // Request a new view layout using our new text layout.
            requestLayout();
            invalidate();
        } else {
            // Dynamic width, so we have no choice but to request a new
            // view layout with a new text layout.
            nullLayouts();
            requestLayout();
            invalidate();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    发现其if else都会执行

    requestLayout();
    invalidate();
    
    • 1
    • 2

    一个请求布局,一个刷新

    同时TextView继承View

    public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
    
    • 1

    查看requestLayout()方法

    public void requestLayout() {
        if (mMeasureCache != null) mMeasureCache.clear();
    
        if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) {
            // Only trigger request-during-layout logic if this is the view requesting it,
            // not the views in its parent hierarchy
            ViewRootImpl viewRoot = getViewRootImpl();
            if (viewRoot != null && viewRoot.isInLayout()) {
                if (!viewRoot.requestLayoutDuringLayout(this)) {
                    return;
                }
            }
            mAttachInfo.mViewRequestingLayout = this;
        }
    
        mPrivateFlags |= PFLAG_FORCE_LAYOUT;
        mPrivateFlags |= PFLAG_INVALIDATED;
    
        if (mParent != null && !mParent.isLayoutRequested()) {
            mParent.requestLayout();
        }
        if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) {
            mAttachInfo.mViewRequestingLayout = null;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    ViewRootImpl是ViewPrivate的实现类

    在ViewRootImpl中requestLayout()执行了checkThread()方法

    @Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    查看checkThread()方法

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

    当前的Thread和存入的Thread不一致就会抛出异常

    public static Toast makeText(@NonNull Context context, @Nullable Looper looper,
            @NonNull CharSequence text, @Duration int duration) {
        if (Compatibility.isChangeEnabled(CHANGE_TEXT_TOASTS_IN_THE_SYSTEM)) {
            Toast result = new Toast(context, looper);
            result.mText = text;
            result.mDuration = duration;
            return result;
        } else {
            Toast result = new Toast(context, looper);
            View v = ToastPresenter.getTextToastView(context, text);
            result.mNextView = v;
            result.mDuration = duration;
    
            return result;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    Toast()最后也调用了view

    如果执行的时候invalidate()快于requestLayout()方法,那么就不会抛出异常,如果requestLayout()先执行一段再invalidate()就会抛出异常

    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)
    
    • 1
    • 2
    • 3

    这里的异常说明也很清晰了ViewRootImpl.requestLayout ViewRootImpl.checkThread抛出异常主线程和子线程不一致

    4.Handler的两种写法

    //handler创建方法一 使用Callback
    private Handler handler1=new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(@NonNull Message msg) {
            startActivity(new Intent(MainActivity.this,SecondActivity.class));
            return false;
        }
    });
    //handler创建方法二 重写handlerMessage()方法
    private Handler handler2=new Handler(){
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            textView.setText(msg.obj.toString());
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    方法二是谷歌备胎api不推荐使用

    Handler收消息是

    public void dispatchMessage(@NonNull Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    如果mCallback不等于空,则直接返回,否则handleMessage(msg) 两个handleMessage(msg)是有差别的,一个是可以重写,一个是接口固定的方法

    public void handleMessage(@NonNull Message msg) {
    }
    
    /**
     * Handle system messages here.
     */
    public void dispatchMessage(@NonNull Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    其上边执行的handleCallback就是开启run方法

    private static void handleCallback(Message message) {
        message.callback.run();
    }
    
    • 1
    • 2
    • 3

    也就是子线程的run切换到主线程中,去执行run

    5.ThreadLocal

    创建测试方法

    void test(){
        final ThreadLocal<String> threadLocal=new ThreadLocal<String>(){
            @Nullable
            @Override
            protected String initialValue() {
                //重写初始化方法,默认返回为null,如果ThreadLocalMap拿不到值再调用初始化方法
                threadLocal.get();
                return "andy";
    
            }
        };
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    注意注释,查看threadLocal.get()方法

    private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    key是主线程 value是T 是你定义的泛型

    发现从threadLocal.get()获取的是主线程

    注意

    在每个线程Thread内部有一个ThreadLocal.ThreadLocalMap类型的成员变量threadLocals,这个threadLocals就是用来存储实际的变量副本的,键值为当前ThreadLocal变量,value为变量副本(即T类型的变量)。 初始时,在Thread里面,threadLocals为空,当通过ThreadLocal变量调用get()方法或者set()方法,就会对Thread类中的threadLocals进行初始化,并且以当前ThreadLocal变量为键值,以ThreadLocal要保存的副本变量为value,存到threadLocals。 然后在当前线程里面,如果要使用副本变量,就可以通过get方法在threadLocals里面查找。

    1. 实际的通过ThreadLocal创建的副本是存储在每个线程自己的threadLocals中的;
    2. 为何threadLocals的类型ThreadLocalMap的键值为ThreadLocal对象,因为每个线程中可有多个threadLocal变量,就像上面代码中的longLocal和stringLocal;
    3. 在进行get之前,必须先set,否则会报空指针异常;如果想在get之前不需要调用set就能正常访问的话,必须重写initialValue()方法。 因为在上面的代码分析过程中,我们发现如果没有先set的话,即在map中查找不到对应的存储,则会通过调用setInitialValue方法返回i,而在setInitialValue方法中,有一个语句是T value = initialValue(), 而默认情况下,initialValue方法返回的是null。
  • 相关阅读:
    laravel 阿里云短信发送
    AI人工智能—数据标注的主要类型和标注注意事项
    FreeSWITCH添加自定义endpoint之api及app开发
    java基于springboot+vue的大学生在线答疑系统 elementui
    OpenGl之纹理坐标及纹理映射
    强强联合 加速科技“牵手”清华大学达成深度战略合作
    数据库常用的几大范式NF
    Java基础 | 反射机制详解
    [附源码]计算机毕业设计springboot-Steam游戏平台系统论文
    centos7.9离线安装docker
  • 原文地址:https://blog.csdn.net/liuwushuang233/article/details/126404693