古人学问无遗力,少壮工夫老始成。这篇文章主要讲述Android源代码解析之(十三)--&
gt;
apk安装流程相关的知识,希望能为你提供帮助。
转载请标明出处:一片枫叶的专栏上一篇文章中给大家分析了一下android系统启动之后调用PackageManagerService服务并解析系统特定文件夹。解析apk文件并安装的过程,这个安装过程实际上是没有图形界面的,底层调用的是我们平时比較熟悉的adb命令,那么我们平时安装apk文件的时候大部分是都过图形界面安装的,那么这样的方式安装apk详细的流程是如何的呢?
本文我们就来详细看一下apk的详细安装过程,通过本文的学习希望帮助大家大概的了解到Android系统安装Apk文件的基本流程。好了,话不多说,開始我们今天的学习吧。
相信大家都知道假设我们想在代码里运行apk的安装,那么一般都是这样:
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setDataAndType(Uri.parse("file://" + path),"application/vnd.android.package-archive");
context.startActivity(intent);
这样,我们就会打开安装apk文件的程序并运行安装逻辑了。大家应该都知道这段代码运行的结果是打开一个隐式的Activity,那么这段代码详细是打开那个activity呢?从这个问题開始。我们来解析apk的安装流程…
这里顺便跟大家简介一下android的源代码,平时我们使用的android.jar里面的java源代码仅仅是android系统源代码的一部分,还有好多源代码并没有打入到android.jar中。这里为大家推荐一个android源代码的地址:https://github.com/android
里面依据android系统的不同模块包括了很多android模块的源代码。
文章图片
这里我们找到platform_packages_apps_packageinstaller库,这里面就是android系统安装程序的源代码了。
文章图片
这里我们找到其androidManifest.xml,然后我们来看一下其详细的定义:
<
?
xml version="1.0" encoding="utf-8"?>
<
manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.packageinstaller" coreApp="true">
<
original-package android:name="com.android.packageinstaller" />
...<
application android:label="@string/app_name"
android:allowBackup="false"
android:theme="@style/Theme.DialogWhenLarge"
android:supportsRtl="true">
<
activity android:name=".PackageInstallerActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:excludeFromRecents="true">
<
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="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="file" />
<
data android:scheme="package" />
<
/intent-filter>
<
intent-filter>
<
action android:name="android.content.pm.action.CONFIRM_PERMISSIONS" />
<
category android:name="android.intent.category.DEFAULT" />
<
/intent-filter>
<
/activity>
<
activity android:name=".InstallAppProgress"
android:configChanges="orientation|keyboardHidden|screenSize"
android:exported="false" />
...
<
/application>
<
/manifest>
好吧,这里我们大概看一下Activity的定义,这里我们重点看一下PackageInstallerActivity的定义:
<
activity android:name=".PackageInstallerActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:excludeFromRecents="true">
<
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="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="file" />
<
data android:scheme="package" />
<
/intent-filter>
<
intent-filter>
<
action android:name="android.content.pm.action.CONFIRM_PERMISSIONS" />
<
category android:name="android.intent.category.DEFAULT" />
<
/intent-filter>
<
/activity>
恩?这里不就是我们刚刚定义的启动安装Apk activity的intent filter?好吧,所以说一開始我们调用的startActivity事实上启动的就是PackageInstallerActivity。那么以下我们就看一下PackageInstellerActivity的详细实现:
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
mPm = getPackageManager();
mInstaller = mPm.getPackageInstaller();
mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);
final Intent intent = getIntent();
if (PackageInstaller.ACTION_CONFIRM_PERMISSIONS.equals(intent.getAction())) {
final int sessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1);
final PackageInstaller.SessionInfo info = mInstaller.getSessionInfo(sessionId);
if (info == null || !info.sealed || info.resolvedBaseCodePath == null) {
Log.w(TAG, "Session " + mSessionId + " in funky state;
ignoring");
finish();
return;
}mSessionId = sessionId;
mPackageURI = Uri.fromFile(new File(info.resolvedBaseCodePath));
mOriginatingURI = null;
mReferrerURI = null;
} else {
mSessionId = -1;
mPackageURI = intent.getData();
mOriginatingURI = intent.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);
mReferrerURI = intent.getParcelableExtra(Intent.EXTRA_REFERRER);
}final boolean unknownSourcesAllowedByAdmin = isUnknownSourcesAllowedByAdmin();
final boolean unknownSourcesAllowedByUser = isUnknownSourcesEnabled();
boolean requestFromUnknownSource = isInstallRequestFromUnknownSource(intent);
mInstallFlowAnalytics = new InstallFlowAnalytics();
mInstallFlowAnalytics.setContext(this);
mInstallFlowAnalytics.setStartTimestampMillis(SystemClock.elapsedRealtime());
mInstallFlowAnalytics.setInstallsFromUnknownSourcesPermitted(unknownSourcesAllowedByAdmin
&
&
unknownSourcesAllowedByUser);
mInstallFlowAnalytics.setInstallRequestFromUnknownSource(requestFromUnknownSource);
mInstallFlowAnalytics.setVerifyAppsEnabled(isVerifyAppsEnabled());
mInstallFlowAnalytics.setAppVerifierInstalled(isAppVerifierInstalled());
mInstallFlowAnalytics.setPackageUri(mPackageURI.toString());
if (DeviceUtils.isWear(this)) {
showDialogInner(DLG_NOT_SUPPORTED_ON_WEAR);
mInstallFlowAnalytics.setFlowFinished(
InstallFlowAnalytics.RESULT_NOT_ALLOWED_ON_WEAR);
return;
}final String scheme = mPackageURI.getScheme();
if (scheme != null &
&
!"file".equals(scheme) &
&
!"package".equals(scheme)) {
Log.w(TAG, "Unsupported scheme " + scheme);
setPmResult(PackageManager.INSTALL_FAILED_INVALID_URI);
mInstallFlowAnalytics.setFlowFinished(
InstallFlowAnalytics.RESULT_FAILED_UNSUPPORTED_SCHEME);
finish();
return;
}final PackageUtil.AppSnippet as;
if ("package".equals(mPackageURI.getScheme())) {
mInstallFlowAnalytics.setFileUri(false);
try {
mPkgInfo = mPm.getPackageInfo(mPackageURI.getSchemeSpecificPart(),
PackageManager.GET_PERMISSIONS | PackageManager.GET_UNINSTALLED_PACKAGES);
} catch (NameNotFoundException e) {
}
if (mPkgInfo == null) {
Log.w(TAG, "Requested package " + mPackageURI.getScheme()
+ " not available. Discontinuing installation");
showDialogInner(DLG_PACKAGE_ERROR);
setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK);
mInstallFlowAnalytics.setPackageInfoObtained();
mInstallFlowAnalytics.setFlowFinished(
InstallFlowAnalytics.RESULT_FAILED_PACKAGE_MISSING);
return;
}
as = new PackageUtil.AppSnippet(mPm.getApplicationLabel(mPkgInfo.applicationInfo),
mPm.getApplicationIcon(mPkgInfo.applicationInfo));
} else {
mInstallFlowAnalytics.setFileUri(true);
final File sourceFile = new File(mPackageURI.getPath());
PackageParser.Package parsed = PackageUtil.getPackageInfo(sourceFile);
// Check for parse errors
if (parsed == null) {
Log.w(TAG, "Parse error when parsing manifest. Discontinuing installation");
showDialogInner(DLG_PACKAGE_ERROR);
setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK);
mInstallFlowAnalytics.setPackageInfoObtained();
mInstallFlowAnalytics.setFlowFinished(
InstallFlowAnalytics.RESULT_FAILED_TO_GET_PACKAGE_INFO);
return;
}
mPkgInfo = PackageParser.generatePackageInfo(parsed, null,
PackageManager.GET_PERMISSIONS, 0, 0, null,
new PackageUserState());
mPkgDigest = parsed.manifestDigest;
as = PackageUtil.getAppSnippet(this, mPkgInfo.applicationInfo, sourceFile);
}
mInstallFlowAnalytics.setPackageInfoObtained();
//set view
setContentView(R.layout.install_start);
mInstallConfirm = findViewById(R.id.install_confirm_panel);
mInstallConfirm.setVisibility(View.INVISIBLE);
PackageUtil.initSnippetForNewApp(this, as, R.id.app_snippet);
mOriginatingUid = getOriginatingUid(intent);
// Block the install attempt on the Unknown Sources setting if necessary.
if (!requestFromUnknownSource) {
initiateInstall();
return;
}// If the admin prohibits it, or we‘re running in a managed profile, just show error
// and exit. Otherwise show an option to take the user to Settings to change the setting.
final boolean isManagedProfile = mUserManager.isManagedProfile();
if (!unknownSourcesAllowedByAdmin
|| (!unknownSourcesAllowedByUser &
&
isManagedProfile)) {
showDialogInner(DLG_ADMIN_RESTRICTS_UNKNOWN_SOURCES);
mInstallFlowAnalytics.setFlowFinished(
InstallFlowAnalytics.RESULT_BLOCKED_BY_UNKNOWN_SOURCES_SETTING);
} else if (!unknownSourcesAllowedByUser) {
// Ask user to enable setting first
showDialogInner(DLG_UNKNOWN_SOURCES);
mInstallFlowAnalytics.setFlowFinished(
InstallFlowAnalytics.RESULT_BLOCKED_BY_UNKNOWN_SOURCES_SETTING);
} else {
initiateInstall();
}
}
这里我们主要先看一下PackageInstallerActivity的onCreate方法。能够发现,在onCreate方法中。首先运行一些初始化操作,获取PackageManager和Installer、UserManager等对象,然后会依据当前Intent的信息最一些逻辑推断并弹出消息弹窗,我们能够看一下详细的消息弹窗类型:
private static final int DLG_BASE = 0;
private static final int DLG_UNKNOWN_SOURCES = DLG_BASE + 1;
private static final int DLG_PACKAGE_ERROR = DLG_BASE + 2;
private static final int DLG_OUT_OF_SPACE = DLG_BASE + 3;
private static final int DLG_INSTALL_ERROR = DLG_BASE + 4;
private static final int DLG_ALLOW_SOURCE = DLG_BASE + 5;
private static final int DLG_ADMIN_RESTRICTS_UNKNOWN_SOURCES = DLG_BASE + 6;
private static final int DLG_NOT_SUPPORTED_ON_WEAR = DLG_BASE + 7;
能够发现当分析Intent对象的时候。假设能够得到这样几种结果:不知道apk的来源。package信息错误。存储空间不够,安装时报,来源正确,同意未知来源的apk文件。在wear上不支持等。这样依据不同的消息类型会弹出不同的消息弹窗:
@Override
public Dialog onCreateDialog(int id, Bundle bundle) {
switch (id) {
case DLG_UNKNOWN_SOURCES:
return new AlertDialog.Builder(this)
.setTitle(R.string.unknown_apps_dlg_title)
.setMessage(R.string.unknown_apps_dlg_text)
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
Log.i(TAG, "Finishing off activity so that user can navigate to settings manually");
finish();
}})
.setPositiveButton(R.string.settings, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
Log.i(TAG, "Launching settings");
launchSecuritySettings();
}
})
.setOnCancelListener(this)
.create();
case DLG_ADMIN_RESTRICTS_UNKNOWN_SOURCES:
return new AlertDialog.Builder(this)
.setTitle(R.string.unknown_apps_dlg_title)
.setMessage(R.string.unknown_apps_admin_dlg_text)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
finish();
}
})
.setOnCancelListener(this)
.create();
case DLG_PACKAGE_ERROR :
return new AlertDialog.Builder(this)
.setTitle(R.string.Parse_error_dlg_title)
.setMessage(R.string.Parse_error_dlg_text)
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
finish();
}
})
.setOnCancelListener(this)
.create();
case DLG_OUT_OF_SPACE:
// Guaranteed not to be null. will default to package name if not set by app
CharSequence appTitle = mPm.getApplicationLabel(mPkgInfo.applicationInfo);
String dlgText = getString(R.string.out_of_space_dlg_text,
appTitle.toString());
return new AlertDialog.Builder(this)
.setTitle(R.string.out_of_space_dlg_title)
.setMessage(dlgText)
.setPositiveButton(R.string.manage_applications, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
//launch manage applications
Intent intent = new Intent("android.intent.action.MANAGE_PACKAGE_STORAGE");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
finish();
}
})
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
Log.i(TAG, "Canceling installation");
finish();
}
})
.setOnCancelListener(this)
.create();
case DLG_INSTALL_ERROR :
// Guaranteed not to be null. will default to package name if not set by app
CharSequence appTitle1 = mPm.getApplicationLabel(mPkgInfo.applicationInfo);
String dlgText1 = getString(R.string.install_failed_msg,
appTitle1.toString());
return new AlertDialog.Builder(this)
.setTitle(R.string.install_failed)
.setNeutralButton(R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
finish();
}
})
.setMessage(dlgText1)
.setOnCancelListener(this)
.create();
case DLG_ALLOW_SOURCE:
CharSequence appTitle2 = mPm.getApplicationLabel(mSourceInfo);
String dlgText2 = getString(R.string.allow_source_dlg_text,
appTitle2.toString());
return new AlertDialog.Builder(this)
.setTitle(R.string.allow_source_dlg_title)
.setMessage(dlgText2)
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
setResult(RESULT_CANCELED);
finish();
}})
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
SharedPreferences prefs = getSharedPreferences(PREFS_ALLOWED_SOURCES,
Context.MODE_PRIVATE);
prefs.edit().putBoolean(mSourceInfo.packageName, true).apply();
startInstallConfirm();
}
})
.setOnCancelListener(this)
.create();
case DLG_NOT_SUPPORTED_ON_WEAR:
return new AlertDialog.Builder(this)
.setTitle(R.string.wear_not_allowed_dlg_title)
.setMessage(R.string.wear_not_allowed_dlg_text)
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
setResult(RESULT_OK);
finish();
}
})
.setOnCancelListener(this)
.create();
}
return null;
}
消息弹窗的主要作用,用于提示用户当前安装apk文件的特性。都知道android系统在android apk文件之前会解析器manifest文件,这个操作也是早onCreate方法中运行的:
PackageParser.Package parsed = PackageUtil.getPackageInfo(sourceFile);
我们详细看一下getPackageInfo方法的实现:
public static PackageParser.Package getPackageInfo(File sourceFile) {
final PackageParser parser = new PackageParser();
try {
PackageParser.Package pkg = parser.parseMonolithicPackage(sourceFile, 0);
parser.collectManifestDigest(pkg);
return pkg;
} catch (PackageParserException e) {
return null;
}
}
好吧,到了这里是不是代码变得非常熟悉了?parseMonolithicPackage就是我们上一节分析的android系统解析manifest文件的过程,详细的可參考:http://blog.csdn.net/qq_23547831/article/details/51203482
而collectManifestDigest方法,我们这里简单的介绍一下。其主要是要争apk的签名是否正确。好吧通过这两部我们就把apk文件的manifest和签名信息都解析完毕并保存在了Package中。
接着往下走,在全部的解析完毕之后我们会在onCreate方法中运行initiateInstall(); 方法。刚方法的主要作用是初始化安装。
private void initiateInstall() {
String pkgName = mPkgInfo.packageName;
// Check if there is already a package on the device with this name
// but it has been renamed to something else.
String[] oldName = mPm.canonicalToCurrentPackageNames(new String[] { pkgName });
if (oldName != null &
&
oldName.length >
0 &
&
oldName[0] != null) {
pkgName = oldName[0];
mPkgInfo.packageName = pkgName;
mPkgInfo.applicationInfo.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;
}mInstallFlowAnalytics.setReplace(mAppInfo != null);
mInstallFlowAnalytics.setSystemApp(
(mAppInfo != null) &
&
((mAppInfo.flags &
ApplicationInfo.FLAG_SYSTEM) != 0));
// If we have a session id, we‘re invoked to verify the permissions for the given
// package. Otherwise, we start the install process.
if (mSessionId != -1) {
startInstallConfirm();
} else {
startInstall();
}
}
【Android源代码解析之(十三)--& gt; apk安装流程】好吧。这里面有调用了startInstallConfirm方法,然后我们看一下startInstallConfirm方法的实现:
private void startInstallConfirm() {
...
//初始化安装确认界面
...
}
好吧,这种方法的实现比較简单。基本的实现逻辑就是现实该activity的用户界面。平时我们安装某一个应用的时候会弹出一个安装确认页面,另一个确认和取消button。有印象么?事实上就是在这里运行的界面初始化操作。
好吧,普通情况下在apk安装确认页面,我们会点击确认button运行安装逻辑吧?那么这里我们找一下确认button的点击事件:
public void onClick(View v) {
if (v == mOk) {
if (mOkCanInstall || mScrollView == null) {
mInstallFlowAnalytics.setInstallButtonClicked();
if (mSessionId != -1) {
mInstaller.setPermissionsResult(mSessionId, true);
// We‘re only confirming permissions, so we don‘t really know how the
// story ends;
assume success.
mInstallFlowAnalytics.setFlowFinishedWithPackageManagerResult(
PackageManager.INSTALL_SUCCEEDED);
finish();
} 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);
}
mInstallFlowAnalytics.setFlowFinished(
InstallFlowAnalytics.RESULT_CANCELLED_BY_USER);
finish();
}
}
非常明显了。这里当我们点击确认button的时候会运行startInstall方法,也就是開始运行安装逻辑:
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);
newIntent.putExtra(InstallAppProgress.EXTRA_MANIFEST_DIGEST, mPkgDigest);
newIntent.putExtra(
InstallAppProgress.EXTRA_INSTALL_FLOW_ANALYTICS, mInstallFlowAnalytics);
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();
}
能够发现。点击确认button之后我们调用启用了一个新的Activity–> InstallAppProgress,这个Activity主要用于运行apk的安装逻辑了。
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
Intent intent = getIntent();
mAppInfo = intent.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
mInstallFlowAnalytics = intent.getParcelableExtra(EXTRA_INSTALL_FLOW_ANALYTICS);
mInstallFlowAnalytics.setContext(this);
mPackageURI = intent.getData();
final String scheme = mPackageURI.getScheme();
if (scheme != null &
&
!"file".equals(scheme) &
&
!"package".equals(scheme)) {
mInstallFlowAnalytics.setFlowFinished(
InstallFlowAnalytics.RESULT_FAILED_UNSUPPORTED_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();
}
能够发现InstallAppProcess这个Activity的onCreate方法中主要初始化了一些成员变量,并调用initView方法,我们在iniTView方法中能够看到:
void initView() {
...
mInstallHandler.post(new Runnable() {
@Override
public void run() {
doPackageStage(pm, params);
}
});
...
}
经过一些view的初始化操作之后调用了doPackageStage方法,该方法主要是通过调用PackageInstaller运行apk文件的安装,这里就不在详细的介绍了,在apk文件安装完毕之后PackageInstaller会发送一个安装完毕的广播。刚刚我们在onCreate方法中注冊了一个广播接收器。其能够用来接收apk安装完毕的广播:
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);
}
}
};
这样apk安装完毕之后,这里的广播接收器会接收到广播并运行onPackageInstalled方法,运行兴许的处理逻辑,那么我们来看一下onPackageInstalled方法的详细实现逻辑:
void onPackageInstalled(int statusCode) {
Message msg = mHandler.obtainMessage(INSTALL_COMPLETE);
msg.arg1 = statusCode;
mHandler.sendMessage(msg);
}
好吧。这里是发送Handler异步消息,我们来看一下异步消息的处理逻辑:
private Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case INSTALL_COMPLETE:
mInstallFlowAnalytics.setFlowFinishedWithPackageManagerResult(msg.arg1);
if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
Intent result = new Intent();
result.putExtra(Intent.EXTRA_INSTALL_RESULT, msg.arg1);
setResult(msg.arg1 == PackageInstaller.STATUS_SUCCESS
? Activity.RESULT_OK : Activity.RESULT_FIRST_USER,
result);
finish();
return;
}
// Update the status text
mProgressBar.setVisibility(View.INVISIBLE);
// Show the ok button
int centerTextLabel;
int centerExplanationLabel = -1;
LevelListDrawable centerTextDrawable =
(LevelListDrawable) getDrawable(R.drawable.ic_result_status);
if (msg.arg1 == PackageInstaller.STATUS_SUCCESS) {
mLaunchButton.setVisibility(View.VISIBLE);
centerTextDrawable.setLevel(0);
centerTextLabel = R.string.install_done;
// Enable or disable launch button
mLaunchIntent = getPackageManager().getLaunchIntentForPackage(
mAppInfo.packageName);
boolean enabled = false;
if(mLaunchIntent != null) {
List<
ResolveInfo>
list = getPackageManager().
queryIntentActivities(mLaunchIntent, 0);
if (list != null &
&
list.size() >
0) {
enabled = true;
}
}
if (enabled) {
mLaunchButton.setOnClickListener(InstallAppProgress.this);
} else {
mLaunchButton.setEnabled(false);
}
} else if (msg.arg1 == PackageInstaller.STATUS_FAILURE_STORAGE){
showDialogInner(DLG_OUT_OF_SPACE);
return;
} else {
// Generic error handling for all other error codes.
centerTextDrawable.setLevel(1);
centerExplanationLabel = getExplanationFromErrorCode(msg.arg1);
centerTextLabel = R.string.install_failed;
mLaunchButton.setVisibility(View.INVISIBLE);
}
if (centerTextDrawable != null) {
centerTextDrawable.setBounds(0, 0,
centerTextDrawable.getIntrinsicWidth(),
centerTextDrawable.getIntrinsicHeight());
mStatusTextView.setCompoundDrawablesRelative(centerTextDrawable, null,
null, null);
}
mStatusTextView.setText(centerTextLabel);
if (centerExplanationLabel != -1) {
mExplanationTextView.setText(centerExplanationLabel);
mExplanationTextView.setVisibility(View.VISIBLE);
} else {
mExplanationTextView.setVisibility(View.GONE);
}
mDoneButton.setOnClickListener(InstallAppProgress.this);
mOkPanel.setVisibility(View.VISIBLE);
break;
default:
break;
}
}
};
能够发现,当apk安装完毕之后,我们会更新UI。显示完毕和打开button,是不是和我们平时安装apk的逻辑相应上了?这时候我们能够看一下这两个button的点击事件。
public void onClick(View v) {
if(v == mDoneButton) {
if (mAppInfo.packageName != null) {
Log.i(TAG, "Finished installing "+mAppInfo.packageName);
}
finish();
} else if(v == mLaunchButton) {
startActivity(mLaunchIntent);
finish();
}
}
好吧,比較简单,点击完毕button,直接finish掉这个activity,点击打开,则直接调用startActivity启动安装的应用。然后直接finish自身。
总结:
- 代码中运行intent.setDataAndType(Uri.parse(“file://” + path),”application/vnd.android.package-archive”);
能够调起PackageInstallerActivity;
- PackageInstallerActivity主要用于运行解析apk文件,解析manifest,解析签名等操作;
- InstallAppProcess主要用于运行安装apk逻辑,用于初始化安装界面,用于初始化用户UI。并调用PackageInstaller运行安装逻辑;
- InstallAppProcess内注冊有广播,当安装完毕之后接收广播,更新UI。显示apk安装完毕界面;
android源代码解析之(一)–> android项目构建过程
android源代码解析之(二)–> 异步消息机制
android源代码解析之(三)–> 异步任务AsyncTask
android源代码解析之(四)–> HandlerThread
android源代码解析之(五)–> IntentService
android源代码解析之(六)–> Log
android源代码解析之(七)–> LruCache
android源代码解析之(八)–> Zygote进程启动流程
android源代码解析之(九)–> SystemServer进程启动流程
android源代码解析之(十)–> Launcher启动流程
android源代码解析之(十一)–> 应用进程启动流程
android源代码解析之(十二)–> 系统启动并解析Manifest的流程
本文以同步至github中:https://github.com/yipianfengye/androidSource。欢迎star和follow
推荐阅读
- 分享(Android系统的经常使用权限整理)
- Android中Handler原理
- 安装Android Studio
- Draw your Next App Idea with Ink to Code
- iPad Pro 10.5+Apple Pencil之专注文献阅读与笔记效率的App综述
- [android] - 解决Error:(16, 0) Minimum supported Gradle version is 3.3. Current version is 2.14.1
- Fix-Mapped Addresses
- Dynamic DMA mapping Guide
- APP功能测试