本文是 Android Widget(小部件) 系列的第十篇,主要从源码角度是梳理 widget 升级时,系统是如何感知,以及视图是如何更新的。
本系列的目的是通过对 Android 小部件的梳理,了解小部件刷新流程、添加、删除、恢复流程、以及系统发生变化时,小部件是如何适配的,解决在开发小部件过程中遇到的问题。系列文章大部份来自源码的解读,内容非常多,也非常容易遗忘,因此记录分享。
系列文章
Android Widget (小部件)基础介绍以及常见问题
Android Widget (小部件)刷新源码解析一非列表
Android Widget (小部件) option刷新源码解析
Android Widget(小部件)添加源码分析一--申请widgetId
Android Widget(小部件)添加源码分析二--绑定widgetId
Android Widget(小部件)添加源码分析三--创建视图
# # Android Widget(小部件) 小部件版本升级更新分析
一、描述
在手机关机重启后,系统会根据存储小部件信息进行恢复,了解恢复流程有助于解手机重启后,各种奇奇怪怪的问题,同时更加熟悉小部件的运行机制。
二、系统重启后恢复流程
java
1、初始化AppWidgetService
2、系统解锁后恢复widget
1、初始化AppWidgetService
1.1、启动服务
- 描述:在SystemServer启动后,在startOtherServices() 调用启动SystemServiceManager 启动AppWidgetService服务。
- 详细代码
less
classs SystemServer {
private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
...
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS)
|| context.getResources().getBoolean(R.bool.config_enableAppWidgetService)) {
// 启动服务,详看 2
mSystemServiceManager.startService(APPWIDGET_SERVICE_CLASS);
}
...
}
}
1.2、SystemServiceManager启动AppWidgetService服务
- 描述:SystemServiceManager通过反射创建 AppWidgetService 对象并调用起onStart()方法
- 详细代码:
typescript
class SystemServiceManager {
public SystemService startService(String className) {
// 根据类名获取class
final Class<SystemService> serviceClass = loadClassFromLoader(className,
this.getClass().getClassLoader());
// 后文有详细代码
return startService(serviceClass);
}
public <T extends SystemService> T startService(Class<T> serviceClass) {
...
final T service;
// 获取构造函数,创建对象。
Constructor<T> constructor = serviceClass.getConstructor(Context.class);
service = constructor.newInstance(mContext);
...
// 注册服务,调用start,后文有详细代码
startService(service);
return service;
}
public void startService(@NonNull final SystemService service) {
...
// 启动服务 详看 3
service.onStart();
...
}
}
class AppWidgetService extends SystemService{
@Override
public void onStart() {
// mImpl为 AppWidgetServiceImpl 详看4
mImpl.onStart();
// 发布服务
publishBinderService(Context.*APPWIDGET_SERVICE*, mImpl);
// 静态对象持有AppWidgetServiceImpl,用于获取widget 的状态,以及恢复
AppWidgetBackupBridge.register(mImpl);
}
}
1.3、AppWidgetServiceImpl onStart 逻辑
- 描述:AppWidgetServiceImpl onStart 时会获取包管理、闹钟管理、用户管理等一系列管理,计算限制 bitmap 大小限制,注册包改变监听等。
- 详细代码:
ini
class AppWidgetServiceImpl {
public void onStart() {
mPackageManager = AppGlobals.getPackageManager();
mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
mKeyguardManager = (KeyguardManager) mContext.getSystemService(KEYGUARD_SERVICE);
mDevicePolicyManagerInternal = LocalServices.getService(DevicePolicyManagerInternal.class);
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
mSaveStateHandler = BackgroundThread.getHandler();
mCallbackHandler = new CallbackHandler(mContext.getMainLooper());
mBackupRestoreController = new BackupRestoreController();
mSecurityPolicy = new SecurityPolicy();
// 计算 widget bitmap 可占用内存
computeMaximumWidgetBitmapMemory();
// 注册包改变的广播
registerBroadcastReceiver();
// 注册设备策略改变的监听
registerOnCrossProfileProvidersChangedListener();
// 放入公开的服务池,供各个进程使用
LocalServices.addService(AppWidgetManagerInternal.class, new AppWidgetManagerLocal());
}
}
2、系统恢复
2.1、解锁后恢复
- 描述:解锁系统解锁后通过LocalServices获取小部件服务,调用unlockUser开始恢复小部件。
- 详细代码
java
void finishUserUnlockedCompleted(final UserState uss) {
...
//获取小部件服务,开始恢复小部件 详看2
mInjector.startUserWidgets(userId);
...
}
static class Injector {
void startUserWidgets(@UserIdInt int userId) {
AppWidgetManagerInternal awm = LocalServices.getService(AppWidgetManagerInternal.class);
if (awm != null) {
// Out of band, because this is called during a sequence with
// sensitive cross-service lock management
FgThread.getHandler().post(() -> {
// 处理用户解锁后的处理
awm.unlockUser(userId);
});
}
}
}
2.2、恢复小部件
描述:确定小部件加载状态、发送enable、update 广播,并注册定数刷新 详细代码
scss
void handleUserUnlocked(int userId) {
// 检查uid 是否在使用中
if (isProfileWithLockedParent(userId)) {
return;
}
// 检查是否解锁
if (!mUserManager.isUserUnlockingOrUnlocked(userId)) {
Slog.w(TAG, "User " + userId + " is no longer unlocked - exiting");
return;
}
long time = SystemClock.elapsedRealtime();
synchronized (mLock) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "appwidget ensure");
//确定小部件的加载状态,详看2.3.1
ensureGroupStateLoadedLocked(userId);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "appwidget reload");
// 加载小部件的遮挡状态,可能和应用的可用时间有关
reloadWidgetsMaskedStateForGroup(mSecurityPolicy.getGroupParent(userId));
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
final int N = mProviders.size();
for (int i = 0; i < N; i++) {
Provider provider = mProviders.get(i);
// Send broadcast only to the providers of the user.
if (provider.getUserId() != userId) {
continue;
}
if (provider.widgets.size() > 0) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
"appwidget init " + provider.id.componentName.getPackageName());
//发送enable 广播,详看2.3.2
sendEnableIntentLocked(provider);
int[] appWidgetIds = getWidgetIds(provider.widgets);
// 发送更新广播,详看2.3.3
sendUpdateIntentLocked(provider, appWidgetIds);
// 注册定时刷新广播
registerForBroadcastsLocked(provider, appWidgetIds);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
}
}
Slog.i(TAG, "Processing of handleUserUnlocked u" + userId + " took "
+ (SystemClock.elapsedRealtime() - time) + " ms");
}
2.3.1、ensureGroupStateLoadedLocked
描述:若已经加载过了则return,若没有加载过则根据uid 获取widgetProvider(过滤带刷新action 的广播),根据uid 获取相应的配置文件,根据配置文件设置widget,并绑定相应的host。放入mWidgets中。
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 构建 ,
//详看 2.3.1.1
loadGroupWidgetProvidersLocked(newProfileIds);
// 从系统配置文件/data/system/users/0/appwidgets.xml 加载状态、
loadGroupStateLocked(newProfileIds);
}
2.3.1.1 AppWidgetServiceImpl.loadGroupWidgetProvidersLocked
根据刷新的action 在packageManager中查询想要的receiver,根据receiver创建provider。provider 包含关联的widgets、定时广播、AppWidgetProviderInfo
ini
private void loadGroupWidgetProvidersLocked(int[] profileIds) {
List<ResolveInfo> allReceivers = null;
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
final int profileCount = profileIds.length;
for (int i = 0; i < profileCount; i++) {
final int profileId = profileIds[i];
List<ResolveInfo> receivers = queryIntentReceivers(intent, profileId);
if (receivers != null && !receivers.isEmpty()) {
if (allReceivers == null) {
allReceivers = new ArrayList<>();
}
allReceivers.addAll(receivers);
}
}
final int N = (allReceivers == null) ? 0 : allReceivers.size();
for (int i = 0; i < N; i++) {
ResolveInfo receiver = allReceivers.get(i);
addProviderLocked(receiver);
}
}
3、相应广播
描述:enable广播和update广播 详细代码:
java
class AppWidgetServiceImpl {
private void sendEnableIntentLocked(Provider p) {
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
intent.setComponent(p.id.componentName);
sendBroadcastAsUser(intent, p.id.getProfile());
}
private void sendUpdateIntentLocked(Provider provider, int[] appWidgetIds) {
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
intent.setComponent(provider.id.componentName);
sendBroadcastAsUser(intent, provider.id.getProfile());
}
}
到这里,小部件系列所有的文章都已经更新完了,从刷新流程、添加、删除、到恢复流程、以及系统发生变化时视图变化都有介绍,篇幅虽然长但其实里面有很多逻辑都是一样的,当理解完一篇再理解其他就不会那么难,最后希望他可以帮助到很多人解决问题,体现他的价值。