android艺术开发探索之RemoteViews跨进程更新UI

RemoteViews表示的是一个view结构,它提供了一组基础的操作用于跨进程更新它的界面,常见的应用场景是通知栏和桌面小部件。在学习它之前我们先了解PendingIntent。

PendingIntent

PendingIntent表示一种处于pending状态的意图,是在将来的某个不确定的时刻发生,而Intent是立刻发生,PendingIntent是通过send和cancle方法来发送和取消待定的Intent.

PendingIntent的Flag

FLAG_NOE_SHOT
PendingIntent只能被调用一次,然后它会自动cancel,如果后续还有相同的PendingIntent,那么send方法会调用失败。
FLAG_NO_CREATE
PendingIntent不会主动创建如果当前PendingIntent之前不存在,那么getActivity、getService、getBroadcast方法会直接返回null,即获取PendingIntent失败。
FLAG_CANCEL_CURRENT
当前描述的PendingIntent已经存在,那么它们都会被cancel,然后系统会创建一个新的PendingIntent,对于那些被取消的通知单击后无法打开

FLAG_UPDATE_CURRENT
当前描述的PendingIntent如果已经存在,那么他们都会被更新,即他们的Intent的Extras会被替换成最新的。

PendingIntent的匹配规则

如果两个PendingIntent它们内部的Intent相同,并且requestCode也相同,那么这两个PendingIntent就是相同的。如果两个Intent的ComponentName和intent-filter都相同那么这两个Intent就是相同。

RemoteViews所支持的View

RemoteViews只支持几个set方法,没有提供findViewbyid,所以无法获取到RemoteViews的子view,同时它所支持的view也很少,有如下几个,除了一下View,其他的一概不支持:
Layout:
FrameLayout、LinearLayout、RelativeLayout、GridLayout
View:
AnalogClock,Button,Chronometer,ImageButton,ImageView,ProgressBar,TextView,ViewFlipper,ListView,GridView,StackView,AdapterViewFlipper,ViewSub

RemoteViews的工作原理

桌面小部件和通知都是由binder和SystemServer进程中的各自的服务进行通信的,这就构成了跨进程通信, RemoteViews实现了Parcelable,因此可以跨进程传输,当需要更新的时候就采用RemoteViews的一系列的set方法提交到SystemServer进程中的各自的服务去加载更新,大量的IPC操作会影响效率,因此一个view操作代表一个action,然后action实现了Parcelable,每调一次set方法, RemoteViews 就添加一个对应的action,当调用各自的manager提交的时候,这些action就会被传输到远程并在远程中依次执行,远程进程通过 RemoteViews的apply方法来进行view的更新,apply方法会遍历所有的action对象并调用它们的apply方法,避免了大量的IPC操作,提高了远程的性能。
RemoteViews的单击事件只支持发起PendingIntent,setOnClickPendingIntent用于给普通的view设置单击事件,但不能给Listview以及StackView这些控件的item设置点击事件,因为开销大系统禁用了这种方式,如果要给这些item设置则必须将setPendingIntentTemplate和setOnClickFillInIntent组合使用才可以。

RemoteViews的应用场景:

一个应用去更新另一个应用的中的某个界面,当更新频繁时这个时候有效率问题,可以采用RemoteViews和aidl实现,但是RemoteViews只支持常见的view.A中发广播并且把RemoteViews放在intent中传递到B中去,B中接受广播获取RemoteViews调用apply方法的到view并加以利用。但是RemoteViews实现两个应用之间的界面更新,如果将B中的布局文件的资源id传输到A中有可能是无效的。这时候需要两个应用提前约定好布局文件的名称,然后B中根据名称获取layoutId,然后根据layoutID找到view,然后调用reapply方法加载到B中,伪代码如下:
int layoutid=getResource().getIdentifier(“layoutid”,”layout”,getPackageName());
View view=getLayoutInfater().inflate(layoutid,mView,false);
remoteViews.reapply(this,view);
mView.addView(view);

RemoteViews在通知中的应用:

使用系统默认样式的通知 Notification.Builder builder=new Notification.Builder(this); builder.setSmallIcon(R.mipmap.ic_launcher); builder.setTicker("测试"); builder.setContentTitle("xxxx"); builder.setContentText("yyyy"); builder.setWhen(System.currentTimeMillis()); Intent intent=new Intent(this,MainActivity.class); PendingIntent pendingIntent=PendingIntent.getActivity(this,0,intent,PendingIntent.FLAG_UPDATE_CURRENT); builder.setContentIntent(pendingIntent); Notification notification= builder.build(); notification.flags=Notification.FLAG_AUTO_CANCEL; NotificationManager manager=(NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE); manager.notify(1,notification);
使用自定义的样式通知Notification.Builder builder=new Notification.Builder(this);builder.setSmallIcon(R.mipmap.ic_launcher);builder.setTicker("测试");builder.setContentTitle("xxxx");builder.setContentText("yyyy");builder.setWhen(System.currentTimeMillis());Intent intent=new Intent(this,MainActivity.class);PendingIntent pendingIntent=PendingIntent.getActivity(this,0,intent,PendingIntent.FLAG_UPDATE_CURRENT);builder.setContentIntent(pendingIntent);RemoteViews remoteViews=new RemoteViews(getPackageName(),R.layout.noti);remoteViews.setTextViewText(R.id.textView,"11111");remoteViews.setImageViewResource(R.id.imageView,R.mipmap.aa); //remoteViews.setOnClickPendingIntent(R.id.zy,pendingIntent);        builder.setContent(remoteViews);        Notification notification= builder.build();        notification.flags=Notification.FLAG_AUTO_CANCEL;        NotificationManager manager=(NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);        manager.notify(1,notification); 自定义桌面小控件 首先在layout下创建widght.xml,此为小控件的布局desk.x <?xml version="1.0" encoding="utf-8"?> "http://schemas.android.com/apk/res/android"     android:layout_width="match_parent"     android:id="@+id/zy"     android:layout_height="match_parent"     >     "wrap_content"         android:layout_height="wrap_content"         android:src="@mipmap/aa"         android:layout_alignTop="@+id/textView"         android:layout_toRightOf="@+id/textView"         android:layout_toEndOf="@+id/textView"         android:layout_marginLeft="52dp"         android:layout_marginStart="52dp"         android:id="@+id/imageView" /> 在res/xml下新建appwidget_provider_info.xml,添加如下"@layout/desk"    android:minHeight="84dp"    android:minWidth="84dp"    android:updatePeriodMillis="864000"    xmlns:android="http://schemas.android.com/apk/res/android">建一个类继承AppWidgetProvider类,实现onReceive,AppWidgetProvider的父类是一个广播类,因此需要在manifest注册。AppWidgetProvider除了常用的onUpdate还有其他的方法onEnabled、onDisable、onDeleted以及onReceive,这些方法会被onReceive合适的时间给分发掉。onEnabled:当该小部件第一次添加到桌面时调用该方法,可添加多次但只在第一次调用onUpdate:小部件被添加时或者每次更新时都会调用一次该方法,小部件的更新周期由updatePeriodMillis来指定,每个周期小部件会自动更新一次。onDeleted:每删除一次桌面小部件就调用一次。onDisabled:当最后一个该类型的桌面小部件被删除时调用该方法,注意是最后一个。onReceive:广播内置的方法,用于分发具体的事件给其他的方public class MyAppWidgetProvider extends AppWidgetProvider {    private static final String TAG="MyAppWidgetProvider";    private static final String Click_ACTION="site.zhangyun.appliction.Click";    @Override    public void onReceive(final Context context, Intent intent) {        super.onReceive(context, intent);        switch (intent.getAction()){            case Click_ACTION:                Toast.makeText(context,"2222", Toast.LENGTH_LONG).show();                new Thread(new Runnable() {                    @Override                    public void run() {                        Bitmap bitmap= BitmapFactory.decodeResource(context.getResources(), R.mipmap.aa);                        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.desk);                            remoteViews.setImageViewBitmap(R.id.imageView, rotateBitmap( bitmap,degree));                            Intent clickIntent=new Intent();                            clickIntent.setAction(Click_ACTION);                            PendingIntent pendingIntent=PendingIntent.getBroadcast(context,0,clickIntent,0);                            remoteViews.setOnClickPendingIntent(R.id.imageView,pendingIntent);                            appWidgetManager.updateAppWidget(new ComponentName(context,MyAppWidgetProvider.class),remoteViews);                            SystemClock.sleep(30);                        }                    }                }).start();            break;        }    }    @Override    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {        super.onUpdate(context, appWidgetManager, appWidgetIds);        final int length=appWidgetIds.length;        for (int i = 0; i < length; i++) {            onWidgetUpdate( context, appWidgetManager,appWidgetIds[i]);        }    }    private void onWidgetUpdate(Context context,AppWidgetManager appWidgetManager,int appWidgetId){        RemoteViews remoteViews=new RemoteViews(context.getPackageName(),R.layout.desk);        Intent intentClick=new Intent();        intentClick.setAction(Click_ACTION);        PendingIntent pendingIntent=PendingIntent.getBroadcast(context,0,intentClick,0);        remoteViews.setOnClickPendingIntent(R.id.imageView,pendingIntent);        appWidgetManager.updateAppWidget(appWidgetId,remoteViews);    }    private Bitmap rotateBitmap(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中的配置如下:  "com.robot.zhangyun.deme.md.MyAppWidgetProvider" >                            "android.appwidget.action.APPWIDGET_UPDATE" />//小部件的标示,必须存在                "site.zhangyun.appliction.Click"/>//识别小部件的单击事件                        "android.appwidget.provider"                android:resource="@xml/appwidget_provider_info" />        

更多相关文章

  1. Android中多线程的handler与Thread
  2. android 事件机制,捕捉与事件监听总结整理(二)Event Handlers,Handli
  3. app launcher 名称不是清单文件中的android:label
  4. android 拦截机制的分析
  5. android 数据传递详解(Serialization、Parcelable、Parcel、Inten
  6. Android基础知识学习-Instrumentation启动源码简析
  7. android悬浮按钮(Floating action button)的两种实现方法
  8. Android(安卓)Handler 机制以及各方法所在线程原理分析
  9. android 通过setContentView切换Activity的View,保存动态修改后

随机推荐

  1. Android UI之QMUI_Android(腾讯ui库)
  2. Android Wifi模块分析(二)
  3. Android开发/源码资源汇总
  4. Android SDK离线安装
  5. 让android studio 支持Lambda
  6. Android TP(二)
  7. Android(安卓)ORM 框架之 greenDAO应用基
  8. Android 设置EditText光标颜色及粗细
  9. Android中文API(142) —— Gravity
  10. Android网络通信(2):HTTP通信