Dynamic loading of installed apk s

Keywords: Android Linux Attribute Mobile

After reading an article about plug-in development, I feel a little interesting. So I tried.

First of all, we need to know what plug-in is, or why it is used, and what application scenarios are.

Everyone knows that in Android App, the maximum number of methods for an application dex file can not exceed 65536, otherwise, your app will be abnormal, and the larger the project, the more it will be. So plug-ins have been used.

One of the concepts involved here is dynamic loading technology. Dynamic loading technology is to use class loader to load the corresponding apk, dex, jar (must contain DEX files), and then get the internal resources (class, image, color, etc.) of the apk, dex, jar through reflection for the use of the host app.

Class loaders typically require two PathClassLoader s and DexClassLoader s.
1,PathClassLoader - can only load the installed apk, that is, the APK in the data/app directory
2, DexClassLoader - can load apk, jar, dex that are not installed in the mobile phone, as long as the corresponding path can be found.

These two loaders correspond to different scenarios, so let's start with loading the installed APK with the PathClassLoader class. This article demonstrates with a simple demo. Load the image in another APK through apk. Application scenarios are similar to loading skin plug-ins.

1. First of all, we need to know that there is an android:sharedUserId= "xxx x" attribute in the mainfest file, which is used to run two apk s in the same linux process. Simply put, when the application is installed on the Android system from the beginning, the system will assign it a linux user id, and then the application will run in the future. In a separate process, other applications can't access its resources. If the sharedUserId of two applications is the same, then they will run together in the same linux process, so they can share data and access resources. So the shareUserId attribute in the manifest file of our main application and plug-in should be defined as the same. Plug-ins are usually downloaded and saved locally.

2. Next, create two new projects. The main application will look like this. The simple controls will not paste the code. The main thing is to click on the menu in the upper right corner to get the picture of the plug-in and set it in the background of the main application. At this point, you have to think about how to get the shareUserId, because it's the property of the manifest file. Considering how to get other information in manifest, we use Packgemanger to get PackageInfo information from apk file, and then judge that ShareUserId is
Does it have the same id as the main application? In the same way, packageInfo information is passed to a PluginBean entity.

`/**
 * Find all the plug-ins in your phone
 * @return Return a plug-in List
 */
private List<PluginBean> findAllPlugin() {
    List<PluginBean> plugins = new ArrayList<>();
    PackageManager pm = getPackageManager();
    //Find all installed apk files through the package manager
    List<PackageInfo> packageInfos = pm.getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES);
    for (PackageInfo info : packageInfos) {
        //Get the package name of the current apk
        String pkgName = info.packageName;
        //sharedUserId to get the current apk
        String shareUesrId = info.sharedUserId;
        //Determine whether this apk is a plug-in for our application
        if (shareUesrId != null && shareUesrId.equals("com.shenxu.picture") && !pkgName.equals(this.getPackageName())) {
            String label = pm.getApplicationLabel(info.applicationInfo).toString();//Get the name of the plug-in apk
            PluginBean bean = new PluginBean(label,pkgName);
            plugins.add(bean);
        }
    }
    return plugins;
}`

3. Then use this PathClassLoader class to get the image resources. PathClassLoader's reflection mechanism is used to obtain the corresponding attributes.

`/**
 * Load the installed apk
 * @param packageName Application package name
 * @param pluginContext Context of plug-in app
 * @return id of corresponding resource
 */
private int dynamicLoadApk(String packageName, Context pluginContext) throws Exception {
    //The first parameter is the path of the apk or jar containing dex
    Log.v("sx",pluginContext.getPackageResourcePath());
    PathClassLoader pathClassLoader = new PathClassLoader(pluginContext.getPackageResourcePath(),ClassLoader.getSystemClassLoader());
    //Class <?> clazz = pathClassLoader. loadClass (packageName + ".R $mipmap"); // Reflect the mipmap class by using its own loader and then use its functions
    //Parameters: 1. Full name of class, 2. Initialization of class, 3. Class loader used in loading
    Class<?> clazz = Class.forName(packageName + ".R$mipmap", true, pathClassLoader);
    //Both of the above methods can be used. Here we get the internal class mipmap in R class, through which we can get the corresponding picture id, and then use it for us.
    Field field = clazz.getDeclaredField("one");
    int resourceId = field.getInt(R.mipmap.class);
    return resourceId;
}`

4. Before invoking the above method, you should first use a method createPackageContext (package Name, CONTEXT_IGNORE_SECURITY | CONTEXT_INCLUDE_CODE) to get the context of the corresponding plug-in, through which you can get the Resource of the plug-in. Then pass the value to the dynamicLoadApk method to get the resourceId, and finally call the method of setting up the image resources to show. setBackgroundDrawable(plugnContext.getResources().getDrawable(resouceId))

5. Final click on the menu to get the effect of the picture

Posted by millsy007 on Mon, 15 Jul 2019 16:28:16 -0700