无侵入获取全局Context的新方式详解手机开发

很多第三方库都需要利用到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/6245.html

(0)
上一篇 2021年7月17日
下一篇 2021年7月17日

相关推荐

发表回复

登录后才能评论