Activity与Fragment通信(99%)完美解决方案Android工程师

/ 福清师大Android工程师 / 2017-05-10

android工程师,android初学者,经验分享

先简单说下Javascript这门语言吧,或许有人就会问:咱们不是聊Android的java问题吗?怎么话题转到JavaScript了。因为我的解决方案的启发是从它来的,没兴趣的朋友可以略过。最近在学习javascript这门语言,同时自己搞Android(java)开发也有5年多时间了,所以在学习js的过程中,就会惯性的把这两者进行比较。


与java语言的 严谨 相比 Javascript是一门“放荡不羁”、”不拘小节”(宽泛)的语言。


为什么要用“放荡不羁”这个词呢,下面是它的一个解释:


放荡不羁 [fàng dàng bù jī][解释] 羁:约束。放纵任性,不加检点,不受约束。


因为我觉得这个词更能充分的体现js弱类型的特点。


在给变量赋值时 可以这样写:


var a = 1;


还可以这样写:


var b = '123';
var o = new Object();


甚至还可以这样写:


var fun = new function(){};
fun1 = new function(){};


可以把任何类型的值赋给一个变量,也可以不加var关键字来声明一个变量,是不是很任性,很不拘束啊。


“不拘小节”主要体现了JavaScript的语法更宽泛、更简单的特点: 比如:


  //函数声明不需要定义返回值,参数前面不需要有类型出现,
 //函数体里面就可以有返回值
 function max(a,b){ return a > b? a:b; }
 /* *可以传递任意多个参数,在java里面根本不可以 */
 function print(){
     var len = arguments.length;
     for(var i = 0; i < len; i++){
         console.log(arguments[i]);
    }
 }

 相应java代码:
  int max(int a, int b){
      return a> b? a:b;
 }

 /* *传递任意多个Object类型的参数 */
 void print(Object... args){
      for (int i = 0; i < args.length; i++){
             System.out.println(args[i]);
       }
  }


上面的代码说明了JavaScript在声明函数时,不会有像java那么严格的规定,语法不拘小节,语法更简单(这里没有说java不好的意思)。


启发点


JavaScript中有一个重要的点(万事万物皆对象),函数也不列外,并且函数可以作为另外一个函数的参数,如:


    //遍历一个数组如果是它是数组,就把它乘以10再输出
    var array = [1,2, '你好' , '不' ,31,15];
   //数组的each方法接收一个函数
    testArray.each( function( value ){
          typeof value == 'number' ? alert( value *10 ):null;
   })  ;


当我看到上面JavaScript中函数的用法时我眼前一亮,为啥我不可以借鉴之来解决android中activity与fragment通信的问题呢?


Fragment的使命


先让我们聊聊Fragment为什么出现,这对于我们解决Activity与Fragment的通信有帮助。一个新事物的产生总是为了解决旧事物存在的问题,Fragment是android3.0的产物,在android3.0之前解决手机、平板电脑的适配问题是很头疼的,对ActivityGroup有印象的朋友,应该能深深的体会到ActivityGroup包裹的多个Activity之间切换等一系列的性能问题。由此Fragment诞生了。个人总结的Fragment的使命:


  • 解决手机、平板电脑等各种设备的适配问题

  • 解决多个Activity之间切换性能问题

  • 模块化,因为模块化导致复用的好处


Fragment的使用


Fragment是可以被包裹在多个不同Activity内的,同时一个Activity内可以包裹多个Fragment,Activity就如一个大的容器,它可以管理多个Fragment。所有Activity与Fragment之间存在依赖关系。


Activity与Fragment通信方案


上文提到Activity与Fragment之间是存在依赖关系的,因此它们之间必然会涉及到通信问题,解决通信问题必然会涉及到对象之间的引用。因为Fragment的出现有一个重要的使命就是:模块化,从而提高复用性。若达到此效果,Fragment必须做到高内聚,低耦合。


现在大家动动脚趾都能想到的解决它们之间通信的方案有:handler,广播,EvnetBus,接口等(或许还有别的方案,请大家多多分享),那我们就聊下这些方案。


handler方案:


先上代码


   public class MainActivity extends FragmentActivity{
     //声明一个Handler
     public Handler mHandler = new Handler(){
         @Override
          public void handleMessage(Message msg) {
               super.handleMessage(msg);
                ...相应的处理代码
          }
    }
    ...相应的处理代码
  }

   public class MainFragment extends Fragment{
         //保存Activity传递的handler
          private Handler mHandler;
          @Override
          public void onAttach(Activity activity) {
               super.onAttach(activity);
              //这个地方已经产生了耦合,若还有其他的activity,这个地方就得修改
               if(activity instance MainActivity){
                     mHandler =  ((MainActivity)activity).mHandler;
               }
          }
          ...相应的处理代码
    }


该方案存在的缺点:


  • Fragment对具体的Activity存在耦合,不利于Fragment复用

  • 不利于维护,若想删除相应的Activity,Fragment也得改动

  • 没法获取Activity的返回数据

  • handler的使用个人感觉就很不爽(不知大家是否有同感)


广播方案:


具体的代码就不写了,说下该方案的缺点:


  • 用广播解决此问题有点大材小用了,个人感觉广播的意图是用在一对多,接收广播者是未知的情况

  • 广播性能肯定会差(不要和我说性能不是问题,对于手机来说性能是大问题)

  • 传播数据有限制(必须得实现序列化接口才可以)
    暂时就想到这些缺点,其他的缺点请大家集思广益下吧。


EventBus方案:


具体的EventBus的使用可以自己搜索下,个人对该方案的看法:


  • EventBus是用反射机制实现的,性能上会有问题(不要和我说性能不是问题,对于手机来说性能是大问题)

  • EventBus难于维护代码

  • 没法获取Activity的返回数据


接口方案


我想这种方案是大家最易想到,使用最多的一种方案吧,具体上代码:


  //MainActivity实现MainFragment开放的接口
 public class MainActivity extends FragmentActivity implements FragmentListener{
       @override
        public void toH5Page(){ }
      ...其他处理代码省略
  }

   public class MainFragment extends Fragment{

        public FragmentListener mListener;
       //MainFragment开放的接口
       public static interface FragmentListener{
           //跳到h5页面
          void toH5Page();
        }

        @Override
       public void onAttach(Activity activity) {
             super.onAttach(activity);
             //对传递进来的Activity进行接口转换
              if(activity instance FragmentListener){
                  mListener = ((FragmentListener)activity);
             }
        }
        ...其他处理代码省略
 }


这种方案应该是既能达到复用,又能达到很好的可维护性,并且性能也是杠杠的。但是唯一的一个遗憾是假如项目很大了,Activity与Fragment的数量也会增加,这时候为每对Activity与Fragment交互定义交互接口就是一个很头疼的问题(包括为接口的命名,新定义的接口相应的Activity还得实现,相应的Fragment还得进行强制转换)。 想看更好的解决方案请看下面章节。


大招来也


设计模式里经常提到的一个概念就是封装变化,同时受javascript中的函数的参数可以是函数对象的启发下,我有了下面的想法,先上代码:代码地址


  /** * + Created by niuxiaowei on 2016/1/20.
 * 各种方法集合的类,可以把一个方法类以key-value的形式放入本类,
 * 可以通过key值来调用相应的方法 */
  public class Functions {

     //带参数方法的集合,key值为方法的名字
     private  HashMap<String,FunctionWithParam> mFunctionWithParam ;
     //无参数无返回值的方法集合,同理key值为方法名字
    private HashMap<String,FunctionNoParamAndResult> mFunctionNoParamAndResult ;

     /** * 基础方法类 */
    public static abstract class Function{
        //方法的名字,用来做调用,也可以理解为方法的指针
         public String mFunctionName;
         public Function(String functionName){
               this.mFunctionName = functionName;
        }
     }

     /** * 带有参数没有返回值的方法
    * @param 参数 */
    public static abstract class FunctionWithParam extends Function{

         public FunctionWithParam(String functionName) {
             super(functionName);
        }

       public abstract void function(Param param);
   }

   /** * 没有参数和返回值的方法 */
  public static abstract class FunctionNoParamAndResult extends Function{
         public FunctionNoParamAndResult(String functionName) {
               super(functionName);
         }

         public abstract void function();
   }

   /** * 添加带参数的函数
    * @param function {@link com.niu.myapp.myapp.view.util.Functions.FunctionWithParam}
   * @return */
    public Functions addFunction(FunctionWithParam function){
            if(function == null){
                 return this;
            }
           if(mFunctionWithParam == null){
                 mFunctionWithParam = new HashMap<>(1);
           }

       mFunctionWithParam.put(function.mFunctionName,function);
       return this;
     }

     /** * 添加带返回值的函数
     * @param function {@link com.niu.myapp.myapp.view.util.Functions.FunctionWithResult}
    * @return */
    public Functions addFunction(FunctionNoParamAndResult function){
         if(function == null){ return this; }
         if(mFunctionNoParamAndResult == null){
               mFunctionNoParamAndResult = new HashMap<>(1);
        }
        mFunctionNoParamAndResult.put(function.mFunctionName,function);
     return this;
   }

    /**


公众号,微信

汇鱼网海峡创乐汇
汇鱼网海峡创乐汇