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方法控制的。
本文参考《安卓开发艺术探索》。