專業(yè)移動(dòng)微網(wǎng)站設(shè)計(jì)海南seo
今天簡(jiǎn)單講解一下PackageInstaller
文件路徑:
packages/apps/PackageInstaller
frameworks/base/core/java/android/content/pm&res
下面開(kāi)始講解:
首先,我們說(shuō)一下安裝apk的幾種方式,整體上可以分為2類,一類是有界面安裝,一類是無(wú)界面安裝。無(wú)界面安裝分為內(nèi)置apk開(kāi)機(jī)安裝和命令安裝,命令安裝又分為兩類,一類電腦安裝也就是adb命令,另一類是手機(jī)安裝也就是pm命令。今天我們主要介紹有界面安裝。
當(dāng)然,我們從這個(gè)安裝界面說(shuō)起,這個(gè)界面是那個(gè)呢?就是PackageInstallerActivity這個(gè)acitvity。它是如何啟動(dòng)起來(lái)的呢?我們?nèi)タ纯此贏ndroidManifest是如何定義的
<activity android:name=".PackageInstallerActivity"android:configChanges="orientation|keyboardHidden|screenSize"android:excludeFromRecents="true"android:screenOrientation="unspecified"><intent-filter><action android:name="android.intent.action.VIEW" /><action android:name="android.intent.action.INSTALL_PACKAGE" /><category android:name="android.intent.category.DEFAULT" /><data android:scheme="content" /><data android:scheme="file" /><data android:mimeType="application/vnd.android.package-archive" /></intent-filter><intent-filter><action android:name="android.intent.action.INSTALL_PACKAGE" /><category android:name="android.intent.category.DEFAULT" /><data android:scheme="content" /><data android:scheme="file" /></intent-filter></activity>
很明顯了,我們可以通過(guò)android.intent.action.INSTALL_PACKAGE這個(gè)action啟動(dòng),也可以通過(guò)android.intent.action.VIEW這個(gè)action加上"application/vnd.android.package-archive"這個(gè)type啟動(dòng),當(dāng)然不加這個(gè)type也能啟動(dòng),但是會(huì)找到很多這樣的activity哦。另外,通過(guò)類名或包名啟動(dòng)也未嘗不可的。所以,大部分啟動(dòng)是這樣的
String apkFileString = Environment.getExternalStorageDirectory().getAbsolutePath()+"/.../packageName.pac";File apkFile = new File(apkFileString);Intent intent = new Intent(Intent.ACTION_VIEW);intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive");mContext.startActivity(intent);
這里我們傳進(jìn)去一個(gè)數(shù)據(jù)就是pakFile的Uri,然后我們?nèi)ackageInstallerActivity的onCreate中看看
final Intent intent = getIntent();mPackageURI = intent.getData();mPm = getPackageManager();mPkgInfo = PackageUtil.getPackageInfo(mPackageURI);
獲取到,我們剛才傳進(jìn)來(lái)的apkFile的Uri給了mPackageURI,接著獲取到PackageManager,然后生成一個(gè)mPkgInfo也就是PackageParser.Package,這個(gè)很重要。我們看看PackageParser.Package是如何生成的,PackageParser.Package里面都包含了什么東西。那我們就要去PackageUtil.getPackageInfo中了
public static PackageParser.Package getPackageInfo(Uri packageURI) {final String archiveFilePath = packageURI.getPath();PackageParser packageParser = new PackageParser(archiveFilePath);File sourceFile = new File(archiveFilePath);DisplayMetrics metrics = new DisplayMetrics();metrics.setToDefaults();PackageParser.Package pkg = packageParser.parsePackage(sourceFile,archiveFilePath, metrics, 0);// Nuke the parser reference.packageParser = null;return pkg;}
生成一個(gè)Package解析器,通過(guò)這個(gè)解析器來(lái)獲取到PackageParser.Package中需要的數(shù)據(jù),生成一個(gè)PackageParser.Package。我們看看PackageParser.parsePackage是如何生成一個(gè)PackageParser.Package的,這里傳進(jìn)去四個(gè)參數(shù),一個(gè)Source File,apk文件,一個(gè)apk路徑,一個(gè)屏幕信息,最后一個(gè)0,具體做什么的,進(jìn)去之后就能明白了
public Package parsePackage(File sourceFile, String destCodePath,DisplayMetrics metrics, int flags) {mParseError = PackageManager.INSTALL_SUCCEEDED;mArchiveSourcePath = sourceFile.getPath();if (!sourceFile.isFile()) {Slog.w(TAG, "Skipping dir: " + mArchiveSourcePath);mParseError = PackageManager.INSTALL_PARSE_FAILED_NOT_APK;return null;}if (!isPackageFilename(sourceFile.getName())&& (flags&PARSE_MUST_BE_APK) != 0) {if ((flags&PARSE_IS_SYSTEM) == 0) {// We expect to have non-.apk files in the system dir,// so don't warn about them.Slog.w(TAG, "Skipping non-package file: " + mArchiveSourcePath);}mParseError = PackageManager.INSTALL_PARSE_FAILED_NOT_APK;return null;}if (DEBUG_JAR)Slog.d(TAG, "Scanning package: " + mArchiveSourcePath);XmlResourceParser parser = null;AssetManager assmgr = null;Resources res = null;boolean assetError = true;try {assmgr = new AssetManager();int cookie = assmgr.addAssetPath(mArchiveSourcePath);if (cookie != 0) {res = new Resources(assmgr, metrics, null);assmgr.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,Build.VERSION.RESOURCES_SDK_INT);parser = assmgr.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);assetError = false;} else {Slog.w(TAG, "Failed adding asset path:"+mArchiveSourcePath);}} catch (Exception e) {Slog.w(TAG, "Unable to read AndroidManifest.xml of "+ mArchiveSourcePath, e);}if (assetError) {if (assmgr != null) assmgr.close();mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;return null;}String[] errorText = new String[1];Package pkg = null;Exception errorException = null;try {// XXXX todo: need to figure out correct configuration.pkg = parsePackage(res, parser, flags, errorText);} catch (Exception e) {errorException = e;mParseError = PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;}if (pkg == null) {// If we are only parsing core apps, then a null with INSTALL_SUCCEEDED// just means to skip this app so don't make a fuss about it.if (!mOnlyCoreApps || mParseError != PackageManager.INSTALL_SUCCEEDED) {if (errorException != null) {Slog.w(TAG, mArchiveSourcePath, errorException);} else {Slog.w(TAG, mArchiveSourcePath + " (at "+ parser.getPositionDescription()+ "): " + errorText[0]);}if (mParseError == PackageManager.INSTALL_SUCCEEDED) {mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;}}parser.close();assmgr.close();return null;}parser.close();assmgr.close();// Set code and resource pathspkg.mPath = destCodePath;pkg.mScanPath = mArchiveSourcePath;//pkg.applicationInfo.sourceDir = destCodePath;//pkg.applicationInfo.publicSourceDir = destRes;pkg.mSignatures = null;return pkg;}
首先sourceFile.isFile()判斷一下是不是文件,如果不是,返回;接著isPackageFilename(sourceFile.getName())判斷是不是apk文件,如果不是,返回;接著去獲取三個(gè)關(guān)鍵變量,也就是
XmlResourceParser parser = null;AssetManager assmgr = null;Resources res = null;
這三個(gè)是什么呢?這里簡(jiǎn)單說(shuō)一下,AssetManager資產(chǎn)管理器,用來(lái)管理包中獲取到的資源
assmgr = new AssetManager();int cookie = assmgr.addAssetPath(mArchiveSourcePath);
通過(guò)addAssetPath可以獲取到唯一標(biāo)識(shí)該apk包資產(chǎn)的關(guān)鍵字cookie,也就是通過(guò)cookie可以找到該包的資源信息。Resources就是資源了,包括圖片,color,xml等資源
res = new Resources(assmgr, metrics, null);
當(dāng)然Resources信息也是通過(guò)AssetManager獲取到的。XmlResourceParser顧名思義就是Xml資源文件解析器了,用來(lái)解析我們xml文件的
parser = assmgr.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
ANDROID_MANIFEST_FILENAME也就是
private static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml";
這樣就很明顯了,這里生成的xml文件資源解析器是用來(lái)解析AndroidManifest文件的了。接下來(lái)就是關(guān)鍵了
String[] errorText = new String[1];Package pkg = null;Exception errorException = null;try {// XXXX todo: need to figure out correct configuration.pkg = parsePackage(res, parser, flags, errorText);} catch (Exception e) {errorException = e;mParseError = PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;}
這里才是我們Package真正生成的地方了,也就是pkg = parsePackage(res, parser, flags, errorText)了。parsePackage是同構(gòu)函數(shù),一個(gè)是以File為首個(gè)參數(shù),就是我們現(xiàn)在分析的這個(gè),一個(gè)是以Resources為首個(gè)參數(shù),就是我們接下來(lái)要講的了,由于這個(gè)函數(shù)比較大,所以不再全部列出,只選取主要的
String pkgName = parsePackageName(parser, attrs, flags, outError);
獲取包名。
final Package pkg = new Package(pkgName);boolean foundApp = false;TypedArray sa = res.obtainAttributes(attrs,com.android.internal.R.styleabl