Intent intent = new Intent();
intent.setAction("android.intent.action.INSTALL_PACKAGE");
intent.addCategory("android.intent.category.DEFAULT");
intent.setData(mUri);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
startActivity(intent);
这样就可以打开apk安装的界面:
上面的代码是通过隐式的方式 打开apk install Activity,
com.android.packageinstaller.InstallStart
该app的 AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.packageinstaller" coreApp="true">
<application android:label="@string/app_name"
...
<activity android:name=".InstallStart"
android:exported="true"
android:excludeFromRecents="true">
<intent-filter android:priority="1">
<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:scheme="content" />
<data android:mimeType="application/vnd.android.package-archive" />
intent-filter>
// 上面安装apk 使用的这个 intent-filter
<intent-filter android:priority="1">
<action android:name="android.intent.action.INSTALL_PACKAGE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="file" />
<data android:scheme="package" />
<data android:scheme="content" />
intent-filter>
<intent-filter android:priority="1">
<action android:name="android.content.pm.action.CONFIRM_PERMISSIONS" />
<category android:name="android.intent.category.DEFAULT" />
intent-filter>
activity>
...
application>
manifest>
首先到 InstallStart 的 oncreate()
判断 安装包来源和权限,不符合条件,废弃安装:
if (!isTrustedSource && originatingUid != PackageInstaller.SessionParams.UID_UNKNOWN) {
final int targetSdkVersion = getMaxTargetSdkVersionForUid(originatingUid);
if (targetSdkVersion < 0) {
Log.w(LOG_TAG, "Cannot get target sdk version for uid " + originatingUid);
// Invalid originating uid supplied. Abort install.
mAbortInstall = true;
} else if (targetSdkVersion >= Build.VERSION_CODES.O && !declaresAppOpPermission(
originatingUid, Manifest.permission.REQUEST_INSTALL_PACKAGES)) {
Log.e(LOG_TAG, "Requesting uid " + originatingUid + " needs to declare permission "
+ Manifest.permission.REQUEST_INSTALL_PACKAGES);
mAbortInstall = true;
}
}
// 废弃安装
if (mAbortInstall) {
setResult(RESULT_CANCELED);
finish();
return;
}
符合条件跳转到:
PackageInstallerActivity
Intent nextActivity = new Intent(intent);
nextActivity.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
// The the installation source as the nextActivity thinks this activity is the source, hence
// set the originating UID and sourceInfo explicitly
nextActivity.putExtra(PackageInstallerActivity.EXTRA_CALLING_PACKAGE, callingPackage);
nextActivity.putExtra(PackageInstallerActivity.EXTRA_ORIGINAL_SOURCE_INFO, sourceInfo);
nextActivity.putExtra(Intent.EXTRA_ORIGINATING_UID, originatingUid);
if (PackageInstaller.ACTION_CONFIRM_PERMISSIONS.equals(intent.getAction())) {
nextActivity.setClass(this, PackageInstallerActivity.class);
} else {
Uri packageUri = intent.getData();
if (packageUri == null) {
// if there's nothing to do, quietly slip into the ether
Intent result = new Intent();
result.putExtra(Intent.EXTRA_INSTALL_RESULT,
PackageManager.INSTALL_FAILED_INVALID_URI);
setResult(RESULT_FIRST_USER, result);
nextActivity = null;
} else {
if (packageUri.getScheme().equals(SCHEME_CONTENT)) {
nextActivity.setClass(this, InstallStaging.class);
} else {
nextActivity.setClass(this, PackageInstallerActivity.class);
}
}
}
if (nextActivity != null) {
startActivity(nextActivity);
}
finish();
PackageInstallerActivity onCreate()
做一些初始化操作,获取 intent传递过来的数据
if (icicle != null) {
mAllowUnknownSources = icicle.getBoolean(ALLOW_UNKNOWN_SOURCES_KEY);
}
mPm = getPackageManager();
mIpm = AppGlobals.getPackageManager();
mAppOpsManager = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE);
mInstaller = mPm.getPackageInstaller();
mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);
final Intent intent = getIntent();
mCallingPackage = intent.getStringExtra(EXTRA_CALLING_PACKAGE);
mSourceInfo = intent.getParcelableExtra(EXTRA_ORIGINAL_SOURCE_INFO);
mOriginatingUid = intent.getIntExtra(Intent.EXTRA_ORIGINATING_UID,
PackageInstaller.SessionParams.UID_UNKNOWN);
mOriginatingPackage = (mOriginatingUid != PackageInstaller.SessionParams.UID_UNKNOWN)
? getPackageNameForUid(mOriginatingUid) : null;
继续安装调用:
//解析 url里的包信息,解析器manifest文件 得到apk相关信息
boolean wasSetUp = processPackageUri(packageUri);
if (!wasSetUp) {
return;
}
// load dummy layout with OK button disabled until we override this layout in
// startInstallConfirm
bindUi(R.layout.install_confirm, false);
checkIfAllowedAndInitiateInstall();
checkIfAllowedAndInitiateInstall()
检查安装,如果用户没有打开未知来源安装,弹dialog 提示,具体消息弹窗类型定义如下:
// Dialog identifiers used in showDialog
private static final int DLG_BASE = 0;
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_UNKNOWN_SOURCES_RESTRICTED_FOR_USER = DLG_BASE + 5;
private static final int DLG_ANONYMOUS_SOURCE = DLG_BASE + 6;
private static final int DLG_NOT_SUPPORTED_ON_WEAR = DLG_BASE + 7;
private static final int DLG_EXTERNAL_SOURCE_BLOCKED = DLG_BASE + 8;
/**
* Check if it is allowed to install the package and initiate install if allowed. If not allowed
* show the appropriate dialog.
*/
private void checkIfAllowedAndInitiateInstall() {
if (mAllowUnknownSources || !isInstallRequestFromUnknownSource(getIntent())) {
initiateInstall();
return;
}
// If the admin prohibits it, just show error and exit.
if (isUnknownSourcesDisallowed()) {
if ((mUserManager.getUserRestrictionSource(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
Process.myUserHandle()) & UserManager.RESTRICTION_SOURCE_SYSTEM) != 0) {
// Someone set user restriction via UserManager#setUserRestriction. We don't want to
// break apps that might already be doing this
// 弹diolag 引导用户在设置里打开 允许未知来源安装
showDialogInner(DLG_UNKNOWN_SOURCES_RESTRICTED_FOR_USER);
return;
} else {
startActivity(new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS));
finish();
}
} else {
// 处理安装
handleUnknownSources();
}
}
handleUnknownSources() 调用 initiateInstall()
initiateInstall() 里调用 startInstallConfirm()
startInstallConfirm() 调用 startInstallConfirm()
先初始化界面,引到用户执行安装的逻辑在 onclick()方法里
private void startInstallConfirm() {
// We might need to show permissions, load layout with permissions
//初始化界面
if (mAppInfo != null) {
bindUi(R.layout.install_confirm_perm_update, true);
} else {
bindUi(R.layout.install_confirm_perm, true);
}
...
}
用户是开始安装,还是取消:
public void onClick(View v) {
if (v == mOk) {
if (mOk.isEnabled()) {
if (mOkCanInstall || mScrollView == null) {
if (mSessionId != -1) {
mInstaller.setPermissionsResult(mSessionId, true);
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);
}
finish();
}
}
启动 InstallInstalling 执行真正的安装逻辑
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);
// 安装apk的 activity InstallInstalling
newIntent.setClass(this, InstallInstalling.class);
...
startActivity(newIntent);
finish();
}
这个就是安装apk,有进度条的界面
/**
* Send package to the package manager and handle results from package manager. Once the
* installation succeeds, start {@link InstallSuccess} or {@link InstallFailed}.
* This has two phases: First send the data to the package manager, then wait until the package
* manager processed the result.
*/
public class InstallInstalling extends Activity {...}
从注释看:该activity 负责把apk 安装包 发送给 package manager,然后等待 package manager 的返回结果,安装成功 跳到 InstallSuccess,失败 InstallFailed 界面。
InstallInstalling onCreate()做一些初始化操作,初始化界面和对象,解析intent的数据,判断是新安装的apk,还是覆盖安装。
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.install_installing);
ApplicationInfo appInfo = getIntent()
.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
mPackageURI = getIntent().getData();
安装进度回调:
mSessionCallback = new InstallSessionCallback();
PackageInstaller.SessionCallback 主要用来更新安装进度
private class InstallSessionCallback extends PackageInstaller.SessionCallback {
...
// 更新安装进度
@Override
public void onProgressChanged(int sessionId, float progress) {
if (sessionId == mSessionId) {
ProgressBar progressBar = (ProgressBar)findViewById(R.id.progress_bar);
progressBar.setMax(Integer.MAX_VALUE);
progressBar.setProgress((int) (Integer.MAX_VALUE * progress));
}
}
...
}
}
@Override
protected void onStart() {
super.onStart();
getPackageManager().getPackageInstaller().registerSessionCallback(mSessionCallback);
}
安装成功或者失败,PackageInstaller 结果回调:
mInstallId = InstallEventReceiver
.addObserver(this, EventResultPersister.GENERATE_NEW_ID,
this::launchFinishBasedOnResult);
/**
* Launch the appropriate finish activity (success or failed) for the installation result.
*
* @param statusCode The installation result.
* @param legacyStatus The installation as used internally in the package manager.
* @param statusMessage The detailed installation result.
*/
private void launchFinishBasedOnResult(int statusCode, int legacyStatus, String statusMessage) {
if (statusCode == PackageInstaller.STATUS_SUCCESS) {
launchSuccess();
} else {
launchFailure(legacyStatus, statusMessage);
}
}
onResume() 里开始真正的安装逻辑
protected void onResume() {
super.onResume();
// This is the first onResume in a single life of the activity
if (mInstallingTask == null) {
PackageInstaller installer = getPackageManager().getPackageInstaller();
PackageInstaller.SessionInfo sessionInfo = installer.getSessionInfo(mSessionId);
if (sessionInfo != null && !sessionInfo.isActive()) {
//最终的安装逻辑
mInstallingTask = new InstallingAsyncTask();
mInstallingTask.execute();
异步把apk 传给 PackageInstaller
/**
* Send the package to the package installer and then register a event result observer that
* will call {@link #launchFinishBasedOnResult(int, int, String)}
*/
private final class InstallingAsyncTask extends AsyncTask<Void, Void,
PackageInstaller.Session> {
volatile boolean isDone;
@Override
protected PackageInstaller.Session doInBackground(Void... params) {
PackageInstaller.Session session;
try {
session = getPackageManager().getPackageInstaller().openSession(mSessionId);
} catch (IOException e) {
return null;
}
session.setStagingProgress(0);
try {
File file = new File(mPackageURI.getPath());
try (InputStream in = new FileInputStream(file)) {
long sizeBytes = file.length();
try (OutputStream out = session
.openWrite("PackageInstaller", 0, sizeBytes)) {
byte[] buffer = new byte[4096];
while (true) {
int numRead = in.read(buffer);
if (numRead == -1) {
session.fsync(out);
break;
}
if (isCancelled()) {
session.close();
break;
}
// io流 把apk从源目录,传给 PackageInstaller
out.write(buffer, 0, numRead);
if (sizeBytes > 0) {
float fraction = ((float) numRead / (float) sizeBytes);
session.addProgress(fraction);
}
}
}
}
return session;
} catch (IOException | SecurityException e) {
Log.e(LOG_TAG, "Could not write package", e);
session.close();
return null;
} finally {
synchronized (this) {
isDone = true;
notifyAll();
}
}
}
@Override
protected void onPostExecute(PackageInstaller.Session session) {
if (session != null) {
Intent broadcastIntent = new Intent(BROADCAST_ACTION);
broadcastIntent.setPackage(
getPackageManager().getPermissionControllerPackageName());
broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, mInstallId);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
InstallInstalling.this,
mInstallId,
broadcastIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
session.commit(pendingIntent.getIntentSender());
mCancelButton.setEnabled(false);
setFinishOnTouchOutside(false);
} else {
getPackageManager().getPackageInstaller().abandonSession(mSessionId);
if (!isCancelled()) {
launchFailure(PackageManager.INSTALL_FAILED_INVALID_APK, null);
}
}
}
根据安装回调结果,打开成功和失败的activity
/**
* Launch the "success" version of the final package installer dialog
*/
private void launchSuccess() {
Intent successIntent = new Intent(getIntent());
successIntent.setClass(this, InstallSuccess.class);
successIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
// 安装成功 启动 InstallSuccess
startActivity(successIntent);
finish();
}
/**
* Launch the "failure" version of the final package installer dialog
*
* @param legacyStatus The status as used internally in the package manager.
* @param statusMessage The status description.
*/
private void launchFailure(int legacyStatus, String statusMessage) {
Intent failureIntent = new Intent(getIntent());
failureIntent.setClass(this, InstallFailed.class);
failureIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
failureIntent.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, legacyStatus);
failureIntent.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE, statusMessage);
// 安装成功 启动 InstallFailed
startActivity(failureIntent);
finish();
}
安装完成,进入到 InstallSuccess onCreate
两个按钮,完成和打开
“完成“按钮的逻辑:
findViewById(R.id.done_button).setOnClickListener(view -> {
if (appInfo.packageName != null) {
Log.i(LOG_TAG, "Finished installing " + appInfo.packageName);
}
finish();
});
“打开” 按钮,启动apk
Button launchButton = (Button)findViewById(R.id.launch_button);
if (enabled) {
launchButton.setOnClickListener(view -> {
try {
startActivity(launchIntent);
} catch (ActivityNotFoundException | SecurityException e) {
Log.e(LOG_TAG, "Could not start activity", e);
}
finish();
});
}
PackageInstallerActivity主要用于执行解析apk文件,解析manifest,解析签名等操作;
InstallInstalling主要用于执行安装apk逻辑,用于初始化安装界面,用于初始化用户UI。并调用PackageInstaller执行安装逻辑;
InstallInstalling 内注册有广播,当安装完成之后接收广播,更新UI。显示apk安装完成界面;