RemoteViews
RemoteView是一種遠程View,可以在其他進程中顯示。RemoteView在安卓中的使用主要有兩種:通知欄和桌面小部件。通知欄主要由NotifycationManager實現,桌面小部件主要由AppWidgetProvider實現,AppWidgetProvide其實是一個廣播。二者都運行在其他進程中,即SystemServer進程中。
通知欄
在開發中可以使用自定義佈局的通知欄時需要使用RemoteView:
NotificationCompat.Builder builder1 = new NotificationCompat.Builder(this, Notification.CATEGORY_PROMO)
.setSmallIcon(R.mipmap.ic_launcher)
.setTicker("helloWord")
.setWhen(System.currentTimeMillis());
Intent mIntent = new Intent(this, NotifyDetailActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, requestCode, mIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Notification notification = builder1.build();
RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.layout_define_notify);
remoteViews.setTextViewText(R.id.tv_title, "nihao");
remoteViews.setTextViewText(R.id.tv_content, "nihao");
remoteViews.setOnClickPendingIntent(R.id.open, pendingIntent);
notification.contentView = remoteViews;
notification.contentIntent = pendingIntent;
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(1, notification);
RemoteView的創建主要由包名和佈局文件決定。
桌面小部件
新建一個類繼承AppWidgetProvider,需要在清單文件中註冊,因爲AppWidget是一個廣播
public class MyAppWidgetProvide extends AppWidgetProvider {
在onReciver中創建一個小部件
@Override
public void onReceive(final Context context, Intent intent) {
super.onReceive(context, intent);
if (intent.getAction() == CLICK_ACTION) {
Toast.makeText(context, "click", Toast.LENGTH_SHORT).show();
new Thread(new Runnable() {
@Override
public void run() {
Bitmap srcbBit = BitmapFactory.decodeResource(context.getResources(), R.mipmap.bg_clear);
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
for (int i = 0; i < 37; i++) {
float degree = (i * 10) % 360;
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget);
remoteViews.setImageViewBitmap(R.id.imageView, rotateBitmap(context, srcbBit, degree));
Intent mIntent = new Intent();
mIntent.setAction(CLICK_ACTION);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, mIntent, 0);
remoteViews.setOnClickPendingIntent(R.id.imageView, pendingIntent);
appWidgetManager.updateAppWidget(new ComponentName(context, MyAppWidgetProvide.class), remoteViews);
SystemClock.sleep(300);
}
}
}).start();
}
}
RemoteView的內部機制
RemoteView沒有提供findViewByid的方法獲取View,但是提供了一系列的set方法例如setTextViewText等方法更新Ui,大部分的set方法是根據反射獲取的。
通知欄和桌面小部件是通過NotifycationManager和AppWidgetManager管理的,這二者又通過Binder和SystemServer中NotifycationManagerService和AppWidgetManagerService通信,這就構成了進程間的通信,但是系統並沒有通過Binder去支持所有的View和View的更新,而是將所有的View的操作放在了一個Action類中,當每次調用setXX方法時RemoteViews就會添加一個對應Action,當View進行更新操作時,就會將Action對象發送到遠程,遠程進程則會調用RemotesView的apply方法進行View的更新,最終View的更新操作則是由Action內部的apply方法控制的。
本文參考《安卓開發藝術探索》。