Android應用的安裝是通過packageinstaller完成. 在源碼中, PackageInstaller位於packages/apps/PackageInstaller中. 根據代碼量來看並不是太大.
主要是PackageInstallerActivity類, 安裝過程中一切狀態轉換與判斷都在這個Activity中進行. 原文鏈接: http://www.ldkxingzhe.top/post/Android PackageInstaller 7.1.2源碼分析
安裝流程
先看一下最常見的安裝流程.
PackageInstaller中處理安裝前的判斷與確認操作
首先processPackageUri檢測解析Uri. 提取scheme(目前支持file, content, package三種協議, 便於分析只保留了file部分)
private boolean processPackageUri(final Uri packageUri) {
mPackageURI = packageUri;
final String scheme = packageUri.getScheme();
// .... 專注於file協議
File sourceFile = new File(packageUri.getPath());
// 調用PackageParser的parsePackage方法
PackageParser.Package parsed = PackageUtil.getPackageInfo(sourceFile);
// 獲取一個PackageInfo對象(獲取權限部分)
mPkgInfo = PackageParser.generatePackageInfo(parsed, null,
PackageManager.GET_PERMISSIONS, 0, 0, null,
new PackageUserState());
// As 就是一個應用程序的標題與圖標的數據類
as = PackageUtil.getAppSnippet(this, mPkgInfo.applicationInfo, sourceFile);
PackageUtil.initSnippetForNewApp(this, as, R.id.app_snippet);
return true;
}
可以看出 processPackageUrl調用framework的api獲取了apk的權限信息與應用名稱與圖標.
然后就是判斷該包是否允許安裝, 如果允許則初始化准備安裝. 首先在checkIfAllowedAndInitiateInstall中判斷是否是未知來源的應用, 一般是開啟允許未知來源的. 直接開啟初始化安裝
initiateInstall僅僅獲取一下本機是否安裝了該應用, 如果安裝獲取到該應用的信息
private void initiateInstall() {
String pkgName = mPkgInfo.packageName;
// 源碼這里進行了pkgName判斷轉換, 一般不會遇到
// Check if package is already installed. display confirmation dialog if replacing pkg
try {
// This is a little convoluted because we want to get all uninstalled
// apps, but this may include apps with just data, and if it is just
// data we still want to count it as "installed".
mAppInfo = mPm.getApplicationInfo(pkgName,
PackageManager.GET_UNINSTALLED_PACKAGES);
if ((mAppInfo.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {
mAppInfo = null;
}
} catch (NameNotFoundException e) {
mAppInfo = null;
}
startInstallConfirm();
}
最后startInstallConfirm()方法中顯示了界面中顯示的權限列表以及, 確認取消列表. 查看點擊OK的事件, 確定安裝事件為:
private void startInstall() {
// Start subactivity to actually install the application
Intent newIntent = new Intent();
newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,
mPkgInfo.applicationInfo);
newIntent.setData(mPackageURI);
newIntent.setClass(this, InstallAppProgress.class);
String installerPackageName = getIntent().getStringExtra(
Intent.EXTRA_INSTALLER_PACKAGE_NAME);
if (mOriginatingURI != null) {
newIntent.putExtra(Intent.EXTRA_ORIGINATING_URI, mOriginatingURI);
}
if (mReferrerURI != null) {
newIntent.putExtra(Intent.EXTRA_REFERRER, mReferrerURI);
}
if (mOriginatingUid != VerificationParams.NO_UID) {
newIntent.putExtra(Intent.EXTRA_ORIGINATING_UID, mOriginatingUid);
}
if (installerPackageName != null) {
newIntent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME,
installerPackageName);
}
if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
}
if(localLOGV) Log.i(TAG, "downloaded app uri="+mPackageURI);
startActivity(newIntent);
finish();
}
好吧, 安裝確認后跳轉到安裝進度頁面, 具體安裝過程也在這個界面中.
InstallAppProcess開始安裝並處理安裝進度
onCreate中, 接參數mAppInfo與mPackageURI
// 開啟安裝線程, 使用了HandlerThread
mInstallThread = new HandlerThread("InstallThread");
mInstallThread.start();
mInstallHandler = new Handler(mInstallThread.getLooper());
// 注冊一個安裝結束的監聽
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BROADCAST_ACTION);
registerReceiver(
mBroadcastReceiver, intentFilter, BROADCAST_SENDER_PERMISSION, null /*scheduler*/);
這個handlerThread進行的任務是 doPackageState
private void doPackageStage(PackageManager pm, PackageInstaller.SessionParams params) {
// 1. 獲取一個PackageInstaller
final PackageInstaller packageInstaller = pm.getPackageInstaller();
PackageInstaller.Session session = null;
try {
final String packageLocation = mPackageURI.getPath();
final File file = new File(packageLocation);
// 2. create一個sessionId
final int sessionId = packageInstaller.createSession(params);
final byte[] buffer = new byte[65536];
// 3. 根據sessionId打開一個session回話
session = packageInstaller.openSession(sessionId);
final InputStream in = new FileInputStream(file);
final long sizeBytes = file.length();
// 4. 根據這個session回話, 獲取一個OutPutstream, 然后將文件寫入到這個OutPutStream中
final OutputStream out = session.openWrite("PackageInstaller", 0, sizeBytes);
try {
int c;
while ((c = in.read(buffer)) != -1) {
out.write(buffer, 0, c);
if (sizeBytes > 0) {
final float fraction = ((float) c / (float) sizeBytes);
session.addProgress(fraction);
}
}
session.fsync(out);
} finally {
IoUtils.closeQuietly(in);
IoUtils.closeQuietly(out);
}
// Create a PendingIntent and use it to generate the IntentSender
Intent broadcastIntent = new Intent(BROADCAST_ACTION);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
InstallAppProgress.this /*context*/,
sessionId,
broadcastIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
// 5. 最后調用session的commit方法進行提交
session.commit(pendingIntent.getIntentSender());
} catch (IOException e) {
onPackageInstalled(PackageInstaller.STATUS_FAILURE);
} finally {
IoUtils.closeQuietly(session);
}
}
結論
PackageInstaller只是在應用安裝前進行信息提取, 篩選, 以及apk文件的獲取. 最后的安裝還是調用Framework的PackageInstaller進行安裝.
