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的接口,而接口主要有以下几种形式:
- 有参数无返回值 addFunction(FunctionWithParamNoResult)
- 无参数无返回值 addFunction(FunctionNoParamNoResult)
- 有参数有返回值 addFunction(FunctionWithParamWithResult)
无参数有返回值 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下)