返回介绍

APK 安装过程及原理详解

发布于 2025-02-28 12:35:46 字数 21675 浏览 0 评论 0 收藏 0

应用程序包的安装是 android 的特点

APK 为 AndroidPackage 的缩写

Android 应用安装有如下四种方式:

1.系统应用安装――开机时完成,没有安装界面

2.网络下载应用安装――通过 market 应用完成,没有安装界面

3.ADB 工具安装――没有安装界面。

4.第三方应用安装――通过 SD 卡里的 APK 文件安装,有安装界面,由 packageinstaller.apk 应用处理安装及卸载过程的界面。

应用安装的流程及路径
应用安装涉及到如下几个目录:

system/app ---------------系统自带的应用程序,获得 adb root 权限才能删除

data/app ---------------用户程序安装的目录。安装时把 apk 文件复制到此目录
data/data ---------------存放应用程序的数据
data/dalvik-cache--------将 apk 中的 dex 文件安装到 dalvik-cache 目录下(dex 文件是 dalvik 虚拟机的可执行文件,其大小约为原始 apk 文件大小的四分之一)

安装过程:

复制 APK 安装包到 data/app 目录下,解压并扫描安装包,把 dex 文件(Dalvik 字节码) 保存到 dalvik-cache 目录,并 data/data 目录下创建对应的应用数据目录。

卸载过程:

删除安装过程中在上述三个目录下创建的文件及目录。

安装应用的过程解析

一.开机安装
PackageManagerService 处理各种应用的安装,卸载,管理等工作,开机时由 systemServer 启动此服务

(源文件路径:android\frameworks\base\services\java\com\android\server\PackageManagerService.java)

PackageManagerService 服务启动的流程:

1.首先扫描安装“system\framework”目录下的 jar 包

 // Find base frameworks (resource packages without code).  
      mFrameworkInstallObserver = new AppDirObserver(  
        mFrameworkDir.getPath(), OBSERVER_EVENTS, true);  
      mFrameworkInstallObserver.startWatching();  
      scanDirLI(mFrameworkDir, PackageParser.PARSE_IS_SYSTEM  
          | PackageParser.PARSE_IS_SYSTEM_DIR,  
          scanMode | SCAN_NO_DEX, 0);  

2.扫描安装系统 system/app 的应用程序

 // Collect all system packages.  
       mSystemAppDir = new File(Environment.getRootDirectory(), "app");  
       mSystemInstallObserver = new AppDirObserver(  
         mSystemAppDir.getPath(), OBSERVER_EVENTS, true);  
       mSystemInstallObserver.startWatching();  
       scanDirLI(mSystemAppDir, PackageParser.PARSE_IS_SYSTEM  
           | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);  

3.制造商的目录下/vendor/app 应用包

 // Collect all vendor packages.  
      mVendorAppDir = new File("/vendor/app");  
      mVendorInstallObserver = new AppDirObserver(  
        mVendorAppDir.getPath(), OBSERVER_EVENTS, true);  
      mVendorInstallObserver.startWatching();  
      scanDirLI(mVendorAppDir, PackageParser.PARSE_IS_SYSTEM  
          | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);  

4.扫描“data\app”目录,即用户安装的第三方应用

 scanDirLI(mAppInstallDir, 0, scanMode, 0);  

5.扫描" data\app-private"目录,即安装 DRM 保护的 APK 文件(一个受保护的歌曲或受保 护的视频是使用 DRM 保护的文件)

 scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK,  
           scanMode, 0);  

扫描方法的代码清单

 private void scanDirLI(File dir, int flags, int scanMode, long currentTime) {  
     String[] files = dir.list();  
     if (files == null) {  
       Log.d(TAG, "No files in app dir " + dir);  
       return;  
     }  
     if (false) {  
       Log.d(TAG, "Scanning app dir " + dir);  
     }  
     int i;  
     for (i=0; i<files.length; i++) {  
       File file = new File(dir, files[i]);  
       if (!isPackageFilename(files[i])) {  
         // Ignore entries which are not apk's  
         continue;  
       }  
       PackageParser.Package pkg = scanPackageLI(file,  
           flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime);  
       // Don't mess around with apps in system partition.  
       if (pkg == null && (flags & PackageParser.PARSE_IS_SYSTEM) == 0 &&  
           mLastScanError == PackageManager.INSTALL_FAILED_INVALID_APK) {  
         // Delete the apk  
         Slog.w(TAG, "Cleaning up failed install of " + file);  
         file.delete();  
       }  
     }  
   }  

并且从该扫描方法中可以看出调用了 scanPackageLI()

private PackageParser.Package scanPackageLI(File scanFile,

int parseFlags, int scanMode, long currentTime)

跟踪 scanPackageLI()方法后发现,程序经过很多次的 if else 的筛选,最后判定可以安装后调用了 mInstaller.install

 if (mInstaller != null) {  
           int ret = mInstaller.install(pkgName, useEncryptedFSDir,  pkg.applicationInfo.uid,pkg.applicationInfo.uid);  
           if(ret < 0) {  
             // Error from installer  
             mLastScanError =  PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;  
             return null;  
           }  
         }  

mInstaller.install()  通过  

  LocalSocketAddress address = new LocalSocketAddress(

        "installd", LocalSocketAddress.Namespace.RESERVED);

指挥 installd 在 C 语言的文件中完成工作

PackageManagerService 小节 :1) 从 apk, xml 中载入 pacakge 信息,存储到内部成员变量中,用于后面的查找. 关键的方法是 scanPackageLI().
2) 各种查询操作,包括 query Intent 操作。
3)install package 和 delete package 的操作. 还有后面的关键方法是 installPackageLI().

二、从网络上下载应用:

下载完成后,会自动调用 Packagemanager 的安装方法 installPackage()

/ Called when a downloaded package installation has been confirmed by the user /

由英文注释可见 PackageManagerService 类的 installPackage()函数为安装程序入口。

 public void installPackage(  
      final Uri packageURI, final IPackageInstallObserver observer, final int flags,  
      final String installerPackageName) {  
    mContext.enforceCallingOrSelfPermission(  
        android.Manifest.permission.INSTALL_PACKAGES, null);  
    Message msg = mHandler.obtainMessage(INIT_COPY);  
    msg.obj = new InstallParams(packageURI, observer, flags,  
        installerPackageName);  
    mHandler.sendMessage(msg);  
  }  

其中是通过 PackageHandler 的实例 mhandler.sendMessage(msg)把信息发给继承 Handler 的类 HandleMessage() 方法

 class PackageHandler extends Handler{  
          
 *****************省略若干********************  
      public void handleMessage(Message msg) {  
       try {  
         doHandleMessage(msg);  
       } finally {  
         Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);  
       }  
     }  
  ******************省略若干**********************  
  }  

把信息发给 doHandleMessage() 方法,方法中用 switch()语句进行判定传来 Message

  void doHandleMessage(Message msg) {  
       switch (msg.what) {  
        
         case INIT_COPY: {  
           if (DEBUG_SD_INSTALL) Log.i(TAG, "init_copy");  
           HandlerParams params = (HandlerParams) msg.obj;  
           int idx = mPendingInstalls.size();  
           if (DEBUG_SD_INSTALL) Log.i(TAG, "idx=" + idx);  
           // If a bind was already initiated we dont really  
           // need to do anything. The pending install  
           // will be processed later on.  
           if (!mBound) {  
             // If this is the only one pending we might  
             // have to bind to the service again.  
             if (!connectToService()) {  
               Slog.e(TAG, "Failed to bind to media container service");  
               params.serviceError();  
               return;  
             } else {  
               // Once we bind to the service, the first  
               // pending request will be processed.  
               mPendingInstalls.add(idx, params);  
             }  
           } else {  
             mPendingInstalls.add(idx, params);  
             // Already bound to the service. Just make  
             // sure we trigger off processing the first request.  
             if (idx == 0) {  
               mHandler.sendEmptyMessage(MCS_BOUND);  
             }  
           }  
           break;  
         }  
         case MCS_BOUND: {  
           if (DEBUG_SD_INSTALL) Log.i(TAG, "mcs_bound");  
           if (msg.obj != null) {  
             mContainerService = (IMediaContainerService) msg.obj;  
           }  
           if (mContainerService == null) {  
             // Something seriously wrong. Bail out  
             Slog.e(TAG, "Cannot bind to media container service");  
             for (HandlerParams params : mPendingInstalls) {  
               mPendingInstalls.remove(0);  
               // Indicate service bind error  
               params.serviceError();  
             }  
             mPendingInstalls.clear();  
           } else if (mPendingInstalls.size() > 0) {  
             HandlerParams params = mPendingInstalls.get(0);  
             if (params != null) {  
               params.startCopy();  
             }  
           } else {  
             // Should never happen ideally.  
             Slog.w(TAG, "Empty queue");  
           }  
          break;  
         }  
       ****************省略若干**********************  
 }  
 }         

public final boolean sendMessage ( Message msg)

public final boolean sendEmptyMessage (int what)

两者参数有别。

然后调用抽象类 HandlerParams 中的一个 startCopy()方法

abstract class HandlerParams {

final void startCopy() {

   ***************若干 if 语句判定否这打回 handler 消息*******

handleReturnCode();

}  
}

handleReturnCode()复写了两次其中有一次是删除时要调用的,只列出安装调用的一个方法

 @Override  
    void handleReturnCode() {  
      // If mArgs is null, then MCS couldn't be reached. When it  
      // reconnects, it will try again to install. At that point, this  
      // will succeed.  
      if (mArgs != null) {  
        processPendingInstall(mArgs, mRet);  
      }  
     }  

这时可以清楚的看见 processPendingInstall()被调用。

其中 run()方法如下

 run(){  
 synchronized (mInstallLock) {  
             ************省略*****************  
             installPackageLI(args, true, res);  
            
  }  
 }  
 instaPacakgeLI()args,res 参数分析  

//InstallArgs 是在 PackageService 定义的 static abstract class InstallArgs 静态抽象类。

 static abstract class InstallArgs {  
 *********************************************************************  
 其中定义了 flag 标志,packageURL,创建文件,拷贝 apk,修改包名称,  
           还有一些删除文件的清理,释放存储函数。  
   *********************************************************************  
 }  
   class PackageInstalledInfo {  
     String name;  
     int uid;  
     PackageParser.Package pkg;  
     int returnCode;  
     PackageRemovedInfo removedInfo;  
  }  

 private void installPackageLI(InstallArgs args,  
       boolean newInstall, PackageInstalledInfo res) {  
     int pFlags = args.flags;  
     String installerPackageName = args.installerPackageName;  
     File tmpPackageFile = new File(args.getCodePath());  
     boolean forwardLocked = ((pFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0);  
     boolean onSd = ((pFlags & PackageManager.INSTALL_EXTERNAL) != 0);  
     boolean replace = false;  
     int scanMode = (onSd ? 0 : SCAN_MONITOR) | SCAN_FORCE_DEX | SCAN_UPDATE_SIGNATURE  
         | (newInstall ? SCAN_NEW_INSTALL : 0);  
     // Result object to be returned  
     res.returnCode = PackageManager.INSTALL_SUCCEEDED;  
     // Retrieve PackageSettings and parse package  
     int parseFlags = PackageParser.PARSE_CHATTY |  
     (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0) |  
     (onSd ? PackageParser.PARSE_ON_SDCARD : 0);  
     parseFlags |= mDefParseFlags;  
     PackageParser pp = new PackageParser(tmpPackageFile.getPath());  
     pp.setSeparateProcesses(mSeparateProcesses);  
     final PackageParser.Package pkg = pp.parsePackage(tmpPackageFile,  
         null, mMetrics, parseFlags);  
     if (pkg == null) {  
       res.returnCode = pp.getParseError();  
       return;  
     }  
     String pkgName = res.name = pkg.packageName;  
     if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_TEST_ONLY) != 0) {  
       if ((pFlags&PackageManager.INSTALL_ALLOW_TEST) == 0) {  
         res.returnCode = PackageManager.INSTALL_FAILED_TEST_ONLY;  
         return;  
       }  
     }  
     if (GET_CERTIFICATES && !pp.collectCertificates(pkg, parseFlags)) {  
       res.returnCode = pp.getParseError();  
       return;  
     }  
     // Get rid of all references to package scan path via parser.  
     pp = null;  
     String oldCodePath = null;  
     boolean systemApp = false;  
     synchronized (mPackages) {  
       // Check if installing already existing package  
       if ((pFlags&PackageManager.INSTALL_REPLACE_EXISTING) != 0) {  
         String oldName = mSettings.mRenamedPackages.get(pkgName);  
         if (pkg.mOriginalPackages != null  
             && pkg.mOriginalPackages.contains(oldName)  
             && mPackages.containsKey(oldName)) {  
           // This package is derived from an original package,  
           // and this device has been updating from that original  
           // name.  We must continue using the original name, so  
           // rename the new package here.  
           pkg.setPackageName(oldName);  
           pkgName = pkg.packageName;  
           replace = true;  
         } else if (mPackages.containsKey(pkgName)) {  
           // This package, under its official name, already exists  
           // on the device; we should replace it.  
           replace = true;  
         }  
       }  
       PackageSetting ps = mSettings.mPackages.get(pkgName);  
      if (ps != null) {  
         oldCodePath = mSettings.mPackages.get(pkgName).codePathString;  
         if (ps.pkg != null && ps.pkg.applicationInfo != null) {  
           systemApp = (ps.pkg.applicationInfo.flags &  
               ApplicationInfo.FLAG_SYSTEM) != 0;  
         }  
       }  
     }  
     if (systemApp && onSd) {  
       // Disable updates to system apps on sdcard  
       Slog.w(TAG, "Cannot install updates to system apps on sdcard");  
       res.returnCode = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;  
       return;  
     }  
     if (!args.doRename(res.returnCode, pkgName, oldCodePath)) {  
       res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;  
       return;  
     }  
     // Set application objects path explicitly after the rename  
     setApplicationInfoPaths(pkg, args.getCodePath(), args.getResourcePath());  
     pkg.applicationInfo.nativeLibraryDir = args.getNativeLibraryPath();  
     if (replace) {  
       replacePackageLI(pkg, parseFlags, scanMode,  
           installerPackageName, res);  
     } else {  
       installNewPackageLI(pkg, parseFlags, scanMode,  
           installerPackageName,res);  
     }  
   }  

最后判断如果以前不存在那么调用 installNewPackageLI()

 private void installNewPackageLI(PackageParser.Package pkg,  
       int parseFlags,int scanMode,  
       String installerPackageName, PackageInstalledInfo res) {  
    ***********************省略若干*************************************************  
     PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanMode,  
        System.currentTimeMillis());  
    ***********************省略若干**************************************************  
 }  

最后终于回到了和开机安装一样的地方.与开机方式安装调用统一方法。

三、从 ADB 工具安装

其入口函数源文件为 pm.java

(源文件路径:android\frameworks\base\cmds\pm\src\com\android\commands\pm\pm.java)

其中\system\framework\pm.jar 包管理库

包管理脚本 \system\bin\pm 解析

showUsage 就是使用方法

private static void showUsage() {   
    System.err.println("usage: pm [list|path|install|uninstall]");   
    System.err.println("     pm list packages [-f]");   
    System.err.println("     pm list permission-groups");   
    System.err.println("     pm list permissions [-g] [-f] [-d] [-u] [GROUP]");   
    System.err.println("     pm list instrumentation [-f] [TARGET-PACKAGE]");   
    System.err.println("     pm list features");   
    System.err.println("     pm path PACKAGE");   
    System.err.println("     pm install [-l] [-r] [-t] [-i INSTALLER_PACKAGE_NAME] [-s] [-f] PATH");   
    System.err.println("     pm uninstall [-k] PACKAGE");   
    System.err.println("     pm enable PACKAGE_OR_COMPONENT");   
    System.err.println("     pm disable PACKAGE_OR_COMPONENT");   
    System.err.println("     pm setInstallLocation [0/auto] [1/internal] [2/external]");  
    **********************省略**************************  
   }

安装时候会调用 runInstall()方法

private void runInstall() {  
    int installFlags = 0;  
    String installerPackageName = null;  
    String opt;  
    while ((opt=nextOption()) != null) {  
      if (opt.equals("-l")) {  
        installFlags |= PackageManager.INSTALL_FORWARD_LOCK;  
      } else if (opt.equals("-r")) {  
        installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;  
      } else if (opt.equals("-i")) {  
        installerPackageName = nextOptionData();  
        if (installerPackageName == null) {  
          System.err.println("Error: no value specified for -i");  
          showUsage();  
          return;  
        }  
      } else if (opt.equals("-t")) {  
        installFlags |= PackageManager.INSTALL_ALLOW_TEST;  
      } else if (opt.equals("-s")) {  
        // Override if -s option is specified.  
        installFlags |= PackageManager.INSTALL_EXTERNAL;  
      } else if (opt.equals("-f")) {  
        // Override if -s option is specified.  
        installFlags |= PackageManager.INSTALL_INTERNAL;  
      } else {  
        System.err.println("Error: Unknown option: " + opt);  
        showUsage();  
        return;  
      }  
    }  
    String apkFilePath = nextArg();  
    System.err.println("\tpkg: " + apkFilePath);  
    if (apkFilePath == null) {  
      System.err.println("Error: no package specified");  
      showUsage();  
      return;  
    }  
    PackageInstallObserver obs = new PackageInstallObserver();  
    try {  
      mPm.installPackage(Uri.fromFile(new File(apkFilePath)), obs, installFlags,  
          installerPackageName);  
      synchronized (obs) {  
        while (!obs.finished) {  
          try {  
            obs.wait();  
          } catch (InterruptedException e) {  
          }  
        }  
        if (obs.result == PackageManager.INSTALL_SUCCEEDED) {  
          System.out.println("Success");  
        } else {  
          System.err.println("Failure ["  
              + installFailureToString(obs.result)  
              + "]");  
        }  
      }  
    } catch (RemoteException e) {  
      System.err.println(e.toString());  
      System.err.println(PM_NOT_RUNNING_ERR);  
    }  
  }

其中的

PackageInstallObserver obs = new PackageInstallObserver();

mPm.installPackage(Uri.fromFile(new File(apkFilePath)), obs, installFlags,

installerPackageName);

如果安装成功

obs.result == PackageManager.INSTALL_SUCCEEDED)

又因为有

IPackageManage mPm;

mPm = IpackageManager.Stub.asInterface(ServiceManager.getService("package"));

Stub 是接口 IPackageManage 的静态抽象类,asInterface 是返回 IPackageManager 代理的静态方法。

因为 class PackageManagerService extends IPackageManager.Stub

所以 mPm.installPackage 调用

/ Called when a downloaded package installation has been confirmed by the user /

public void installPackage(

final Uri packageURI, final IPackageInstallObserver observer, final int flags,final String installerPackageName)

这样就是从网络下载安装的入口了。

四,从 SD 卡安装

系统调用 PackageInstallerActivity.java(/home/zhongda/androidSRC/vortex-8inch-for-hoperun/packages/apps/PackageInstaller/src/com/android/packageinstaller)

进入这个 Activity 会判断信息是否有错,然后调用

private void initiateInstall() 判断是否曾经有过同名包的安装,或者包已经安装

通过后执行 private void startInstallConfirm() 点击 OK 按钮后经过一系列的安装信息的判断 Intent 跳转到

public class InstallAppProgress extends Activity implements View.OnClickListener, OnCancelListener  
   public void onCreate(Bundle icicle) {  
    super.onCreate(icicle);  
    Intent intent = getIntent();  
    mAppInfo = intent.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);  
    mPackageURI = intent.getData();  
    initView();  
  } 

方法中调用了 initView()方法

public void initView() {  
     requestWindowFeature(Window.FEATURE_NO_TITLE);  
     setContentView(R.layout.op_progress);  
     int installFlags = 0;  
     PackageManager pm = getPackageManager();  
     try {  
       PackageInfo pi = pm.getPackageInfo(mAppInfo.packageName,   
           PackageManager.GET_UNINSTALLED_PACKAGES);  
       if(pi != null) {  
         installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;  
       }  
     } catch (NameNotFoundException e) {  
     }  
     if((installFlags & PackageManager.INSTALL_REPLACE_EXISTING )!= 0) {  
       Log.w(TAG, "Replacing package:" + mAppInfo.packageName);  
     }  
     PackageUtil.AppSnippet as = PackageUtil.getAppSnippet(this, mAppInfo,  
         mPackageURI);  
     mLabel = as.label;  
     PackageUtil.initSnippetForNewApp(this, as, R.id.app_snippet);  
     mStatusTextView = (TextView)findViewById(R.id.center_text);  
     mStatusTextView.setText(R.string.installing);  
     mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);  
     mProgressBar.setIndeterminate(true);  
     // Hide button till progress is being displayed  
     mOkPanel = (View)findViewById(R.id.buttons_panel);  
     mDoneButton = (Button)findViewById(R.id.done_button);  
     mLaunchButton = (Button)findViewById(R.id.launch_button);  
     mOkPanel.setVisibility(View.INVISIBLE);  
     String installerPackageName = getIntent().getStringExtra(  
         Intent.EXTRA_INSTALLER_PACKAGE_NAME);  
     PackageInstallObserver observer = new PackageInstallObserver();  
     pm.installPackage(mPackageURI, observer, installFlags, installerPackageName);  
   }

方法最后我们可以看到再次调用安装接口完成安装。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文