上一篇文章中给大家分析了一下android系统启动之后调用PackageManagerService服务并解析系统特定目录,解析apk文件并安装的过程,这个安装过期实际上是没有图形界面的,底层调用的是我们平时比较熟悉的adb命令,那么我们平时安装apk文件的时候大部分是都过图形界面安装的,那么这种方式安装apk具体的流程是怎样的呢?
下面我们就来具体看一下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呢?好吧,从这个问题开始,我们来解析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" />
<activity android:name=".UninstallerActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:excludeFromRecents="true"
android:theme="@style/Theme.AlertDialogActivity">
<intent-filter android:priority="1">
<action android:name="android.intent.action.DELETE" />
<action android:name="android.intent.action.UNINSTALL_PACKAGE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="package" />
</intent-filter>
</activity>
<activity android:name=".UninstallAppProgress"
android:configChanges="orientation|keyboardHidden|screenSize"
android:exported="false" />
<activity android:name=".permission.ui.GrantPermissionsActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:excludeFromRecents="true"
android:theme="@style/GrantPermissions">
<intent-filter>
<action android:name="android.content.pm.action.REQUEST_PERMISSIONS" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity android:name=".permission.ui.ManagePermissionsActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:excludeFromRecents="true"
android:label="@string/app_permissions"
android:theme="@style/Settings"
android:permission="android.permission.GRANT_RUNTIME_PERMISSIONS">
<intent-filter>
<action android:name="android.intent.action.MANAGE_PERMISSIONS" />
<action android:name="android.intent.action.MANAGE_APP_PERMISSIONS" />
<action android:name="android.intent.action.MANAGE_PERMISSION_APPS" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity android:name=".permission.ui.OverlayWarningDialog"
android:excludeFromRecents="true"
android:theme="@android:style/Theme.DeviceDefault.Light.Dialog.NoActionBar" />
<provider android:name=".wear.WearPackageIconProvider"
android:authorities="com.google.android.packageinstaller.wear.provider"
android:grantUriPermissions="true"
android:exported="true" />
<activity android:name=".permission.ui.wear.WarningConfirmationActivity"
android:permission="android.permission.GRANT_RUNTIME_PERMISSIONS"
android:theme="@style/Settings"/>
</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方法:
@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();
}
}
可以发现,在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();
}
}
好吧,这里面有调用了startInstallConfirm方法,然后我们看一下startInstallConfirm方法的实现:
private void startInstallConfirm() {
...
//初始化安装确认界面
...
}
好吧,这个方法的实现比较简单,主要的实现逻辑就是现实该activity的用户界面,平时我们安装某一个应用的时候会弹出一个安装确认页面,还有一个确认和取消按钮,有印象么?其实就是在这里执行的界面初始化操作。
好吧,一般情况下在apk安装确认页面,我们会点击确认按钮执行安装逻辑吧?那么这里我们找一下确认按钮的点击事件:
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();
}
}
很明显了,这里当我们点击确认按钮的时候会执行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();
}
可以发现,点击确认按钮之后我们调用启用了一个新的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,显示完成和打开按钮,是不是和我们平时安装apk的逻辑对应上了?这时候我们可以看一下这两个按钮的点击事件。
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();
}
}
好吧,比较简单,点击完成按钮,直接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安装完成界面。
Copyright© 2013-2020
All Rights Reserved 京ICP备2023019179号-8