理解RemoteViews————读书笔记

理解RemoteViews

RemoteViews的应用

RemoteViews在实际开发中主要用于通知栏和桌面小部件。

RemoteViews在通知栏上的应用

 Notification notification = new Notification();
        notification.icon = R.drawable.ic_launcher;
        notification.tickerText = "hello world";
        notification.when = System.currentTimeMillis();
        notification.flags = Notification.FLAG_AUTO_CANCEL;
        Intent intent = new Intent(this, DemoActivity_1.class);
        intent.putExtra("sid", "" + sId);
        PendingIntent pendingIntent = PendingIntent.getActivity(this,
                0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
        System.out.println(pendingIntent);
        RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.layout_notification);
        remoteViews.setTextViewText(R.id.msg, "chapter_5: " + sId);
        remoteViews.setImageViewResource(R.id.icon, R.drawable.icon1);
        PendingIntent openActivity2PendingIntent = PendingIntent.getActivity(this,
                0, new Intent(this, DemoActivity_2.class), PendingIntent.FLAG_UPDATE_CURRENT);
        remoteViews.setOnClickPendingIntent(R.id.open_activity2, openActivity2PendingIntent);
        notification.contentView = remoteViews;
        notification.contentIntent = pendingIntent;
        NotificationManager manager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
        manager.notify(sId, notification);

只要提供当前应用的包名和布局文件的资源ID即可创建一个RemoteViews对象。更新RemoteViews只能使用setTextViewText, setImageViewResource, setOnClickPendingIntent等固定方法来设置View,不能像操作普通View的方式来操作。

RemoteViews在桌面小部件上的应用

AppWidgetProvider是Android中提供的用于实现桌面小部件的类,其本质是一个广播,即继承了BroadcastReceiver.
APPWidgetProvider的开发步骤:定义界面xml,定义配置信息xml,定义实现类(继承AppWidgetProvider),AndroidManifest中声明。
重要回调:onEnable,第一次被添加时调用,只有一次;onUpdate,添加或更新时回调;onDelete,每次删除时回调;onDisable,最后一次删除时回调;onReceive,接收广播的action。

PendingIntent

PendingIntent表示接下来有一个Intent将在某个待定的时刻发生。
1.典型的使用场景就是和RemoteViews的点击事件配合使用;
2.支持三种待定Intent:Activity,Service和Broadcast,主要方法:getActicity、getService、getBroadcast
3.PendingIntent相同的定义:内部的Intent和requestCode都相同。Intent相同的定义:两个Intent的componentName和intent-filter相同(不包括extras)
4.flag定义:FLAG_NO_CREATE,如果当前得PendingIntent之前不存在,那么getActicity、getService、getBroadcast方法直接返回null,即获取PendingIntent失败;FLAG_ONE_SHOT,以第一个为准,后续的会全部和第一条保持一致,任意一条被触发,其他的都cancel;FLAG_CANCEL_CURRENT,前面的相同的PendingIntent都会被cancel,只有最新的可用;FLAG_UDPATE_CURRENT,前面的PendingIntent都会被更新(它们Intent中的extras都会被更新)

RemoteViews的内部机制

1.RemoteViews的作用是在其他进程中显示并更新View界面,支持的View类型:Layout:FrameLayout, LinearLayout, RelativeLayout, GridLayout; View: AnalogClock, Button, Chronometer, ImageButton, ImageView, ProgressBar, TextView, ViewFlipper, ListView, GridView, StackView, AdapterViewFlipper, ViewStub。
2.源码分析:
RemoteViews#setBitmap

public void setBitmap(int viewId, String methodName, Bitmap value) {
    addAction(new BitmapReflectionAction(viewId, methodName, value));
}

RemoteViews#addAction

private void addAction(Action a) {
    if (hasLandscapeAndPortraitLayouts()) {
        throw new RuntimeException("RemoteViews specifying separate landscape and portrait" +
                " layouts cannot be modified. Instead, fully configure the landscape and" +
                " portrait layouts individually before constructing the combined layout.");
    }
    if (mActions == null) {
        mActions = new ArrayList<Action>();
    }
    mActions.add(a);

    // update the memory usage stats
    a.updateMemoryUsageEstimate(mMemoryUsageCounter);
    }

RemoteViews#apply

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

    View result;
    // RemoteViews may be built by an application installed in another
    // user. So build a context that loads resources from that user but
    // still returns the current users userId so settings like data / time formats
    // are loaded without requiring cross user persmissions.
    final Context contextForResources = getContextForResources(context);
    Context inflationContext = new ContextWrapper(context) {
        @Override
        public Resources getResources() {
            return contextForResources.getResources();
        }
        @Override
        public Resources.Theme getTheme() {
            return contextForResources.getTheme();
        }
        @Override
        public String getPackageName() {
            return contextForResources.getPackageName();
        }
    };

    LayoutInflater inflater = (LayoutInflater)
            context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

    // Clone inflater so we load resources from correct context and
    // we don't add a filter to the static version returned by getSystemService.
    inflater = inflater.cloneInContext(inflationContext);
    inflater.setFilter(this);
    result = inflater.inflate(rvToApply.getLayoutId(), parent, false);

    rvToApply.performApply(result, parent, handler);

    return result;
}

RemoteViews#ReflectionAction extends Action#apply

    @Override
    public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
        final View view = root.findViewById(viewId);
        if (view == null) return;

        Class<?> param = getParameterType();
        if (param == null) {
            throw new ActionException("bad type: " + this.type);
        }

        try {
            getMethod(view, this.methodName, param).invoke(view, wrapArg(this.value));
        } catch (ActionException e) {
            throw e;
        } catch (Exception ex) {
            throw new ActionException(ex);
        }
    }

RemoteViews#TextViewSizeAction extends Action

private class TextViewSizeAction extends Action {
        public TextViewSizeAction(int viewId, int units, float size) {
            this.viewId = viewId;
            this.units = units;
            this.size = size;
        }

        public TextViewSizeAction(Parcel parcel) {
            viewId = parcel.readInt();
            units = parcel.readInt();
            size  = parcel.readFloat();
        }

        public void writeToParcel(Parcel dest, int flags) {
            dest.writeInt(TAG);
            dest.writeInt(viewId);
            dest.writeInt(units);
            dest.writeFloat(size);
        }

        @Override
        public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
            final TextView target = (TextView) root.findViewById(viewId);
            if (target == null) return;
            target.setTextSize(units, size);
        }

        public String getActionName() {
            return "TextViewSizeAction";
        }

        int units;
        float size;

        public final static int TAG = 13;
    }

简单归纳,客户端的RemoteViews通过binder机制来传递到SystemServer进程,(RemoteViews本身也实现了Parcelable接口)。本地进程的View操作(一系列的set方法)都转化成一系列的Action对象,远程进程通过Action对象的apply方法就是真正操作View的地方。方法apply和reapply,前者会加载布局并更新界面,后者则只更新界面。

你可能感兴趣的:(android,读书笔记,RemoteView)