remoteview最简单的应用应该就是在notification里面了,以下为简单示例。
public class MainActivity extends AppCompatActivity{
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
String channelID = "1";
String channelName = "channel_name";
NotificationChannel channel = null;
channel = new NotificationChannel(channelID, channelName, NotificationManager.IMPORTANCE_HIGH);
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
manager.createNotificationChannel(channel);
// 利用channelName可以在系统设置页面对app某个名称的通知进行管理
NotificationCompat.Builder builder =new NotificationCompat.Builder(this, channelName);
Intent intent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.layout_notificaiton);
remoteViews.setTextViewText(R.id.tv_5_s, "Start");//通过id-内容的方式设置remoteview中控件的内容,底层实现是通过Binder跨进程通信
remoteViews.setTextViewText(R.id.tv_5_e, "End");
remoteViews.setImageViewResource(R.id.icon_5, R.drawable.test);
remoteViews.setOnClickPendingIntent(R.id.tv_5_e, pendingIntent);
builder.setContent(remoteViews);
builder.setSmallIcon(R.mipmap.ic_launcher);
//创建通知时指定channelID
builder.setChannelId(channelID);
Notification notification = builder.build();
manager.notify(1, notification);
}
}
}
如果是8.0以下系统,notification不用设置channelID ,代码如下所示
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
Notification.Builder builder =new Notification.Builder(this);
Intent intent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.layout_notificaiton);
remoteViews.setTextViewText(R.id.tv_5_s, "Start");//通过id-内容的方式设置remoteview中控件的内容,底层实现是通过Binder跨进程通信
remoteViews.setTextViewText(R.id.tv_5_e, "End");
remoteViews.setImageViewResource(R.id.icon_5, R.drawable.test);
remoteViews.setOnClickPendingIntent(R.id.tv_5_e, pendingIntent);
builder.setCustomContentView(remoteViews);
builder.setSmallIcon(R.mipmap.ic_launcher);
Notification notification = builder.build();
manager.notify(1, notification);
通过在notification中简单使用remoteview可以对其有一个简单的了解。
以下文字出处:https://www.jianshu.com/p/91c30eb0a90b
RemoteViews的原理
RemoteViews本身并不是个View.它是个Pacelable,是个可以跨进程传递的一个数据.它携带了UI的布局及对应的数据
其它进程收到这个RemoteViews后会在其进程中根据这个布局inflat出对应的View.然后根据RemoteViews里的属性反射设置到对应的View上,然后根据设置点击监听.监听的处理就是调用对应的PendingIntent.
因为是跨进程,所以无法直接操作View,所以系统把对view的一个操作定义为Action对象.Action对象本身也是个Pacelable,所以可以跨进程.其封装了操作View的数据.远程进程遍历所有的Action并执行其apply方法通过反射更新View
向先前说的设置view的属性,设置点击监听,都是一个Action.
这种说法对我而言太过抽象。
//vendor\mediatek\proprietary\packages\apps_700_es\SystemUI_700_es\src\com\android\systemui\statusbar\notification\NotificationInflater.java
@Override
protected InflationProgress doInBackground(Void... params) {
try {
final Notification.Builder recoveredBuilder
= Notification.Builder.recoverBuilder(mContext,
mSbn.getNotification());
Context packageContext = mSbn.getPackageContext(mContext);
Notification notification = mSbn.getNotification();
if (mIsLowPriority) {
int backgroundColor = mContext.getColor(
R.color.notification_material_background_low_priority_color);
recoveredBuilder.setBackgroundColorHint(backgroundColor);
}
if (notification.isMediaNotification()) {
MediaNotificationProcessor processor = new MediaNotificationProcessor(mContext,
packageContext);
processor.setIsLowPriority(mIsLowPriority);
processor.processNotification(notification, recoveredBuilder);
}
return createRemoteViews(mReInflateFlags,
recoveredBuilder, mIsLowPriority, mIsChildInGroup,
mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight, mRedactAmbient,
packageContext);
} catch (Exception e) {
mError = e;
return null;
}
}
private static InflationProgress createRemoteViews(int reInflateFlags,
Notification.Builder builder, boolean isLowPriority, boolean isChildInGroup,
boolean usesIncreasedHeight, boolean usesIncreasedHeadsUpHeight, boolean redactAmbient,
Context packageContext) {
InflationProgress result = new InflationProgress();
isLowPriority = isLowPriority && !isChildInGroup;
if ((reInflateFlags & FLAG_REINFLATE_CONTENT_VIEW) != 0) {
result.newContentView = createContentView(builder, isLowPriority, usesIncreasedHeight);
}
if ((reInflateFlags & FLAG_REINFLATE_EXPANDED_VIEW) != 0) {
result.newExpandedView = createExpandedView(builder, isLowPriority);
}
if ((reInflateFlags & FLAG_REINFLATE_HEADS_UP_VIEW) != 0) {
result.newHeadsUpView = builder.createHeadsUpContentView(usesIncreasedHeadsUpHeight);
}
if ((reInflateFlags & FLAG_REINFLATE_PUBLIC_VIEW) != 0) {
result.newPublicView = builder.makePublicContentView();
}
if ((reInflateFlags & FLAG_REINFLATE_AMBIENT_VIEW) != 0) {
result.newAmbientView = redactAmbient ? builder.makePublicAmbientNotification()
: builder.makeAmbientNotification();
}
result.packageContext = packageContext;
return result;
}
NotificationInflater中的createRemoteViews这里我没有深入看下去,从方法名字上来看是这里创建了remoteview。
RemoteViews 本身的属性并不多,这里重点关注了一下mLayoutId和mActions,以及ReflectionAction。
//frameworks\base\core\java\android\widget\RemoteViews.java
public class RemoteViews implements Parcelable, Filter {
/**
* The resource ID of the layout file. (Added to the parcel)
*/
private final int mLayoutId;
/**
* An array of actions to perform on the view tree once it has been
* inflated
*/
private ArrayList mActions;
/**
* Inflates the view hierarchy represented by this object and applies
* all of the actions.
*
* Caller beware: this may throw
*
* @param context Default context to use
* @param parent Parent that the resulting view hierarchy will be attached to. This method
* does not attach the hierarchy. The caller should do so when appropriate.
* @return The inflated view hierarchy
*/
public View apply(Context context, ViewGroup parent) {
return apply(context, parent, null);
}
/** @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);
}
}
}
private final class ReflectionAction extends Action {
ReflectionAction(int viewId, String methodName, int type, Object value) {
this.viewId = viewId;
this.methodName = methodName;
this.type = type;
this.value = value;
}
}
}
...................
}
大部分资料显示remoteview更新ui调用的是apply,但是在此过程中ReflectionAction 到底做了什么对此我有些疑惑。于是在ReflectionAction构造函数中打印log(对于反射这一块我存在知识盲区待补),结果如下所示。
2010-01-03 04:33:15.328 966-966/? D/RemoteViews: methodName = setText , param = interface java.lang.CharSequence ,view = android.widget.TextView{10b0fc2 VFED..... ........ 48,0-79,48 #7f0700c1 app:id/tv_5_s} ,value = Start
2010-01-03 04:33:15.329 966-966/? D/RemoteViews: methodName = setText , param = interface java.lang.CharSequence ,view = android.widget.TextView{28f01d3 VFED..C.. ........ 408,0-432,48 #7f0700c0 app:id/tv_5_e} ,value = End
2010-01-03 04:33:15.341 966-966/? D/RemoteViews: methodName = setExpanded , param = boolean ,view = android.view.NotificationHeaderView{5a44110 V.E...... R.....ID 0,0-480,48 #102032b android:id/notification_header} ,value = false
2010-01-03 04:33:15.341 966-966/? D/RemoteViews: methodName = setText , param = interface java.lang.CharSequence ,view = android.widget.TextView{f7e660e V.ED..... ......ID 40,15-121,32 #10201af android:id/app_name_text} ,value = null
2010-01-03 04:33:15.342 966-966/? D/RemoteViews: methodName = setVisibility , param = int ,view = android.widget.TextView{8bde82f G.ED..... ......I. 0,0-0,0 #1020281 android:id/header_text} ,value = 8
2010-01-03 04:33:15.342 966-966/? D/RemoteViews: methodName = setText , param = interface java.lang.CharSequence ,view = android.widget.TextView{8bde82f G.ED..... ......I. 0,0-0,0 #1020281 android:id/header_text} ,value = null
2010-01-03 04:33:15.342 966-966/? D/RemoteViews: methodName = setVisibility , param = int ,view = android.widget.TextView{8f45e3c G.ED..... ......I. 0,0-0,0 #1020282 android:id/header_text_divider} ,value = 8
2010-01-03 04:33:15.342 966-966/? D/RemoteViews: methodName = setVisibility , param = int ,view = android.widget.TextView{32a97c5 V.ED..... ......ID 125,15-129,32 #102043f android:id/time_divider} ,value = 8
2010-01-03 04:33:15.343 966-966/? D/RemoteViews: methodName = setVisibility , param = int ,view = android.widget.DateTimeView{abed51a V.ED..... ......ID 133,15-151,32 #102043b android:id/time} ,value = 8
2010-01-03 04:33:15.343 966-966/? D/RemoteViews: methodName = setVisibility , param = int ,view = android.widget.ImageView{44ad84b G.ED..... ......I. 0,0-0,0 #102037c android:id/profile_badge} ,value = 8
2010-01-03 04:33:15.344 966-966/? D/RemoteViews: methodName = setOriginalIconColor , param = int ,view = android.view.NotificationHeaderView{5a44110 V.E...... R.....ID 0,0-480,48 #102032b android:id/notification_header} ,value = -9079435
2010-01-03 04:33:15.345 966-966/? D/RemoteViews: methodName = setText , param = interface java.lang.CharSequence ,view = android.widget.TextView{f7e660e V.ED..... ......ID 40,15-121,32 #10201af android:id/app_name_text} ,value = My Application
2010-01-03 04:33:15.345 966-966/? D/RemoteViews: methodName = setTextColor , param = int ,view = android.widget.TextView{f7e660e V.ED..... ......ID 40,15-121,32 #10201af android:id/app_name_text} ,value = -9079435
2010-01-03 04:33:15.345 966-966/? D/RemoteViews: methodName = setVisibility , param = int ,view = android.widget.TextView{32a97c5 G.ED..... ......ID 125,15-129,32 #102043f android:id/time_divider} ,value = 0
2010-01-03 04:33:15.345 966-966/? D/RemoteViews: methodName = setTextColor , param = int ,view = android.widget.TextView{32a97c5 V.ED..... ......ID 125,15-129,32 #102043f android:id/time_divider} ,value = -1979711488
2010-01-03 04:33:15.345 966-966/? D/RemoteViews: methodName = setVisibility , param = int ,view = android.widget.DateTimeView{abed51a G.ED..... ......ID 133,15-151,32 #102043b android:id/time} ,value = 0
2010-01-03 04:33:15.346 966-966/? D/RemoteViews: methodName = setTime , param = long ,view = android.widget.DateTimeView{abed51a V.ED..... ......ID 133,15-151,32 #102043b android:id/time} ,value = 1262464395151
2010-01-03 04:33:15.353 966-966/? D/RemoteViews: methodName = setTextColor , param = int ,view = android.widget.DateTimeView{abed51a V.ED..... ......ID 133,15-151,32 #102043b android:id/time} ,value = -1979711488
2010-01-03 04:33:15.353 966-966/? D/RemoteViews: methodName = setOriginalNotificationColor , param = int ,view = android.view.NotificationHeaderView{5a44110 V.E...... R.....ID 0,0-480,48 #102032b android:id/notification_header} ,value = -9079435
2010-01-03 04:33:15.353 966-966/? D/RemoteViews: methodName = setExpandOnlyOnButton , param = boolean ,view = android.view.NotificationHeaderView{5a44110 V.E...... R.....ID 0,0-480,48 #102032b android:id/notification_header} ,value = true
2010-01-03 04:33:15.354 966-966/? D/RemoteViews: methodName = setExpanded , param = boolean ,view = android.view.NotificationHeaderView{221eb41 V.E...... ......ID 16,0-464,48 #102032b android:id/notification_header} ,value = false
2010-01-03 04:33:15.355 966-966/? D/RemoteViews: methodName = setText , param = interface java.lang.CharSequence ,view = android.widget.TextView{36528e6 I.ED..... ......ID 42,14-134,33 #10201af android:id/app_name_text} ,value = null
2010-01-03 04:33:15.355 966-966/? D/RemoteViews: methodName = setVisibility , param = int ,view = android.widget.TextView{c6cae27 G.ED..... ......I. 0,0-0,0 #1020281 android:id/header_text} ,value = 8
2010-01-03 04:33:15.355 966-966/? D/RemoteViews: methodName = setText , param = interface java.lang.CharSequence ,view = android.widget.TextView{c6cae27 G.ED..... ......I. 0,0-0,0 #1020281 android:id/header_text} ,value = null
2010-01-03 04:33:15.355 966-966/? D/RemoteViews: methodName = setVisibility , param = int ,view = android.widget.TextView{8038d4 G.ED..... ......I. 0,0-0,0 #1020282 android:id/header_text_divider} ,value = 8
2010-01-03 04:33:15.356 966-966/? D/RemoteViews: methodName = setVisibility , param = int ,view = android.widget.TextView{fe0fa7d G.ED..... ......I. 0,0-0,0 #102043f android:id/time_divider} ,value = 8
2010-01-03 04:33:15.356 966-966/? D/RemoteViews: methodName = setVisibility , param = int ,view = android.widget.DateTimeView{8caed72 G.ED..... ......I. 0,0-0,0 #102043b android:id/time} ,value = 8
2010-01-03 04:33:15.356 966-966/? D/RemoteViews: methodName = setVisibility , param = int ,view = android.widget.ImageView{aec05c3 G.ED..... ......I. 0,0-0,0 #102037c android:id/profile_badge} ,value = 8
2010-01-03 04:33:15.356 966-966/? D/RemoteViews: methodName = setVisibility , param = int ,view = android.widget.TextView{8120e40 G.ED..... ......ID 0,0-0,0 #1020016 android:id/title} ,value = 8
2010-01-03 04:33:15.356 966-966/? D/RemoteViews: methodName = setText , param = interface java.lang.CharSequence ,view = android.widget.TextView{8120e40 G.ED..... ......ID 0,0-0,0 #1020016 android:id/title} ,value = null
2010-01-03 04:33:15.356 966-966/? D/RemoteViews: methodName = setVisibility , param = int ,view = android.widget.TextView{587c179 G.ED..... ......I. 0,0-0,0 #102041e android:id/text} ,value = 8
2010-01-03 04:33:15.356 966-966/? D/RemoteViews: methodName = setText , param = interface java.lang.CharSequence ,view = android.widget.TextView{587c179 G.ED..... ......I. 0,0-0,0 #102041e android:id/text} ,value = null
2010-01-03 04:33:15.357 966-966/? D/RemoteViews: methodName = setBackgroundResource , param = int ,view = android.widget.FrameLayout{4566ebe I.E...... R.....ID 0,0-480,48 #102040b android:id/status_bar_latest_event_content} ,value = 0
2010-01-03 04:33:15.358 966-966/? D/RemoteViews: methodName = setOriginalIconColor , param = int ,view = android.view.NotificationHeaderView{221eb41 V.E...... ......ID 16,0-464,48 #102032b android:id/notification_header} ,value = -3947581
2010-01-03 04:33:15.358 966-966/? D/RemoteViews: methodName = setText , param = interface java.lang.CharSequence ,view = android.widget.TextView{36528e6 I.ED..... ......ID 42,14-134,33 #10201af android:id/app_name_text} ,value = My Application
2010-01-03 04:33:15.359 966-966/? D/RemoteViews: methodName = setTextColor , param = int ,view = android.widget.TextView{36528e6 I.ED..... ......ID 42,14-134,33 #10201af android:id/app_name_text} ,value = -3947581
2010-01-03 04:33:15.359 966-966/? D/RemoteViews: methodName = setOriginalNotificationColor , param = int ,view = android.view.NotificationHeaderView{221eb41 V.E...... ......ID 16,0-464,48 #102032b android:id/notification_header} ,value = -9079435
2010-01-03 04:33:15.359 966-966/? D/RemoteViews: methodName = setMinimumHeight , param = int ,view = android.widget.LinearLayout{37f3b1f V.E...... ......ID 0,4-448,4 #102032c android:id/notification_main_column} ,value = 0
2010-01-03 04:33:15.359 966-966/? D/RemoteViews: methodName = setVisibility , param = int ,view = com.android.internal.widget.NotificationActionListLayout{a29ce6c G.E...... ......ID 0,0-0,0 #102018d android:id/actions} ,value = 8
2010-01-03 04:33:15.359 966-966/? D/RemoteViews: methodName = setEmphasizedMode , param = boolean ,view = com.android.internal.widget.NotificationActionListLayout{a29ce6c G.E...... ......ID 0,0-0,0 #102018d android:id/actions} ,value = false
2010-01-03 04:33:15.360 966-966/? D/RemoteViews: methodName = setVisibility , param = int ,view = android.widget.FrameLayout{439fc35 G.E...... ......I. 0,0-0,0 #102018e android:id/actions_container} ,value = 8
由于原理的理解重点还是在反射那一块,因此暂不深入。
整个流程看下来,remoteview无法直接通过findview获取到view从而进行操作,需要使用action。
参考链接:
Android开发艺术探索 第5章 理解RemoteViews 读书笔记
Android中的RemoteViews
Android通知及RemoteViews整理
Android View - RemoteViews