美文网首页
子线程弹出Toast引发思考

子线程弹出Toast引发思考

作者: 刀鱼z | 来源:发表于2021-03-29 17:07 被阅读0次

子线程弹出toast引发思考如何保证

点击按钮我们直接调用下面方法

 private fun showToast1() {
    thread {
        Toast.makeText(this@HandlerTestActivity,"toast",Toast.LENGTH_SHORT).show()
    }

}

大家都知道会报错

Can't toast on a thread that has not called Looper.prepare()

是的,提示的是我们不是子线程不能更新UI而是上面的提示
为什么呢?带着疑问我们去看下源码

查看源码

public static Toast makeText(Context context, CharSequence text, @Duration int duration) {
    return makeText(context, null, text, duration);
}


  public static Toast makeText(@NonNull Context context, @Nullable Looper looper,
        @NonNull CharSequence text, @Duration int duration) {
        XXX
        XXX
        XXX
    }

第二个入参是一个null的 Looper

我们继续查看

 Toast result = new Toast(context, looper);

public Toast(@NonNull Context context, @Nullable Looper looper) {
    mContext = context;
    mTN = new TN(context.getPackageName(), looper);
    mTN.mY = context.getResources().getDimensionPixelSize(
            com.android.internal.R.dimen.toast_y_offset);
    mTN.mGravity = context.getResources().getInteger(
            com.android.internal.R.integer.config_toastDefaultGravity);
}

TN中传入的looper,我们继续点进去查看TN的构造方法

 TN(String packageName, @Nullable Looper looper) {
        // XXX This should be changed to use a Dialog, with a Theme.Toast
        // defined that sets up the layout params appropriately.
        final WindowManager.LayoutParams params = mParams;
        params.height = WindowManager.LayoutParams.WRAP_CONTENT;
        params.width = WindowManager.LayoutParams.WRAP_CONTENT;
        params.format = PixelFormat.TRANSLUCENT;
        params.windowAnimations = com.android.internal.R.style.Animation_Toast;
        params.type = WindowManager.LayoutParams.TYPE_TOAST;
        params.setTitle("Toast");
        params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
                | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;

        mPackageName = packageName;

        if (looper == null) {
            // Use Looper.myLooper() if looper is not specified.
            looper = Looper.myLooper();
            if (looper == null) {
                throw new RuntimeException(
                        "Can't toast on a thread that has not called Looper.prepare()");
            }
        }

我们直接看到我们截止的地方,先判断构造方法的looper是否为空

我们没有传,所以looper.myLooper()获取当前的线程的looper

主线程直接可以使用是因为,主线程的Looper在ActivityThread里面就已经创建好了

但是子线程是没有Looper的,我们通过改造方法

private fun showToast2(){
    thread {
        if (Looper.myLooper()==null) {
            Looper.prepare()
        }
        Toast.makeText(this@HandlerTestActivity,"toast",Toast.LENGTH_SHORT).show()
        Looper.loop()
    }
}

解决问题 toast成功弹出

但是我的思考才刚刚开始

我们知道Looper每个线程最多只有一个

每个Looper里面有个sThreadLocal

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

他是被final修饰的所以只有一个

我们创建Looper的时候

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

sThreadLocal的set和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();
}


public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

如何保证Looper唯一?

我们看到了上面的set函数所有的key都是唯一的sThreadLocal,但是所有每个线程有自己的ThreadLocalMap

所以线程1<k,V> sThreadLocal looper1

线程2<k,V> sThreadLocal looper2

    static class ThreadLocalMap {

    /**
     * The entries in this hash map extend WeakReference, using
     * its main ref field as the key (which is always a
     * ThreadLocal object).  Note that null keys (i.e. entry.get()
     * == null) mean that the key is no longer referenced, so the
     * entry can be expunged from table.  Such entries are referred to
     * as "stale entries" in the code that follows.
     */
    static class Entry extends WeakReference<ThreadLocal<?>> {
        /** The value associated with this ThreadLocal. */
        Object value;

        Entry(ThreadLocal<?> k, Object v) {
            super(k);
            value = v;
        }
    }

注意 WeakReference?????会导致什么问题呢?

相关文章

网友评论

      本文标题:子线程弹出Toast引发思考

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