文章目录
卸载时弹窗源码梳理
弹窗定位
卸载应用时,会弹窗询问是否要卸载,如下图

要定位此UI代码,可以通过查找提示的文本找到对应的xml及代码
find frameworks/base/ -name "*.xml" | xargs grep "要卸载此应用吗"
frameworks/base/packages/PackageInstaller/res/values-zh-rCN/strings.xml: <string name="uninstall_application_text" msgid="3816830743706143980">"要卸载此应用吗?"</string>
从上面查找到"要卸载此应用吗"对应的id是uninstall_application_text,再查找这个id
find frameworks/base/ -name "*.java" | xargs grep "uninstall_application_text"
frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/television/UninstallAlertFragment.java: messageBuilder.append(getString(R.string.uninstall_application_text_all_users));
frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/television/UninstallAlertFragment.java: getString(R.string.uninstall_application_text_user, userInfo.name));
frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/television/UninstallAlertFragment.java: messageBuilder.append(getString(R.string.uninstall_application_text));
frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java: messageBuilder.append(getString(R.string.uninstall_application_text_all_users));
frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java: getString(R.string.uninstall_application_text_user, userInfo.name));
frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java: messageBuilder.append(getString(R.string.uninstall_application_text));
上面找到两种java代码,UninstallAlertFragment与UninstallAlertDialogFragment,第一个是tv电视应用才用到的,显然第二个是要找的java代码。
UninstallAlertDialogFragment
来看下java代码:
frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java
java
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// 获取系统的包管理器,用于获取应用相关信息(如应用名称、图标等)
final PackageManager pm = getActivity().getPackageManager();
// 从宿主Activity(UninstallerActivity)中获取卸载弹窗所需的核心信息对象
// DialogInfo包含了要卸载的应用信息、用户信息、是否跨用户卸载等关键数据
final UninstallerActivity.DialogInfo dialogInfo =
((UninstallerActivity) getActivity()).getDialogInfo();
// 加载并获取要卸载应用的安全标签(应用名称),loadSafeLabel是安全的标签加载方法,避免空指针
final CharSequence appLabel = dialogInfo.appInfo.loadSafeLabel(pm);
// 创建AlertDialog构建器,用于构建卸载确认弹窗
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getActivity());
// 构建弹窗的提示消息内容,使用StringBuilder提升字符串拼接效率
StringBuilder messageBuilder = new StringBuilder();
// 如果存在Activity信息(说明是从某个Activity触发的卸载)
if (dialogInfo.activityInfo != null) {
// 获取该Activity的显示名称
final CharSequence activityLabel = dialogInfo.activityInfo.loadSafeLabel(pm);
// 如果Activity的名称和应用名称不一致,需要明确告知用户该Activity所属的应用
if (!activityLabel.equals(appLabel)) {
// 拼接提示文本:说明该Activity属于要卸载的应用
messageBuilder.append(
getString(R.string.uninstall_activity_text, activityLabel));
messageBuilder.append(" ").append(appLabel).append(".\n\n");
}
}
// 判断当前要卸载的应用是否是"更新的系统应用"
// FLAG_UPDATED_SYSTEM_APP:标记该系统应用被用户更新过,卸载只会移除更新部分,恢复到出厂版本
final boolean isUpdate =
((dialogInfo.appInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
// 获取用户管理服务,用于处理多用户场景下的卸载逻辑
UserManager userManager = UserManager.get(getActivity());
// 根据应用类型(更新的系统应用/普通应用)和用户场景拼接不同的提示文本
if (isUpdate) {
// 卸载更新的系统应用场景
if (isSingleUser(userManager)) {
// 单用户设备:使用单用户版卸载更新提示文本
messageBuilder.append(getString(R.string.uninstall_update_text));
} else {
// 多用户设备:使用多用户版卸载更新提示文本
messageBuilder.append(getString(R.string.uninstall_update_text_multiuser));
}
} else {
// 卸载普通应用场景
if (dialogInfo.allUsers && !isSingleUser(userManager)) {
// 跨所有用户卸载应用:使用全用户卸载提示文本
messageBuilder.append(getString(R.string.uninstall_application_text_all_users));
} else if (!dialogInfo.user.equals(android.os.Process.myUserHandle())) {
// 卸载其他用户下的应用:获取目标用户信息,并拼接该用户专属的卸载提示
UserInfo userInfo = userManager.getUserInfo(dialogInfo.user.getIdentifier());
messageBuilder.append(
getString(R.string.uninstall_application_text_user, userInfo.name));
} else {
// 卸载当前用户的普通应用:使用默认卸载提示文本
messageBuilder.append(getString(R.string.uninstall_application_text));
}
}
// 设置弹窗标题为要卸载的应用名称
dialogBuilder.setTitle(appLabel);
// 设置弹窗确认按钮(OK),并将当前Fragment设为点击事件处理者
dialogBuilder.setPositiveButton(android.R.string.ok, this);
// 设置弹窗取消按钮(Cancel),并将当前Fragment设为点击事件处理者
dialogBuilder.setNegativeButton(android.R.string.cancel, this);
// 获取要卸载应用的包名(唯一标识)
String pkg = dialogInfo.appInfo.packageName;
// 标记是否建议保留应用数据(根据应用是否标记有易失性用户数据)
boolean suggestToKeepAppData;
try {
// 获取应用的包信息(包含应用的各种配置信息)
PackageInfo pkgInfo = pm.getPackageInfo(pkg, 0);
// 判断应用是否标记了"有易失性用户数据",这类应用卸载时建议保留数据
suggestToKeepAppData = pkgInfo.applicationInfo.hasFragileUserData();
} catch (PackageManager.NameNotFoundException e) {
// 捕获包未找到异常(应用可能已被卸载),打印错误日志
Log.e(LOG_TAG, "Cannot check hasFragileUserData for " + pkg, e);
// 异常时默认不建议保留数据
suggestToKeepAppData = false;
}
// 应用数据大小,初始化为0
long appDataSize = 0;
// 如果建议保留应用数据,则计算该应用的数据大小
if (suggestToKeepAppData) {
// 获取应用数据大小(根据是否跨用户卸载,传入不同的用户参数)
appDataSize = getAppDataSize(pkg, dialogInfo.allUsers ? null : dialogInfo.user);
}
// 根据应用数据大小决定弹窗的布局样式
if (appDataSize == 0) {
// 应用无数据/数据大小为0:直接设置文本消息
dialogBuilder.setMessage(messageBuilder.toString());
} else {
// 应用有数据:加载自定义布局,显示保留数据的勾选框
// 获取布局填充器,用于加载自定义布局文件
LayoutInflater inflater = getContext().getSystemService(LayoutInflater.class);
// 加载卸载弹窗的自定义布局(uninstall_content_view.xml)
ViewGroup content = (ViewGroup) inflater.inflate(R.layout.uninstall_content_view, null);
// 设置自定义布局中的提示消息文本
((TextView) content.requireViewById(R.id.message)).setText(messageBuilder.toString());
// 获取"保留数据"的复选框控件
mKeepData = content.requireViewById(R.id.keepData);
// 显示保留数据复选框
mKeepData.setVisibility(View.VISIBLE);
// 设置复选框文本:提示用户可以保留指定大小的应用数据
mKeepData.setText(getString(R.string.uninstall_keep_data,
formatFileSize(getContext(), appDataSize)));
// 将自定义布局设置为弹窗的内容视图
dialogBuilder.setView(content);
}
// 构建并返回最终的AlertDialog弹窗
return dialogBuilder.create();
}
找到这个卸载确认窗口是由onCreateDialog函数创建的AlertDialog对象。
- 核心功能:该方法的核心是构建 Android 应用卸载确认弹窗,根据应用类型(系统更新应用 / 普通应用)、用户场景(单用户 / 多用户 / 其他用户)和应用数据情况,动态生成不同的弹窗内容和布局。
- 关键逻辑分支:
- 区分「更新的系统应用」和「普通应用」,展示不同的卸载提示文本;
- 区分「单用户」「多用户」「其他用户」场景,适配不同的卸载文案;
- 检测应用是否有易失性用户数据,如有则显示「保留数据」复选框并展示数据大小。
UninstallerActivity
UninstallAlertDialogFragment是由UninstallerActivity调用的
-
UninstallerActivity 是宿主 / 发起者:负责创建并弹出 UninstallAlertDialogFragment 这个弹窗碎片;
-
UninstallAlertDialogFragment 是依赖方 / 执行者:依赖 UninstallerActivity 提供的卸载相关数据(DialogInfo),并将用户的操作(确认 / 取消卸载)回调给 UninstallerActivity。
frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
showConfirmationDialog函数会创建UninstallAlertDialogFragment

java
@Override
public void onCreate(Bundle icicle) {
getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
// 其它代码省略......
showConfirmationDialog(); // 这一行调用的弹窗
}
二者的关系:
- UninstallerActivity 主动创建并显示 UninstallAlertDialogFragment
- UninstallAlertDialogFragment 依赖 UninstallerActivity 获取数据
- UninstallAlertDialogFragment 将用户操作回调给 UninstallerActivity
实现卸载时不弹窗
修改UninstallerActivity代码,把showConfirmationDialog注释掉并进行卸载
java
//showConfirmationDialog(); // 注释掉这行
// 添加以下两行
startUninstallProgress(false);
finish();
Android相关专栏
作者:帅得不敢出门