完全理解Android中的RemoteViews
一、什么是RemoteViews
RemoteViews翻译过来就是远程视图.顾名思义,RemoteViews不是当前进程的View,是属于SystemServer进程.应用程序与RemoteViews之间依赖Binder实现了进程间通信.
二、RemoteViews的用法
RemoteViews使用最多的场合是通知栏和桌面小插件. 以通知栏为例,讲解下它的用法.
1、新建一个Notification
这里要注意是在android3.0之前都是使用如下的形式构建一个Notification
// 1.新建一个Notification对象 Notification mNotification = new Notification(); // 2.添加属性,比如标题、内容、优先级、图片等 mNotification.tickerText = "这是通知栏的标题"; mNotification.icon = R.drawable.ic_launcher; mNotification.flags=Notification.FLAG_NO_CLEAR; mNotification.setLatestEventInfo(this, "这是内容", "这是标题", null);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
在3.0之后官方推荐使用建造者模式创建Notification.
Notification mNotification = new Notification.Builder(this) .setContentTitle("这是标题 ") .setContentText("这是内容") .setSmallIcon(R.drawable.ic_launcher) .build();
- 1
- 2
- 3
- 4
- 5
Notification有很多属性,这里列举一些
- setContentTitle 设置标题 - setContentText 设置内容 - setLargeIcon 设置通知栏大图标 - setSmallIcon 设置通知栏小图标 - setContent 设置RemoteViews - setContentIntent 当通知条目被点击,就执行这个被设置的Intent. - setDeleteIntent 当用户点击"Clear All Notifications"按钮区删除所有的通知的时候,这个被设置的Intent被执行 - setLights 设置闪光灯 - setSound 设置声音 - setPriority 设置优先级
2、设置Notification的RemoteViews
如果要给通知栏使用自定义布局就要使用RemoteViews了,传入包名和相应的布局.
RemoteViews mRemoteViews=new RemoteViews("com.example.remoteviewdemo", R.layout.remoteview_layout);
- 1
然后通过setContent()传入RemoteViews 对象即可.
这里顺便讲一下PendingIntent,PendingIntent是”延迟意图”的意思,就是当满足某一条件时出触发这个Intent.通过PendingIntent的getActivity、getBroadcast、getService等分别构建一个打开对应组件的延迟Intent.
传入四个参数,context、intent、requestCode(自定义)、flag.
Intent intent=new Intent(MainActivity.this,MainActivity.class); PendingIntent mPendingIntent=PendingIntent.getActivity(MainActivity.this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
- 1
- 2
PendingIntent有4种flag.
- FLAG_ONE_SHOT 只执行一次 - FLAG_NO_CREATE 若描述的Intent不存在则返回NULL值 - FLAG_CANCEL_CURRENT 如果描述的PendingIntent已经存在,则在产生新的Intent之前会先取消掉当前的 - FLAG_UPDATE_CURRENT 总是执行,这个flag用的最多
3、获取通知管理者
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
- 1
4、弹出通知
调用notify方法,传入一个id(自定义)和通知实例即可.
manager.notify(1, mNotification);
- 1
5、例子
我用一个按钮弹出通知,点击这个通知时进入到该Activity
public class MainActivity extends Activity { private NotificationManager manager; private Notification mNotification; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //1.创建RemoteViews实例 RemoteViews mRemoteViews=new RemoteViews("com.example.remoteviewdemo", R.layout.remoteview_layout); //2.构建一个打开Activity的PendingIntent Intent intent=new Intent(MainActivity.this,MainActivity.class); PendingIntent mPendingIntent=PendingIntent.getActivity(MainActivity.this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); //3.创建一个Notification mNotification = new Notification.Builder(this) .setSmallIcon(R.drawable.ic_launcher) .setContentIntent(mPendingIntent) .setContent(mRemoteViews) .build(); //4.获取NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); Button button1 = (Button) findViewById(R.id.button1); button1.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { //弹出通知 manager.notify(1, mNotification); } }); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
如下图
6、改变RemoteViews的布局
RemoteViews并不能直接获得控件实例,然后对控件进行操作.它提供了
setTextViewText(viewId, text)、setImageViewResource(viewId, srcId)等方法进行操作,传入控件id和相应的修改内容.
列举一下常用的属性
- setTextViewText(viewId, text) 设置文本 - setTextColor(viewId, color) 设置文本颜色 - setTextViewTextSize(viewId, units, size) 设置文本大小 - setImageViewBitmap(viewId, bitmap) 设置图片 - setImageViewResource(viewId, srcId) 根据图片资源设置图片 - setViewPadding(viewId, left, top, right, bottom) 设置Padding间距 - setOnClickPendingIntent(viewId, pendingIntent) 设置点击事件
我这里就以setTextViewText改变文本的属性来讲解改变RemoteViews的原理.
我在原来的代码上加上一个按钮点击改变内容
Button button2 = (Button) findViewById(R.id.button2); button2.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { mRemoteViews.setTextViewText(R.id.remote_content, "改变了内容"); manager.notify(1, mNotification); } });
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
看下效果
三、RemoteViews的改变原理
1.setTextViewText方法代码如下
public class RemoteViews implements Parcelable, Filter { ...... public void setTextViewText(int viewId, CharSequence text) { setCharSequence(viewId, "setText", text); } ...... }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
2.调用了setCharSequence方法
public class RemoteViews implements Parcelable, Filter { ...... public void setCharSequence(int viewId, String methodName, CharSequence value) { addAction(new ReflectionAction(viewId, methodName, ReflectionAction.CHAR_SEQUENCE, value)); } ...... }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
3.在setCharSequence方法里调用了addAction方法,传入一个ReflectionAction实例,ReflectionAction继承自Action,它是用反射调用的
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; } ...... }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
4.看下addAction方法,用了一个集合来保存Action实例,然后更新已使用内存的统计情况
public class RemoteViews implements Parcelable, Filter { ...... private void addAction(Action a) { if (mActions == null) { mActions = new ArrayList<Action>(); } //添加Action mActions.add(a); // 更新已使用内存的统计情况 a.updateMemoryUsageEstimate(mMemoryUsageCounter); } ...... }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
这一步之后,会调用
manager.notify(1, mNotification);
- 1
来更新,追踪这个notify方法.
public class NotificationManager { ...... public void notify(String tag, int id, Notification notification) { ...... INotificationManager service = getService(); try { service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id, stripped, idOut, UserHandle.myUserId()); ...... } ...... }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
5.上面会调用getService方法返回INotificationManager这个系统服务,它是在SystemServer进程添加的.然后该服务调用 enqueueNotificationWithTag方法最后层层调用到
public class NotificationManagerService extends INotificationManager.Stub { ...... StatusBarNotification n = new StatusBarNotification(pkg, id, tag, r.uid, r.initialPid, notification); try { mStatusBar.updateNotification(r.statusBarKey, n) } ...... }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
新建了StatusBarNotification实例,然后调用updateNotification方法.
这个方法会进入到
public class PhoneStatusBar extends StatusBar { ...... public void updateNotification(IBinder key, StatusBarNotification notification) { ...... final RemoteViews contentView = notification.notification.contentView; ...... contentView.reapply(mContext, oldEntry.content); ...... }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
会调用StatusBarNotification 的notification.contentView返回RemoteViews 对象,然后调用reapply方法.
6.回到RemoteViews 的reapply方法
public class RemoteViews implements Parcelable, Filter { ...... public void reapply(Context context, View v, OnClickHandler handler) { RemoteViews rvToApply = getRemoteViewsToApply(context); ...... rvToApply.performApply(v, (ViewGroup) v.getParent(), handler); } 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); //调用apply方法 a.apply(v, parent, handler); } } } ...... }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
最终调用apply方法,在这里加载新的布局,RemoteViews就是这么完成的.
public class RemoteViews implements Parcelable, Filter { ...... public View apply(Context context, ViewGroup parent, OnClickHandler handler) { RemoteViews rvToApply = getRemoteViewsToApply(context); View result; LayoutInflater inflater =(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); ...... //加载布局 result = inflater.inflate(rvToApply.getLayoutId(), parent, false); rvToApply.performApply(result, parent, handler); return result; } ...... }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
总结
RemoteViews运行在SystemServer进程,更新RemoteViews要通过Binder获取到对应的服务然后调用RemoteViews内部的apply方法加载更新布局.
完全理解Android中的RemoteViews的更多相关文章
- 【转】Android菜单详解——理解android中的Menu--不错
原文网址:http://www.cnblogs.com/qingblog/archive/2012/06/08/2541709.html 前言 今天看了pro android 3中menu这一章,对A ...
- 深入理解Android中View
文章目录 [隐藏] 一.View是什么? 二.View创建的一个概述: 三.View的标志(Flag)系统 四.MeasureSpec 五.几个重要方法简介 5.1 onFinishInflate ...
- Android菜单详解(一)——理解android中的Menu
前言 今天看了pro android 3中menu这一章,对Android的整个menu体系有了进一步的了解,故整理下笔记与大家分享. PS:强烈推荐<Pro Android 3>,是我至 ...
- 深入理解Android中ViewGroup
文章目录 [隐藏] 一.ViewGroup是什么? 二.ViewGroup这个容器 2.1 添加View的算法 2.1.1 我们先来分析addViewInner方法: 2.1.2 addInArr ...
- 彻底理解 Android 中的阴影
如果我们想创造更好的 Android App,我相信我们需要遵循 Material Design 的设计规范.一般而言,Material Design 是一个包含光线,材质和投影的三维环境.如果我们想 ...
- 理解android中ListFragment和Loader
一直以来不知Android中Loader怎么用,今天晚上特意花了时间来研究,算是基本上搞明白了,现在把相关的注释和代码发出来,以便笔记和给网友一个参考,错误之处还望大家给我留言,共同进步,这个例子采用 ...
- 一个demo让你彻底理解Android中触摸事件的分发
注:本文涉及的demo的地址:https://github.com/absfree/TouchDispatch 1. 触摸动作及事件序列 (1)触摸事件的动作 触摸动作一共有三种:ACTION_DOW ...
- 绝对让你理解Android中的Context
这个问题是StackOverFlow上面一个热门的问题What is Context in Android? 整理这篇文章的目的是Context确实是一个非常抽象的东西.我们在项目中随手都会用到它,但 ...
- 理解Android中的注解与反射
反射 Java反射(Reflection)定义 Java反射机制是指在运行状态中 对于任意一个类,都能知道这个类的所有属性和方法:对于任何一个对象,都能够调用它的任何一个方法和属性: 这样动态获取新的 ...
随机推荐
- hiho一下第107周《Give My Text Back》
题目链接:传送门 题目大意:给你多组格式不标准的字符串句子,要你恢复到标准格式(单词中间只有一个空格或者一个逗号一个空格,句子的首字母大写其他小写,遇到回车也要换行) 题目思路:直接按题意模拟,注意几 ...
- cdr X6 64位32位缩略图补丁包
cdr X6 64位32位缩略图补丁包下载 安装了X6没有缩略图的话,点击下面链接下载安装插件即可 点击下载
- 七牛wordpress
当你看到柯南君的时候说明:我的七牛云图加速已经生效了. 准备工作: ①申请一个七牛账号并新增空间 ②到wordpress后台搜索qiniu并安装插件 在七牛后台找到AK和SK,配置wp后台七牛镜像存储 ...
- POJ 3037 Skiing(Dijkstra)
Skiing Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 4668 Accepted: 1242 Special ...
- nodemailer发送邮件各个服务器接口
来自:https://github.com/nodemailer/nodemailer-wellknown/blob/master/services.json#L125 { "1und1 ...
- delphi弹出信息框大全(转载)
1. 警告信息框 MessageBox(Handle,'警告信息框','警告信息框',MB_ICONWARNING); 2.疑问信息框 MessageBox(Handle,'疑问信息框','疑问信息框 ...
- Oracle存储——逻辑结构
Oracle 数存储——物理结构 Oracle存储结构:物理结构+逻辑结构 Oracle 数据库存储逻辑结构 Oracle Schema Objects(Schema Object Storage A ...
- 9.JavaScript简单计算器的实现
1.难点,怎么获取标签的值,注意点,获取到的值都是string类型,还要转换 var num1 = parseInt(document.getElementById("num1") ...
- event.preventDefault(); Please enter your name using lowercase letters only.
w 可以用于移动实际项目. 输入 android qq输入法 输入第一个字符“中”后 w PC 点击enter键13 空格键32 w 没有阻挡中午输入. CODE <!DOCTYPE html& ...
- 模块化之SeaJS(一)
模块化(之SeaJS) 刚接触的童鞋可能会有很多疑惑,比喻:什么是模块?模块的目的是干嘛呀?怎么样实现模块化呢? 不要急,博主正是带着这三个问题来写这篇文章的. 一,什么是模块化? 在前端开发领域,一 ...