fragment通信的几种方式(接口框架)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/HUandroid/article/details/79481363

fragment通信是我们经常遇到问题,各种解决方案都有。
首先明确的重要点:谷歌官方建议两个fragment不要直接通信,会造成碎片耦合。
方案一:handler方案
就是通过handler发送消息,

  • activity向fragment传递消息
    activity响应事件,发送msg,到fragment中,而fragment中用来接收msg,所以用注册Handler对象,activity中发送msg的对象与fragment中handler对象必须为同一个(handler,msgqueen和looper相互唯一绑定),所以要在activity中暴露pubilic方法让fragment的acttch周期中传递handler。部分代码如下:

activity的响应事件发送msg:

tvMain.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //如果是activity相应事件,发送msg
                if(handler == null){
                    return;
                }
                Message message = new Message();
                Bundle bundle = new Bundle();
                bundle.putString("hehe","我是activity发送的msg");
                message.setData(bundle);
                handler.sendMessage(message);
            }
        });

activity中暴露的setHandler方法:

 //activity暴露方法来给fragment来设置
    public void setHandler(Handler handler){
        this.handler = handler;
    }

fragment中传递handler:

@Override
    public void onAttach(Context context) {
        super.onAttach(context);
        MainActivity activity = (MainActivity) getActivity();
        activity.setHandler(mHandler);
    }

fragment注册handler用来接收msg:

 private Handler mHandler = new Handler(){

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);

            Bundle bundle = msg.getData();
            strHandler = bundle.getString("hehe");
            textView.setText(strHandler+TwoFragment.class.getName());
        }
    };

这里写图片描述

  • fragment向activity传递消息
    反过来就行了,跟上面一样,不多讲了。
    缺点了:
    Fragment对具体的Activity存在耦合,不利于Fragment复用
    不利于维护,若想删除相应的Activity,Fragment也得改动
    没法获取Activity的返回数据,需要双向通信才行

方案二,利用广播的方式

上面是activity向fragment传递,这里用广播方式由fragment向activity传递数据。
在fragment中发送广播,在activity中注册广播接收者,并作出相应逻辑。

在fragment中发送广播信息

 button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //发送广播
                Intent intent = new Intent();
                intent.setAction("co.daggertest.fragment.ThreeFragment");
                intent.putExtra("heihei","fragment发送广播");
                //使用的本地广播,用于单个app内通信,性能和安全性好点
                LocalBroadcastManager.getInstance(getActivity()).sendBroadcast(intent);
            }
        });

在activity中注册广播:

         //注册本地广播接收者
        myBroadcast = new MyBroadcast();
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction("co.daggertest.fragment.ThreeFragment");
        LocalBroadcastManager.getInstance(this).registerReceiver(myBroadcast,intentFilter);
    内部类创建实例
 class MyBroadcast extends BroadcastReceiver{

        @Override
        public void onReceive(Context context, Intent intent) {
            heihei = intent.getStringExtra("heihei");
            tvMain.setText(heihei+MainActivity.class.getName());

        }
    }

在activity的onDestory中注销广播:

 @Override
    protected void onDestroy() {
        super.onDestroy();
        LocalBroadcastManager.getInstance(this).unregisterReceiver(myBroadcast);
    }

动态注册的广播一定要注意注销。
这里写图片描述

* 方案三、使用EventBus3.0通信*
EventBus是一款针对Android优化的发布/订阅事件总线。简化了应用程序内各组件间、组件与后台线程间的通信。

(EventBus不熟悉的可以看下这位大牛的博客:
Android事件总线(一)EventBus3.0用法全解析)
第三方库肯定首先需要依赖库:

在app下的build.gradle的dependencies下

compile 'org.greenrobot:eventbus:3.1.1'

在activity中注册EventBus来接收

 @Override
    protected void onResume() {
        super.onResume();
        //在onResume中注册,在可交互状态下占用更少内存
        EventBus.getDefault().register(this);
    }

    @Override
    protected void onPause() {
        super.onPause();
        EventBus.getDefault().unregister(this);
    }

在fragment中响应事件发送msg

 button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
               //利用EventBus发送
                EventBus.getDefault().post("我是Four用EventBus传递的数据");
            }
        });

在activity接收事件

@Subscribe(threadMode = ThreadMode.MAIN)
    public void getFragmentMsg(String msg){
        tvMain.setText(msg+MainActivity.class.getName());
    }

这里写图片描述

方案四,使用接口’
直接上代码,在fragment中创建接口,activity实现接口中方法,完成回调和参数传递。

创建接口:

 public interface FourMainActivity{
        void changerSomething(String msg);
    }

绑定activity:


    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        fourMainActivity = (FourMainActivity) context;
    }

设置监听回调:

 button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                fourMainActivity.changerSomething("我是通过接口传递到MainActivity中");
            }
        });

activity实现接口:

public class MainActivity extends AppCompatActivity implements FourFragment.FourMainActivity

实现接口方法:

@Override
    public void changerSomething(String msg) {
        tvMain.setText(msg);
    }

这里写图片描述

方案五:万能的接口框架

上次看了动能学院的公开课,里面讲到的方案。设计一个接口基类Function,抽象接口。每个Function实例都有对于的名字,用于区分多个fragment的接口,而接口主要有以下几种形式:

  1. 有参数无返回值 addFunction(FunctionWithParamNoResult)
  2. 无参数无返回值 addFunction(FunctionNoParamNoResult)
  3. 有参数有返回值 addFunction(FunctionWithParamWithResult)
  4. 无参数有返回值 addFunction(FunctionNoParamWithResult)

    基类Function:

public abstract class Function {

    public String mFunctionName;


    /**
     * 构造方法
     * @param functionName  接口的名字,必须实现
     */
    public Function(String functionName) {
        this.mFunctionName = functionName;
    }
}

没有参数没有返回值接口:FunctionNoParamNoResault

public abstract class FunctionNoParamNoResault extends Function {
    /**
     * 构造方法
     *
     * @param functionName 接口的名字,必须实现
     */
    public FunctionNoParamNoResault(String functionName) {
        super(functionName);
    }

    /**
     * 无参数
     */
    public abstract  void  function();
}

没有参数带有返回值接口:FunctionNoParamWithResault

public abstract class FunctionNoParamWithResault<Result> extends Function {
    /**
     * 构造方法
     *
     * @param functionName 接口的名字,必须实现
     */
    public FunctionNoParamWithResault(String functionName) {
        super(functionName);
    }

    /**
     * 无参数,带有返回值
     * Result  泛型
     */
    public abstract  Result  function();
}

有参数带有返回值:FunctionWithParamWithResault

public abstract class FunctionWithParamWithResault<Param , Result> extends Function {
    /**
     * 构造方法
     *
     * @param functionName 接口的名字,必须实现
     */
    public FunctionWithParamWithResault(String functionName) {
        super(functionName);
    }

    /**
     * 有参数,带有返回值
     */
    public abstract  Result  function(Param data);
}

有参数无返回值的:FunctionWithParamNoResault

public abstract class FunctionWithParamNoResault<Param> extends Function {
    /**
     * 构造方法
     *
     * @param functionName 接口的名字,必须实现
     */
    public FunctionWithParamNoResault(String functionName) {
        super(functionName);
    }

    /**
     * 有参数,无返回值
     */
    public abstract  void   function(Param data);
}

管理类FunctionManager:

public class FunctionManager {

    private static FunctionManager instance;
    private HashMap<String, FunctionNoParamNoResault> mFunctionNoParamNoResault;
    private HashMap<String, FunctionNoParamWithResault> mFunctionNoParamWithResault;
    private HashMap<String, FunctionWithParamNoResault> mFunctionWithParamNoResault;
    private HashMap<String, FunctionWithParamWithResault> mFunctionWithParamWithResault;


    public FunctionManager() {
        mFunctionNoParamNoResault = new HashMap<>();
        mFunctionNoParamWithResault = new HashMap<>();
        mFunctionWithParamNoResault = new HashMap<>();
        mFunctionWithParamWithResault = new HashMap<>();
    }

    public static FunctionManager getInstance(){
        if(instance == null){
            instance = new FunctionManager();
        }
        return instance;
    }

    /**
     * 管理类添加  无参无返 
     * @param function
     * @return 链式调用返回本身
     */
    public FunctionManager addFunction(FunctionNoParamNoResault function){
        mFunctionNoParamNoResault.put(function.mFunctionName,function);
        return this;
    }

    /**
     * 管理类调用  无参无返 
     * @param name
     */
    public void invokeFunc(String name){
        if(TextUtils.isEmpty(name)){
            return;
        }

        if(mFunctionNoParamNoResault != null){
            FunctionNoParamNoResault fNPNR = mFunctionNoParamNoResault.get(name);
            if(fNPNR != null){
                fNPNR.function();
            }
            if(fNPNR == null){
                try {
                    throw  new FunctionException("Has no this function" + name);
                } catch (FunctionException e) {
                    e.printStackTrace();
                }
            }
        }
    }


    /**
     * 管理类添加  无参有返 
     * @param function
     * @return 链式调用返回本身
     */
    public FunctionManager addFunction(FunctionNoParamWithResault function){
        mFunctionNoParamWithResault.put(function.mFunctionName,function);
        return this;
    }

    /**
     * 管理类调用 无参有返 
     * @param name 接口名字
     * @param clz   万能对象
     * @param <Result> 参数类型
     * @return 返回类型
     */
    public <Result> Result invokeFunc(String name,Class<Result> clz){
        if(TextUtils.isEmpty(name)){
            return null;
        }
        if(mFunctionNoParamWithResault != null){
            FunctionNoParamWithResault fNPWR = mFunctionNoParamWithResault.get(name);
            if(fNPWR != null){
                if(clz != null){
                    return clz.cast(fNPWR.function());
                }else {
                    return (Result) fNPWR.function();
                }
            }else {
                try {
                    throw  new FunctionException("Has no this function" + name);
                } catch (FunctionException e) {
                    e.printStackTrace();
                }
            }

        }
        return null;
    }


    /**
     * 管理类 添加有参有返 
     * @param function 实例
     * @return 链式调用
     */
    public FunctionManager addFunction(FunctionWithParamWithResault function){
        mFunctionWithParamWithResault.put(function.mFunctionName,function);
        return this;
    }

    /**
     * 管理类调用 有参有返 
     * @param name 接口名字
     * @param clz   万能对象
     * @param <Result> 参数类型
     * @return 返回类型
     */
    public <Result,Param> Result invokeFunc(String name, Class<Result> clz, Param data){
        if(TextUtils.isEmpty(name)){
            return null;
        }
        if(mFunctionWithParamWithResault != null){
            FunctionWithParamWithResault fWPWR = mFunctionWithParamWithResault.get(name);
            if(fWPWR != null){
                if(clz != null){
                    return clz.cast(fWPWR.function(data));
                }else {
                    return (Result) fWPWR.function(data);
                }
            }else {
                try {
                    throw  new FunctionException("Has no this function" + name);
                } catch (FunctionException e) {
                    e.printStackTrace();
                }
            }

        }
        return null;
    }

    /**
     * 管理类 添加有参无返 
     * @param function
     * @return
     */
    public FunctionManager addFunction(FunctionWithParamNoResault function){
        mFunctionWithParamNoResault.put(function.mFunctionName,function);
        return this;
    }

    /**
     * 管理类调用 有参无返 
     * @param name 名字
     * @param data 参数
     * @param <Param> 泛型
     */
    public <Param> void invokeFunc(String name, Param data){
        if(TextUtils.isEmpty(name)){
            return;
        }
        if(mFunctionWithParamWithResault != null){
            FunctionWithParamWithResault fWPWR = mFunctionWithParamWithResault.get(name);
            if(fWPWR != null){
                fWPWR.function(data);
            }else {
                try {
                    throw  new FunctionException("Has no this function" + name);
                } catch (FunctionException e) {
                    e.printStackTrace();
                }
            }

        }

    }


}

这里写图片描述
原理如上图一样,具体看下面实现代码:
BaseFragment提取公共类:

public class BaseFragment extends Fragment {


    protected FunctionManager mFunctionManager;
    private MainActivity mBaseActivity;

    /**
     * 为要实现接口的Fragment添加FunctionManager
     * @param functionManager
     */
    public void setmFunctionManager(FunctionManager functionManager){
        this.mFunctionManager = functionManager;
    }

    /**
     * 确保Mainctivity实现了Fragment相应的接口回调
     * @param context
     */
    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        if(context instanceof MainActivity){
            mBaseActivity = (MainActivity) context;
           mBaseActivity.setFunctionForFragment(getId());
        }
    }

}

MainActivity中往HashMap中添加对应的实例
这里用有参有返FunctionWithParamWithResault为例子

/**
     * 添加接口并实现接口方法的回调
     * @param id  Fragment的标记
     */
    public void setFunctionForFragment(int id){
        FragmentManager fm = getSupportFragmentManager();
        BaseFragment fragment = (BaseFragment) fm.findFragmentById(id);
        FunctionManager functionManager = FunctionManager.getInstance();
        fragment.setmFunctionManager(functionManager.addFunction(new FunctionWithParamWithResault<String, String>(ThreeFragment.INTERFACE_PARAM_RESULT) {

            @Override
            public String function(String data) {
                Toast.makeText(MainActivity.this,data,Toast.LENGTH_SHORT).show();
                //data为fragment传过来的参数
                tvMain.setText(data);
                //返回给fragment的值
                return "activity返回的值";
            }
        }));
    }

fragment中响应事件,获取到对应的实例(注意泛型),这里是有返回值得,re即为返回值

 button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
               String re = FunctionManager.getInstance().invokeFunc(INTERFACE_PARAM_RESULT,String.class, "我是传递的参数");
               buttonRe.setText(re);
            }
        });

看下运行效果:
这里写图片描述

关于多参数的问题
bundle传参,通过key value方式:代码如下修改:
fragment修改如下,传递bundle就行

button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
               Bundle bundle = new Bundle();
               bundle.putString("str","str");
               bundle.putInt("int",1010);
               String re = FunctionManager.getInstance().invokeFunc(INTERFACE_PARAM_RESULT,String.class, bundle);
               buttonRe.setText(re);
            }
        });

activity修改如下:

public void setFunctionForFragment(int id){
        FragmentManager fm = getSupportFragmentManager();
        BaseFragment fragment = (BaseFragment) fm.findFragmentById(id);
        FunctionManager functionManager = FunctionManager.getInstance();
        fragment.setmFunctionManager(functionManager.addFunction(new FunctionWithParamWithResault<Bundle, String>(ThreeFragment.INTERFACE_PARAM_RESULT) {

            @Override
            public String function(Bundle bundle) {
                String str = bundle.getString("str");
                int anInt = bundle.getInt("int");
                Toast.makeText(MainActivity.this,"str="+str+"   int="+anInt,Toast.LENGTH_SHORT).show();
                tvMain.setText("str="+str+"   int="+anInt);
                return "activity返回的值";
            }
        }));
    }

看下运行效果:
这里写图片描述

同理,返回参数也可以设置成Bundle。

总结:

  • 如果项目中fragment不是很多话,可以用广播传递,注意广播的注销,也比较简单。
  • 最后不要使用Handler,耦合比较高,处理不好容易内存泄露,无法获取activity返回值
  • EventBus采用反射机制,造成性能上问题,无法获取activity的返回值
  • 普通接口,用于少量fragment中,性能还不错,怕扩展。
  • 万能接口,可以获取activity的返回值,原理简单,写起来有难度(不copy下)

猜你喜欢

转载自blog.csdn.net/HUandroid/article/details/79481363