APK的安裝有不少方式,應用商城下載、文件瀏覽器安裝、adb命令安裝,咱們以文件瀏覽器爲例。java
將一個APK文件放到SD卡目錄下,文件瀏覽器顯示apk文件。android
當咱們點擊APK文件時,文件瀏覽器根據文件的後綴(.apk)解析,執行startActivity,調起packageinstaller.PackageInstallerActivity。瀏覽器
ActivityManager: START u0 {act=android.intent.action.VIEW dat=file:///storage/emulated/0/com.dewmobile.kuaiya.apk typ=application/vnd.android.package-archive flg=0x88000 cmp=com.android.packageinstaller/.PackageInstallerActivity} from uid 10032 on display 0
複製代碼
PackageInstallerActivity這個類的主要做用是顯示安裝彈窗,對APK進行解析,判斷是否容許未知來源安裝,判斷應用權限,等待用戶安裝。markdown
packages/apps/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
session
public void onClick(View v) {
if (v == mOk) {
if (mOkCanInstall || mScrollView == null) {
if (mSessionId != -1) {
mInstaller.setPermissionsResult(mSessionId, true);
clearCachedApkIfNeededAndFinish();
} else {
startInstall(); //開始安裝
}
} else {
mScrollView.pageScroll(View.FOCUS_DOWN);
}
} else if (v == mCancel) {
// Cancel and finish
setResult(RESULT_CANCELED);
if (mSessionId != -1) {
mInstaller.setPermissionsResult(mSessionId, false);
}
clearCachedApkIfNeededAndFinish();
}
}
複製代碼
而後用戶點擊安裝按鈕,就會調用startInstall()函數app
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(localLOGV) Log.i(TAG, "downloaded app uri="+mPackageURI);
startActivity(newIntent);
finish();
}
複製代碼
startInstall函數用於跳轉到InstallAppProgress這個Activity,關閉PackageInstallerActivity。框架
InstallAppProgress主要用於展現安裝界面、向PMS發送包信息, 處理回調。ide
packages/apps/PackageInstaller/src/com/android/packageinstaller/InstallAppProgress.java
函數
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
Intent intent = getIntent();
mAppInfo = intent.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
mPackageURI = intent.getData();
final String scheme = mPackageURI.getScheme();
if (scheme != null && !"file".equals(scheme) && !"package".equals(scheme)) {
throw new IllegalArgumentException("unexpected scheme " + scheme);
}
// 開啓安裝線程
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*/);
initView();
}
複製代碼
InstallAppProgress中啓動了一個HandlerThread,開啓安裝線程,註冊了一個廣播,這個廣播用來監聽系統返回的安裝結果。oop
主要代碼在在initView()函數中
final PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
PackageInstaller.SessionParams.MODE_FULL_INSTALL);
params.referrerUri = getIntent().getParcelableExtra(Intent.EXTRA_REFERRER);
params.originatingUri = getIntent().getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);
params.originatingUid = getIntent().getIntExtra(Intent.EXTRA_ORIGINATING_UID,
UID_UNKNOWN);
File file = new File(mPackageURI.getPath());
try {
//解析安裝包,設置安裝位置,從AndroidManifest中獲取
PackageLite pkg = PackageParser.parsePackageLite(file, 0);
params.setAppPackageName(pkg.packageName);
params.setInstallLocation(pkg.installLocation);
params.setSize(
PackageHelper.calculateInstalledSize(pkg, false, params.abiOverride));
} catch (PackageParser.PackageParserException e) {
Log.e(TAG, "Cannot parse package " + file + ". Assuming defaults.");
Log.e(TAG, "Cannot calculate installed size " + file + ". Try only apk size.");
params.setSize(file.length());
} catch (IOException e) {
Log.e(TAG, "Cannot calculate installed size " + file + ". Try only apk size.");
params.setSize(file.length());
}
mInstallHandler.post(new Runnable() {
@Override
public void run() {
//執行安裝
doPackageStage(pm, params);
}
});
複製代碼
private void doPackageStage(PackageManager pm, PackageInstaller.SessionParams params) {
//初始化安裝器
final PackageInstaller packageInstaller = pm.getPackageInstaller();
PackageInstaller.Session session = null;
try {
final String packageLocation = mPackageURI.getPath();
final File file = new File(packageLocation);
//獲取sessionId
final int sessionId = packageInstaller.createSession(params);
final byte[] buffer = new byte[65536];
//獲取session
session = packageInstaller.openSession(sessionId);
final InputStream in = new FileInputStream(file);
final long sizeBytes = file.length();
// 根據這個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);
//最後調用session的commit方法進行提交
session.commit(pendingIntent.getIntentSender());
} catch (IOException e) {
onPackageInstalled(PackageInstaller.STATUS_FAILURE);
} finally {
IoUtils.closeQuietly(session);
}
}
複製代碼
最終廣播接受安裝結果
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final int statusCode = intent.getIntExtra(
PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE);
if (statusCode == PackageInstaller.STATUS_PENDING_USER_ACTION) {
context.startActivity((Intent)intent.getParcelableExtra(Intent.EXTRA_INTENT));
} else {
onPackageInstalled(statusCode);
}
}
};
複製代碼
應用安裝的前期工做,用戶能夠看到的部分基本就這些,接下來的工做就都由Java框架層處理。
上面最終調用PackageInstallerSession的commit方法,將安裝APK的信息發給框架層。
frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java
@Override
public void commit(IntentSender statusReceiver) {
......
mActiveCount.incrementAndGet();
final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter(mContext,
statusReceiver, sessionId, mIsInstallerDeviceOwner, userId);
mHandler.obtainMessage(MSG_COMMIT, adapter.getBinder()).sendToTarget();
}
複製代碼
commit函數中向Handler發送一個類型爲MSG_COMMIT的消息,adapter.getBinder()會獲得IPackageInstallObserver2觀察者,處理消息邏輯以下:
frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java
private final Handler.Callback mHandlerCallback = new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
// Cache package manager data without the lock held
final PackageInfo pkgInfo = mPm.getPackageInfo(
params.appPackageName, PackageManager.GET_SIGNATURES /*flags*/, userId);
final ApplicationInfo appInfo = mPm.getApplicationInfo(
params.appPackageName, 0, userId);
synchronized (mLock) {
if (msg.obj != null) {
mRemoteObserver = (IPackageInstallObserver2) msg.obj;
}
try {
commitLocked(pkgInfo, appInfo);
} catch (PackageManagerException e) {
final String completeMsg = ExceptionUtils.getCompleteMessage(e);
Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg);
destroyInternal();
dispatchSessionFinished(e.error, completeMsg, null);
}
return true;
}
}
};
複製代碼
private void commitLocked(PackageInfo pkgInfo, ApplicationInfo appInfo) throws PackageManagerException {
...
mPm.installStage(mPackageName, stageDir, stageCid, localObserver, params,
installerPackageName, installerUid, user, mCertificates);
}
複製代碼
在commitLocked方法中,調用PMS的installStage方法,這樣邏輯就進入PMS中了。
PackageInstaller安裝APK的過程,簡單來講就兩步: