DroidPlugin 之 安裝與卸載

1.開始安裝APK使用之前需要判斷一下插件服務是否連接(待研究)

if (!PluginManager.getInstance().isConnected()) {
  //return "連接失敗"; // 連接失敗
  Toast.makeText(MainActivity.this, "連接失敗", Toast.LENGTH_SHORT).show();
}

2.插件安裝

真正的實現(xiàn)在IPluginManagerImpl中,(具體的從PliginManager調(diào)用鏈過程之后分析)。

 @Override
    public int installPackage(String filepath, int flags) throws RemoteException {
        //install plugin
        String apkfile = null;
        try {
            //拿到pcakageInfo信息
            PackageManager pm = mContext.getPackageManager();
            PackageInfo info = pm.getPackageArchiveInfo(filepath, 0);
            if (info == null) {
                return PackageManagerCompat.INSTALL_FAILED_INVALID_APK;
            }

            apkfile = PluginDirHelper.getPluginApkFile(mContext, info.packageName);
          //打印下:apkfile: /data/data/com.example.TestPlugin/Plugin/com.iflytek.flownotification/apk/base-1.apk
          //這里暫時理解為安裝后的路徑
            if ((flags & PackageManagerCompat.INSTALL_REPLACE_EXISTING) != 0) {
                //新安裝的插件
                // 停止,???
                forceStopPackage(info.packageName);
                //刪除插件的緩存  ??
                if (mPluginCache.containsKey(info.packageName)) {
                    deleteApplicationCacheFiles(info.packageName, null);
                }
                //刪除文件???
                new File(apkfile).delete();
                //將安裝包拷貝到apkfile
                Utils.copyFile(filepath, apkfile);
                //初始化解析器并完成Mainfest的解析,返回解析器,保存了解析內(nèi)容
                PluginPackageParser parser = new PluginPackageParser(mContext, new File(apkfile));  
                //獲取應用簽名
                parser.collectCertificates(0);
                //獲取權(quán)限和簽名
                PackageInfo pkgInfo = parser.getPackageInfo(PackageManager.GET_PERMISSIONS | PackageManager.GET_SIGNATURES);
                //校驗權(quán)限是否在宿主中有聲明過
                if (pkgInfo != null && pkgInfo.requestedPermissions != null && pkgInfo.requestedPer
                //保存簽名信息
                saveSignatures(pkgInfo);
//                if (pkgInfo.reqFeatures != null && pkgInfo.reqFeatures.length > 0) {
//                    for (FeatureInfo reqFeature : pkgInfo.reqFeatures) {
//                        Log.e(TAG, "reqFeature name=%s,flags=%s,glesVersion=%s", reqFeature.name, reqFeature.flags, reqFeature.getGlEsVersion());
//                    }
//                }
                // 拷貝插件中的native庫文件。
                copyNativeLibs(mContext, apkfile, parser.getApplicationInfo(0));
                //opt,暫時沒用到,忽略
                dexOpt(mContext, apkfile, parser);
                //緩存下
                mPluginCache.put(parser.getPackageName(), parser);
                //回調(diào)函數(shù)???,通知插件安裝
                mActivityManagerService.onPkgInstalled(mPluginCache, parser, parser.getPackageName());
                sendInstalledBroadcast(info.packageName);
                return PackageManagerCompat.INSTALL_SUCCEEDED;
            } else {
              //已經(jīng)安裝過了,做升級處理
                if (mPluginCache.containsKey(info.packageName)) {
                    return PackageManagerCompat.INSTALL_FAILED_ALREADY_EXISTS;
                } else {
                    forceStopPackage(info.packageName);
                    new File(apkfile).delete();
                    Utils.copyFile(filepath, apkfile);
                    PluginPackageParser parser = new PluginPackageParser(mContext, new File(apkfile));
                    parser.collectCertificates(0);
                    PackageInfo pkgInfo = parser.getPackageInfo(PackageManager.GET_PERMISSIONS | PackageManager.GET_SIGNATURES);
                    if (pkgInfo != null && pkgInfo.requestedPermissions != null && pkgInfo.requestedPermissions.length > 0) {
                        for (String requestedPermission : pkgInfo.requestedPermissions) {
                            boolean b = false;
                            try {
                                b = pm.getPermissionInfo(requestedPermission, 0) != null;
                            } catch (NameNotFoundException e) {
                            }
                            if (!mHostRequestedPermission.contains(requestedPermission) && b) {
                                Log.e(TAG, "No Permission %s", requestedPermission);
                                new File(apkfile).delete();
                                return PluginManager.INSTALL_FAILED_NO_REQUESTEDPERMISSION;
                            }
                        }
                    }
                    saveSignatures(pkgInfo);
//                    if (pkgInfo.reqFeatures != null && pkgInfo.reqFeatures.length > 0) {
//                        for (FeatureInfo reqFeature : pkgInfo.reqFeatures) {
//                            Log.e(TAG, "reqFeature name=%s,flags=%s,glesVersion=%s", reqFeature.name, reqFeature.flags, reqFeature.getGlEsVersion());
//                        }
//                    }

                    copyNativeLibs(mContext, apkfile, parser.getApplicationInfo(0));
                    dexOpt(mContext, apkfile, parser);
                    mPluginCache.put(parser.getPackageName(), parser);
                    mActivityManagerService.onPkgInstalled(mPluginCache, parser, parser.getPackageName());
                    sendInstalledBroadcast(info.packageName);
                    return PackageManagerCompat.INSTALL_SUCCEEDED;
                }
            }
        } catch (Exception e) {
            if (apkfile != null) {
                new File(apkfile).delete();
            }
            handleException(e);
            return PackageManagerCompat.INSTALL_FAILED_INTERNAL_ERROR;
        }
    }

其中

PluginPackageParser構(gòu)造函數(shù)部分代碼如下:

public PluginPackageParser(Context hostContext, File pluginFile) throws Exception {
        mHostContext = hostContext; //保存宿主進程Context
        mPluginFile = pluginFile;//插件Apk文件路徑。
        mParser = PackageParser.newPluginParser(hostContext);//PackageParser(插件自定義)類實例,他是一個兼容系統(tǒng)各個版本的PackageParser,解析Mainfest
        mParser.parsePackage(pluginFile, 0);
        mPackageName = mParser.getPackageName();//解析后獲得插件Apk的包名。
        mHostPackageInfo = mHostContext.getPackageManager().getPackageInfo(mHostContext.getPackageName(), 0);// 宿主進程的PackageInfo實例。
        //mActivityIntentFilterCache,mServiceIntentFilterCache,mProviderIntentFilterCache,mReceiverIntentFilterCache: 這幾個變量主要保存四大組件ComponentName對應的IntentFilter。
        
        //獲取保存Activity
        List datas = mParser.getActivities();
        for (Object data : datas) {
            ComponentName componentName = new ComponentName(mPackageName, mParser.readNameFromComponent(data));
            synchronized (mActivityObjCache) {
                mActivityObjCache.put(componentName, data);
            }
            synchronized (mActivityInfoCache) {
                ActivityInfo value = mParser.generateActivityInfo(data, 0);
                fixApplicationInfo(value.applicationInfo);
                if (TextUtils.isEmpty(value.processName)) {
                    value.processName = value.packageName;
                }
                mActivityInfoCache.put(componentName, value);
            }

            List<IntentFilter> filters = mParser.readIntentFilterFromComponent(data);
            synchronized (mActivityIntentFilterCache) {
                mActivityIntentFilterCache.remove(componentName);
                mActivityIntentFilterCache.put(componentName, new ArrayList<IntentFilter>(filters));
            }
        }
        //獲取保存Service
        //獲取保存ContentProvide
        //獲取保存 Receiver
        //獲取保存Instrumentation
        //獲取保存Permissions
        //獲取保存PermissionGroups
        //獲取保存PermissionGroups
}

總結(jié):插件的安裝過程其實就是,
1 把插件Apk文件保存在宿主進程:/data/data/宿主進程報名/plugin/plugin包名/apk/base-1.apk下面。
2 通過PluginPackageParser解析插件Apk AndroidManifest文件,保存插件Apk 四大組件以及權(quán)限等信息,來方便查詢。
3 PluginClassLoader保存優(yōu)化后的Dex文件,加載插件Apk的類。

3.卸載

@Override
public int deletePackage(String packageName, int flags) throws RemoteException {
    try {
        if (mPluginCache.containsKey(packageName)) {
            forceStopPackage(packageName);

            PluginPackageParser parser;
            synchronized (mPluginCache) {
                parser = mPluginCache.remove(packageName);
            }
            Utils.deleteDir(PluginDirHelper.makePluginBaseDir(mContext, packageName));
            mActivityManagerService.onPkgDeleted(mPluginCache, parser, packageName);
            mSignatureCache.remove(packageName);
            sendUninstalledBroadcast(packageName);
            return PackageManagerCompat.DELETE_SUCCEEDED;
        }
    } catch (Exception e) {
        handleException(e);
    }
    return PackageManagerCompat.DELETE_FAILED_INTERNAL_ERROR;
}

這個函數(shù)主要工作如下:
? 從mPluginCache中通過要卸載的插件Apk包名查看是否已經(jīng)安裝并緩存相關(guān)信息。如果在mPluginCache存在對應包的PluginPackageParser類實例,就對一些緩存進行處理:

  • 停止包運行的所在進程,
  • 把包對應的PluginPackageParser從緩存中刪除
  • 刪除宿主進程安裝包對應的目錄(/data/data/宿主進程報名/plugin/plugin包名)中的文件
  • 移除插件包對應的簽名
  • 發(fā)送卸載成功廣播
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容