android PackageInstaller那点事儿

今天简单讲解一下PackageInstaller

    文件路径:

    packages/apps/PackageInstaller

    frameworks/base/core/java/android/content/pm&res

   下面开始讲解:

    首先,我们说一下安装apk的几种方式,整体上可以分为2类,一类是有界面安装,一类是无界面安装。无界面安装分为内置apk开机安装和命令安装,命令安装又分为两类,一类电脑安装也就是adb命令,另一类是手机安装也就是pm命令。今天我们主要介绍有界面安装。

    当然,我们从这个安装界面说起,这个界面是那个呢?就是PackageInstallerActivity这个acitvity。它是如何启动起来的呢?我们去看看它在AndroidManifest是如何定义的

 

[html]  view plain copy
 
  1. <activity android:name=".PackageInstallerActivity"  
  2.         android:configChanges="orientation|keyboardHidden|screenSize"  
  3.         android:excludeFromRecents="true"  
  4.         android:screenOrientation="unspecified">  
  5.     <intent-filter>  
  6.         <action android:name="android.intent.action.VIEW" />  
  7.         <action android:name="android.intent.action.INSTALL_PACKAGE" />  
  8.         <category android:name="android.intent.category.DEFAULT" />  
  9.         <data android:scheme="content" />  
  10.         <data android:scheme="file" />  
  11.         <data android:mimeType="application/vnd.android.package-archive" />  
  12.     intent-filter>  
  13.     <intent-filter>  
  14.         <action android:name="android.intent.action.INSTALL_PACKAGE" />  
  15.         <category android:name="android.intent.category.DEFAULT" />  
  16.         <data android:scheme="content" />  
  17.         <data android:scheme="file" />  
  18.     intent-filter>  
  19. activity>  


很明显了,我们可以通过android.intent.action.INSTALL_PACKAGE这个action启动,也可以通过android.intent.action.VIEW这个action加上"application/vnd.android.package-archive"这个type启动,当然不加这个type也能启动,但是会找到很多这样的activity哦。另外,通过类名或包名启动也未尝不可的。所以,大部分启动是这样的

 

 

[java]  view plain copy
 
  1. String apkFileString = Environment.getExternalStorageDirectory().getAbsolutePath()+"/.../packageName.pac";  
  2. File apkFile = new File(apkFileString);  
  3. Intent intent = new Intent(Intent.ACTION_VIEW);  
  4. intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
  5. intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive");  
  6. mContext.startActivity(intent);  


这里我们传进去一个数据就是pakFile的Uri,然后我们去PackageInstallerActivity的onCreate中看看

 

 

[java]  view plain copy
 
  1. final Intent intent = getIntent();  
  2. mPackageURI = intent.getData();  
  3. mPm = getPackageManager();  
  4. mPkgInfo = PackageUtil.getPackageInfo(mPackageURI);  

 

获取到,我们刚才传进来的apkFile的Uri给了mPackageURI,接着获取到PackageManager,然后生成一个mPkgInfo也就是PackageParser.Package,这个很重要。我们看看PackageParser.Package是如何生成的,PackageParser.Package里面都包含了什么东西。那我们就要去PackageUtil.getPackageInfo中了

 

[java]  view plain copy
 
  1. public static  PackageParser.Package getPackageInfo(Uri packageURI) {  
  2.     final String archiveFilePath = packageURI.getPath();  
  3.     PackageParser packageParser = new PackageParser(archiveFilePath);  
  4.     File sourceFile = new File(archiveFilePath);  
  5.     DisplayMetrics metrics = new DisplayMetrics();  
  6.     metrics.setToDefaults();  
  7.     PackageParser.Package pkg =  packageParser.parsePackage(sourceFile,  
  8.             archiveFilePath, metrics, 0);  
  9.     // Nuke the parser reference.  
  10.     packageParser = null;  
  11.     return pkg;  
  12. }  


生成一个Package解析器,通过这个解析器来获取到PackageParser.Package中需要的数据,生成一个PackageParser.Package。我们看看PackageParser.parsePackage是如何生成一个PackageParser.Package的,这里传进去四个参数,一个Source File,apk文件,一个apk路径,一个屏幕信息,最后一个0,具体做什么的,进去之后就能明白了

 

 

[java]  view plain copy
 
  1. public Package parsePackage(File sourceFile, String destCodePath,  
  2.         DisplayMetrics metrics, int flags) {  
  3.     mParseError = PackageManager.INSTALL_SUCCEEDED;  
  4.   
  5.     mArchiveSourcePath = sourceFile.getPath();  
  6.     if (!sourceFile.isFile()) {  
  7.         Slog.w(TAG, "Skipping dir: " + mArchiveSourcePath);  
  8.         mParseError = PackageManager.INSTALL_PARSE_FAILED_NOT_APK;  
  9.         return null;  
  10.     }  
  11.     if (!isPackageFilename(sourceFile.getName())  
  12.             && (flags&PARSE_MUST_BE_APK) != 0) {  
  13.         if ((flags&PARSE_IS_SYSTEM) == 0) {  
  14.             // We expect to have non-.apk files in the system dir,  
  15.             // so don't warn about them.  
  16.             Slog.w(TAG, "Skipping non-package file: " + mArchiveSourcePath);  
  17.         }  
  18.         mParseError = PackageManager.INSTALL_PARSE_FAILED_NOT_APK;  
  19.         return null;  
  20.     }  
  21.   
  22.     if (DEBUG_JAR)  
  23.         Slog.d(TAG, "Scanning package: " + mArchiveSourcePath);  
  24.   
  25.     XmlResourceParser parser = null;  
  26.     AssetManager assmgr = null;  
  27.     Resources res = null;  
  28.     boolean assetError = true;  
  29.     try {  
  30.         assmgr = new AssetManager();  
  31.         int cookie = assmgr.addAssetPath(mArchiveSourcePath);  
  32.         if (cookie != 0) {  
  33.             res = new Resources(assmgr, metrics, null);  
  34.             assmgr.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  
  35.                     Build.VERSION.RESOURCES_SDK_INT);  
  36.             parser = assmgr.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);  
  37.             assetError = false;  
  38.         } else {  
  39.             Slog.w(TAG, "Failed adding asset path:"+mArchiveSourcePath);  
  40.         }  
  41.     } catch (Exception e) {  
  42.         Slog.w(TAG, "Unable to read AndroidManifest.xml of "  
  43.                 + mArchiveSourcePath, e);  
  44.     }  
  45.     if (assetError) {  
  46.         if (assmgr != null) assmgr.close();  
  47.         mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;  
  48.         return null;  
  49.     }  
  50.     String[] errorText = new String[1];  
  51.     Package pkg = null;  
  52.     Exception errorException = null;  
  53.     try {  
  54.         // XXXX todo: need to figure out correct configuration.  
  55.         pkg = parsePackage(res, parser, flags, errorText);  
  56.     } catch (Exception e) {  
  57.         errorException = e;  
  58.         mParseError = PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;  
  59.     }  
  60.   
  61.   
  62.     if (pkg == null) {  
  63.         // If we are only parsing core apps, then a null with INSTALL_SUCCEEDED  
  64.         // just means to skip this app so don't make a fuss about it.  
  65.         if (!mOnlyCoreApps || mParseError != PackageManager.INSTALL_SUCCEEDED) {  
  66.             if (errorException != null) {  
  67.                 Slog.w(TAG, mArchiveSourcePath, errorException);  
  68.             } else {  
  69.                 Slog.w(TAG, mArchiveSourcePath + " (at "  
  70.                         + parser.getPositionDescription()  
  71.                         + "): " + errorText[0]);  
  72.             }  
  73.             if (mParseError == PackageManager.INSTALL_SUCCEEDED) {  
  74.                 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;  
  75.             }  
  76.         }  
  77.         parser.close();  
  78.         assmgr.close();  
  79.         return null;  
  80.     }  
  81.   
  82.     parser.close();  
  83.     assmgr.close();  
  84.   
  85.     // Set code and resource paths  
  86.     pkg.mPath = destCodePath;  
  87.     pkg.mScanPath = mArchiveSourcePath;  
  88.     //pkg.applicationInfo.sourceDir = destCodePath;  
  89.     //pkg.applicationInfo.publicSourceDir = destRes;  
  90.     pkg.mSignatures = null;  
  91.   
  92.     return pkg;  
  93. }  


首先sourceFile.isFile()判断一下是不是文件,如果不是,返回;接着isPackageFilename(sourceFile.getName())判断是不是apk文件,如果不是,返回;接着去获取三个关键变量,也就是

 

 

[java]  view plain copy
 
  1. XmlResourceParser parser = null;  
  2. AssetManager assmgr = null;  
  3. Resources res = null;  


这三个是什么呢?这里简单说一下,AssetManager资产管理器,用来管理包中获取到的资源

 

 

[java]  view plain copy
 
  1. assmgr = new AssetManager();  
  2. int cookie = assmgr.addAssetPath(mArchiveSourcePath);  


通过addAssetPath可以获取到唯一标识该apk包资产的关键字cookie,也就是通过cookie可以找到该包的资源信息。Resources就是资源了,包括图片,color,xml等资源

 

 

[java]  view plain copy
 
  1. res = new Resources(assmgr, metrics, null);  

当然Resources信息也是通过AssetManager获取到的。XmlResourceParser顾名思义就是Xml资源文件解析器了,用来解析我们xml文件的

 

 

[java]  view plain copy
 
  1. parser = assmgr.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);  


ANDROID_MANIFEST_FILENAME也就是

 

 

[java]  view plain copy
 
  1. private static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml";  


这样就很明显了,这里生成的xml文件资源解析器是用来解析AndroidManifest文件的了。接下来就是关键了

 

 

[java]  view plain copy
 
  1. String[] errorText = new String[1];  
  2. Package pkg = null;  
  3. Exception errorException = null;  
  4. try {  
  5.     // XXXX todo: need to figure out correct configuration.  
  6.     pkg = parsePackage(res, parser, flags, errorText);  
  7. catch (Exception e) {  
  8.     errorException = e;  
  9.     mParseError = PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;  
  10. }  


这里才是我们Package真正生成的地方了,也就是pkg = parsePackage(res, parser, flags, errorText)了。parsePackage是同构函数,一个是以File为首个参数,就是我们现在分析的这个,一个是以Resources为首个参数,就是我们接下来要讲的了,由于这个函数比较大,所以不再全部列出,只选取主要的

 

 

[java]  view plain copy
 
  1. String pkgName = parsePackageName(parser, attrs, flags, outError);  


获取包名。

 

 

[java]  view plain copy
 
  1. final Package pkg = new Package(pkgName);  
  2. boolean foundApp = false;  
  3.   
  4. TypedArray sa = res.obtainAttributes(attrs,  
  5.         com.android.internal.R.styleable.AndroidManifest);  
  6. pkg.mVersionCode = sa.getInteger(  
  7.         com.android.internal.R.styleable.AndroidManifest_versionCode, 0);  
  8. pkg.mVersionName = sa.getNonConfigurationString(  
  9.         com.android.internal.R.styleable.AndroidManifest_versionName, 0);  
  10. if (pkg.mVersionName != null) {  
  11.     pkg.mVersionName = pkg.mVersionName.intern();  
  12. }  
  13. String str = sa.getNonConfigurationString(  
  14.         com.android.internal.R.styleable.AndroidManifest_sharedUserId, 0);  
  15. if (str != null && str.length() > 0) {  
  16.     String nameError = validateName(str, true);  
  17.     if (nameError != null && !"android".equals(pkgName)) {  
  18.         outError[0] = " specifies bad sharedUserId name \""  
  19.             + str + "\": " + nameError;  
  20.         mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID;  
  21.         return null;  
  22.     }  
  23.     pkg.mSharedUserId = str.intern();  
  24.     pkg.mSharedUserLabel = sa.getResourceId(  
  25.             com.android.internal.R.styleable.AndroidManifest_sharedUserLabel, 0);  
  26. }  
  27. sa.recycle();  
  28.   
  29. pkg.installLocation = sa.getInteger(  
  30.         com.android.internal.R.styleable.AndroidManifest_installLocation,  
  31.         PARSE_DEFAULT_INSTALL_LOCATION);  
  32. pkg.applicationInfo.installLocation = pkg.installLocation;  


解析获取,我们AndroidManifest的attrs,也就是frameworks/base/core/res/res/values下的attrs_manifest.xml中定义的

 

 

[java]  view plain copy
 
  1. "AndroidManifest">  
  2.     "versionCode" />  
  3.     "versionName" />  
  4.     "sharedUserId" />  
  5.     "sharedUserLabel" />  
  6.     "installLocation" />  
  7.   


这些变量信息。接下来就是一个大循环了,这里解析的内容比较多了,我们举几个常见的例子,如"application"也就是

 

 

[java]  view plain copy
 
  1. "@string/app_name">  
  2.   

 

这里面包含的信息,例如这里的lable等等,还有以其为父的"activity","receiver","service","provider"等等,这里以"activity"为例,还是去frameworks/base/core/res/res/values下的attrs_manifest.xml中,也就是

 

[html]  view plain copy
 
  1. <declare-styleable name="AndroidManifestActivity" parent="AndroidManifestApplication">  
  2.     

你可能感兴趣的:(android PackageInstaller那点事儿)