引用Google官方的话来说,Two Fragments should never communicate directly.
即:Fragment间不应该直接通信。
既然组件间的通信不应该直接进行,那么就需要引入第三方来做一个信使。通信双方应彼此独立,互不依赖,需要对方什么,就直接去找第三方代为传达,相当于中介的存在。
第三方应掌握所有参与通信的对象的需要提供给外部的信息和接口,例如,FragmentOne有一个方法methodA(),需要提供给FragmentTwo来调用,那么FragmentOne就需要让第三方保存methodA(),当FragmentTwo需要调用methodA的时候,只需要向第三方发送调用请求。
在Java中,可以很容易的将一个字段,一个对象传给第三方来保存,但没有一个直接的方式来保存一个方法的引用,那该如何去实现方法的保存呢?
很显然,通过接口,将我们的方法封装到对象中去。
这里说的接口,不是说声明为interface的才叫接口,它也可以是一个抽象基类。
首先为所有的抽象基类定义一个公共基类,来保存方法的基本信息。
Function.java
public class Function {
private String functionName;
public Function(String functionName) {
this.functionName = functionName;
}
public String getFunctionName() {
return functionName;
}
}
这里我们只需要保存方法名即可,因为这里并不是要通过反射的方式去实现方法的调用。
这里的参数,个数统一设置为1,对于需要多个参数的方法,可以通过封装一个对象来进行传参,和EventBus要求单个参数的原因一致,都是为了方便管理,统一规范。
接下来就来实现这四种类型方法的抽象类:
FunctionNoParamNoResult.java
public abstract class FunctionNoParamNoResult extends Function {
public FunctionNoParamNoResult(String functionName) {
super(functionName);
}
public abstract void invoke();
}
FunctionHasParamNoResult.java
public abstract class FunctionHasParamNoResult<T> extends Function {
public FunctionHasParamNoResult(String functionName) {
super(functionName);
}
public abstract void invoke(T param);
}
FunctionNoParamHasResult.java
public abstract class FunctionNoParamHasResult<T> extends Function {
public FunctionNoParamHasResult(String functionName) {
super(functionName);
}
public abstract T invoke();
}
FunctionHasParamHasResult.java
public abstract class FunctionHasParamHasResult<T, P> extends Function {
public FunctionHasParamHasResult(String functionName) {
super(functionName);
}
public abstract T invoke(P param);
}
四个类都非常简单,只有invoke方法不同,而在组件中,只需将要保存的方法在invoke中进行实现即可。
所有的方法类都实现了,那么如何对他们进行管理呢?很简单,使用四个Map就可以了。
在管理类中,只需要为每一种类型的方法分别提供两个接口:添加方法和调用方法。
当然,第三方管理类需要做成单例,并保存四种方法类型的Map:
FunctionManager.java
private static volatile FunctionManager sInstance;
private Map<String, FunctionNoParamNoResult> mNoParamNoResultMap;
private Map<String, FunctionNoParamHasResult> mNoParamHasResultMap;
private Map<String, FunctionHasParamNoResult> mHasParamNoResultMap;
private Map<String, FunctionHasParamHasResult> mHasParamHasResultMap;
public static FunctionManager getInstance(){
if(sInstance == null){
synchronized (FunctionManager.class){
if(sInstance == null) {
sInstance = new FunctionManager();
}
}
}
return sInstance;
}
private FunctionManager() {
mNoParamNoResultMap = new HashMap<>();
mNoParamHasResultMap = new HashMap<>();
mHasParamNoResultMap = new HashMap<>();
mHasParamHasResultMap = new HashMap<>();
}
为无参数无返回值的方法提供添加和调用:
public void addFunction(FunctionNoParamNoResult function){
mNoParamNoResultMap.put(function.getFunctionName(), function);
}
public void invokeFunction(String functionName){
if(TextUtils.isEmpty(functionName)){
return;
}
if(mNoParamNoResultMap != null){
FunctionNoParamNoResult f = mNoParamNoResultMap.get(functionName);
if(f != null){
f.invoke();
} else {
new FunctionNotFoundException("has no this invoke " +
functionName).printStackTrace();
}
}
}
为无参数有返回值的方法提供添加和调用:
public void addFunction(FunctionNoParamHasResult function){
mNoParamHasResultMap.put(function.getFunctionName(), function);
}
public <T> T invokeFunction(String functionName, Class<T> t){
if(TextUtils.isEmpty(functionName)){
return null;
}
if(mNoParamHasResultMap != null){
FunctionNoParamHasResult f = mNoParamHasResultMap.get(functionName);
if(f != null){
return t.cast(f.invoke());
} else {
new FunctionNotFoundException("has no this invoke " +
functionName).printStackTrace();
}
}
return null;
}
为有参数无返回值的方法提供添加和调用:
public void addFunction(FunctionHasParamNoResult function){
mHasParamNoResultMap.put(function.getFunctionName(), function);
}
public <P> void invokeFunction(String functionName, P p){
if(TextUtils.isEmpty(functionName)){
return;
}
if(mHasParamNoResultMap != null){
FunctionHasParamNoResult f = mHasParamNoResultMap.get(functionName);
if(f != null){
f.invoke(p);
} else {
new FunctionNotFoundException("has no this invoke " +
functionName).printStackTrace();
}
}
}
为有参数有返回值的方法提供添加和调用:
public void addFunction(FunctionHasParamHasResult function){
mHasParamHasResultMap.put(function.getFunctionName(), function);
}
public <T,P> T invokeFunction(String functionName, P p, Class<T> t) {
if(TextUtils.isEmpty(functionName)){
return null;
}
if(mHasParamHasResultMap != null){
FunctionHasParamHasResult f = mHasParamHasResultMap.get(functionName);
if(f != null) {
if(t != null){
return t.cast(f.invoke(p));
}
}
}
return null;
}
FunctionNotFoundException.java
public class FunctionNotFoundException extends Exception {
public FunctionNotFoundException(String message) {
super(message);
}
}
到此就完成了所有的封装工作,每一个类都非常的简单,也很容易理解。
调用也很简单:
添加方法
FunctionManager.getInstance().addFunction(
new FunctionHasParamNoResult<String>("hasParamNoResult") {
@Override
public void invoke(String param) {
//do something
}
});
调用方法
FunctionManager.getInstance().invokeFunction("hasParamNoResult", "Hello from the FragmentOne");
从头到尾,都只是使用了非常基础的Java知识,没有调用生疏的方法、也没有晦涩的知识点。
当然,这样的封装只是为了表达解耦的思想,如果真要在项目中使用,那还是直接去用已经封装得非常好且功能更全面、性能也更好的第三方插件就好了。在开发中,解耦是个非常重要的问题,项目越大,解耦带来的便利性越明显;相反,如果模块间耦合度太高,就会出现牵一发而动全身的问题,稍微改一个地方就可能让整个项目都无法正常运行。
很多时候,并不是技术水平阻碍了自己,而是我的视角,高度,让我连这么简单的方案可能都想不到。当然,经验可能会是很重要的原因,但我相信,一定还有其他的办法来提高自己的视野,格局,而不单是仅仅靠工作经验。