通过ActivityThread获取Context

背景

为了保存一个全局可用的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];
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

未子涵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值