`
蓝月儿
  • 浏览: 48343 次
  • 性别: Icon_minigender_2
  • 来自: 北京
社区版块
存档分类
最新评论

android 应用的安装过程

 
阅读更多
转载自
[url]
http://blog.csdn.net/hdhd588/article/details/6739281#comments
[/url]
APK安装过程及原理详解

2011-09-01 14:09 13614人阅读 评论(7) 收藏 举报
nullsysteminstallerframeworksstringfile
应用程序包的安装是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包

          

[java] view plaincopy
// 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的应用程序
         
[java] view plaincopy
// 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应用包
          
[java] view plaincopy
// 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”目录,即用户安装的第三方应用
[java] view plaincopy
scanDirLI(mAppInstallDir, 0, scanMode, 0); 


5.扫描" data\app-private"目录,即安装DRM保护的APK文件(一个受保护的歌曲或受保 护的视频是使用 DRM 保护的文件)
[java] view plaincopy
scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK, 
                    scanMode, 0); 


扫描方法的代码清单
[java] view plaincopy
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
[java] view plaincopy
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()函数为安装程序入口。
  
[java] view plaincopy
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()方法
[java] view plaincopy
class PackageHandler extends Handler{ 
                  
*****************省略若干******************** 
         public void handleMessage(Message msg) { 
            try { 
                doHandleMessage(msg); 
            } finally { 
                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 
            } 
        } 
   ******************省略若干********************** 



把信息发给doHandleMessage()方法,方法中用switch()语句进行判定传来Message
      
[java] view plaincopy
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()复写了两次其中有一次是删除时要调用的,只列出安装调用的一个方法
      
[java] view plaincopy
@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()方法如下
[java] view plaincopy
run(){ 
synchronized (mInstallLock) { 
                        ************省略***************** 
                        installPackageLI(args, true, res); 
                    


instaPacakgeLI()args,res参数分析 


-----------------------------------------------------------------------------------------
//InstallArgs 是在PackageService定义的static abstract class InstallArgs 静态抽象类。
 
[java] view plaincopy
static abstract class InstallArgs { 
********************************************************************* 
其中定义了flag标志,packageURL,创建文件,拷贝apk,修改包名称, 
                    还有一些删除文件的清理,释放存储函数。 
    ********************************************************************* 

  class PackageInstalledInfo { 
        String name; 
        int uid; 
        PackageParser.Package pkg; 
        int returnCode; 
        PackageRemovedInfo removedInfo; 


-----------------------------------------------------------------------------------------
 
[java] view plaincopy
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()
   
[java] view plaincopy
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就是使用方法
[java] view plaincopy
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()方法
 
[java] view plaincopy
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跳转到
[java] view plaincopy
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()方法
  
[java] view plaincopy
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); 
   } 



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

安装指定目录的apk



 /***
  * 安装应用<br>
  * @param file apk的路径<br>
  */
 private void installApp(String file){
  Intent intent = new Intent(Intent.ACTION_VIEW);
  intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
  intent.setDataAndType(
    Uri.fromFile(new File(file)),
    "application/vnd.android.package-archive");
  startActivity(intent);
 }


分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics