完全理解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.
- 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改變文本的屬性來講解改變RemoteViews的原理.?
我在原來的代碼上加上一個按鈕點擊改變內容
- 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方法.?
這個方法會進入到
- 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方法加載更新布局.
轉載于:https://www.cnblogs.com/wxmdevelop/p/7099745.html
總結
以上是生活随笔為你收集整理的完全理解Android中的RemoteViews的全部內容,希望文章能夠幫你解決所遇到的問題。