RemoteViews简单学习记录

2019-12-23  本文已影响0人  梧叶已秋声

remoteview最简单的应用应该就是在notification里面了,以下为简单示例。

public class MainActivity extends AppCompatActivity{
    private static final String TAG = "MainActivity";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
            String channelID = "1";
            String channelName = "channel_name";

            NotificationChannel channel = null;
            channel = new NotificationChannel(channelID, channelName, NotificationManager.IMPORTANCE_HIGH);
            NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

            manager.createNotificationChannel(channel);
            // 利用channelName可以在系统设置页面对app某个名称的通知进行管理
            NotificationCompat.Builder builder =new NotificationCompat.Builder(this, channelName);
            Intent intent = new Intent(this, MainActivity.class);
            PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);

            RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.layout_notificaiton);
            remoteViews.setTextViewText(R.id.tv_5_s, "Start");//通过id-内容的方式设置remoteview中控件的内容,底层实现是通过Binder跨进程通信
            remoteViews.setTextViewText(R.id.tv_5_e, "End");
            remoteViews.setImageViewResource(R.id.icon_5, R.drawable.test);
            remoteViews.setOnClickPendingIntent(R.id.tv_5_e, pendingIntent);

            builder.setContent(remoteViews);
            builder.setSmallIcon(R.mipmap.ic_launcher);
            //创建通知时指定channelID
            builder.setChannelId(channelID);
            Notification notification = builder.build();
            manager.notify(1, notification);
        }
    }
}

如果是8.0以下系统,notification不用设置channelID ,代码如下所示

            NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
            Notification.Builder builder =new Notification.Builder(this);
            Intent intent = new Intent(this, MainActivity.class);
            PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
            RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.layout_notificaiton);
            remoteViews.setTextViewText(R.id.tv_5_s, "Start");//通过id-内容的方式设置remoteview中控件的内容,底层实现是通过Binder跨进程通信
            remoteViews.setTextViewText(R.id.tv_5_e, "End");
            remoteViews.setImageViewResource(R.id.icon_5, R.drawable.test);
            remoteViews.setOnClickPendingIntent(R.id.tv_5_e, pendingIntent);

            builder.setCustomContentView(remoteViews);
            builder.setSmallIcon(R.mipmap.ic_launcher);

            Notification notification = builder.build();
            manager.notify(1, notification);
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="48dp" android:orientation="vertical"
    android:paddingEnd="48dp"
    android:paddingStart="48dp"
    android:focusable="true">

    <ImageView
        android:id="@+id/icon_5"
        android:layout_width="48dp"
        android:layout_height="48dp"
        android:layout_centerInParent="true"
        android:src="@mipmap/ic_launcher_round" />
    <TextView
        android:id="@+id/tv_5_s"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_alignParentStart="true"
        android:gravity="center_vertical"
        android:text="Left"
        android:focusable="true"/>
    <TextView
        android:id="@+id/tv_5_e"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_alignParentEnd="true"
        android:gravity="center_vertical"
        android:text="Right"
        android:focusable="true"/>
</RelativeLayout>

通过在notification中简单使用remoteview可以对其有一个简单的了解。

以下文字出处:https://www.jianshu.com/p/91c30eb0a90b
RemoteViews的原理
RemoteViews本身并不是个View.它是个Pacelable,是个可以跨进程传递的一个数据.它携带了UI的布局及对应的数据
其它进程收到这个RemoteViews后会在其进程中根据这个布局inflat出对应的View.然后根据RemoteViews里的属性反射设置到对应的View上,然后根据设置点击监听.监听的处理就是调用对应的PendingIntent.
因为是跨进程,所以无法直接操作View,所以系统把对view的一个操作定义为Action对象.Action对象本身也是个Pacelable,所以可以跨进程.其封装了操作View的数据.远程进程遍历所有的Action并执行其apply方法通过反射更新View
向先前说的设置view的属性,设置点击监听,都是一个Action.

这种说法对我而言太过抽象。

//vendor\mediatek\proprietary\packages\apps_700_es\SystemUI_700_es\src\com\android\systemui\statusbar\notification\NotificationInflater.java
        @Override
        protected InflationProgress doInBackground(Void... params) {
            try {
                final Notification.Builder recoveredBuilder
                        = Notification.Builder.recoverBuilder(mContext,
                        mSbn.getNotification());
                Context packageContext = mSbn.getPackageContext(mContext);
                Notification notification = mSbn.getNotification();
                if (mIsLowPriority) {
                    int backgroundColor = mContext.getColor(
                            R.color.notification_material_background_low_priority_color);
                    recoveredBuilder.setBackgroundColorHint(backgroundColor);
                }
                if (notification.isMediaNotification()) {
                    MediaNotificationProcessor processor = new MediaNotificationProcessor(mContext,
                            packageContext);
                    processor.setIsLowPriority(mIsLowPriority);
                    processor.processNotification(notification, recoveredBuilder);
                }
                return createRemoteViews(mReInflateFlags,
                        recoveredBuilder, mIsLowPriority, mIsChildInGroup,
                        mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight, mRedactAmbient,
                        packageContext);
            } catch (Exception e) {
                mError = e;
                return null;
            }
        }

   private static InflationProgress createRemoteViews(int reInflateFlags,
            Notification.Builder builder, boolean isLowPriority, boolean isChildInGroup,
            boolean usesIncreasedHeight, boolean usesIncreasedHeadsUpHeight, boolean redactAmbient,
            Context packageContext) {
        InflationProgress result = new InflationProgress();
        isLowPriority = isLowPriority && !isChildInGroup;
        if ((reInflateFlags & FLAG_REINFLATE_CONTENT_VIEW) != 0) {
            result.newContentView = createContentView(builder, isLowPriority, usesIncreasedHeight);
        }

        if ((reInflateFlags & FLAG_REINFLATE_EXPANDED_VIEW) != 0) {
            result.newExpandedView = createExpandedView(builder, isLowPriority);
        }

        if ((reInflateFlags & FLAG_REINFLATE_HEADS_UP_VIEW) != 0) {
            result.newHeadsUpView = builder.createHeadsUpContentView(usesIncreasedHeadsUpHeight);
        }

        if ((reInflateFlags & FLAG_REINFLATE_PUBLIC_VIEW) != 0) {
            result.newPublicView = builder.makePublicContentView();
        }

        if ((reInflateFlags & FLAG_REINFLATE_AMBIENT_VIEW) != 0) {
            result.newAmbientView = redactAmbient ? builder.makePublicAmbientNotification()
                    : builder.makeAmbientNotification();
        }
        result.packageContext = packageContext;
        return result;
    }

NotificationInflater中的createRemoteViews这里我没有深入看下去,从方法名字上来看是这里创建了remoteview。

RemoteViews 本身的属性并不多,这里重点关注了一下mLayoutId和mActions,以及ReflectionAction。

//frameworks\base\core\java\android\widget\RemoteViews.java
public class RemoteViews implements Parcelable, Filter {

    /**
     * The resource ID of the layout file. (Added to the parcel)
     */
    private final int mLayoutId;
    /**
     * An array of actions to perform on the view tree once it has been
     * inflated
     */
    private ArrayList<Action> mActions;

    /**
     * Inflates the view hierarchy represented by this object and applies
     * all of the actions.
     *
     * <p><strong>Caller beware: this may throw</strong>
     *
     * @param context Default context to use
     * @param parent Parent that the resulting view hierarchy will be attached to. This method
     * does <strong>not</strong> attach the hierarchy. The caller should do so when appropriate.
     * @return The inflated view hierarchy
     */
    public View apply(Context context, ViewGroup parent) {
        return apply(context, parent, null);
    }

    /** @hide */
    public View apply(Context context, ViewGroup parent, OnClickHandler handler) {
        RemoteViews rvToApply = getRemoteViewsToApply(context);

        View result = inflateView(context, rvToApply, parent);
        loadTransitionOverride(context, handler);

        rvToApply.performApply(result, parent, handler);

        return result;
    }

    private void performApply(View v, ViewGroup parent, OnClickHandler handler) {
        if (mActions != null) {
            handler = handler == null ? DEFAULT_ON_CLICK_HANDLER : handler;
            final int count = mActions.size();
            for (int i = 0; i < count; i++) {
                Action a = mActions.get(i);
                a.apply(v, parent, handler);
            }
        }
    }

    private final class ReflectionAction extends Action {
      ReflectionAction(int viewId, String methodName, int type, Object value) {
            this.viewId = viewId;
            this.methodName = methodName;
            this.type = type;
            this.value = value;
        }
    }

}
...................


}

大部分资料显示remoteview更新ui调用的是apply,但是在此过程中ReflectionAction 到底做了什么对此我有些疑惑。于是在ReflectionAction构造函数中打印log(对于反射这一块我存在知识盲区待补),结果如下所示。

2010-01-03 04:33:15.328 966-966/? D/RemoteViews: methodName = setText , param = interface java.lang.CharSequence ,view = android.widget.TextView{10b0fc2 VFED..... ........ 48,0-79,48 #7f0700c1 app:id/tv_5_s} ,value = Start
2010-01-03 04:33:15.329 966-966/? D/RemoteViews: methodName = setText , param = interface java.lang.CharSequence ,view = android.widget.TextView{28f01d3 VFED..C.. ........ 408,0-432,48 #7f0700c0 app:id/tv_5_e} ,value = End
2010-01-03 04:33:15.341 966-966/? D/RemoteViews: methodName = setExpanded , param = boolean ,view = android.view.NotificationHeaderView{5a44110 V.E...... R.....ID 0,0-480,48 #102032b android:id/notification_header} ,value = false
2010-01-03 04:33:15.341 966-966/? D/RemoteViews: methodName = setText , param = interface java.lang.CharSequence ,view = android.widget.TextView{f7e660e V.ED..... ......ID 40,15-121,32 #10201af android:id/app_name_text} ,value = null
2010-01-03 04:33:15.342 966-966/? D/RemoteViews: methodName = setVisibility , param = int ,view = android.widget.TextView{8bde82f G.ED..... ......I. 0,0-0,0 #1020281 android:id/header_text} ,value = 8
2010-01-03 04:33:15.342 966-966/? D/RemoteViews: methodName = setText , param = interface java.lang.CharSequence ,view = android.widget.TextView{8bde82f G.ED..... ......I. 0,0-0,0 #1020281 android:id/header_text} ,value = null
2010-01-03 04:33:15.342 966-966/? D/RemoteViews: methodName = setVisibility , param = int ,view = android.widget.TextView{8f45e3c G.ED..... ......I. 0,0-0,0 #1020282 android:id/header_text_divider} ,value = 8
2010-01-03 04:33:15.342 966-966/? D/RemoteViews: methodName = setVisibility , param = int ,view = android.widget.TextView{32a97c5 V.ED..... ......ID 125,15-129,32 #102043f android:id/time_divider} ,value = 8
2010-01-03 04:33:15.343 966-966/? D/RemoteViews: methodName = setVisibility , param = int ,view = android.widget.DateTimeView{abed51a V.ED..... ......ID 133,15-151,32 #102043b android:id/time} ,value = 8
2010-01-03 04:33:15.343 966-966/? D/RemoteViews: methodName = setVisibility , param = int ,view = android.widget.ImageView{44ad84b G.ED..... ......I. 0,0-0,0 #102037c android:id/profile_badge} ,value = 8
2010-01-03 04:33:15.344 966-966/? D/RemoteViews: methodName = setOriginalIconColor , param = int ,view = android.view.NotificationHeaderView{5a44110 V.E...... R.....ID 0,0-480,48 #102032b android:id/notification_header} ,value = -9079435
2010-01-03 04:33:15.345 966-966/? D/RemoteViews: methodName = setText , param = interface java.lang.CharSequence ,view = android.widget.TextView{f7e660e V.ED..... ......ID 40,15-121,32 #10201af android:id/app_name_text} ,value = My Application
2010-01-03 04:33:15.345 966-966/? D/RemoteViews: methodName = setTextColor , param = int ,view = android.widget.TextView{f7e660e V.ED..... ......ID 40,15-121,32 #10201af android:id/app_name_text} ,value = -9079435
2010-01-03 04:33:15.345 966-966/? D/RemoteViews: methodName = setVisibility , param = int ,view = android.widget.TextView{32a97c5 G.ED..... ......ID 125,15-129,32 #102043f android:id/time_divider} ,value = 0
2010-01-03 04:33:15.345 966-966/? D/RemoteViews: methodName = setTextColor , param = int ,view = android.widget.TextView{32a97c5 V.ED..... ......ID 125,15-129,32 #102043f android:id/time_divider} ,value = -1979711488
2010-01-03 04:33:15.345 966-966/? D/RemoteViews: methodName = setVisibility , param = int ,view = android.widget.DateTimeView{abed51a G.ED..... ......ID 133,15-151,32 #102043b android:id/time} ,value = 0
2010-01-03 04:33:15.346 966-966/? D/RemoteViews: methodName = setTime , param = long ,view = android.widget.DateTimeView{abed51a V.ED..... ......ID 133,15-151,32 #102043b android:id/time} ,value = 1262464395151
2010-01-03 04:33:15.353 966-966/? D/RemoteViews: methodName = setTextColor , param = int ,view = android.widget.DateTimeView{abed51a V.ED..... ......ID 133,15-151,32 #102043b android:id/time} ,value = -1979711488
2010-01-03 04:33:15.353 966-966/? D/RemoteViews: methodName = setOriginalNotificationColor , param = int ,view = android.view.NotificationHeaderView{5a44110 V.E...... R.....ID 0,0-480,48 #102032b android:id/notification_header} ,value = -9079435
2010-01-03 04:33:15.353 966-966/? D/RemoteViews: methodName = setExpandOnlyOnButton , param = boolean ,view = android.view.NotificationHeaderView{5a44110 V.E...... R.....ID 0,0-480,48 #102032b android:id/notification_header} ,value = true
2010-01-03 04:33:15.354 966-966/? D/RemoteViews: methodName = setExpanded , param = boolean ,view = android.view.NotificationHeaderView{221eb41 V.E...... ......ID 16,0-464,48 #102032b android:id/notification_header} ,value = false
2010-01-03 04:33:15.355 966-966/? D/RemoteViews: methodName = setText , param = interface java.lang.CharSequence ,view = android.widget.TextView{36528e6 I.ED..... ......ID 42,14-134,33 #10201af android:id/app_name_text} ,value = null
2010-01-03 04:33:15.355 966-966/? D/RemoteViews: methodName = setVisibility , param = int ,view = android.widget.TextView{c6cae27 G.ED..... ......I. 0,0-0,0 #1020281 android:id/header_text} ,value = 8
2010-01-03 04:33:15.355 966-966/? D/RemoteViews: methodName = setText , param = interface java.lang.CharSequence ,view = android.widget.TextView{c6cae27 G.ED..... ......I. 0,0-0,0 #1020281 android:id/header_text} ,value = null
2010-01-03 04:33:15.355 966-966/? D/RemoteViews: methodName = setVisibility , param = int ,view = android.widget.TextView{8038d4 G.ED..... ......I. 0,0-0,0 #1020282 android:id/header_text_divider} ,value = 8
2010-01-03 04:33:15.356 966-966/? D/RemoteViews: methodName = setVisibility , param = int ,view = android.widget.TextView{fe0fa7d G.ED..... ......I. 0,0-0,0 #102043f android:id/time_divider} ,value = 8
2010-01-03 04:33:15.356 966-966/? D/RemoteViews: methodName = setVisibility , param = int ,view = android.widget.DateTimeView{8caed72 G.ED..... ......I. 0,0-0,0 #102043b android:id/time} ,value = 8
2010-01-03 04:33:15.356 966-966/? D/RemoteViews: methodName = setVisibility , param = int ,view = android.widget.ImageView{aec05c3 G.ED..... ......I. 0,0-0,0 #102037c android:id/profile_badge} ,value = 8
2010-01-03 04:33:15.356 966-966/? D/RemoteViews: methodName = setVisibility , param = int ,view = android.widget.TextView{8120e40 G.ED..... ......ID 0,0-0,0 #1020016 android:id/title} ,value = 8
2010-01-03 04:33:15.356 966-966/? D/RemoteViews: methodName = setText , param = interface java.lang.CharSequence ,view = android.widget.TextView{8120e40 G.ED..... ......ID 0,0-0,0 #1020016 android:id/title} ,value = null
2010-01-03 04:33:15.356 966-966/? D/RemoteViews: methodName = setVisibility , param = int ,view = android.widget.TextView{587c179 G.ED..... ......I. 0,0-0,0 #102041e android:id/text} ,value = 8
2010-01-03 04:33:15.356 966-966/? D/RemoteViews: methodName = setText , param = interface java.lang.CharSequence ,view = android.widget.TextView{587c179 G.ED..... ......I. 0,0-0,0 #102041e android:id/text} ,value = null
2010-01-03 04:33:15.357 966-966/? D/RemoteViews: methodName = setBackgroundResource , param = int ,view = android.widget.FrameLayout{4566ebe I.E...... R.....ID 0,0-480,48 #102040b android:id/status_bar_latest_event_content} ,value = 0
2010-01-03 04:33:15.358 966-966/? D/RemoteViews: methodName = setOriginalIconColor , param = int ,view = android.view.NotificationHeaderView{221eb41 V.E...... ......ID 16,0-464,48 #102032b android:id/notification_header} ,value = -3947581
2010-01-03 04:33:15.358 966-966/? D/RemoteViews: methodName = setText , param = interface java.lang.CharSequence ,view = android.widget.TextView{36528e6 I.ED..... ......ID 42,14-134,33 #10201af android:id/app_name_text} ,value = My Application
2010-01-03 04:33:15.359 966-966/? D/RemoteViews: methodName = setTextColor , param = int ,view = android.widget.TextView{36528e6 I.ED..... ......ID 42,14-134,33 #10201af android:id/app_name_text} ,value = -3947581
2010-01-03 04:33:15.359 966-966/? D/RemoteViews: methodName = setOriginalNotificationColor , param = int ,view = android.view.NotificationHeaderView{221eb41 V.E...... ......ID 16,0-464,48 #102032b android:id/notification_header} ,value = -9079435
2010-01-03 04:33:15.359 966-966/? D/RemoteViews: methodName = setMinimumHeight , param = int ,view = android.widget.LinearLayout{37f3b1f V.E...... ......ID 0,4-448,4 #102032c android:id/notification_main_column} ,value = 0
2010-01-03 04:33:15.359 966-966/? D/RemoteViews: methodName = setVisibility , param = int ,view = com.android.internal.widget.NotificationActionListLayout{a29ce6c G.E...... ......ID 0,0-0,0 #102018d android:id/actions} ,value = 8
2010-01-03 04:33:15.359 966-966/? D/RemoteViews: methodName = setEmphasizedMode , param = boolean ,view = com.android.internal.widget.NotificationActionListLayout{a29ce6c G.E...... ......ID 0,0-0,0 #102018d android:id/actions} ,value = false
2010-01-03 04:33:15.360 966-966/? D/RemoteViews: methodName = setVisibility , param = int ,view = android.widget.FrameLayout{439fc35 G.E...... ......I. 0,0-0,0 #102018e android:id/actions_container} ,value = 8

由于原理的理解重点还是在反射那一块,因此暂不深入。
整个流程看下来,remoteview无法直接通过findview获取到view从而进行操作,需要使用action。

参考链接:
Android开发艺术探索 第5章 理解RemoteViews 读书笔记

Android中的RemoteViews

Android通知及RemoteViews整理

Android View - RemoteViews

上一篇下一篇

猜你喜欢

热点阅读