本文是 Android Widget(小部件) 系列的第五篇,主要从源码角度是对 Android widget 添加过程进行分析 。
本系列的目的是通过对Android 小部件的梳理,了解小部件刷新流程、添加、删除、恢复流程、以及系统发生变化时,小部件是如何适配的,解决在开发小部件过程中遇到的问题。系列文章大部份来自源码的解读,内容非常多,也非常容易遗忘,因此记录分享。
系列文章
Android Widget (小部件)基础介绍以及常见问题
Android Widget (小部件)刷新源码解析一非列表
Android Widget (小部件) option刷新源码解析
# Android Widget(小部件)添加源码分析一申请widgetId
一、描述
添加小部件通常是 host 的逻辑,但了解该过程能够帮助我们熟悉小部件体系,分析小部件问题。由于每个 host 对小部件的逻辑处理都不一样,因此本文不梳理 host 中添加逻辑,仅从 ### AppWidgetHost .allocateAppWidgetId 开始。添加 widget 分三步、本篇文章讲解第一步申请 widgetId
1. 申请widgetId
2. 绑定widgetId
3. 创建WidgetHostView
//1、申请
int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
//2、绑定
bound = mAppWidgetManager.bindAppWidgetIdIfAllowed(
appWidgetId, providerInfo.getProfile(), providerInfo.provider,bundle)
//3、创建视图
WidgetHostView hostView = (WidgetHostView) mAppWidgetHost.createView(mContext, appWidgetId ,
appWidgetProviderInfo);
二、申请widgetId流程
1、AppWidgetHost 进程通过 AIDL 回调到system_server中的 AppWidgetServiceImpl
2、进行安全性校验,确认小部件已经加载
3、自增创建widgetid
4、找到对应host、创建widget,关联widget 和host,将widget 放入对应集合中
三、详细流程
1.1、AppWidgetHost .allocateAppWidgetId()
- 描述:通过AIDL 调用系统服务AppWidgetServiceImpl
- 详细代码
public class AppWidgetHost {
public int allocateAppWidgetId() {
if (*sService* == null) {
return -1;
}
try {
return *sService*.allocateAppWidgetId(mContextOpPackageName, mHostId);
}catch (RemoteException e) {
throw new RuntimeException("system server dead?", e);
}
}
}
1.1.1、AppWidgetServiceImpl.allocateAppWidgetId()
- 描述:进行安全性校验、自增创建widgetId、添加小部件、存储小部件信息
- 详细代码
class AppWidgetServiceImpl {
@Override
public int allocateAppWidgetId(String callingPackage, int hostId) {
final int userId = UserHandle.getCallingUserId();
if (DEBUG) {
Slog.i(TAG, "allocateAppWidgetId() " + userId);
}
// Make sure the package runs under the caller uid.
// 安全性校验,确定请求的包命和uid 是一致的。后面详讲
mSecurityPolicy.enforceCallFromPackage(callingPackage);
synchronized (mLock) {
// Instant apps cannot host app widgets.
// 排除快应用,快应用不能作为widget 的host
if (mSecurityPolicy.isInstantAppLocked(callingPackage, userId)) {
Slog.w(TAG, "Instant package " + callingPackage + " cannot host app widgets");
return AppWidgetManager.INVALID_APPWIDGET_ID;
}
// 确定小部件的信息已经加载,后面有详细解析
ensureGroupStateLoadedLocked(userId);
// 如果该用户没有创建过widget 则从0开始。widget 初始化时,会从文件中读取之前的值,详看readProfileStateFromFileLocked
if (mNextAppWidgetIds.indexOfKey(userId) < 0) {
mNextAppWidgetIds.put(userId, AppWidgetManager.INVALID_APPWIDGET_ID + 1);
}
// 以userId 作为key ,每次递增后更新
final int appWidgetId = incrementAndGetAppWidgetIdLocked(userId);
// NOTE: The lookup is enforcing security across users by making
// sure the caller can only access hosts it owns.
HostId id = new HostId(Binder.getCallingUid(), hostId, callingPackage);
Host host = lookupOrAddHostLocked(id);
Widget widget = new Widget();
widget.appWidgetId = appWidgetId;
widget.host = host;
host.widgets.add(widget);
// 添加小部件,后面详讲
addWidgetLocked(widget);
// 存储状态,后面详讲
saveGroupStateAsync(userId);
if (DEBUG) {
Slog.i(TAG, "Allocated widget id " + appWidgetId
+ " for host " + host.id);
}
return appWidgetId;
}
}
// 获取新添加小部件的widgetId
private int incrementAndGetAppWidgetIdLocked(int userId) {
final int appWidgetId = peekNextAppWidgetIdLocked(userId) + 1;
mNextAppWidgetIds.put(userId, appWidgetId);
return appWidgetId;
}
}
private int peekNextAppWidgetIdLocked(int userId) {
if (mNextAppWidgetIds.indexOfKey(userId) < 0) {
return AppWidgetManager.INVALID_APPWIDGET_ID + 1;
} else {
return mNextAppWidgetIds.get(userId);
}
}
1.1.1.1、AppWidgetServiceImpl. enforceCallFromPackage()
- 描述:安全性校验,确定请求的包名和uid 是一致的。
- 详细代码:
public void enforceCallFromPackage(String packageName) {
mAppOpsManager.checkPackage(Binder.getCallingUid(), packageName);
}
@Deprecated
public void checkPackage(int uid, @NonNull String packageName) {
try {
// 检查请求的 uid 和 packageName 是否一致
if (mService.checkPackage(uid, packageName) != MODE_ALLOWED) {
throw new SecurityException(
"Package " + packageName + " does not belong to " + uid);
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
1.1.1.2、AppWidgetServiceImpl. ensureGroupStateLoadedLocked()
描述:确定小部件的信息已经加载。 详细代码:
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);
}
}
1.1.1.3、AppWidgetServiceImpl. incrementAndGetAppWidgetIdLocked()
- 描述:生成新的widgetId
- 详细代码:
class AppWidgetServiceImpl {
// 获取新添加小部件的widgetId
private int incrementAndGetAppWidgetIdLocked(int userId) {
final int appWidgetId = peekNextAppWidgetIdLocked(userId) + 1;
mNextAppWidgetIds.put(userId, appWidgetId);
return appWidgetId;
}
private int peekNextAppWidgetIdLocked(int userId) {
if (mNextAppWidgetIds.indexOfKey(userId) < 0) {
return AppWidgetManager.INVALID_APPWIDGET_ID + 1;
} else {
return mNextAppWidgetIds.get(userId);
}
}
}
1.1.1.4、AppWidgetServiceImpl.addWidgetLocked()
描述:添加widget,将widget,以及widget 对应的包名存储到相应的集合中 详细代码:
class AppWidgetServiceImpl {
void addWidgetLocked(Widget widget) {
mWidgets.add(widget);
onWidgetProviderAddedOrChangedLocked(widget);
}
// 将widget 的packageName 存入mWidgetPackages
void onWidgetProviderAddedOrChangedLocked(Widget widget) {
if (widget.provider == null) return;
int userId = widget.provider.getUserId();
synchronized (mWidgetPackagesLock) {
ArraySet<String> packages = mWidgetPackages.get(userId);
if (packages == null) {
mWidgetPackages.put(userId, packages = new ArraySet<String>());
}
packages.add(widget.provider.id.componentName.getPackageName());
}
// If we are adding a widget it might be for a provider that
// is currently masked, if so mask the widget.
if (widget.provider.isMaskedLocked()) {
maskWidgetsViewsLocked(widget.provider, widget);
} else {
widget.clearMaskedViewsLocked();
}
}
}
到这里,添加 widget 申请 widgetId 流程就分析完了,过程其实也挺简单,安全校验、小部件加载确定也多次介绍过,下一篇讲添加 widget 之 绑定widgetId。