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();  
                }  
            }  
        }  

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

private PackageParser.Package scanPackageList(File scanFile,int parseFlags, int scanMode, long currentTime)

跟踪scanPackageList()方法后发现,程序经过很多次的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信息, 存储到内部成员变量中, 用于后面的查找. 关键的方法是scanPackageList().
2.各种查询操作, 包括query Intent操作.
3.install package和delete package的操作. 还有后面的关键方法是installPackageList().

二、从网络上下载应用:
下载完成后,会自动调用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);  
       }  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值