盒子
盒子

RemoteViews分析

RemoteViews

远程视图,通过该类可实现跨进程更新视图

RemoteViews(String packageName, int layoutId)

  • packageName
  • 当前进程包名,用于生成ApplicationInfo实例对象

    1
    2
    3
    public RemoteViews(String packageName, int layoutId) {
    this(getApplicationInfo(packageName, UserHandle.myUserId()), layoutId);
    }

    在该构造方法中调用以下构造方法:

    1
    2
    3
    4
    5
    6
    protected RemoteViews(ApplicationInfo application, int layoutId) {
    mApplication = application;
    mLayoutId = layoutId;
    mBitmapCache = new BitmapCache();
    mClassCookies = null;
    }

  • layoutId
  • 通过该id让其它进程获取该进程的View:

    1
    2
    3
    4
    5
    6
    7
    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;
    }

    先获得RemoteViews,然后调用inflateView方法获取视图View,inflateView方法如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    private View inflateView(Context context, RemoteViews rv, ViewGroup parent) {
    // 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 RemoteViewsContextWrapper(context, contextForResources);

    // If mApplyThemeResId is not given, Theme.DeviceDefault will be used.
    if (mApplyThemeResId != 0) {
    inflationContext = new ContextThemeWrapper(inflationContext, mApplyThemeResId);
    }
    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);
    View v = inflater.inflate(rv.getLayoutId(), parent, false);
    v.setTagInternal(R.id.widget_frame, rv.getLayoutId());
    return v;
    }

    通过getLayoutId()获取layoutId:

    1
    2
    3
    public int getLayoutId() {
    return mLayoutId;
    }

    在RemoteViews中的apply方法中有一个performApply方法,该方法源码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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);
    }
    }
    }

    该方法会遍历每一个Action并调用对应action的apply方法,对View进行更新处理,而该apply方法是属于Action类的抽象方法:

    1
    public abstract void apply(View root, ViewGroup rootParent,OnClickHandler handler) throws ActionException;

    而该Action有很多用于操作View的子类,例如操作Text的ReflectionAction类:

    1
    2
    3
    4
    5
    6
    public void setTextViewText(int viewId, CharSequence text) {
    setCharSequence(viewId, "setText", text);
    }
    public void setCharSequence(int viewId, String methodName, CharSequence value) {
    addAction(new ReflectionAction(viewId, methodName, ReflectionAction.CHAR_SEQUENCE, value));
    }

    viewId为视图(根据layoutId得出的)中的具体控件id,methodName为方法名(这里是setText),text为要设置的内容,addAction表明每进行一次对远程视图的操作都会封装成一个Action并添加到一个Action容器中,每一个操作都是一个Action的子类,该子类重写了Action的apply方法,并在内部通过反射机制调用View的具体方法,如setText。

    通过RemoteViews更新不同线程的UI例子

    MainActivity.class:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Intent intent=new Intent(this,RemoteActivity.class);
    startActivity(intent);
    }
    private void notifyRemote(){
    RemoteViews remoteViews=new RemoteViews(getPackageName(),R.layout.view_notification);
    remoteViews.setTextViewText(R.id.text_notify_custom,"remote");
    Intent intent=new Intent("ACTION_NOTIFY_REMOTE_ACTIVITY");
    intent.putExtra("REMOTE_VIEWS",remoteViews);
    sendBroadcast(intent);
    }

    @Override
    protected void onStop() {
    super.onStop();
    notifyRemote();
    }
    }

    RemoteActivity.class:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    package io.github.grooters.fifth;

    import android.content.BroadcastReceiver;
    import android.content.Context;
    import android.content.Intent;
    import android.content.IntentFilter;
    import android.os.Bundle;
    import android.support.annotation.Nullable;
    import android.support.v7.app.AppCompatActivity;
    import android.util.Log;
    import android.view.View;
    import android.widget.LinearLayout;
    import android.widget.RemoteViews;

    /**
    * Create by 李林浪 in 2018/12/17
    * Elegant Code...
    */
    public class RemoteActivity extends AppCompatActivity {
    private final String TAG="RemoteActivity_debug";
    private LinearLayout content;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_remote);
    initView();
    }
    private BroadcastReceiver receiver=new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
    Log.d(TAG,"onReceive");
    RemoteViews remoteViews=intent.getParcelableExtra("REMOTE_VIEWS");
    updateUI(remoteViews);
    }
    };
    private void updateUI(RemoteViews remoteViews){
    if(remoteViews!=null){
    Log.d(TAG,"remoteViews!=null");
    View view=remoteViews.apply(this,content);
    content.addView(view);
    }
    }
    private void initView(){
    IntentFilter filter=new IntentFilter("ACTION_NOTIFY_REMOTE_ACTIVITY");
    registerReceiver(receiver,filter);
    content=findViewById(R.id.linear_content);
    }
    @Override
    protected void onDestroy() {
    super.onDestroy();
    unregisterReceiver(receiver);
    }
    }

    AndroidManifest.xml:

    1
    <activity android:name=".RemoteActivity"  android:process=":remote"/>

    其它RemoteViews的使用还有通知和桌面小部件,具体使用参照:

    Android桌面小部件

    通知与自定义通知

    支持一下
    扫一扫,支持Grooter
    • 微信扫一扫
    • 支付宝扫一扫