1 RemoteViews的两种应用
1 桌面widget小部件( 使用戳这里)
2 通知栏通知信息
2 RemoteViews应用之通知栏信息
2.1 通知栏通知示例
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
remoteViews = new RemoteViews(getPackageName(), R.layout.remoteviews_item_dl);
remoteViews.setTextViewText(R.id.tv_filename, getPackageName()); // 设置标题
// remoteViews.setTextViewText(R.id.tv_content, "this is notification"); // 设置内容
Intent btIntent = new Intent();
btIntent.setAction("com.yu.sendnotification.action_dl");
PendingIntent btPendingIntent = PendingIntent.getBroadcast(this, 1, btIntent, PendingIntent.FLAG_CANCEL_CURRENT); // 发送广播意图
remoteViews.setOnClickPendingIntent(R.id.bt_stop, btPendingIntent); // 设置点击事件
Intent intent = new Intent(this, NewActivity.class);
PendingIntent pi = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
noti = new Notification.Builder(context)
.setTicker("Hello notification")
.setWhen(System.currentTimeMillis())
.setAutoCancel(true)
.setContent(remoteViews)
.setContentIntent(pi).build();
manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
manager.notify(0, noti);
}
2.2 通知栏通知使用步骤
1 通过Notification.Builder创建一个builder对象
Notification.Builder builder = new Notification.Builder(context);
2 给builder设置相关选项
setContent(remoteViews) // 设置RemoteViews setContentIntent(intent) // 设置view的intent事件 setContentText() // 设置内容文字 setContentTitle() //设置标题文字 setAutoCancel(bool) //设置自动取消 setSmallIcon(). //设置小图标 setTicker(CharSequence tickerText) // 设置新消息状态栏滚动文字 setDeleteIntent(PendingIntent intent) // 设置用户滑动删除通知intent setProgress(int max,int progress,boolean indeterminate) //设置进度 setWhen(long when) // 设置发送时间 setSound(Uri sound):设定一个铃声,用于在通知的时候响应。传递一个Uri的参数,格式为“file:///mnt/sdcard/Xxx.mp3”。 setLights(int argb, int onMs, int offMs):设定前置LED灯的闪烁速率,持续毫秒数,停顿毫秒数。 setVibrate(long[] pattern):设定震动的模式,以一个long数组保存毫秒级间隔的震动。 setDefaults(int)方法设定默认响应参数,在Notification中,对它的参数使用常量定义了,我们只需使用即可: --DEFAULT_ALL:铃声、闪光、震动均系统默认。 --DEFAULT_SOUND:系统默认铃声。 --DEFAULT_VIBRATE:系统默认震动。 --DEFAULT_LIGHTS:系统默认闪光。 需要权限 <!-- 闪光灯权限 --> <uses-permission android:name="android.permission.FLASHLIGHT"/> <!-- 振动器权限 --> <uses-permission android:name="android.permission.VIBRATE"/>
3 builder.build() 创建一个Notification对象
4 获取NotificationManger并发送通知
manager=(NotificationManager)=getSystemService(Context.NOTIFICATION_SERVICE); manager.notify(0, noti);
例子Demo:使用RemoteView发送通知栏消息,模拟下载进度
3 了解PendingIntent
PendingIntent表示即将发生的意图,在RemoteViews的应用中起着非常关键的作用。
3.1 PendingIntent作用
1) intent是即时发生的意图,PendingIntent不确定发生时间的意图
2) 用于给RemoteViews添加点击事件
3) PendingIntent支持启动Activity、启动Service,发送广播三种意图
通过PendingIntent中如下静态方法获取对应pending意图:
方法名称 | 描述 |
---|---|
getActivity(Context context, int requestCode, Intent intent, int flags) | 获得一个PendingIntent,该待定意图发生时,作用相当于Context.startActivity(Intent) |
getService(Context context, int requestCode, Intent intent, int flags) | 获得一个PendingIntent,该待定意图发生时,作用相当于 Context.startService(Intent) |
getBroadcast(Context context, int requestCode, Intent intent, int flags) | 获得一个PendingIntent,该待定意图发生时,作用相当于Context.sendBroadcast(Intent) |
以上三种方法只是一个例举,还有其他类似方法与重载方法可供使用。例如,getActivity方法有多个重载方法,并且还有getActivities方法可供使用。
3.2 三种意图参数
(Context context, int requestCode, Intent intent, int flags)
requestCode表示请求码Private request code for the sender
flags标志,有4种
intent 意图
3.3 flags参数的意义
1 FLAG_CANCEL_CURRENT:如果当前系统中已经存在一个相同的PendingIntent对象,
那么就将先将已有的PendingIntent取消,然后重新生成一个PendingIntent对象。2 FLAG_NO_CREATE:如果当前系统中不存在相同的PendingIntent对象,
系统将不会创建该PendingIntent对象而是直接返回null。3 FLAG_ONE_SHOT:该PendingIntent只作用一次。在该PendingIntent对象通过send()方法触发过
后,PendingIntent将自动调用cancel()进行销毁,那么如果你再调用send()方法的话,系统将会返回一个SendIntentException。4 FLAG_UPDATE_CURRENT:如果系统中有一个和你描述的PendingIntent对等的PendingInent,
那么系统将使用该PendingIntent对象,但是会使用新的Intent来更新之前PendingIntent中的Intent对象数据,
例如更新Intent中的Extras。
3.4 PendingInent的匹配规则
两个PendingInent相同条件:requestCode与Intent都相同
Intent相同指Component与intent-filter相同(与Extras无关)
3.5 id与flags对通知更新的影响
manager.notify(id, notification)发通知
1、当id相同时,后面的通知覆盖原来
2、当id不同时
--当PendingIntent不匹配时, 通知间不互相干扰,不受flags影响
--当PendingIntent匹配时,按flags分析
*使用FLAG_ONE_SHOT:后续的PendingIntent会和第一条保持一致,包括Extras,单击任意一条后,其他无法打开
*使用FLAG_CANCEL_CURRENT:只有最新的通知可以打开
*使用FLAG_UPDATE_CURRENT:之前弹出的通知中的PendingIntent都会被更新,包括Extras,并且通知都可以打开
4 RemoteViews的内部原理
4.1 常用构造函数
public RemoteViews(String packageName, int layoutId)
4.2 支持的View类型 (不支持自定义View类型)
layout:
FrameLayout LinearLayout RelativeLayout GridLayout
View:
AnalogClock button Chronometer ImageButton ImageView ProgressBar TextView ViewFlipper ListView GridView
4.3 常用set方法(跨进程显示view,无法使用findViewById方法)
- setTextViewText(int viewId,CharSequence text) 设置TextView的文本内容 第一个参数是TextView的id 第二个参数是设置的内容
- setTextViewTextSize(int viewId,int units,float size) 设置TextView的字体大小 第二个参数是字体的单位
- setTextColor(int viewId,int color) 设置TextView字体颜色
- setImageViewResource(int viewId,int srcId) 设置ImageView的图片
- setInt(int viewId,String methodName,int value) 反射调用View对象的参数类型为Int的方法 比如上述的setImageViewResource的方法内部就是这个方法实现 因为srcId为int型参数
- setLong setBoolean 类似于setInt
- setOnClickPendingIntent(int viewId,PendingIntent pendingIntent) 添加点击事件的方法,事件类型只能为PendingIntent
还有很多方法,大部分是通过反射调用View的方法。反射不了解的可以猫一眼Java反射机制
4.4 RemoteVews内部机制
4.4.1运行环境
- NotificationManger与AppWidgetManger通过Binder分别和运行于SystemServer的NotificationMangerService与AppWidgetMangerService进行通信
- 通知栏和桌面小部件的布局分别在NotificationMangerService与AppWidgetMangerService被加载(两者运行于SystemServer)
如下图所示
4.4.2 执行流程
1 RemoteViews通过Binder传递至SystemServer进程(因为RemoteViews实现了Parcelable接口,因此它可以跨进程传输)
2 系统根据RemoteViews中的packageName, layoutId去得到该应用的资源,之后通过LayoutInflater去加载RemoteViews中的布局文件 (在SystemServer中加载的是一个普通的View,只不过相对于我们的进程他是一个RemoteView而已)
3 系统对View界面执行一系列的更新操作,即通过set方法提交的,但更新操作不是立即执行(在RemoteViews内部会记录所有的更新操作),而是到RemoteViews被加载后执行
4 RemoteViews显示后,再次更新时,则调用set方法并通过NotificationManger与AppWidgetManger来提交更新任务,具体更新操作在SystemServer中完成
5 每一次的set操作对应着一个action (对应着一个view操作,实现了Parcelable接口),RemoteViews会添加一个对应的action对象 ,当这些更新操作被提交时,这些action会被跨进程传输到远程进程中并在远程进程执行。远程进程通过RemoteViews的apply方法进行view的更新操作(apply内部回去遍历所有的action并执行action的apply方法),通知栏和桌面小插件在初始化界面时调用apply,后续更新界面会调用reapply。如图示 :
4.5 单击事件的使用
RemoteViews中只支持发起PendingIntent,不支持onClickListener模式
例: Intent btIntent = new Intent(); btIntent.setAction("com.yu.sendnotification.action_dl"); PendingIntent btPendingIntent = PendingIntent.getBroadcast(this, 1, btIntent, PendingIntent.FLAG_CANCEL_CURRENT); remoteViews.setOnClickPendingIntent(R.id.bt_stop, btPendingIntent);//给id为stop的view设置点击事件
setPendingIntentTemplatey与setOnClickFillInIntent须结合,给ListView和StackView中的item添加点击事件
4.6 使用RemoteViews的好处
- 1.不需要定义大量的Binder接口
- 2.通过在远程进程中批量执行RemoteViews的修改操作从而避免了大量的IPC操作这就提高了程序性能
- 3.只需要操作RemoteViews即可实现效果,操作简单
5 AIDL和RemoteView的应用场景
1 一个应用简单的更新另一个应用的界面,可以选择AIDL去实现。但对界面的更新比较频繁,这个时候就会有效率问题,同时AIDL接口就有可能使问题复杂化,此时不宜使用aidl
2 对于界面更新比较频繁,适宜采用RemoteView来实现就,当然remoteView也有点缺点,remoteView仅支持一些常见的View,不支持自定View
后记:此篇为总结篇,作了个人梳理。参照了安卓开发艺术探索相关内容。