Android PackageInstaller 7.1.2源碼分析


Android應用的安裝是通過packageinstaller完成. 在源碼中, PackageInstaller位於packages/apps/PackageInstaller中. 根據代碼量來看並不是太大.

主要是PackageInstallerActivity類, 安裝過程中一切狀態轉換與判斷都在這個Activity中進行. 原文鏈接: http://www.ldkxingzhe.top/post/Android PackageInstaller 7.1.2源碼分析

安裝流程

先看一下最常見的安裝流程.

PackageInstaller中處理安裝前的判斷與確認操作

首先processPackageUri檢測解析Uri. 提取scheme(目前支持file, content, package三種協議, 便於分析只保留了file部分)

private boolean processPackageUri(final Uri packageUri) {
    mPackageURI = packageUri;
    final String scheme = packageUri.getScheme();
    // .... 專注於file協議
            File sourceFile = new File(packageUri.getPath());
            // 調用PackageParser的parsePackage方法
            PackageParser.Package parsed = PackageUtil.getPackageInfo(sourceFile);
            // 獲取一個PackageInfo對象(獲取權限部分)
            mPkgInfo = PackageParser.generatePackageInfo(parsed, null,
                    PackageManager.GET_PERMISSIONS, 0, 0, null,
                    new PackageUserState());
            // As 就是一個應用程序的標題與圖標的數據類
            as = PackageUtil.getAppSnippet(this, mPkgInfo.applicationInfo, sourceFile);
    PackageUtil.initSnippetForNewApp(this, as, R.id.app_snippet);
    return true;
}

可以看出 processPackageUrl調用framework的api獲取了apk的權限信息與應用名稱與圖標.

然后就是判斷該包是否允許安裝, 如果允許則初始化准備安裝. 首先在checkIfAllowedAndInitiateInstall中判斷是否是未知來源的應用, 一般是開啟允許未知來源的. 直接開啟初始化安裝

initiateInstall僅僅獲取一下本機是否安裝了該應用, 如果安裝獲取到該應用的信息

private void initiateInstall() {
    String pkgName = mPkgInfo.packageName;
    // 源碼這里進行了pkgName判斷轉換, 一般不會遇到
    // Check if package is already installed. display confirmation dialog if replacing pkg
    try {
        // This is a little convoluted because we want to get all uninstalled
        // apps, but this may include apps with just data, and if it is just
        // data we still want to count it as "installed".
        mAppInfo = mPm.getApplicationInfo(pkgName,
                PackageManager.GET_UNINSTALLED_PACKAGES);
        if ((mAppInfo.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {
            mAppInfo = null;
        }
    } catch (NameNotFoundException e) {
        mAppInfo = null;
    }

    startInstallConfirm();
}

最后startInstallConfirm()方法中顯示了界面中顯示的權限列表以及, 確認取消列表. 查看點擊OK的事件, 確定安裝事件為:

 private void startInstall() {
    // Start subactivity to actually install the application
    Intent newIntent = new Intent();
    newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,
            mPkgInfo.applicationInfo);
    newIntent.setData(mPackageURI);
    newIntent.setClass(this, InstallAppProgress.class);
    String installerPackageName = getIntent().getStringExtra(
            Intent.EXTRA_INSTALLER_PACKAGE_NAME);
    if (mOriginatingURI != null) {
        newIntent.putExtra(Intent.EXTRA_ORIGINATING_URI, mOriginatingURI);
    }
    if (mReferrerURI != null) {
        newIntent.putExtra(Intent.EXTRA_REFERRER, mReferrerURI);
    }
    if (mOriginatingUid != VerificationParams.NO_UID) {
        newIntent.putExtra(Intent.EXTRA_ORIGINATING_UID, mOriginatingUid);
    }
    if (installerPackageName != null) {
        newIntent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME,
                installerPackageName);
    }
    if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
        newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
        newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
    }
    if(localLOGV) Log.i(TAG, "downloaded app uri="+mPackageURI);
    startActivity(newIntent);
    finish();
}

好吧, 安裝確認后跳轉到安裝進度頁面, 具體安裝過程也在這個界面中.

InstallAppProcess開始安裝並處理安裝進度

onCreate中, 接參數mAppInfo與mPackageURI

// 開啟安裝線程, 使用了HandlerThread
mInstallThread = new HandlerThread("InstallThread");
mInstallThread.start();
mInstallHandler = new Handler(mInstallThread.getLooper());

// 注冊一個安裝結束的監聽
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BROADCAST_ACTION);
registerReceiver(
        mBroadcastReceiver, intentFilter, BROADCAST_SENDER_PERMISSION, null /*scheduler*/);

這個handlerThread進行的任務是 doPackageState

private void doPackageStage(PackageManager pm, PackageInstaller.SessionParams params) {
     // 1. 獲取一個PackageInstaller
     final PackageInstaller packageInstaller = pm.getPackageInstaller();
     PackageInstaller.Session session = null;
     try {
         final String packageLocation = mPackageURI.getPath();
         final File file = new File(packageLocation);
         // 2. create一個sessionId
         final int sessionId = packageInstaller.createSession(params);
         final byte[] buffer = new byte[65536];
         // 3. 根據sessionId打開一個session回話
         session = packageInstaller.openSession(sessionId);

         final InputStream in = new FileInputStream(file);
         final long sizeBytes = file.length();
         // 4. 根據這個session回話, 獲取一個OutPutstream, 然后將文件寫入到這個OutPutStream中
         final OutputStream out = session.openWrite("PackageInstaller", 0, sizeBytes);
         try {
             int c;
             while ((c = in.read(buffer)) != -1) {
                 out.write(buffer, 0, c);
                 if (sizeBytes > 0) {
                     final float fraction = ((float) c / (float) sizeBytes);
                     session.addProgress(fraction);
                 }
             }
             session.fsync(out);
         } finally {
             IoUtils.closeQuietly(in);
             IoUtils.closeQuietly(out);
         }

         // Create a PendingIntent and use it to generate the IntentSender
         Intent broadcastIntent = new Intent(BROADCAST_ACTION);
         PendingIntent pendingIntent = PendingIntent.getBroadcast(
                 InstallAppProgress.this /*context*/,
                 sessionId,
                 broadcastIntent,
                 PendingIntent.FLAG_UPDATE_CURRENT);
         // 5. 最后調用session的commit方法進行提交
         session.commit(pendingIntent.getIntentSender());
     } catch (IOException e) {
         onPackageInstalled(PackageInstaller.STATUS_FAILURE);
     } finally {
         IoUtils.closeQuietly(session);
     }
 }

結論

PackageInstaller只是在應用安裝前進行信息提取, 篩選, 以及apk文件的獲取. 最后的安裝還是調用Framework的PackageInstaller進行安裝.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM