android RemoteViews解析

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);
    }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章