无侵入获取全局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/tech/app/6245.html

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

相关推荐

发表回复

登录后才能评论