LiveData实现组件通信-Message

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 {*;}
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

baiyunkai295

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值