使用LocalBroadcast实现跨Activity的MVVM
2016-05-06 20:53
274 查看
问题
工作中遇到了一个问题:封装了一个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.我这是戳中了什么搜索热词了啊。。。点进来的人们,不好意思,我只是做个笔记。
相关文章推荐
- 我读《把时间当做朋友》
- 初试base64:神秘邮件
- 前端-工具
- 小何讲Linux: 基本文件操作和实例
- AIDL实现Android进程间通信实例
- 分类器性能评估之Lift和Gain(3)
- Find The Multiple
- MFC控件自适应窗口大小类
- HTML addEventListener() 方法和attachEvent()区别分析
- WebBasic06-JS
- java多线程总结
- dig命令 安装
- Java集合Set、List、Map的遍历方法
- js中的正则表达式
- python 爬取一页商品数据
- Jsoup代码解读之四-parser
- Android客户端多文件上传
- javascript入门篇之cookie基础
- POJ 2406 Power Strings
- Jsoup代码解读之四-parser