DynamicLoadApk 运行流程跟踪

一、解析插件Apk

DLPluginManager pluginManager = DLPluginManager.getInstance(BaseApplication.getInstance());

DLPluginPackage pluginPackage = pluginManager.loadApk(pluginApkPath);
DLIntent intent = new DLIntent(pluginPackage.packageName, pluginPackage.defaultActivity);

pluginManager.startPluginActivity(this, intent);

1、loadApk方法

public DLPluginPackage loadApk(final String dexPath, boolean hasSoLib){
    mFrom = DLConstants.From_external;
    
    int packageInfoFlags = PackageManager.Get_activities | PackageManager.Get_services;
    PackageInfo packageInfo = mContext.getPackageManager().getPackageArchiveInfo(dexPath, packageInfoFlags);
    if(packageInfo == null) return null;

    DLPluginPackage pluginPackage = preparePluginEnv(packageInfo, dexPath);
    if(hasSoLib){
        copySoLib(dexPath);
    }

    return pluginPackage;
}

2、preparePluginEnv方法

private DLPluginPackage preparePluginEnv(PackageInfo packageInfo, String dexPath){
    DLPluginPackage pluginPackage = mPackagesHolder.get(packageInfo.packageName);
    if(pluginPackage != null) return pluginPackage;

    DexClassLoader dexClassLoader = createDexClassLoader(dexPath);
    AssetManager assetManager = createAssetManager(dexPath);
    Resources resources = createResources(assetManager);

    // create PluginPackage
    pluginPackage = new DLPluginPackage(dexClassLoader, resources, packageInfo);
    mPackageHolder.put(packageInfo.packageName, pluginPackage);

    return pluginPackage;
}

private DexClassLoader createDexClassLoader(String dexPath){
    File dexOutputDir = mContext.getDir("dex", Context.Mode_private);
    dexOutputPath = dexOutputDir.getAbsolutePath();

    DexClassLoader loader = new DexClassLoader(
        dexPath,
        dexOutputPaht,
        mNativeLibDir,
        mContext.getClassLoader()
    );

    return loader;
}

private AssetManager createAssetManager(String dexPath){
    try{
        AssetManager assetManager = AssetManager.class.newInstance();
        Method addAssetPath = assetManager.getClass().getMethod("addAssetPath", String.class);
        addAssetPath.invoke(assetManager, dexPath);
        return assetManager;
    }catch(Exception e){}
}

private Resources createResources(AssetManager assetManager){
    Resources superRes = mContext.getResources();
    Resources resources = new Resources(
        assetManager,
        superRes.getDisplayMetrics(),
        superRes.getConfiguration()
    );

    return resources;
}

二、运行插件Apk

启动代理的Activity,分别可以:在DLPluginManager里调用startPluginActivityForResult(一般宿主使用),还可以在DLPluginActivity里使用startPluginActivity(在插件里使用)。

DLIntent intent = new DLIntent(getPackageName(), SecondActivity.class);
startPluginActivity(intent);

上面是调用Activity方法,再往下看:

public int startPluginActivity(DLIntent dlIntent){
    return startPluginActivityForResult(dlIntent, -1);
}

/** @return may be 
    {@link #Start_result_success}, 
    {@link #Start_result_no_pkg},  
    {@link #Start_result_no_class}, 
    {@link #Start_result_type_error} */
public int startPluginActivityForResult(DLIntent dlIntent, int requestCode){
    if(mFrom == DLConstants.From_external){
        if(dlIntent.getPluginPackage() == null){
            dlIntent.setPluginPackage(mPluginPackage.packageName);
        }
    }

    return mPluginManager.startPluginActivityForResult(that, dlIntent, requestCode);
}

之后交给PluginManager启动Activity:

public int startPluginActivityForResult(Context context, DLIntent dlIntent, int requestCode){
    if(mFrom == DLConstants.From_internal){
        dlIntent.setClassName(context, dlIntent.getPluginClass());
        preformStartActivityForResult(context, dlIntent, requestCode);
        return DLPluginManager.Start_result_success;
    }

    String packageName = dlIntent.getPluginPackage();
    if(TextUtils.isEmpty(packageName)){
        throw new NullPointerException("disallow null packageName.");
    }

    DLPluginPackage pluginPackage = mPackageHolder.get(packageName);
    if(pluginPackage == null){
        return Start_result_no_pkg;
    }

    final String className = getPluginActivityFullPath(dlIntent, pluginPackage);
    Class<?> clazz = loadPluginClass(pluginPackage.classLoader, className);
    if(clazz == null){
        return Start_result_no_class;
    }

    Class<? extends Activity> activityClass = getProxyActivityClass(clazz);
    if(activityClass == null){
        return Start_result_type_error;
    }

    dlIntent.putExtra(DLConstants.Extra_class, className);
    dlIntent.putExtra(DLConstants.Extra_package, packageName);
    dlIntent.setClass(mContext, activityClass);
    performStartActivityForResult(context, dlIntent, requestCode);
    return Start_result_success;
}

先进行一些判断,然后获取要启动的Activity的全称,然后通过函数loadPluginClass()利用反射获取插件实例activityClass

函数loadPluginClass:

private Class<?> loadPluginClass(ClassLoader classLoader, String className){
    Class<?> clazz = null;
    try{
        clazz = Class.forName(className, true, classLoader);
    }catch(ClassNotFoundException e){}
    return clazz;
}

通过函数getProxyActivityClass(),根据插件实例获取它对应在宿主里对应的代理类:

private Class<? extends Activity> getProxyActivityClass(Class<?> clazz){
    Class<? extends Activity> activityClass = null;
    if(DLBasePluginActivity.class.isAssignableFrom(clazz)){
        activityClass = DLProxyActivity.class;
    }else if(DLBasePluginFragmentActivity.class.isAssignableFrom(clazz)){
        activityClass = DLProxyFragmentActivity.class;
    }else if(Activity.class.isAssignableFrom(clazz)){
        activityClass = Activity.class;
    }
    return activityClass;
}

当这一步基本上完成偷天换柱的任务,表面上要启动的是插件类,其实启动的是一个宿主代理类。
在把插件包名和要启动的插件类名放到Intent中,传递一个代理类,最后直接启动代理Activity就好了。

private void performStartActivityForResult(Context context, DLIntent dlIntent, int requestCode){
    if(context instanceof Activity){
        ((Activity)context).startActivityForResult(dlIntent, requestCode);
    }else{
        context.startActivity(dlIntent);
    }
}

三、通过代理类运行插件类

在这里分析DLProxyActivity,经前面分析,已经启动了代理类,当然就执行到代理类的onCreate了:

protected DLPlugin mRemoteActivity;
private DLProxyImpl impl = new DLProxyImpl(this);

@Override
protected void onCreate(Bundle savedInstanceState){
    super.onCreate(savedInstanceState);
    impl.onCreate(getIntent());
}

接着流程就跑到DLProxyImpl类的onCreate()方法中:

public void onCreate(Intent intent){
    intent.setExtrasClassLoader(DLConfigs.sPluginClassLoader);

    mPackageName = intent.getStringExtra(DLConstants.Extra_package);
    mClass = intent.getStringExtra(DLConstants.Extra_class);

    mPluginManager = DLPluginManager.getInstance(mProxyActivity);
    mPluginPackage = mPluginManager.getPackage(mPackageName);
    mAssetManager = mPluginPackage.assetManager;
    mResources = mPluginPackage.resources;

    initializeActivityInfo();
    handleActivityInfo();
    launchTargetActivity();
}
1、在initializeActivityInfo()之前是获取intent传递过来的插件包名、插件类,并根据它们获取插件信息PluginPackage
2、PluginPackage里面存放了插件的resource和assetManager
3、initializeActivityInfo()和handleActivityInfo()主要是进行一些主题处理
4、重点在launchTargetActivity()方法,初始化插件类
protected void launchTargetActivity(){
    try{
        Class<?> localClass = getClassLoader().loadClass(mClass);
        Constructor<?> localConstructor = localClass.getConstructor(new Class[]{});
        Object instance = localConstructor.newInstance(new Object[]{});
        mPluginActivity = (DLPlugin)instance;

        ((DLAttachable)mProxyActivity).attach(mPluginActivity, mPluginManager);
        mPluginActivity.attach(mProxyActivity, mPluginPackage);

        Bundle bundle = new Bundle();
        bundle.putInt(DLConstants.From, DLConstants.From_external);
        mPluginActivity.onCreate(bundle);
    }catch(Exception e){}
}

创建好了代理类对象和插件类对象,就需要通过两个attach()方法将它们绑定在一起。

第一个是代理类的attach()方法:

public void attach(DLPlugin remoteActivity, DLPluginManager pluginManager){
    mRemoteActivity = remoteActivity;
}

第二个是插件类的attach()方法:

public void attach(Activity proxyActivity, DLPluginPackage pluginPackage){
    mProxyActivity = proxyActivity;
    that = mProxyActivity;
    mPluginPackage = pluginPackage;
}

可以看到插件类里的that被赋值了代理类对象,所以在编写插件代码的时候this的功能将被废弃,不能使用this,必须使用that

打赏