目录
LiveData实现组件通信-Message
初学时,fragment之间的通信总是通过单例模式拿到实例化对象,再通过这个对象访问不同的方法实现。但是在接触到navigation导航组件后,一个activity包含多个fragment,那么如果再使用实例化的方式进行访问那将非常麻烦。
解决通信问题最初想到了一个activity下的不同fragment引用相同的viewmodel,在model中创建livedata对象,并在所属的fragment中进行观察,这是最终Message组件一个雏形,当需要新的通信桥梁时需要在model中创建新的变量,这加大了model的代码量也让代码不太容易维护。
最终的解决方案采用了基类的方式,创建静态的Map对象,全类名作为key,livedata作为对应的value,只需要继承该基类就可以实现组件之间的通信。
功能演示
在动图中可以看到第一个fragment(以下fragment均用F代替)向第二个F发送1、2、3的消息标识,第二个F同步进行更新,点击下方ABC按钮,在两个F中的abc均可以监听到该消息标识并同时更新了视图
原理
LiveData
引用了Google对LiveData的解释
LiveData 是一种可观察的数据存储器类。与常规的可观察类不同,LiveData 具有生命周期感知能力,意指它遵循其他应用组件(如 Activity、Fragment 或 Service)的生命周期。这种感知能力可确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者。
如果观察者(由 Observer 类表示)的生命周期处于 STARTED 或 RESUMED 状态,则 LiveData 会认为该观察者处于活跃状态。LiveData 只会将更新通知给活跃的观察者。为观察 LiveData 对象而注册的非活跃观察者不会收到更改通知。
您可以注册与实现 LifecycleOwner 接口的对象配对的观察者。有了这种关系,当相应的 Lifecycle 对象的状态变为 DESTROYED 时,便可移除此观察者。这对于 Activity 和 Fragment 特别有用,因为它们可以放心地观察 LiveData 对象,而不必担心泄露(当 Activity 和 Fragment 的生命周期被销毁时,系统会立即退订它们)。
反射
因为该模块是采用工厂模式进行设计,所以Activity或Fragment类的获取均通过反射得到
引用百度百科对反射机制的解释
Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。反射被视为动态语言的关键。
具体实现
Message(Java)
这一部分是整个通信结构的主体,它主要实现了消息的发送与观察者的管理。
public class Message {
// 存储对应的消息标识
private static final Map<String, MutableLiveData<Integer>> MESSAGE = new HashMap(2);
// 消息标识对应的数据
private static final Map<String, Map<Integer, Object[]>> MESSAGE_PARAMS = new HashMap(1);
public Message() {
}
/**
* 向c类发送一个消息标识
* 这里只设计了向Activity和Fragment发送,找不到对应类的将会直接返回
*/
public static void to(Class<?> c, int i) {
if (c.getName().contains("Activity") || c.getName().contains("Fragment")) {
try {
Object t = Class.forName(c.getName()).newInstance();
Method setValue = c.getMethod("setValue", Class.class, Integer.TYPE);
setValue.invoke(t, c, i);
String className = (new Exception()).getStackTrace()[1].getClassName();
Log.i("通信", className + " ---> " + c.getName() + " ==== " + i);
} catch (Exception var5) {
var5.printStackTrace();
}
}
}
/**
* 向c类发送一个消息标识,并携带objects参数
* 这里只设计了向Activity和Fragment发送,找不到对应类的将会直接返回
*/
public static void to(Class<?> c, int i, Object... objects) {
if (c.getName().contains("Activity") || c.getName().contains("Fragment")) {
try {
Object t = Class.forName(c.getName()).newInstance();
Method setData = c.getMethod("setData", Class.class, Integer.TYPE, Object[].class);
setData.invoke(t, c, i, objects);
Method setValue = c.getMethod("setValue", Class.class, Integer.TYPE);
setValue.invoke(t, c, i);
String className = (new Exception()).getStackTrace()[1].getClassName();
StringBuilder obj = new StringBuilder();
Object[] var8 = objects;
int var9 = objects.length;
for(int var10 = 0; var10 < var9; ++var10) {
Object object = var8[var10];
obj.append(object.getClass().getName()).append(", ");
}
Log.i("通信", className + " ---> " + c.getName() + " ==== " + i + " :::: " + obj.toString());
} catch (Exception var12) {
var12.printStackTrace();
}
}
}
/**
* 这里使用主要是适配多module的环境,拿不到具体类时使用
* 向cls类发送一个消息标识
* 这里只设计了向Activity和Fragment发送,找不到对应类的将会直接返回
*/
public static void to(String cls, int i) {
Class c = null;
try {
c = Class.forName(cls);
} catch (ClassNotFoundException var4) {
var4.printStackTrace();
}
if (c == null) {
toStrFlag(cls, i);
} else {
to(c, i);
}
}
/**
* 这里使用主要是适配多module的环境,拿不到具体类时使用
* 向c类发送一个消息标识,并携带objects参数
* 这里只设计了向Activity和Fragment发送,找不到对应类的将会直接返回
*/
public static void to(String cls, int i, Object... objects) {
Class c = null;
try {
c = Class.forName(cls);
} catch (ClassNotFoundException var5) {
var5.printStackTrace();
}
if (c == null) {
toStrFlag(cls, i, objects);
} else {
to(c, i, objects);
}
}
/**
* 获取一个动态消息标识
* @param str 可以是类
* 也可以是特殊标识,则不可使用to方法传递消息
*/
public static MutableLiveData<Integer> getMessage(String str) {
if (MESSAGE.get(str) == null) {
MESSAGE.put(str, new MutableLiveData());
}
return (MutableLiveData)MESSAGE.get(str);
}
/**
* 获取一个消息标识对应的一个数据
* @param str 可以是类
* 也可以是特殊标识,则不可使用to方法传递消息
*/
public static Object[] getData(String str, int i) {
if (MESSAGE_PARAMS.get(str) == null) {
MESSAGE_PARAMS.put(str, new HashMap(2));
}
return (Object[])((Map)Objects.requireNonNull((Map)MESSAGE_PARAMS.get(str))).get(i);
}
/**
* 获取一个消息标识对应所有数据
* @param str 可以是类
* 也可以是特殊标识,则不可使用to方法传递消息
*/
private static Map<Integer, Object[]> getData(String str) {
if (MESSAGE_PARAMS.get(str) == null) {
MESSAGE_PARAMS.put(str, new HashMap(2));
}
return (Map)MESSAGE_PARAMS.get(str);
}
/**
* 这里使用主要是适配多module的环境,拿不到具体类时使用
* 向c类发送一个消息标识,并携带objects参数
* 这里只设计了向Activity和Fragment发送,找不到对应类的将会直接返回
*/
private static void toStrFlag(String str, int i) {
getMessage(str).setValue(i);
}
/**
* 这里使用主要是适配多module的环境,拿不到具体类时使用
* 向c类发送一个消息标识,并携带objects参数
* 这里只设计了向Activity和Fragment发送,找不到对应类的将会直接返回
*/
private static void toStrFlag(String str, int i, Object... objects) {
getMessage(str).setValue(i);
getData(str).put(i, objects);
}
}
MessageActivity、MessageFragment(Java)
需要自定义Activity或Fragment基类继承该类
/**
* Fragment同理,继承androidx.fragment.app.Fragment即可
*/
public class MessageActivity extends AppCompatActivity {
private static final Map<String, MutableLiveData<Integer>> MESSAGE = new HashMap(2);
private static final Map<String, Map<Integer, Object[]>> MESSAGE_PARAMS = new HashMap(1);
public MessageActivity() {
}
/**
* 获取该类的动态消息
*/
public <T extends MessageActivity> MutableLiveData<Integer> getMessage(Class<T> c) {
if (MESSAGE.get(c.getName()) == null) {
MESSAGE.put(c.getName(), new MutableLiveData());
}
return (MutableLiveData)MESSAGE.get(c.getName());
}
/**
* 向某一类发送标识
*/
public <T extends MessageActivity> void setValue(Class<T> c, int i) {
this.getMessage(c).setValue(i);
}
/**
* 获取该类下所有消息参数
*/
public <T extends MessageActivity> Map<Integer, Object[]> getData(Class<T> c) {
if (MESSAGE_PARAMS.get(c.getName()) == null) {
MESSAGE_PARAMS.put(c.getName(), new HashMap(2));
}
return (Map)MESSAGE_PARAMS.get(c.getName());
}
/**
* 向某一类发送标识和参数
*/
public <T extends MessageActivity> void setData(Class<T> c, int i, Object[] objects) {
this.getData(c).put(i, objects);
}
/**
* 获取某一类下的数据数组
*/
public <T extends MessageActivity> Object[] getData(Class<T> c, int i) {
return (Object[])this.getData(c).get(i);
}
/**
* 当页面消费过消息时,需要将它值空,这里将标识置为Int.MIN_VALUE
*/
public <T extends MessageActivity> void setDefault(Class<T> c) {
MutableLiveData<Integer> message = this.getMessage(c);
if (message.getValue() == null) {
message.setValue(-2147483648);
}
if ((Integer)message.getValue() != -2147483648) {
this.getMessage(c).setValue(-2147483648);
}
}
/**
* 这里是点击toolbar的返回按钮能够触发返回
* fragment基类中不需要该方法
*/
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == 16908332) {
this.onBackPressed();
return false;
} else {
return true;
}
}
}
使用方法(Kotlin)
基类
open class BaseActivity: MessageActivity() {}
open class BaseFragment: MessageFragment() {}
发送
// 需要MainActivity继承BaseActivity
Message.to(MainActivity::class.java, 1)
Message.to(MainActivity::class.java, 2, "hello", "world")
// 特殊标识,并不支持发送参数
// 可以修改Message.to()方法,让其支持非Activity或Fragment消息发送
Message.getMessage("login_flag").value = 1
接收
// 在Activity或Fragment中使用
getMessage(MainActivity::class.java).observe(this) {
when (it) {
0 -> {}
1 -> {
val array = getData(MessageActivity::class.java, 1)
}
}
setDefault(MainActivity::class.java)
}
// 在存在生命周期的类中使用,或使用了特殊标识
Message.getMessage("login_flag").observe(this) {
when (it) {
0 -> {}
1 -> {}
}
Message.getMessage("login_flag").value = Int.MIN_VALUE
}
注意事项
- 在Fragment中使用时,需要注意生命周期的管理,在大多数情况下可以使用
viewLifecycleOwner
来解决生命周期问题,如果发现不能接收标识变化请先检查生命周期是否出现问题。 - 多module情况下,如果有能力可以将该内容打包,发布到私有仓库中,在需要发送或接收的module中引入该模块即可,
我的域名还没有备案,所以不能提供对应仓库链接 - 添加混淆,防止反射找不到对应方法
-keep class com.example.BaseActivity { public ** getMessage(java.lang.Class); public void setValue(java.lang.Class, int); public void setData(java.lang.Class, int, java.lang.Object[]); } -keep class com.example.BaseFragment { public ** getMessage(java.lang.Class); public void setValue(java.lang.Class, int); public void setData(java.lang.Class, int, java.lang.Object[]); } -keep class com.example.Message {*;}