android艺术开发探索之RemoteViews跨进程更新UI
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" />
更多相关文章
- Android中多线程的handler与Thread
- android 事件机制,捕捉与事件监听总结整理(二)Event Handlers,Handli
- app launcher 名称不是清单文件中的android:label
- android 拦截机制的分析
- android 数据传递详解(Serialization、Parcelable、Parcel、Inten
- Android基础知识学习-Instrumentation启动源码简析
- android悬浮按钮(Floating action button)的两种实现方法
- Android(安卓)Handler 机制以及各方法所在线程原理分析
- android 通过setContentView切换Activity的View,保存动态修改后