本文是 Android Widget(小部件) 系列的第八篇,主要从源码角度是对 Android widget 删除过程进行分析。
本系列的目的是通过对Android 小部件的梳理,了解小部件刷新流程、添加、删除、恢复流程、以及系统发生变化时,小部件是如何适配的,解决在开发小部件过程中遇到的问题。系列文章大部份来自源码的解读,内容非常多,也非常容易遗忘,因此记录分享。
系列文章
Android Widget (小部件)基础介绍以及常见问题
Android Widget (小部件)刷新源码解析一非列表
Android Widget (小部件) option刷新源码解析
Android Widget(小部件)添加源码分析一--申请widgetId
Android Widget(小部件)添加源码分析二--绑定widgetId
Android Widget(小部件)添加源码分析三--创建视图
一、描述
删除小部件通常是 host 的逻辑,但了解该过程能够帮助我们熟悉小部件体系,分析小部件问题。由于每个 host 对小部件的逻辑处理都不一样,因此本文不梳理 host 中删除逻辑,仅从 deleteAppWidgetId 开始。
二、删除流程
java
1、host进程调用 AppWidgetHost.deleteAppWidgetId(),通过AIDL回调到system_server
2、system_server进行一些列的校验,并确定系统中 widget 已经被加载。
3、找到对应的widget,发送相应的删除广播,如果该widget 是该小部件的最后一个卡片,则还会发出 disable 广播
4、去掉定时刷新广播(小部件可以设置定时刷新)
5、存储小部件信息
三、详细流程
1、AppWidgetHost.deleteAppWidgetId()
-
描述:在AppWidgetHostView 数组中移除对应的AppWidgetHostView。通过AIDL调用AppWidgetServiceImpl 的deleteAppWidgetId()
-
详细代码
java
public void deleteAppWidgetId(int appWidgetId) {
if (sService == null) {
return;
}
synchronized (mViews) {
mViews.remove(appWidgetId);
try {
sService.deleteAppWidgetId(mContextOpPackageName, appWidgetId);
}
catch (RemoteException e) {
throw new RuntimeException("system server dead?", e);
}
}
}
2、AppWidgetServiceImpl.deleteAppWidgetId()
- 描述:进行安全校验,确认添加的小部件已经被加载过,找到对应的widget,删除对应小部件。更新系统中的小部件信息
- 详细代码
java
@Override
public void deleteAppWidgetId(String callingPackage, int appWidgetId) {
final int userId = UserHandle.getCallingUserId();
if (DEBUG) {
Slog.i(TAG, "deleteAppWidgetId() " + userId);
}
// Make sure the package runs under the caller uid.
// AppWidgetServiceImpl 运行在system_process ,包名为字符串传入,
// 检查uid 和package 是否对应
mSecurityPolicy.enforceCallFromPackage(callingPackage);
synchronized (mLock) {
ensureGroupStateLoadedLocked(userId);
// NOTE: The lookup is enforcing security across users by making
// sure the caller can only access widgets it hosts or provides.
Widget widget = lookupWidgetLocked(appWidgetId,
Binder.getCallingUid(), callingPackage);
if (widget == null) {
return;
}
deleteAppWidgetLocked(widget);
saveGroupStateAsync(userId);
if (DEBUG) {
Slog.i(TAG, "Deleted widget id " + appWidgetId
+ " for host " + widget.host.id);
}
}
}
3、AppWidgetServiceImpl.ensureGroupStateLoadedLocked()
- 描述:未解锁抛出状态异常,解锁判断是否第一次添加、如第一次添加则构建provider 信息
- 详细代码
java
class AppWidgetServiceImpl {
private void ensureGroupStateLoadedLocked(int userId, boolean enforceUserUnlockingOrUnlocked) {
// 判断该应用是否处在解锁状态,设备锁
if (enforceUserUnlockingOrUnlocked && !isUserRunningAndUnlocked(userId)) {
throw new IllegalStateException(
"User " + userId + " must be unlocked for widgets to be available");
}
// 判断该应用文件配置是否处在解锁状态
if (enforceUserUnlockingOrUnlocked && isProfileWithLockedParent(userId)) {
throw new IllegalStateException(
"Profile " + userId + " must have unlocked parent");
}
// 获取能用的配置id
final int[] profileIds = mSecurityPolicy.getEnabledGroupProfileIds(userId);
// 查看是否有未加载的user
// Careful lad, we may have already loaded the state for some
// group members, so check before loading and read only the
// state for the new member(s).
int newMemberCount = 0;
final int profileIdCount = profileIds.length;
for (int i = 0; i < profileIdCount; i++) {
final int profileId = profileIds[i];
// >=0代表已经加载过,标记数组
if (mLoadedUserIds.indexOfKey(profileId) >= 0) {
profileIds[i] = LOADED_PROFILE_ID;
} else {
newMemberCount++;
}
}
// 没有新加的 便会return
if (newMemberCount <= 0) {
return;
}
// 构建新增加的ProfileId 数组,后续通常在第一次加载的时候执行
int newMemberIndex = 0;
final int[] newProfileIds = new int[newMemberCount];
for (int i = 0; i < profileIdCount; i++) {
final int profileId = profileIds[i];
if (profileId != LOADED_PROFILE_ID) {
mLoadedUserIds.put(profileId, profileId);
newProfileIds[newMemberIndex] = profileId;
newMemberIndex++;
}
}
// 清除provider 和 host 的tag 设置为 TAG_UNDEFINED clearProvidersAndHostsTagsLocked();
// 根据加载ProfileId 获取系统 ResolveInfo 列表, 根据ResolveInfo 构建
provider;
loadGroupWidgetProvidersLocked(newProfileIds);
// 从系统配置文件/data/system/users/0/appwidgets.xml 加载状态、
loadGroupStateLocked(newProfileIds);
}
}
4、AppWidgetServiceImpl.lookupWidgetLocked()
- 描述:寻找对应的widget
- 详细代码
java
class AppWidgetServiceImpl {
private Widget lookupWidgetLocked(int appWidgetId, int uid, String packageName) {
final int N = mWidgets.size();
// 遍历寻找对应的widget,并进行安全校验
for (int i = 0; i < N; i++) {
Widget widget = mWidgets.get(i);
if (widget.appWidgetId == appWidgetId
&& mSecurityPolicy.canAccessAppWidget(widget, uid, packageName)) {
return widget;
}
}
return null;
}
}
5、AppWidgetServiceImpl.deleteAppWidgetLocked()
-
描述:
- 解绑对应的RemoteViewService
- 移除host 中widget 数组对应的widget
- 处理host
- 处理widget 的 packageName 数组,如果该包名下没有widget,从数组中移除对应的packageName
- 移除对应 provider 的widgets 数组中的widget
- 发送删除广播
- 处理定时广播
- 处理disable 广播
-
详细
java
class AppWidgetServiceImpl {
private void deleteAppWidgetLocked(Widget widget) {
// We first unbind all services that are bound to this id
// Check if we need to destroy any services (if no other app widgets are
// referencing the same service)
// 解绑对应的RemoteViewService
decrementAppWidgetServiceRefCount(widget);
// 移除host 中widget 数组对应的widget
Host host = widget.host;
host.widgets.remove(widget);
// 当host 没有数组且没有callback 时从host 数组移除
pruneHostLocked(host);
// 处理widget 的 packageName 数组,如果该包名下没有widget,从数组中移除对应的packageName
removeWidgetLocked(widget);
Provider provider = widget.provider;
if (provider != null) {
// 移除对应 provider 的widgets 数组中的widget
provider.widgets.remove(widget);
// 安全模式下,没有引用也不会删除,会保留,成为僵尸态
if (!provider.zombie) {
// send the broacast saying that this appWidgetId has been deleted
// 发送删除广播
sendDeletedIntentLocked(widget);
if (provider.widgets.isEmpty()) {
// cancel the future updates
// 如果没有该类的 widget 会取消定时广播
cancelBroadcastsLocked(provider);
// send the broacast saying that the provider is not in use any more
// 如果没有该类的 widget 会发 disable 广播
sendDisabledIntentLocked(provider);
}
}
}
}
}
6、AppWidgetServiceImpl.sendDeletedIntentLocked()
- 描述:发送删除广播
- 详细代码
java
class AppWidgetServiceImpl {
private void sendDeletedIntentLocked(Widget widget) {
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
intent.setComponent(widget.provider.id.componentName);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widget.appWidgetId);
sendBroadcastAsUser(intent, widget.provider.id.getProfile());
}
}
7、AppWidgetServiceImpl.cancelBroadcastsLocked()
- 描述:取消定时广播
- 详细代码
java
class AppWidgetServiceImpl {
private void cancelBroadcastsLocked(Provider provider) {
if (DEBUG) {
Slog.i(TAG, "cancelBroadcastsLocked() for " + provider);
}
if (provider.broadcast != null) {
final PendingIntent broadcast = provider.broadcast;
mSaveStateHandler.post(() -> {
mAlarmManager.cancel(broadcast);
broadcast.cancel();
});
provider.broadcast = null;
}
}
}
8、AppWidgetServiceImpl.sendDisabledIntentLocked()
- 描述:发送 disable 广播
- 详细代码
java
class AppWidgetServiceImpl {
private void sendDisabledIntentLocked(Provider provider) {
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED);
intent.setComponent(provider.id.componentName);
sendBroadcastAsUser(intent, provider.id.getProfile());
}
}
到这里小部件的删除流程就分析完了,整体流程比较简单,核心思想就是校验、找widget、内存中移除、发送广播通知widget 进程、存储信息。了解这个流程能够帮助管理删除小部件关联信息。