很多第三方库都需要利用到Context,如何无侵入获取全局Context?
利用ContentProvider获取Context
这是当下很多流行的第三方库,包括LeakCanary、Picasso都是构建一个ContentProvider。
例如 LeakCanary中:
internal class AppWatcherInstaller : ContentProvider() {
override fun onCreate(): Boolean {
SharkLog.logger = DefaultCanaryLog()
// 获取到Context
val application = context!!.applicationContext as Application
InternalAppWatcher.install(application)
return true
}
}
优点是无侵入式,缺点是会拖慢app启动速度。
构建ActivityThread获取Context
通过构建ActivityThread仿造一个app,这是从genymobile的scrcpy学习到的新思路。
public static void fillAppInfo() {
try {
// ActivityThread activityThread = new ActivityThread();
Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
Constructor<?> activityThreadConstructor = activityThreadClass.getDeclaredConstructor();
activityThreadConstructor.setAccessible(true);
Object activityThread = activityThreadConstructor.newInstance();
// ActivityThread.sCurrentActivityThread = activityThread;
Field sCurrentActivityThreadField = activityThreadClass.getDeclaredField("sCurrentActivityThread");
sCurrentActivityThreadField.setAccessible(true);
sCurrentActivityThreadField.set(null, activityThread);
// ActivityThread.AppBindData appBindData = new ActivityThread.AppBindData();
Class<?> appBindDataClass = Class.forName("android.app.ActivityThread$AppBindData");
Constructor<?> appBindDataConstructor = appBindDataClass.getDeclaredConstructor();
appBindDataConstructor.setAccessible(true);
Object appBindData = appBindDataConstructor.newInstance();
ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.packageName = "com.genymobile.scrcpy";
// appBindData.appInfo = applicationInfo;
Field appInfoField = appBindDataClass.getDeclaredField("appInfo");
appInfoField.setAccessible(true);
appInfoField.set(appBindData, applicationInfo);
// activityThread.mBoundApplication = appBindData;
Field mBoundApplicationField = activityThreadClass.getDeclaredField("mBoundApplication");
mBoundApplicationField.setAccessible(true);
mBoundApplicationField.set(activityThread, appBindData);
// Context ctx = activityThread.getSystemContext();
Method getSystemContextMethod = activityThreadClass.getDeclaredMethod("getSystemContext");
// 获取到了Context
Context ctx = (Context) getSystemContextMethod.invoke(activityThread);
// 还构建了Application
Application app = Instrumentation.newApplication(Application.class, ctx);
// activityThread.mInitialApplication = app;
Field mInitialApplicationField = activityThreadClass.getDeclaredField("mInitialApplication");
mInitialApplicationField.setAccessible(true);
mInitialApplicationField.set(activityThread, app);
} catch (Throwable throwable) {
// this is a workaround, so failing is not an error
Ln.w("Could not fill app info: " + throwable.getMessage());
}
}
优点是利用反射构建出Context、Application,缺点是Context、Application跟运行的应用进程下的Context不是同一个,属性环境都不一样。
scrcpy真是太强大了,从里面可以学到好多新思路,包括上篇文章如何绕过系统权限构建一个Virtual Dispaly。
原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/tech/app/6245.html