Android源代码解析之(十三)-->apk安装流程

古人学问无遗力,少壮工夫老始成。这篇文章主要讲述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模块的源代码。
Android源代码解析之(十三)-->apk安装流程

文章图片

这里我们找到platform_packages_apps_packageinstaller库,这里面就是android系统安装程序的源代码了。
Android源代码解析之(十三)-->apk安装流程

文章图片

这里我们找到其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源代码解析之(二)–> 异步消息机制
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

    推荐阅读