安卓小部件刷新源码解析一非列表
一、刷新流程
1、system_process 发送广播
2、应用widget收到广播,准备数据构建RemoteView,并调用AppWidgetManager的updateAppWidget()方法
3、AppWidgetManager 通过AIDL 通知 system_process更新,system_process收到回调后做一些列操作
4、system_process 通过AIDL 回调Host 更新方法,Host 解析RemoteView,并生成相应的视图
二、详细刷新流程
widget 更新 -1-.png1、system_process 发送广播
更新广播的action 为android.appwidget.action.APPWIDGET_UPDATE
2、updateAppWidget()/partiallyUpdateAppWidget 局部更新
调用AppWidgetManager.updateAppWidget(),在此之前一般都会设置相应的RemoteView
3、updateAppWidgetIds()
这里通过AIDL跨进程技术调用system_progress进程的AppWidgetServiceImpl对象。
3.1、enforceCallFromPackage
安全性校验,确定请求的包命和uid 是一致的
3.2、ensureGroupStateLoadedLocked
若已经加载过了则return,若没有加载过则根据uid 获取widgetProvider(过滤带刷新action 的广播),根据uid 获取相应的配置文件,根据配置文件设置widget,并绑定相应的host。放入mWidgets中。
3.3、lookupWidgetLocked
根据widgetId在mWidgets 找到对应的widget,通过uid验证权限
3.4、updateAppWidgetInstanceLocked
替换widget 的remoteView(局部刷新是合并两次的RemoteView),检查RemoteView的bitmap 是否超过最大限制。
3.4.1、scheduleNotifyUpdateAppWidgetLocked
mCallbackHandler 发送更新message
3.5、handleNotifyUpdateAppWidget
host 的 updateAppWidget()方法
4、通过AIDL 回调Host 的updateAppWidget( ),send 更新 message
4.1、handler 处理更新message
4.1.1、updateAppWidgetView
在host的mViews中通过widgetId 找到对应的AppWidgetHostView,让后进行更新
4.1.1.1、applyRemoteViews将remoteView 中的action 设置在相应view 上
三、详细刷新流程代码分析
1、system_process 发送广播 ,更新广播的action 为android.appwidget.action.APPWIDGET_UPDATE。
// AppWidgetProvider 继承于广播,system_process发送广播是会回调onReceive方法
// 如果是更新广播的话会回调onUpdate()方法
public class AppWidgetProvider extends BroadcastReceiver {
...
public void onReceive(Context context, Intent intent) {
// Protect against rogue update broadcasts (not really a security issue,
// just filter bad broacasts out so subclasses are less likely to crash).
String action = intent.getAction();
if (AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {
Bundle extras = intent.getExtras();
if (extras != null) {
int[] appWidgetIds = extras.getIntArray(AppWidgetManager.EXTRA_APPWIDGET_IDS);
if (appWidgetIds != null && appWidgetIds.length > 0) {
this.onUpdate(context, AppWidgetManager.getInstance(context), appWidgetIds);
}
}
}
...
}
2、应用widget收到广播,准备数据构建RemoteView,并调用AppWidgetManager的updateAppWidget()方法
public abstract class TestWidgetProvider extends AppWidgetProvider {
...
/**
*onReceive会回调该方法
*
**/
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds{
// AppWidgetProvider 这里通常会设置new RemoteView,并设置,可设置点击时间、文字、图片等最后调用
// appWidgetManager.updateAppWidget()
super.onUpdate(context, appWidgetManager, appWidgetIds);
for (int widgetId : appWidgetIds) {
RemoteViews remoteViews = new RemoteViews(context.getPackageName(),
R.layout.widget_test);
...
appWidgetManager.updateAppWidget(widgetId, remoteViews);
}
}
...
}
3、updateAppWidgetIds()方法,这里通过AIDL跨进程技术调用system_progress进程的AppWidgetServiceImpl对象。
public class AppWidgetManager {
...
public void updateAppWidget(int[] appWidgetIds, RemoteViews views) {
mService 为 AppWidgetServiceImpl
if (mService == null) {
return;
}
try {
// 通过AIDL 调用 AppWidgetServiceImpl 的 mService.updateAppWidgetIds 方法
mService.updateAppWidgetIds(mPackageName, appWidgetIds, views);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
...
}
// partially 代表是否部分更新
private void updateAppWidgetIds(String callingPackage, int[] appWidgetIds,
RemoteViews views, boolean partially) {
final int userId = UserHandle.getCallingUserId();
if (appWidgetIds == null || appWidgetIds.length == 0) {
return;
}
// Make sure the package runs under the caller uid.
// AppWidgetServiceImpl 运行在system_process ,包名为字符串传入,
// 安全性校验,确定请求的包命和uid 是一致的
mSecurityPolicy.enforceCallFromPackage(callingPackage);
synchronized (mLock) {
// 是否解锁状态,处于解锁状态,若第一次加载则构建widget,后面会详细解析
ensureGroupStateLoadedLocked(userId);
final int N = appWidgetIds.length;
for (int i = 0; i < N; i++) {
final int appWidgetId = appWidgetIds[i];
// NOTE: The lookup is enforcing security across users by making
// sure the caller can only access widgets it hosts or provides.
// 根据widgetId在mWidgets 找到对应的widget,通过uid验证权限,后面会详细解析
Widget widget = lookupWidgetLocked(appWidgetId,
Binder.getCallingUid(), callingPackage);
if (widget != null) {
// 更新widget
updateAppWidgetInstanceLocked(widget, views, partially);
}
}
}
}
3.1、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();
}
}
3.2、ensureGroupStateLoadedLocked()方法,若已经加载过了则return,若没有加载过则根据uid 获取widgetProvider(过滤带刷新action 的广播),根据uid 获取相应的配置文件,根据配置文件设置widget,并绑定相应的host。放入mWidgets中。
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 https://www.jianshu.com/p/3ad2163f7d34
//https://blog.csdn.net/qq_14978113/article/details/94654401
// 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);
}
3.3、lookupWidgetLocked()方法,根据widgetId在mWidgets 找到对应的widget,通过uid验证权限。
private Widget lookupWidgetLocked(int appWidgetId, int uid, String packageName) {
final int N = mWidgets.size();
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;
}
3.4、updateAppWidgetInstanceLocked()方法,替换widget 的remoteView(局部刷新是合并两次的RemoteView),检查RemoteView的bitmap 是否超过最大限制。
private void updateAppWidgetInstanceLocked(Widget widget, RemoteViews views,
boolean isPartialUpdate) {
if (widget != null && widget.provider != null
&& !widget.provider.zombie && !widget.host.zombie) {
// 如果是局部更新,合并remote view
if (isPartialUpdate && widget.views != null) {
// For a partial update, we merge the new RemoteViews with the old.
widget.views.mergeRemoteViews(views);
} else {
// For a full update we replace the RemoteViews completely.
widget.views = views;
}
// 如果非系统应用 bitmap 的大小超过规定大小,则抛出异常
int memoryUsage;
if ((UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID) &&
(widget.views != null) &&
((memoryUsage = widget.views.estimateMemoryUsage()) > mMaxWidgetBitmapMemory)) {
widget.views = null;
throw new IllegalArgumentException("RemoteViews for widget update exceeds"
+ " maximum bitmap memory usage (used: " + memoryUsage
+ ", max: " + mMaxWidgetBitmapMemory + ")");
}
scheduleNotifyUpdateAppWidgetLocked(widget, widget.getEffectiveViewsLocked());
}
}
/***
**合并action
**/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void mergeRemoteViews(RemoteViews newRv) {
if (newRv == null) return;
// We first copy the new RemoteViews, as the process of merging modifies the way the actions
// reference the bitmap cache. We don't want to modify the object as it may need to
// be merged and applied multiple times.
RemoteViews copy = new RemoteViews(newRv);
HashMap<String, Action> map = new HashMap<String, Action>();
if (mActions == null) {
mActions = new ArrayList<Action>();
}
// 将老的action 放入map
int count = mActions.size();
for (int i = 0; i < count; i++) {
Action a = mActions.get(i);
map.put(a.getUniqueKey(), a);
}
ArrayList<Action> newActions = copy.mActions;
if (newActions == null) return;
count = newActions.size();
for (int i = 0; i < count; i++) {
Action a = newActions.get(i);
//由viewId和action tag 组成
String key = newActions.get(i).getUniqueKey();
// R开始支持
int mergeBehavior = newActions.get(i).mergeBehavior();
// 相同则把旧的action 移除
if (map.containsKey(key) && mergeBehavior == Action.MERGE_REPLACE) {
mActions.remove(map.get(key));
map.remove(key);
}
// If the merge behavior is ignore, we don't bother keeping the extra action
if (mergeBehavior == Action.MERGE_REPLACE || mergeBehavior == Action.MERGE_APPEND) {
mActions.add(a);
}
}
存储设置的bitmap
// Because pruning can remove the need for bitmaps, we reconstruct the bitmap cache
mBitmapCache = new BitmapCache();
setBitmapCache(mBitmapCache);
}
3.4.1、scheduleNotifyUpdateAppWidgetLocked()方法,mCallbackHandler 发送更新message。
private void scheduleNotifyUpdateAppWidgetLocked(Widget widget, RemoteViews updateViews) {
long requestId = UPDATE_COUNTER.incrementAndGet();
if (widget != null) {
widget.updateSequenceNos.put(ID_VIEWS_UPDATE, requestId);
}
if (widget == null || widget.provider == null || widget.provider.zombie
|| widget.host.callbacks == null || widget.host.zombie) {
return;
}
SomeArgs args = SomeArgs.obtain();
args.arg1 = widget.host;
args.arg2 = widget.host.callbacks;
args.arg3 = (updateViews != null) ? updateViews.clone() : null;
args.arg4 = requestId;
args.argi1 = widget.appWidgetId;
mCallbackHandler.obtainMessage(
CallbackHandler.MSG_NOTIFY_UPDATE_APP_WIDGET,
args).sendToTarget();
}
3.5、handleNotifyUpdateAppWidget()方法,host 的 updateAppWidget()方法,通过通过AIDL 回调到host 中。
private void handleNotifyUpdateAppWidget(Host host, IAppWidgetHost callbacks,
int appWidgetId, RemoteViews views, long requestId) {
try {
callbacks.updateAppWidget(appWidgetId, views);
host.lastWidgetUpdateSequenceNo = requestId;
} catch (RemoteException re) {
synchronized (mLock) {
Slog.e(TAG, "Widget host dead: " + host.id, re);
host.callbacks = null;
}
}
}
4、通过AIDL 回调Host 的updateAppWidget( ),send 更新 message
public class AppWidgetHost {
public void updateAppWidget(int appWidgetId, RemoteViews views) {
if (isLocalBinder() && views != null) {
views = views.clone();
}
Handler handler = mWeakHandler.get();
if (handler == null) {
return;
}
Message msg = handler.obtainMessage(HANDLE_UPDATE, appWidgetId, 0, views);
msg.sendToTarget();
}
}
4.1、handler 处理更新message。
public void handleMessage(Message msg) {
switch (msg.what) {
case HANDLE_UPDATE: {
updateAppWidgetView(msg.arg1, (RemoteViews)msg.obj);
break;
}
}
}
4.1.1、updateAppWidgetView()方法,在host的mViews中通过widgetId 找到对应的AppWidgetHostView,让后进行更新。
void updateAppWidgetView(int appWidgetId, RemoteViews views) {
AppWidgetHostView v;
synchronized (mViews) {
v = mViews.get(appWidgetId);
}
if (v != null) {
v.updateAppWidget(views);
}
}
4.1.1.1、applyRemoteViews()方法,将remoteView 中的action 设置在相应view 上。
protected void applyRemoteViews(@Nullable RemoteViews remoteViews, boolean useAsyncIfPossible) {
boolean recycled = false;
View content = null;
Exception exception = null;
// Block state restore until the end of the apply.
mLastInflatedRemoteViewsId = -1;
if (mLastExecutionSignal != null) {
mLastExecutionSignal.cancel();
mLastExecutionSignal = null;
}
// 如果remoteViews 为null 加载默认视图
if (remoteViews == null) {
if (mViewMode == VIEW_MODE_DEFAULT) {
// We've already done this -- nothing to do.
return;
}
content = getDefaultView();
mLayoutId = -1;
mViewMode = VIEW_MODE_DEFAULT;
} else {
// Select the remote view we are actually going to apply.
// 查找适合的remoteView 找不到时返回最小的
RemoteViews rvToApply = remoteViews.getRemoteViewsToApply(mContext, mCurrentSize);
// 是否 设置了
/if (mOnLightBackground) {
// 如果深色浅色模式变了,使用相应的 layout id ,new remoteview
rvToApply = rvToApply.getDarkTextViews();
}
if (mAsyncExecutor != null && useAsyncIfPossible) {
// 异步加载
inflateAsync(rvToApply);
return;
}
int layoutId = rvToApply.getLayoutId();
if (rvToApply.canRecycleView(mView)) {
try {
rvToApply.reapply(mContext, mView, mInteractionHandler, mCurrentSize,
mColorResources);
content = mView;
mLastInflatedRemoteViewsId = rvToApply.computeUniqueId(remoteViews);
recycled = true;
if (LOGD) Log.d(TAG, "was able to recycle existing layout");
} catch (RuntimeException e) {
exception = e;
}
}
// Try normal RemoteView inflation
if (content == null) {
try {
content = rvToApply.apply(mContext, this, mInteractionHandler,
mCurrentSize, mColorResources);
mLastInflatedRemoteViewsId = rvToApply.computeUniqueId(remoteViews);
if (LOGD) Log.d(TAG, "had to inflate new layout");
} catch (RuntimeException e) {
exception = e;
}
}
mLayoutId = layoutId;
mViewMode = VIEW_MODE_CONTENT;
}
applyContent(content, recycled, exception);
}
/**
**将action 转成相应的设置
**/
private void reapply(Context context, View v, InteractionHandler handler, SizeF size,
ColorResources colorResources, boolean topLevel) {
RemoteViews rvToApply = getRemoteViewsToApply(context, size);
// In the case that a view has this RemoteViews applied in one orientation or size, is
// persisted across change, and has the RemoteViews re-applied in a different situation
// (orientation or size), we throw an exception, since the layouts may be completely
// unrelated.
if (hasMultipleLayouts()) {
if (!rvToApply.canRecycleView(v)) {
throw new RuntimeException("Attempting to re-apply RemoteViews to a view that" +
" that does not share the same root layout id.");
}
}
// 将action 转成相应行为
rvToApply.performApply(v, (ViewGroup) v.getParent(), handler, colorResources);
// If the parent of the view is has is a root, resolve the recycling.
if (topLevel && v instanceof ViewGroup) {
finalizeViewRecycling((ViewGroup) v);
}
}
/**
**将生成的view 添加到host 中
**/
private void applyContent(View content, boolean recycled, Exception exception) {
if (content == null) {
if (mViewMode == VIEW_MODE_ERROR) {
// We've already done this -- nothing to do.
return ;
}
if (exception != null) {
Log.w(TAG, "Error inflating RemoteViews", exception);
}
content = getErrorView();
mViewMode = VIEW_MODE_ERROR;
}
// 复用的场景直接在view上进行了设置,而view 之前已经添加到host,所以复用的case 这里就不用处理。
if (!recycled) {
prepareView(content);
addView(content);
}
if (mView != content) {
removeView(mView);
mView = content;
}
}