使用LocalBroadcast实现跨Activity的MVVM

问题

工作中遇到了一个问题:
封装了一个Activity组,这组Activity执行了相类似的业务A,但是少部分文案、图片是有差别的,而且要求在这组Activity执行的过程中,后台运行的其他业务可以随时(或在业务A回调时)修改这些显示内容。
不优雅的方案有:
- 回调中传递Activity的引用。这样限制了修改时机
- 在某单例中注册当前Activity,同时在Activity上暴露相应的修改接口。这样一来容易出现内存泄露,同时还要求其他业务了解业务A中的各种UI排布/命名等

实际上,这是一个跨Activity的MVVM问题,即双方定义好一个Model,业务A仅读取Model内容,并做好VM层和V层的工作。其他业务只需要理解Model的定义,持有Model并修改即可。

代码

有绑定功能的Model

public class Config {
    public static final String ACTION_CONFIG_CHANGE = "gt.research.androidbase.ACTION_CONFIG_CHANGE";
    public static final String KEY_CHANGED_PARTS = "KEY_CHANGED_PARTS";

    private int mId;
    //just for convenient
    private AtomicBoolean mHasComsumed = new AtomicBoolean(true);
    //for multithreading
    private final ThreadLocal<ArrayMap<String, Object>> mTransactionMap = new ThreadLocal<>();
    private LocalBroadcastManager mBroadcastManager;

    //model
    private final ArrayMap<String, Object> mConfigs = new ArrayMap<>();

    public Config(int id) {
        mId = id;
    }

    //multiple changes
    public Config beginTransaction(Context context) {
        ArrayMap<String, Object> map = mTransactionMap.get();
        if (null == map) {
            map = new ArrayMap<>();
        }
        map.clear();
        return this;
    }

    public Config commit(final Context context) {
        final ArrayMap<String, Object> map = mTransactionMap.get();
        if (null == map) {
            return this;
        }
        //for multi-threading
        ConfigManager.getInstance().runOnUiThread(new Runnable() {
            @Override
            public void run() {
                mConfigs.putAll(map);
                notifyChange(context, (String[]) map.keySet().toArray());
            }
        });
        return this;
    }

    public Config set(final Context context, final String name, final Object object) {
        ConfigManager.getInstance().runOnUiThread(new Runnable() {
            @Override
            public void run() {
                mConfigs.put(name, object);
                notifyChange(context, name);
            }
        });
        return this;
    }

    public Object get(Context context, String name) {
        //constrain on A
        if (Thread.currentThread() != Looper.getMainLooper().getThread()) {
            throw new RuntimeException("Wrong Thread");
        }
        return mConfigs.get(name);
    }

    //KVO
    private Config notifyChange(Context context, String... names) {
        LocalBroadcastManager broadcastManager = getBroadcastManager(context);
        Intent intent = new Intent(ACTION_CONFIG_CHANGE + mId);
        intent.setPackage("gt.research.androidbase");
        intent.putExtra(KEY_CHANGED_PARTS, names);
        broadcastManager.sendBroadcast(intent);
        mHasComsumed.set(false);
        return this;
    }

    private LocalBroadcastManager getBroadcastManager(Context context) {
        if (null == mBroadcastManager) {
            mBroadcastManager = LocalBroadcastManager.getInstance(context);
        }
        return mBroadcastManager;
    }

    public boolean shouldUpdate() {
        return !mHasComsumed.getAndSet(true);
    }

    public String[] getAllNames() {
        return (String[]) mConfigs.keySet().toArray();
    }
}

公共内存空间

public class ConfigManager {
    private static volatile ConfigManager sInstance;

    public static ConfigManager getInstance() {
        if (null == sInstance) {
            synchronized (ConfigManager.class) {
                if (null == sInstance) {
                    sInstance = new ConfigManager();
                }
            }
        }
        return sInstance;
    }

    private ConfigManager() {

    }

    private final SparseArray<Config> mConfigs = new SparseArray<>();
    private Handler mHandler = new Handler(Looper.getMainLooper());

    public Config getConfig(int id) {
        synchronized (mConfigs) {
            Config config = mConfigs.get(id);
            if (null == config) {
                config = new Config(id);
            }
            mConfigs.put(id, config);
            return config;
        }
    }

    public void runOnUiThread(Runnable runnable) {
        mHandler.post(runnable);
    }
}

业务A

public class Main2Activity extends AppCompatActivity {
    private TextView mText;
    private int mId;
    private String mAction;

    //KVO
    private BroadcastReceiver mConfigReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (mAction.equals(intent.getAction())) {
                String[] changedParts = intent.getStringArrayExtra(Config.KEY_CHANGED_PARTS);
                Config config = ConfigManager.getInstance().getConfig(mId);
                if (config.shouldUpdate()) {
                    updateViews(changedParts, config);
                }
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        mText = (TextView) findViewById(R.id.text2);
        mId = getIntent().getIntExtra(MainActivity.KEY_ID, -1);
        //a unique action just for this Activity
        mAction = Config.ACTION_CONFIG_CHANGE + mId;
        if (-1 == mId) {
            return;
        }
        LocalBroadcastManager.getInstance(this).registerReceiver(mConfigReceiver,
                new IntentFilter(mAction));
        Config config = ConfigManager.getInstance().getConfig(mId);

        //fetch all missed changes
        if (config.shouldUpdate()) {
            updateViews(config.getAllNames(), config);
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        LocalBroadcastManager.getInstance(this).unregisterReceiver(mConfigReceiver);
    }

    private void updateViews(String[] changedParts, Config config) {
        String key = changedParts[0];
        String value = (String) config.get(this, key);
        mText.setText(value);
    }
}

其他业务

public class MainActivity extends AppCompatActivity {
    public static final String KEY_ID = "id";

    private int mId;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.text).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mId = Math.abs(new Random(System.currentTimeMillis()).nextInt());
                Intent intent = new Intent(MainActivity.this, Main2Activity.class);
                intent.putExtra(KEY_ID, mId);
                startActivity(intent);
                new Thread() {
                    @Override
                    public void run() {
                        try {
                            Thread.sleep(1000);
                            ConfigManager.getInstance().getConfig(mId).set(MainActivity.this, "lala", "dlfjlajf");
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }.start();
            }
        });
    }

PS.我这是戳中了什么搜索热词了啊。。。点进来的人们,不好意思,我只是做个笔记。

你可能感兴趣的:(android,UI,MVVM)