美文网首页Android android知识点JavaScript
Android:Activity与Fragment通信(99%)

Android:Activity与Fragment通信(99%)

作者: 牛晓伟 | 来源:发表于2016-01-27 15:28 被阅读27791次

前言


最近一直在想着能否有一种更好的方案来解决:Android中Activity与Fragment之间通信的问题,什么叫更好呢,就是能让Fragment的复用性高,性能还有好(不用反射),代码还要好维护,不需要为每对Activity和Fragment之间定义接口而发愁。

先简单说下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的语法更宽泛、更简单的特点: 比如:

  js代码:  
  //函数声明不需要定义返回值,参数前面不需要有类型出现,
  //函数体里面就可以有返回值
  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中有一个重要的点(万事万物皆对象),函数也不列外,并且函数可以作为另外一个函数的参数,如:

     js代码: 
    //遍历一个数组如果是它是数组,就把它乘以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 <Param> 参数 */
     public static abstract class FunctionWithParam<Param> 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; 
    }

     /** * 根据函数名,回调无参无返回值的函数
   * @param funcName */ 
    public void invokeFunc(String funcName) throws FunctionException {
         FunctionNoParamAndResult f = null; 
        if(mFunctionNoParamAndResult != null){ 
              f = mFunctionNoParamAndResult.get(funcName); 
              if(f != null){ f.function(); } 
        }
         if(f == null){ throw new FunctionException("没有此函数"); }
     } 

    /** * 调用具有参数的函数
     * @param funcName 
    * @param param 
    * @param <Param> */ 
      public <Param> void invokeFunc(String funcName,Param param)throws FunctionException{ 
            FunctionWithParam f = null; 
            if(mFunctionWithParam != null){ 
                  f = mFunctionWithParam.get(funcName);
                   if(f != null){ f.function(param); } 
            }
     } 
}

设计思路:

1. 用一个类来模拟Javascript中的一个Function

Function就是此类,它是一个基类,每个Functioon实例都有一个mFuncName 既然是方法(或者函数)它就有有参数和无参数之分
FunctionWithParam<Param>是Function的子类,代表有参数的方法类,方法参数通过泛型解决
FunctionNoParamAndResult是Function的子类,代表无参无返回值的方法类

2. 一个可以存放多个方法(或者函数)的类

Functions类就是此类,下面简单介绍下Functions有4个主要方法:

  • addFunction(FunctionNoParamAndResult function) 添加一个无参无返回值的方法类
  • addFunction(FunctionWithParam function) 添加一个有参无返回值的方法类
  • invokeFunc(String funcName) 根据funcName调用一个方法
  • invokeFunc(String funcName,Param param) 根据funcName调用有参无返回值的方法类

使用举例:代码地址

每个app都有的基础activity(BaseActivity)

     public abstract class BaseActivity extends FragmentActivity { 
          /** 
          * 为fragment设置functions,具体实现子类来做
         * @param fragmentId */ 
        public void setFunctionsForFragment(
              int fragmentId){
        }
   } 

其中的一个activity:

     public class MainActivity extends BaseActivity { 

          @Override public void setFunctionsForFragment(int fragmentId) {
               super.setFunctionsForFragment(fragmentId); 
               switch (fragmentId) {
                   case R.id.fragment_main:
                     FragmentManager fm = getSupportFragmentManager(); 
                    BaseFragment fragment = (BaseFragment) fm.findFragmentById(fragmentId);
                   //开始添加functions
               fragment.setFunctions(new Functions()
                   .addFunction(new Functions.FunctionNoParamAndResult(MainFragment.FUNCTION_NO_PARAM_NO_RESULT) {

                       @Override 
                      public void function() {
                           Toast.makeText(MainActivity.this, "成功调用无参无返回值方法", Toast.LENGTH_LONG).show(); 
                      }
               }).
                  addFunction(new Functions.FunctionWithParam<Integer>(MainFragment.FUNCTION_HAS_PARAM_NO_RESULT) { 
                        @Override 
                        public void function(Integer o) { 
                            Toast.makeText(MainActivity.this, "成功调用有参无返回值方法 参数值=" + o, Toast.LENGTH_LONG).show(); } }));
                       }
               }
 }

每个app都会有的基础fragment(BaseFragment)

     public abstract class BaseFragment extends Fragment { 
            protected BaseActivity mBaseActivity; 
            /** * 函数的集合 */ 
            protected Functions mFunctions; 

            /** * activity调用此方法进行设置Functions
           * @param functions */
           public void setFunctions(Functions functions){ 
                this.mFunctions = functions;
           } 

          @Override 
          public void onAttach(Activity activity) { 
                super.onAttach(activity);
               //呼叫activity进行回调方法的设置 
              if(activity instanceof BaseActivity){ 
                    mBaseActivity = (BaseActivity)activity;
                     mBaseActivity.setFunctionsForFragment(getId());
               } 
          } 
  } 

MainActivity对应的MainFragment

    public class MainFragment extends BaseFragment {

           /** * 没有参数没有返回值的函数 */ 
          public static final String FUNCTION_NO_PARAM_NO_RESULT = "FUNCTION_NO_PARAM_NO_RESULT"; 
          /** * 有参数没有返回值的函数 */
         public static final String FUNCTION_HAS_PARAM_NO_RESULT = "FUNCTION_HAS_PARAM_NO_RESULT"; 

          @Override
           public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
               super.onViewCreated(view, savedInstanceState);
               mBut1 = (Button) getView().findViewById(R.id.click1); 
                mBut3 = (Button) getView().findViewById(R.id.click3);

               mBut1.setOnClickListener(new View.OnClickListener() { 
                    @Override
                     public void onClick(View v) { 
                          try {
                               //调用无参无返回值的方法 
                               mFunctions.invokeFunc(
                                FUNCTION_NO_PARAM_NO_RESULT);       
                        } catch (FunctionException e) {
                               e.printStackTrace(); 
                        } 

                    } 
              }); 

              mBut3.setOnClickListener(new View.OnClickListener() { 
                  @Override
                   public void onClick(View v) {
                         try { 
                                //调用有参无返回值的方法 
                                mFunctions.invokeFunc(
                                 FUNCTION_HAS_PARAM_NO_RESULT, 100); 
                        } catch (FunctionException e) {
                               e.printStackTrace(); }
                     }
               }); 
  } 

看到这您是不是觉得已经结束了,当然是没有了,因为还有2个问题没解决。方法返回值和方法接收多个参数的问题。

方法返回值的问题

上代码:代码地址

    /** * 有返回值,没有参数的方法
     * @param <Result> */ 
    public static abstract class FunctionWithResult<Result> extends Function{

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

          public abstract Result function(); 
    } 

    /** * 带有参数和返回值的 方法 
    * @param <Result> 
    * @param <Param> */
   public static abstract class FunctionWithParamAndResult<Result,Param> extends Function{ 
        public FunctionWithParamAndResult(String functionName) { 
              super(functionName); 
        } 

        public abstract Result function(Param data);
 } 

FunctionWithResult<Result>无参数有返回值的方法类
FunctionWithParamAndResult<Result,Param> 有参数也有返回值的方法类
在Functions类中定义添加和调用这2种方法类的 相应方法。

其次是方法含有多个参数的问题

在解决此问题时我想了很多办法(比如怎样引入多个泛型,但最终以失败告终,希望有看了这篇文章的朋友可以多提下宝贵意见)。然后我就想到了用Bundle来解决多参数的问题,把多个参数放到Bundle中,但是在往Bundle中塞入数据时得有一个对应的key值,生成key值以及记住key值(记住key值是为了从Bundle中取数据)是一个繁琐的事。同时Bundle不能传递非序列化对象。所以就封装了一个FunctionParams类解决以上问题,请看类的实现: 代码地址

  /** * 函数的参数,当函数的参数涉及到多个值时,可以用此类,
   * 此类使用规则:存参数与取参数的顺序必须一致,
   * 比如存参数顺序是new 
 *FunctionParamsBuilder().putString("a").putString("b").putInt(100); 
    *取的顺序也是: functionParams.getString(),   
    *functionParams.getString(), functionParams.getInt(); */
 public static class FunctionParams { 

      private Bundle mParams = new Bundle(1); 
      private int mIndex = -1; 
      private Map mObjectParams = new HashMap(1); 

      FunctionParams(Bundle mParams,Map mObjectParams){ 
          this.mParams = mParams; 
          this.mObjectParams = mObjectParams;
     } 

    public <Param> Param getObject(Class<Param> p){ 
        if(mObjectParams == null){ return null; } 
        return p.cast(mObjectParams.get((mIndex++) + "")); } 

    /** * 获取int值 
    * @return */ 
    public int getInt(){
         if(mParams != null){ 
              return mParams.getInt((mIndex++) + ""); } return 0; 
    } 

    /** * 获取int值 
    * @param defalut 
    * @return */ 
    public int getInt(int defalut){ 
        if(mParams != null){ 
          return mParams.getInt((mIndex++) + ""); 
        }
       return defalut;
     } 

    /** * 获取字符串 
    * @param defalut * @return */
     public String getString(String defalut){ 
        if(mParams != null){ 
          return mParams.getString((mIndex++) + ""); 
        } 
        return defalut; 
    } 

    /** * 获取字符串 * @return */ 
    public String getString(){ 
        if(mParams != null){
             return mParams.getString((mIndex++) + ""); 
      } return null; 
    } 

      /** * 获取Boolean值 
    * @return 默认返回false */
     public boolean getBoolean(){ 
        if(mParams != null){ 
            return mParams.getBoolean((mIndex++) + ""); 
        } return false;
     }

     /** * 该类用来创建函数参数 */
     public static class FunctionParamsBuilder{

         private Bundle mParams ;
         private int mIndex = -1;
         private Map mObjectParams = new HashMap(1); 

        public FunctionParamsBuilder(){ } 

        public FunctionParamsBuilder putInt(int value){
             if(mParams == null){ 
                  mParams = new Bundle(2);
             } 
              mParams.putInt((mIndex++) + "", value); 
              return this; 
      } 

      public FunctionParamsBuilder putString(String value){ 
            if(mParams == null){ 
                mParams = new Bundle(2);
           } 
            mParams.putString((mIndex++) + "", value); 
            return this; 
    }

     public FunctionParamsBuilder putBoolean(boolean value){ 
          if(mParams == null){ mParams = new Bundle(2); } 
          mParams.putBoolean((mIndex++) + "", value); 
          return this;
     } 

      public FunctionParamsBuilder putObject(Object value){ 
          if(mObjectParams == null){ 
              mObjectParams = new HashMap(1); 
          } 
          mObjectParams.put((mIndex++) + "", value);
           return this;
     }

     public FunctionParams create(){
         FunctionParams instance = new FunctionParams(mParams,mObjectParams); return instance; 
    }
  } 
}

FunctionParams封装了取参数的功能,比如:

   public <Param> Param getObject(Class<Param> p){ 
        if(mObjectParams == null){ return null; }
         return p.cast(mObjectParams.get((mIndex++) + ""));
   }

取对象参数的功能,不需要传人key值,只需要传人需要即将取出来的类的Class实例即可

FunctionParamsBuilder类,看它的名字就知道是用了设计模式里的Builder(构建)模式。该类是用来存放参数的,当所有的参数都存放完毕后调用create()方法创建一个FunctionParams对象事物都是有两面性的,有缺点就有优点,只不过是在某些场合下优点大于缺点,还是反之。
FunctionParams解决了以上提到的Bundle传递多参数种种不便的问题,但同时FunctionParams也有一个缺点就是存参数的顺序与取参数的顺序一定要一致,比如:

    //存的顺序 new       
    FunctionParamsBuilder().putString("1").putInt(2)
    .putBoolean(true).create(); 

    //取的顺序 
    functionParams.getString(); 
    functionParams.getInt(); 
    functionParams.getBoolean();

但是这种缺点函数的定义来看也不是缺点。

Activity与Fragment之间的通信是通过Functions的,即把变化的部分封装在Functions是类中,Functions起一个桥梁作用。

此方案优点:

  • Fragment与Activity的耦合性几乎没有
  • 性能也好(没用反射)
  • 可以从Activity获取返回数据
  • 扩展性好(新增加的成对的Activity与Fragment之间的通信只需做以下几步:
    1.新增加Activity只需要覆盖BaseActivity中的 setFunctionsForFragment(int fragmentId) 方法,把相应的回调函数加入。
    2.相应的Fragment定义函数key值即可)

总结

简单总结为以下几点:

  • Fragment的使命
  • Activity与Fragment之间通信的解决方案(handler,广播,EventBus,接口)的优缺点。
  • 我自己关于Activity与Fragment之间通信的解决方案(Functions),其实解决的主要是Fragment调用Activity的方案。

希望大家能多提宝贵意见,多交流。代码地址

本人微信:704451290

本人公众账号

相关文章

网友评论

  • 16aed46f484e:最关键的eventbus和广播你糊弄过去了
  • 小碗熊Tony:看起来,脑壳痛,没看完
  • guodongAndroid:我觉得可以再写个function分发的类,activity或fragment都可以注册function,也就是和eventbus类似的观察者模式
  • AWeiLoveAndroid:其实就是把接口回调的另一种表现方式而已 把接口变成了对象 还不如使用rxjava方便
    062ecb72c08a:阿伟大佬:relaxed:
  • 3998314a4898:今天看了�动脑学院的课程 avin老师讲的万能的接口通信 就是盗用你的方案 最后还无耻的说 他是和他们三星研究院印度�同事学的。
  • TKK_9c55:认真看了下作者的方案,好坏不做评论,挺用心的,fragment通信建议使用本人的https://github.com/TkkSoCool/RxEventProcessor 框架,基于rxjava和编译时注解,对于作者提到eventbus的缺点都已解决。
  • 望北8261:借鉴JavaScript的思路,想法很不错,但是调用的时候需要传入的参数不确定,不好。
    mFunctions.invokeFunc(FUNCTION_HAS_PARAM_NO_RESULT, 100);
  • 茯庅爺:你好,请一定要回答我这个问题可以吗?
    1 就是Activity调用addFunction的时候,使用匿名内部类 ,存储在集合中,不会泄漏吗?
    2 我不绑定Activity和Fragment对象,直接调用有什么坏处吗?
    Activity 代码
    Functions.getInstance().addFunction(new Functions.FunNoParamsNoResult(CallFragment.TAG) {
    @Override
    void function() {
    Toast.makeText(TestFragmentConnActivity.this, "无参数无返回值", Toast.LENGTH_SHORT).show();
    }
    });

    Fragment代码
    tv.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
    Functions.getInstance().invokeFunction(TAG);
    }
    });
  • 黑白咖:签到,还不错,除了最终传递参数部分不能接受外
  • 石野小真人:最快的还是eventbus
  • 17bc5a1984c6:想法很好,有没有想过使用注解来解决
    牛晓伟:@BigDson 没有,说下你的高见吧
  • ee019e17b345:这种方式虽然实现了解耦,但是使用起来非常麻烦,需要定义不同的key-value,以前我也使用过类似的方法去做一些解耦,但是就是试用起来需要定义key-vaule,而且如果东西大了,在处理的地方就会有和很多 if-elseif-elseif-elseif....代码很不雅观。
    我现在的做法是使用RxJava封装一个事件总线,这个写起来比较优雅,避免了 if-elseif。。。虽然还是需要协议好一些东西,但是看起来也比较优雅了
    牛晓伟:@ee019e17b345 好的我试试
  • 欧阳锋:1)注意编程规范,方法和类名后面的大括号前面需要增加一个空格
    2)注意编译器警告提示
    3)这个解决方案并不能很好地解决问题,反而增加了歧义;另外这和回调无差。
  • d60971551f30:评论更精彩啊!:smile:
    这套方案应该适用于庞大的项目。
    小项目的话略显臃肿
  • 自然like:作者写得真好!
    解耦的思想比较透彻,值得学习。
    另外,最近也困于Activity、Fragment通信的问题,目前都是采用接口的形式,但是项目大了,接口越来越泛滥,维护起来比较累。
    参考了作者的思想,但还是不能使用,因为可读性还需要加强,使用起来也没那么直观,特别是Function的Pram、Result这一块
    牛晓伟:@lextime2013 欢迎改进,
  • 韩拓:完美解决方案!!!???
    作者意识到穿参不够优雅,其实可以考虑一下数组穿参
  • b0c712f2ae41:只想说思想是好的,但不是很适用,可以参考思维,不可以用到项目中
  • Wang_Guan:虽然我也并没有看懂,但是还是能感觉到作者的用心。。。赞
  • baoerstudio:哇,好牛啊,小生膜拜中。
  • bf08208dbd61:首先,我觉得楼主的想法是不错的。但我还觉得楼主的这种方法跟 接口方案 是一样的,只是使用 Functions 类作为通用的接口类,使用 action 去区分接口类型。
    其实,个人觉得,使用接口方案就可以很完美实现 分离复用。
    楼主所说的 接口方案的缺点,也不成立。别人需要定义一个接口,你不是还要定义一个 aciton?别人去实现一个接口,你还不是需要去 add 一个 function?给 Fragment 定义一个 setListener方法,不就可以避免 activity的强制转换了么?至于管理接口的话,每个接口都是属于 对应的 Fragment 去管理的,接口都是写到 需要实现的 Fragment 中的,所以,不会引致代码混乱。
    个人觉得编码 遵循 简单美 是很重要的原则。莫把问题复杂化。
    db1a15ea1c51:@大兴兴点灯 给 Fragment 定义一个 setListener方法,不就可以避免 activity的强制转换了么?这个怎么避免强转??
  • zhuhf:思路非常好,佩服
  • 吕中宜:不错不错,解耦的思想值的学习,很棒
    牛晓伟: @lzyforjianshu 谢谢
  • Zane96:还是思考了问题的
  • Mitnick: 欣赏楼主的类比思想; 其实这种方式本质上还是观察者模式, 使用HashMap 维护 function key 和 function, key 还是取自于 MainFragment, 倒不如让 Activity 直接实现 MainFragment 的监听呢. 这种对 function 的抽象有点像命令模式的思想.
  • w4lle:看看EventBus的源码,原理都是把方法名存起来,然后去找。但是EventBus得解耦要比楼主的方式高明很多,这种实现方式说实话真的没有EventBus用起来好用。
  • 流水不腐小夏:楼主,不好意思,我是来泼冷水的, :smile:

    表面上,Fragment和Activity是没有依赖,依赖性的东西全部换成了Functions,然后通信变成了
    Activity--->Functions--->Fragment
    这样Functions功能就变成了一个Adapter,其实Fragment还是间接引用到Activity的,看看MainActivity里面的代码,如所示:
    new Functions.FunctionWithResult<String>(MainFragment.FUNCTION_NO_PARAM_HAS_RESULT) {
    @Override
    public String function() {
    Toast.makeText(MainActivity.this, "成功调用无参有返回值方法", Toast.LENGTH_LONG).show();
    return "恭喜你,调我成功!";
    }
    }
    ,FUNCTION_NO_PARAM_HAS_RESULT相当于是一个action,后面的匿名内相当于是对action的dispatch,然后进行分发。

    不知道你有没有研究过匿名类,匿名类表面上是没有引用到外部类(也就是外面的ACtivity),实际上你可以看看匿名类生成的class文件,其实是有引用的,这样其实是有问题的。

    还有个问题就是BaseFragment里面的mFunctions,mFunctions赋值是使用setter的方式,使用setter方式对Fragment进行传参,是有问题的,一般传递参数都是使用setArguments的方式。

    所以我觉的楼主这种方式不太靠谱,Fragment调用Activity,完全减少依赖,我觉的还是使用广播方式,建议楼主把在Fragment里面对mFunctions进行invoke,变成通过广播发送你想执行函数的action和param。




    guodongAndroid:@流水不腐小夏 我觉得广播是个重量级的通信方式,轻易还是不要用广播吧
    Komi_:@流水不腐小夏 反驳lz的方法我就不说了,你说广播替代简直杀鸡取卵
  • 我是Asha:说实话,这个使用场景上,可读性要求更高点,
    1,传参、取参在两个不同的地方,用起来就要人命;
    2,函数调用时要记住传函数ID,函数参数,参数类型,参数顺序,还没代码提示,还不能查看引用
  • 57fec3321676:想法是不错,我觉得有两点:
    1,可读性有点差。
    2,你确定这样写不会引起memory leak ?
    牛晓伟:@TaurusDream 谢谢你的建议,可读性差确实存在。内存泄露一般不会发生,若不放心,可以在basefragment的onDestory方法中 对functions的实例销毁
  • a37fc4428182:有必要做一下性能对比,是否真的对比EventBus有提高
  • 68768b474bfc:写得真好
  • 6e120294aa97:好吧,不的不承认,你确实是有想法的,但写法...:(,简单的观察者模式,适配器模式就可以很好的解决了这个问题,fragment初期构建就可以将activity作为观察者注册进来,而activity已一个通用适配器接口完成对象获取并触发实际方法调用就可以了,如果说你们的activity没有基础抽象父类或者统一接口,handle仍做为内部类放在activity中,那我也...,再说fragment.setFunctions时犹如裹脚布似的代码,真是...,难道就不能通过编写annotation来完成初期构建时的自动注入,说白了Function不就是维护一个监听列表吗?还把具体invokeFunc写在这个这个维护类里,是不是应该利用组合模式完成Function对象及集合对象的构建,而真正invokeFunc调用交个manager或者factory来完成,或者交由实现者来自行完成。真的想学习所谓的高阶函数运用,看scala会比看js更有启发性。
    牛晓伟:@r0b1n 谢谢您的建议, 简单的观察者模式,适配器模式就可以很好的解决了这个问题,fragment初期构建就可以将activity作为观察者注册进来,而activity已一个通用适配器接口完成对象获取并触发实际方法调用就可以了,如果说你们的activity没有基础抽象父类或者统一接口,handle仍做为内部类放在activity中,那我也... 这段您能在仔细的说下嘛,没看懂
  • kayakaya:很厲害,我剛入門,之前就是覺得activity和fragment之間通信找不到一個很好的通用function
    rivc:官网一开始就写了怎么通讯的 https://developer.android.com/guide/components/fragments.html?hl=zh-cn#CommunicatingWithActivity
  • 斗破_:想法很赞。不过可以考虑使用ResultReceiver类,其中有个函数
    protected void onReceiveResult(int resultCode, Bundle resultData) {
    }
    可以在此类上进行扩展,添加返回值类型Bundle的。
    通过Fragment.setArguments给Fragment。
    Fragment通过ResultReceiver调用Activity方法。
    牛晓伟:@苍穹_ 谢谢你的建议,好办法,觉得Bundle传递的数据有限制
  • 73c0355f26a7:赞,很好,只是觉得用这种方式调用方法代码的阅读性差,review 代码还得去functions中找与之对应的方法。谢谢分享
    牛晓伟:@依然饭特稀西 假如Functions这类是稳定的,就没必要reviewFunctions类, review新增加的activity与Fragment的话,activity提供给Fragment的所有回调都集中在一起,并且function的key值是Fragment提供的,所以review代码的重点就集中在 上面提到的2处
  • bea442da4d9e:楼主的想法的确很不错,学习了!目前项目中用到的通信方式是EventBus
  • alighters:跟RxJava的写法有一些类似之处了。。赞。。 :+1:
    alighters:@牛晓伟 :clap:
    牛晓伟:@lighters_wei 借鉴了RxJava的思想
  • 子茗:虽然看不懂,看还是一个大写的赞,可以看出作者很用心的总结,支持!!

本文标题:Android:Activity与Fragment通信(99%)

本文链接:https://www.haomeiwen.com/subject/dqwdkttx.html