第五章、理解RemoteView

RemoteView在Android中的使用场景有两种:通知栏和桌面小部件。

  1. RemoteViews的应用
    RemoteViews在实际开发中,主要用在通知栏和桌面小部件的开发过程中。主要是通过NotificationManager的notify方法实现的。桌面小部件则是通过AppWidgetProvider来实现的。AppWidgetProvider本质上是一个广播。

    1. RemoteViews在通知栏上的应用
    2. RemoteViews在桌面小部件上的应用
      步骤:

      1. 定义小部件界面

        <?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>
      2. 定义小部件配置信息

        <?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" ></appwidget-provider>
      3. 定义小部件的实现类

        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));                        Intent intentClick = new Intent();                        intentClick.setAction(CLICK_ACTION);                        PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intentClick, 0);                        remoteViews.setOnClickPendingIntent(R.id.imageView1, pendingIntent);                        appWidgetManager.updateAppWidget(new ComponentName(context, MyAppWidgetProvider.class),remoteViews);                        SystemClock.sleep(30);                    }                }            }).start();        }    }    /**     * 每次窗口小部件被点击更新都调用一次该方法     */    @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);        }    }    /**     * 窗口小部件更新     */    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);        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);        return Bitmap.createBitmap(srcbBitmap, 0, 0, srcbBitmap.getWidth(), srcbBitmap.getHeight(), matrix, true);    }}
      4. 在AndroidManifext.xml中声明小部件

        <receiver android:name=".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>

    3. PendingIntent概述

      1. PendingIntent含义:
        PendingIntent表示一种处于pending状态的意图,而pending表示的是一种待定、等待、即将发生的意思,就是说接下来有一个Intent将在某个特定的时刻发生。
      2. PendingIntent和Intent区别:
        PendingIntent是将来在某个时刻发生,而Intent是立即发生。
      3. 使用场景:
        PendingIntent典型的使用场景是给RemoteViews添加单击事件,因为RemoteViews是运行在远程中,因此RemoteViews不同于普通的View,所以无法直接像View那样通过setOnClickListener方法来设置单击事件。要想给RemoteViews设置单击事件,就必须使用PendingIntent,PendingIntent通过send和cancel方法来发送和取消特定的待定intent。
      4. PendintIntent支持三种特定意图:
        1. 启动Activity:
          getActivity(Context context,Intent intent,int requestCode,int flags)
        2. 启动Service:
          getService(Context context,Intent intent,int requestCode,int flags)
        3. 发送广播:
          getBroadcast(Context context,Intent intent,int requestCode,int flags)
      5. PendingIntent的匹配规则:
        requestCode表示PendingIentent发送方的请求码,多情况下设为0即可。requestCode会影响到flags效果。
        1. 如果两个PendingIntent它们内部的Intent相同并且requestCode也相同,那么这两个PendingIntent就是相同的。什么是Intent相同?intent的匹配规则是:如果两个Intent的ComponentName和intent-filter都相同,那么这两个Intent就是相同的。注意Extras不参与Intent的匹配过程。
        2. flag参数含义:
          1. FLAG_ONE_SHOT
            当前描述的PendingIntent只能被使用一次,然后它就会被自动cancel。
          2. FLAG_NO_CREATE
            当前描述的PendingIntent不会主动创建。日常开发中并没有太多的使用意义。
          3. FLAG_CANCEL_CURRENT
            当前描述的PendingIntent如果已经存在,那么它们都会被cancel,然后系统会创建一个新的PendingIntent。对于通知栏消息来说,那些被cancel的消息单击后将无法打开。
          4. FLAG_UPDATE_CURRENT
            当前描述的PendingIntent如果已经存在,那么它们都会被更新。即它们的Intent中的Extras会被替换成最新的。
        3. NotificationManager.nofify(id, notification) 几种情况:
          1. 如果notify第一个参数id是常量,那么不管PendingIntent是否匹配,后面的通知会直接替换前面的通知。
          2. 如果notify方法的id每次都不同:
            1. 当PendingIntent不匹配时(Intent相同并且requestCode相同),这种情况性爱不管采用何种标记位。这些通知之间不会相互干扰。
            2. 如果PendingIntent处于匹配状态:
              1. 如果采用FLAG_ONE_SHOT标记位,那么后续通知中PendingIntent会后第一条通知保持一致。包括Extras,单机任何一条通知后,剩下的通知均无法再打开,当所有的通知都被消除后,会再次重复这个过程
              2. 如果采用FLAG_CANCEL_CURRENT标记位,那么只有最新的通知可以打开,之前弹出的通知均无法打开
              3. 如果采用了FALG_UPDATE_CURRENT标记位,那么之前弹出的通知栏中的PendingIntent给被更新,最终它们和最新的一条通知保持完全一致,包括Extras。
  2. RemoteViews内部机制:

    1. RemoteViews常用构造方法:public RemoteViews(String packageName,int layoutIedd),它接收两个参数,第第一个表示当前应用的包名,第一个表示待加载的布局文件。

    2. RemoteViews只支持部分布局和View组件,下面列举的组件的子类是不支持的 :
      布局:FrameLayout、LinearLayout、RelativeLayout、GridLayout
      组件:Button、ImageButton、ImageView、TextView、ListView、GridView、ViewStub等

    3. RemoteViews没有提供findViewById方法,因此无法直接访问里面的View元素。必须通过RemoteViews所提供的一些列set方法来完成。

    4. 通知栏和桌面小部件分由:NotificationManager和AppWidgetManager管理。NotificationManager和AppWidgetManager通过Binder分别和SystemServer进程中的NotificationManagerService以及,AppWidgetService进行通信。由此可见,通知栏和桌面小部件中的布局文件实际上是在NotificationManagerService以及AppWidgetService中被加载的,而它们运行在系统的SystemServer中,这就和我们的进程构成了跨进程构成了跨进程通信的场景。

    5. RemoteViews实现了Parcelable接口,通过Binder传递到SystemServer进程,系统根据RemoteViews中使用的包名等信息去得到该应用的资源。然后加载布局文件。

    6. 系统会对View执行一些列界面更行任务。这些任务是通过之前的set方法提交的。RemoteViews会记录这些更新操作,RemoteViews被加载后,这些更新操作才会被执行。

    7. 系统将View操作封装到Action对象并将这些对象跨进程传输到远程进程,接着在远程进程中执行Action对象中的具体操作。

    8. 我们在应用中每调用一次set方法,RemoteViews中就会添加一个对应的Action对象,当我们提交更新时,遍历调用他们它们的apply方法来进行View的更新操作。

    9. apply和reApply的区别在于:apply会加载布局并更新界面。通知栏和桌面小插件在初始化界面时会调用apply方法,而在后续更新界面时调用reapply方法。

    10. setOnClickPendingIntent、setPendingIntentTemplate、setOnClickFillInIntent区别和联系:

      1. setOnClickPendingIntent用于给普通View设置单击事件,但是不能给集合(ListView)中的View设置单击事件,因为开销比较大,所以禁止这种方式。
      2. 如果要给ListView和StackView中的item添加单击事件,则必须将setPendingInntentTemplate和setOnClickFillInIntent组合使用才可以。
  3. RemoteViews意义
    实现了跨进程UI更新。

更多相关文章

  1. android初学常见问题[持续更新]
  2. android检测新版本并下载安装的方法
  3. 【Android】【UI】8.0以上版本Notification的使用
  4. Android(安卓)SDK更新后提示This version of ADT requires the..
  5. Android(安卓)Notification使用系统通知栏布局出现的图标问题
  6. Android(安卓)4.4 Kitkat Phone工作流程浅析(九)__状态通知流程
  7. 搭建andorid开发环境
  8. 如何把批量数据导入到android 的 sqlite 数据库(更新中)
  9. 【原】Android多任务下载,使用Notification更新进度条

随机推荐

  1. java/android String和List的区别
  2. [Android] 收录几个比较火的安卓网
  3. android DexClassLoader动态加载技术详解
  4. android 中.9图片的用法 详解
  5. [置顶] 了解Android微信里的WebView是如
  6. Android蓝牙开发中电话音频(HSP,HFP)和媒体
  7. Android平板份额紧逼苹果 有望超越iPad
  8. 【转】UML建模與Android應用程式開發(上)
  9. 【Android归纳决】阿里笔试题之Android网
  10. 2019 BAT大厂 Android社招最全面试题(面试