1.RemoteViews應用
RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.layout_notication);
remoteViews.setTextViewText(R.id.text, "text");
remoteViews.setImageViewResource(R.id.image, R.drawable.icon);
remoteViews.setOnClickPendingIntent(R.id.button,
PendingIntent.getActivities(this, 0,
new Intent(this, TargetActivity.class),
PendingIntent.FLAG_UPDATE_CURRENT));
getActivity(Context context, int requestCode, Intent intent, @Flags int flags)
getBroadcast(Context context, int requestCode, Intent intent, @Flags int flags)
getService(Context context, int requestCode, @NonNull Intent intent, @Flags int flags)
* PendingIntent用於描述Intent和target action,提供給其他application以執行相應的動作。
* 所以爲了安全,要使用顯示Intent,直接指定目標組件。
* PendingIntent標識了一組將要操作的數據,如果創建它的process被kill了,不影響其他process繼續使用他;之後如果重新創建了一個相同類型的PendingIntent,之前同類型的如果可用將繼續被使用。
* 相同的Intent將得到相同的PendingIntent,而Intent中extra的部分不會影響判斷Intent是否相同。所以如果想得到不同PendingIntent,要保證Intent的不同(單獨區分extra無效),或者在PendingIntent的get方法中指定不同的requestCode;如果同一時刻只想使用同一個PendingIntent,通過設置FLAG_CANCEL_CURRENT或FLAG_UPDATE_CURRENT來取消或更新指定的Intent。
* flags:
* FLAG_ONE_SHOT
該PendingIntent只能被使用一次。Intent被send之後會直接被cancel,之後send將失敗。
* FLAG_CANCEL_CURRENT
如果當前描述的PendingIntent已經存在,則cancel,然後創建一個新的PendingIntent。
* FLAG_UPDATE_CURRENT
如果當前描述的PendingIntent已經存在,則替換其中Intent的extra。
2.RemoteViews內部機制
Remoteviews僅支持部分layout和view,不支持其子類和其他自定義View。
RemoteViews會通過Binder傳遞到SystemServer進程,在SystemServer中加載佈局,更新View。具體流程:
- 本地進程創建RemoteViews實例,然後調用了一系列的set方法設置了View。爲了減少IPC開銷,RemoteViews內部聲明一個Action抽象類,並實現了Parcelable接口,將set調用的操作封裝爲一個action。而set方法實際上是創建了一個action,然後添加到自身維護的一個action list裏面:
public void setTextViewText(int viewId, CharSequence text) {
setCharSequence(viewId, "setText", text);
}
public void setCharSequence(int viewId, String methodName, CharSequence value) {
addAction(new ReflectionAction(viewId, methodName, ReflectionAction.CHAR_SEQUENCE, value));
}
private void addAction(Action a) {
if (hasLandscapeAndPortraitLayouts()) {
throw new RuntimeException("RemoteViews specifying separate landscape and portrait" +
" layouts cannot be modified. Instead, fully configure the landscape and" +
" portrait layouts individually before constructing the combined layout.");
}
if (mActions == null) {
mActions = new ArrayList<>();
}
mActions.add(a);
}
- 在遠程進程中,會調用RemoteViews的apply方法初始化View,調用performApply遍歷action list,執行一系列的action操作來更新View:
/** @hide */
public View apply(Context context, ViewGroup parent, OnClickHandler handler) {
RemoteViews rvToApply = getRemoteViewsToApply(context);
View result = inflateView(context, rvToApply, parent);
loadTransitionOverride(context, handler);
rvToApply.performApply(result, parent, handler);
return result;
}
private void performApply(View v, ViewGroup parent, OnClickHandler handler) {
if (mActions != null) {
handler = handler == null ? DEFAULT_ON_CLICK_HANDLER : handler;
final int count = mActions.size();
for (int i = 0; i < count; i++) {
Action a = mActions.get(i);
a.apply(v, parent, handler);
}
}
}
- Action有很多實現,主要差異就在於其apply方法,如ReflectionAction的apply通過反射執行View的set方法,TextViewSizeAction的apply則直接對View調用setTextSize。ReflectionAction的抽象程度更高,支持的操作更多,但是反射會有一定的開銷。
- setOnClickPendingIntent僅支持給普通View設置click事件;組合setPendingIntentTemplate和setOnClickFillInIntent可以給ListView等的item設置click事件。
- apply方法只負責將RemoteViews中維護的View創建出來,創建完成後需要將得到的View實例添加到hierarchy中。
- 使用RemoteViews的reapply方法可以將一系列action操作執行到指定的View上。如可以在遠程進程中自行創建同一個View,僅通過RemoteViews去執行View的更新操作。