RemoteViews原理分析及应用

转载请注明出处:http://blog.csdn.net/ahence/article/details/62418926

RemoteViews基本概念

RemoteViews乍一看名字似乎也是一种View,实则不然,它并不是View。来看RemoteViews的定义及官方说明:

/**
 * A class that describes a view hierarchy that can be displayed in
 * another process. The hierarchy is inflated from a layout resource
 * file, and this class provides some basic operations for modifying
 * the content of the inflated hierarchy.
 */
public class RemoteViews implements Parcelable, Filter {
   
    ……
}

我们可以得到以下几点结论:

  • RemoteViews只是一个实现了Parcelable和Filter接口的类,而并非继承自View。
  • RemoteViews用来描述可运行在其他进程中的视图结构,但RemoteViews本身不是视图,只是一个描述类。
  • RemoteViews描述的远程视图需要通过layout资源文件定义。
  • RemoteViews类提供了一系列修改远程视图的方法。

现在我们对RemoteViews应该有了一个大概的认识,它可以跨进程来显示和更新视图。RemoteViews主要有两个应用场景:

  • 自定义通知栏
  • 桌面小部件

本文最后会给出RemoteViews的应用实例,接下来我们先分析RemoteViews的实现原理。

RemoteViews原理分析

构造函数

RemoteViews提供了多个构造函数,如:

public RemoteViews(String packageName, int layoutId) {}
public RemoteViews(String packageName, int userId, int layoutId) {}
public RemoteViews(RemoteViews landscape, RemoteViews portrait) {}
public RemoteViews(Parcel parcel) {}

以一个最常用的构造方法为例:

/**
 * Create a new RemoteViews object that will display the views contained
 * in the specified layout file.
 *
 * @param packageName Name of the package that contains the layout resource
 * @param layoutId The id of the layout resource
 */
public RemoteViews(String packageName, int layoutId) {
    this(getApplicationInfo(packageName, UserHandle.myUserId()), layoutId);
}

由注释可知,第一个参数为包名,第二个参数为布局资源文件的ID,接下来会调用:

/**
 * Create a new RemoteViews object that will display the views contained
 * in the specified layout file.
 *
 * @param application The application whose content is shown by the views.
 * @param layoutId The id of the layout resource.
 *
 * @hide
 */
protected RemoteViews(ApplicationInfo application, int layoutId) {
    mApplication = application;
    mLayoutId = layoutId;
    mBitmapCache = new BitmapCache();
    // setup the memory usage statistics
    mMemoryUsageCounter = new MemoryUsageCounter();
    recalculateMemoryUsage();
}

同样也很简单,第一个参数为远程视图展示内容所属的Application信息,第二个参数为布局文件ID。该构造方法主要是初始化mApplicationmLayoutId,其他代码为图片缓存及内存计算的一些逻辑。

核心属性字段

RemoteView有如下几个比较重要的属性字段:

/**
 * Application that hosts the remote views.
 *
 * @hide
 */
private ApplicationInfo mApplication;

/**
 * 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;

前两个比较好理解,而且上文提到是在构造函数中赋值的。mActions是用来存储Action的一个列表,而Action可以理解为对远程视图操作的一个封装,下文会详细解释。

RemoteView注解

在RemoteViews源码中声明了如下注解:

/**
 * This annotation indicates that a subclass of View is alllowed to be used
 * with the {@link RemoteViews} mechanism.
 */
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface RemoteView {
   
}

从注解类型来看为运行时注解,作用于类或接口,结合注释可知此注解用于View的子类,用来标识该View是否可以作为远程视图使用。由此我们也可以推断,并非所有View都可以作为远程视图,只有声明了RemoteView注解的View才可以。我们从源码定义来简单验证一下:

TextView的定义

@RemoteView
public class TextView extends View implements ViewTreeObserver.OnPreDrawListener {
   }

Button的定义

@RemoteView
public class Button extends TextView {
   }

ImageView的定义

@RemoteView
public class ImageView extends View {
   }

ProgressBar的定义

@RemoteView
public class ProgressBar extends View {
   }

LinearLayout的定义

@RemoteView
public class LinearLayout extends ViewGroup {
   }

EditText的定义

public class EditText extends TextView {
   }

不再一一列举,可见EditText虽然是继承自TextView的,但它没有使用@RemoteView注解,因此并不能用作远程视图。

RemoteViews所支持的View类型如下:

LinearLayout、RelativeLayout、FrameLayout、GridLayout、AbsoluteLayout(已弃用)

TextView、Button、ImageView、ImageButton、Chronometer、ProgressBar、ListView、GridView、StackView、ViewFlipper、AdapterViewFlipper、ViewStub、AnalogClock(已弃用)

也就是说远程视图只能使用上述所列举的View,它们的子类及其他View都是不支持的,如果使用了不支持的View,则会报异常。

实现Parcelable和Filter接口的意义

Parcelable比较容易理解,就是支持序列化以便于跨进程操作。

那么Filter的作用是什么呢?Filter接口的定义如下:

/**
 * Hook to allow clients of the LayoutInflater to restrict the set of Views that are allowed
 * to be inflated.
 * 
 */
public interface Filter {
   
    /**
     * Hook to allow clients of the LayoutInflater to restrict the set of Views 
     * that are allowed to be inflated.
     * 
     * @param clazz The class object for the View that is about to be inflated
     * 
     * @return True if this class is allowed to be inflated, or false otherwise
     */
    @SuppressWarnings("unchecked")
    boolean onLoadClass(Class clazz);
}

从注释中不难看出Filter是用来限制和过滤View用的,上文提到并非所有的View都能用作远程视图,如果为上述列举的View,则onLoadClass(Class clazz)返回true,否则返回false。

在RemoteViews中实现了Filter接口的方法:

public boolean onLoadClass(Class clazz) {
    return clazz.isAnnotationPresent(RemoteView.class);
}

可以看到就是根据@RemoteView注解来判断是否可以使用该View作为远程视图。

RemoteViews实现原理
跨进程是哪两个进程

很显然我们的应用自身是一个进程,那么另一个进程是什么呢?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值