1.RemoteViews的作用
在其他進程中顯示並更新view界面,所謂跨進程是因爲view界面是運行在系統的SystemServer進程的。系統除了常見的notification和appwidget也就是通知欄和桌面小部件,notificatio是通過notificationmanager.notify方法來實現的,appwidget則是通過appwidgetProvider來實現的appwidgetProvider本質上就是個廣播。
其實我們也可以通過廣播來模擬發送通知欄消息。
2.RemoteViews在通知欄上的應用
2.1典型用法如下,在android8.0以後通知欄的使用有些變化:
Intent intent = new Intent(this, DemoActivity_2.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,
0, intent, PendingIntent.FLAG_ONE_SHOT);
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
int channelId = 0x22222;
Notification.Builder builder;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(String.valueOf(channelId), "chanel_name", NotificationManager.IMPORTANCE_HIGH);
manager.createNotificationChannel(channel);
builder = new Notification.Builder(this, String.valueOf(channelId));
} else {
builder = new Notification.Builder(this);
}
builder.setContentIntent(pendingIntent);
builder.setTicker("new message")
.setSmallIcon(R.drawable.ic_launcher_background)
.setContentTitle("標題")
.setContentText("內容")
.setContentIntent(pendingIntent);
manager.notify(sId, builder.build());
3.RemoteViews在桌面小部件上的應用
定義桌面小部件也就是remoteviews的佈局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<ImageView
android:id="@+id/imageView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/icon1" />
</LinearLayout>
定義桌面小部件配置信息:
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:initialLayout="@layout/widget"
android:minHeight="84dp"
android:minWidth="84dp"
android:updatePeriodMillis="86400000" >
<!--initialLayout:初始佈局-->
<!--minHeight + minWidth:尺寸-->
<!--updatePeriodMillis:更新週期,單位毫秒-->
</appwidget-provider>
桌面小部件實現類:
public class MyAppWidgetProvider extends AppWidgetProvider {
public static final String TAG = "MyAppWidgetProvider";
public static final String CLICK_ACTION = "com.ryg.chapter_5.action.CLICK";
public MyAppWidgetProvider() {
super();
}
@Override
public void onReceive(final Context context, Intent intent) {
super.onReceive(context, intent);
Log.i(TAG, "onReceive : action = " + intent.getAction());
// 這裏判斷是自己的action,做自己的事情,比如小工具被點擊了要幹啥,這裏是做一個動畫效果
if (intent.getAction().equals(CLICK_ACTION)) {
Toast.makeText(context, "clicked it", Toast.LENGTH_SHORT).show();
new Thread(new Runnable() {
@Override
public void run() {
Bitmap srcbBitmap = BitmapFactory.decodeResource(
context.getResources(), R.drawable.icon1);
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.imageView1,
rotateBitmap(context, srcbBitmap, degree));
appWidgetManager.updateAppWidget(new ComponentName(
context, MyAppWidgetProvider.class),remoteViews);
SystemClock.sleep(30);
}
}
}).start();
}
}
/**
* 最常用的onUpdate方法
* 桌面小部件被添加或者更新時,會走到這裏
*/
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
super.onUpdate(context, appWidgetManager, appWidgetIds);
Log.i(TAG, "onUpdate");
final int counter = appWidgetIds.length;
Log.i(TAG, "counter = " + counter);
for (int i = 0; i < counter; i++) {
int appWidgetId = appWidgetIds[i];
onWidgetUpdate(context, appWidgetManager, appWidgetId);
}
}
/**
* 窗口小部件更新
*
* @param context
* @param appWidgeManger
* @param appWidgetId
*/
private void onWidgetUpdate(Context context,
AppWidgetManager appWidgeManger, int appWidgetId) {
Log.i(TAG, "appWidgetId = " + appWidgetId);
RemoteViews remoteViews = new RemoteViews(context.getPackageName(),
R.layout.widget);
// "窗口小部件"點擊事件發送的Intent廣播
Intent intentClick = new Intent();
intentClick.setAction(CLICK_ACTION);
//8.0以上版本必須寫
intentClick.setComponent(new ComponentName(context,MyAppWidgetProvider.class));
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0,
intentClick, 0);
remoteViews.setOnClickPendingIntent(R.id.imageView1, pendingIntent);
appWidgeManger.updateAppWidget(appWidgetId, remoteViews);
}
private Bitmap rotateBitmap(Context context, Bitmap srcbBitmap, float degree) {
Matrix matrix = new Matrix();
matrix.reset();
matrix.setRotate(degree);
Bitmap tmpBitmap = Bitmap.createBitmap(srcbBitmap, 0, 0,
srcbBitmap.getWidth(), srcbBitmap.getHeight(), matrix, true);
return tmpBitmap;
}
在manifest中聲明桌面小部件
<receiver android:name="com.ryg.chapter_5.MyAppWidgetProvider" >
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/appwidget_provider_info" >
</meta-data>
<intent-filter>
<action android:name="com.ryg.chapter_5.action.CLICK" />
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
</receiver>
4.notificationId的意義,pengdingIntent需要注意的地方?
4.1notificationId常量與變量
如果notificationId是常量多次調用notify則永遠只能彈出一個通知,後面的通知會把前面的通知完全覆蓋掉
如果notificationId是變量每次都不同,這時候如果pendingintent不匹配每次的通知是互不干擾的,如果pendingintent匹配(requestcode和intent都相同)這時候就跟pendingintent的標記位有關係了:
4.2pendingintent標誌位
FLAG_ONE_SHOT(和第一條通知保持一致)
FALG_CANCEL_CURRENT(只有最新的這條通知可以打開,之前的都是沒法打開的)
FLAG_UPDATE_CURRENT(之前的所有通知都會和最新的這一條保持一致)
5.RemoteViews的內部機制,RemoteViews支持哪些view類型,裏面的view是怎麼賦值的?
notificationmanager和appwidgetmanager通過binder跨進程和systemServer進程中的的notificationmanagerService和appwidgetService進行通信,也就是說remoteview其實是在notificationmanagerService和appwidgetService被加載出來的。當remoteviews調用setText
ViewText等方法的時候,remoteviews中就會添加一個Action對象,最終會形成一個actionList,當remoteviews在systemserver進程中調用apply方法時會遍歷actionLsit來更新view。
6.自動模式實現通知欄消息
發送;
RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.layout_simulated_notification);
remoteViews.setTextViewText(R.id.msg, "msg from process:" + Process.myPid());
remoteViews.setImageViewResource(R.id.icon, R.drawable.icon1);
PendingIntent pendingIntent = PendingIntent.getActivity(this,
0, new Intent(this, DemoActivity_1.class), PendingIntent.FLAG_UPDATE_CURRENT);
PendingIntent openActivity2PendingIntent = PendingIntent.getActivity(
this, 0, new Intent(this, DemoActivity_2.class), PendingIntent.FLAG_UPDATE_CURRENT);
remoteViews.setOnClickPendingIntent(R.id.item_holder, pendingIntent);
remoteViews.setOnClickPendingIntent(R.id.open_activity2, openActivity2PendingIntent);
Intent intent = new Intent(MyConstants.REMOTE_ACTION);
intent.putExtra(MyConstants.EXTRA_REMOTE_VIEWS, remoteViews);
sendBroadcast(intent);
模擬接收:
private BroadcastReceiver mRemoteViewsReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
RemoteViews remoteViews = intent
.getParcelableExtra(MyConstants.EXTRA_REMOTE_VIEWS);
if (remoteViews != null) {
updateUI(remoteViews);
}
}
};
private void updateUI(RemoteViews remoteViews) {
// View view = remoteViews.apply(this, mRemoteViewsContent);
int layoutId = getResources().getIdentifier("layout_simulated_notification", "layout", getPackageName());
View view = getLayoutInflater().inflate(layoutId, mRemoteViewsContent, false);
remoteViews.reapply(this, view);
mRemoteViewsContent.addView(view);
}