一横媚、刷新流程
1纠炮、system_process 發(fā)送廣播
2、應(yīng)用widget收到廣播,準(zhǔn)備數(shù)據(jù)構(gòu)建RemoteView灯蝴,并調(diào)用AppWidgetManager的updateAppWidget()方法
3恢口、AppWidgetManager 通過AIDL 通知 system_process更新,system_process收到回調(diào)后做一些列操作
4穷躁、system_process 通過AIDL 回調(diào)Host 更新方法耕肩,Host 解析RemoteView,并生成相應(yīng)的視圖
二、詳細(xì)刷新流程
1问潭、system_process 發(fā)送廣播
更新廣播的action 為android.appwidget.action.APPWIDGET_UPDATE
2猿诸、updateAppWidget()/partiallyUpdateAppWidget 局部更新
調(diào)用AppWidgetManager.updateAppWidget(),在此之前一般都會設(shè)置相應(yīng)的RemoteView
3、updateAppWidgetIds()
這里通過AIDL跨進(jìn)程技術(shù)調(diào)用system_progress進(jìn)程的AppWidgetServiceImpl對象狡忙。
3.1梳虽、enforceCallFromPackage
安全性校驗(yàn),確定請求的包命和uid 是一致的
3.2灾茁、ensureGroupStateLoadedLocked
若已經(jīng)加載過了則return,若沒有加載過則根據(jù)uid 獲取widgetProvider(過濾帶刷新action 的廣播)窜觉,根據(jù)uid 獲取相應(yīng)的配置文件,根據(jù)配置文件設(shè)置widget删顶,并綁定相應(yīng)的host竖螃。放入mWidgets中淑廊。
3.3逗余、lookupWidgetLocked
根據(jù)widgetId在mWidgets 找到對應(yīng)的widget,通過uid驗(yàn)證權(quán)限
3.4、updateAppWidgetInstanceLocked
替換widget 的remoteView(局部刷新是合并兩次的RemoteView)季惩,檢查RemoteView的bitmap 是否超過最大限制录粱。
3.4.1、scheduleNotifyUpdateAppWidgetLocked
mCallbackHandler 發(fā)送更新message
3.5画拾、handleNotifyUpdateAppWidget
host 的 updateAppWidget()方法
4啥繁、通過AIDL 回調(diào)Host 的updateAppWidget( ),send 更新 message
4.1青抛、handler 處理更新message
4.1.1旗闽、updateAppWidgetView
在host的mViews中通過widgetId 找到對應(yīng)的AppWidgetHostView,讓后進(jìn)行更新
4.1.1.1、applyRemoteViews將remoteView 中的action 設(shè)置在相應(yīng)view 上
三蜜另、詳細(xì)刷新流程代碼分析
1适室、system_process 發(fā)送廣播 ,更新廣播的action 為android.appwidget.action.APPWIDGET_UPDATE举瑰。
// AppWidgetProvider 繼承于廣播捣辆,system_process發(fā)送廣播是會回調(diào)onReceive方法
// 如果是更新廣播的話會回調(diào)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、應(yīng)用widget收到廣播,準(zhǔn)備數(shù)據(jù)構(gòu)建RemoteView此迅,并調(diào)用AppWidgetManager的updateAppWidget()方法
public abstract class TestWidgetProvider extends AppWidgetProvider {
...
/**
*onReceive會回調(diào)該方法
*
**/
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds{
// AppWidgetProvider 這里通常會設(shè)置new RemoteView,并設(shè)置汽畴,可設(shè)置點(diǎn)擊時(shí)間旧巾、文字、圖片等最后調(diào)用
// 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跨進(jìn)程技術(shù)調(diào)用system_progress進(jìn)程的AppWidgetServiceImpl對象。
public class AppWidgetManager {
...
public void updateAppWidget(int[] appWidgetIds, RemoteViews views) {
mService 為 AppWidgetServiceImpl
if (mService == null) {
return;
}
try {
// 通過AIDL 調(diào)用 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 運(yùn)行在system_process 罢坝,包名為字符串傳入绳匀,
// 安全性校驗(yàn),確定請求的包命和uid 是一致的
mSecurityPolicy.enforceCallFromPackage(callingPackage);
synchronized (mLock) {
// 是否解鎖狀態(tài)炸客,處于解鎖狀態(tài)疾棵,若第一次加載則構(gòu)建widget,后面會詳細(xì)解析
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.
// 根據(jù)widgetId在mWidgets 找到對應(yīng)的widget,通過uid驗(yàn)證權(quán)限,后面會詳細(xì)解析
Widget widget = lookupWidgetLocked(appWidgetId,
Binder.getCallingUid(), callingPackage);
if (widget != null) {
// 更新widget
updateAppWidgetInstanceLocked(widget, views, partially);
}
}
}
}
3.1痹仙、enforceCallFromPackage()方法是尔,安全性校驗(yàn),確定請求的包命和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()方法,若已經(jīng)加載過了則return,若沒有加載過則根據(jù)uid 獲取widgetProvider(過濾帶刷新action 的廣播)众弓,根據(jù)uid 獲取相應(yīng)的配置文件恩溅,根據(jù)配置文件設(shè)置widget,并綁定相應(yīng)的host谓娃。放入mWidgets中脚乡。
private void ensureGroupStateLoadedLocked(int userId, boolean enforceUserUnlockingOrUnlocked) {
// 判斷該應(yīng)用是否處在解鎖狀態(tài),設(shè)備鎖
if (enforceUserUnlockingOrUnlocked && !isUserRunningAndUnlocked(userId)) {
throw new IllegalStateException(
"User " + userId + " must be unlocked for widgets to be available");
}
// 判斷該應(yīng)用文件配置是否處在解鎖狀態(tài)
if (enforceUserUnlockingOrUnlocked && isProfileWithLockedParent(userId)) {
throw new IllegalStateException(
"Profile " + userId + " must have unlocked parent");
}
// 獲取能用的配置配置id
final int[] profileIds = mSecurityPolicy.getEnabledGroupProfileIds(userId);
// 查看是否有未加載的user http://www.reibang.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代表已經(jīng)加載過,標(biāo)記數(shù)組
if (mLoadedUserIds.indexOfKey(profileId) >= 0) {
profileIds[i] = LOADED_PROFILE_ID;
} else {
newMemberCount++;
}
}
// 沒有新加的 便會return
if (newMemberCount <= 0) {
return;
}
// 構(gòu)建新增加的ProfileId 數(shù)組,后續(xù)通常在第一次加載的時(shí)候執(zhí)行
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 設(shè)置為 TAG_UNDEFINED
clearProvidersAndHostsTagsLocked();
// 根據(jù)加載ProfileId 獲取系統(tǒng) ResolveInfo 列表滨达, 根據(jù)ResolveInfo 構(gòu)建
provider;
loadGroupWidgetProvidersLocked(newProfileIds);
// 從系統(tǒng)配置文件/data/system/users/0/appwidgets.xml 加載狀態(tài)奶稠、
loadGroupStateLocked(newProfileIds);
}
3.3、lookupWidgetLocked()方法捡遍,根據(jù)widgetId在mWidgets 找到對應(yīng)的widget,通過uid驗(yàn)證權(quán)限锌订。
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;
}
// 如果非系統(tǒng)應(yīng)用 bitmap 的大小超過規(guī)定大小蜈项,則拋出異常
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);
}
}
存儲設(shè)置的bitmap
// Because pruning can remove the need for bitmaps, we reconstruct the bitmap cache
mBitmapCache = new BitmapCache();
setBitmapCache(mBitmapCache);
}
3.4.1、scheduleNotifyUpdateAppWidgetLocked()方法良拼,mCallbackHandler 發(fā)送更新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 回調(diào)到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 回調(diào)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 找到對應(yīng)的AppWidgetHostView,讓后進(jìn)行更新。
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 設(shè)置在相應(yīng)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 加載默認(rèn)視圖
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 找不到時(shí)返回最小的
RemoteViews rvToApply = remoteViews.getRemoteViewsToApply(mContext, mCurrentSize);
// 是否 設(shè)置了
/if (mOnLightBackground) {
// 如果深色淺色模式變了征堪,使用相應(yīng)的 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 轉(zhuǎn)成相應(yīng)的設(shè)置
**/
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 轉(zhuǎn)成相應(yīng)行為
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;
}
// 復(fù)用的場景直接在view上進(jìn)行了設(shè)置瘩缆,而view 之前已經(jīng)添加到host,所以復(fù)用的case 這里就不用處理佃蚜。
if (!recycled) {
prepareView(content);
addView(content);
}
if (mView != content) {
removeView(mView);
mView = content;
}
}