美文网首页
通过ActivityThread获取Context

通过ActivityThread获取Context

作者: 未子涵 | 来源:发表于2021-05-26 22:05 被阅读0次

    背景

    为了保存一个全局可用的ApplicationContext对象,通过反射ActivityThread.currentActivityThread()来实现。近期在分析线上错误日志时,偶有发现这里会小概率死锁,分析堆栈后发现问题出在“切换至主线程反射调用currentActivityThread()”时加的同步锁这里,虽然最直接的方向是如何避免死锁场景的出现,也就是不要用容易产生死锁的调用方式,但可惜在我们的应用场景下这种调用方式是无法避免的,所以只能从别的方向入手,那为什么这里一定要切换至主线程调用,如果没有这步操作,就不会有死锁问题了,所以就从这里着手调查。

    理论讲解

    https://zhuanlan.zhihu.com/p/26285030

    根据上文中的分析,在低版本系统中,由于ActivityThread对象被保存在ThreadLocal变量中,所以必须在主线程调用 currentActivityThread() 方法才能获取到,子线程下context为null。

    通过阅读 android.app.ActivityThread 的源码,确认了从 API 18(Android 4.3)开始,ActivityThread对象由ThreadLocal变量改为普通的全局变量保存。经过测试,确实在API 18以下的机器上,子线程无法获取到context,必须切换主线程,而从API 18开始,子线程也能正常获取context,因此最优方案是在API 18以下的系统才做子线程到主线程的切换,其他情况下不用在意是否是主线程,而现在市面上用4.3以下系统的设备很少,这样可以最大限度避免死锁的隐患。

    API 17的ActivityThread:

    public static ActivityThread currentActivityThread() {
        return sThreadLocal.get();
    }
    

    API 18的ActivityThread:

    public static ActivityThread currentActivityThread() {
        return sCurrentActivityThread;
    }
    

    示例代码

    private Context context;
    public static Context getContext() {
        if (context == null) {
            try {
                Object actThread = currentActivityThread();
                if (actThread != null) {
                    Context app = ReflectHelper.invokeInstanceMethod(actThread, "getApplication");
                    if (app != null) {
                        context = app;
                    }
                }
            } catch (Throwable t) {
                t.printStacktrace();
            }
        }
        return context;
    }
    
    /** 获取当前进程的ActivityThread对象 */
    public static Object currentActivityThread() {
        Object activityThread;
        final ReflectHelper.ReflectRunnable<Void, Object> mainThreadAct = new ReflectHelper.ReflectRunnable<Void, Object>() {
            public Object run(Void arg) {
                try {
                    String clzName = ReflectHelper.importClass("android.app.ActivityThread");
                    return ReflectHelper.invokeStaticMethod(clzName, "currentActivityThread");
                } catch (Throwable t) {
                    t.printStacktrace();
                }
                return null;
            }
        };
        // 当前在主线程,或者系统版本>=18(Android 4.3)的子线程上,就直接在当前线程中获取ActivityThread对象
        if (Thread.currentThread().getId() == Looper.getMainLooper().getThread().getId() || Build.VERSION.SDK_INT >= 18) {
            activityThread = mainThreadAct.run(null);
            if (activityThread != null) {
                return activityThread;
            }
        }
        // 如果是在系统版本<18的子线程上,必须切换到主线程获取ActivityThread对象
        final Object lock = new Object();
        final Object[] output = new Object[1];
        synchronized (lock) {
            UIHandler.sendEmptyMessage(0, new Handler.Callback() {
                public boolean handleMessage(Message msg) {
                    synchronized (lock) {
                        try {
                            output[0] = mainThreadAct.run(null);
                        } catch (Throwable t) {
                            t.printStacktrace();
                        } finally {
                            try {
                                lock.notify();
                            } catch (Throwable t) {
                                t.printStacktrace();
                            }
                        }
                    }
                    return false;
                }
            });
            try {
                lock.wait();
            } catch (Throwable t) {
                t.printStacktrace();
            }
        }
        return output[0];
    }
    

    相关文章

      网友评论

          本文标题:通过ActivityThread获取Context

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