前段时间完成的项目,fragment之间的通信都是都是使用handler传送Message。然后学习了EventBus的一些内容,现在反过来看传统的接口调用,做一些笔记。
步骤:
1、在发送方定义一个接口
2、在接收方实现这个接口
3、传递信息
定义接口
这里我们假如一个fragment想要给他的activity发送一个string信息,那么,首先要在发送的fragment定义一个接口。
public interface OnUpdateContentListener {
void setContext(String msg);
}
/**
* 当Fragment与Activity发生关联时调用。
*
* @param activity
*/
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// 这是为了保证Activity容器实现了用以回调的接口。如果没有,它会抛出一个异常。
try {
// 实例化接口
mCallback = (OnUpdateContentListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnHeadlineSelectedListener");
}
}
}
发送信息
现在fragment可以在合适的地方调用接口的setContent方法,将string传递给activity了。这里我们使用了一个button
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mCallback.setContext("1234567890");
}
});
实现接口
接收到activity要干点什么呢,我们希望他能在自己的textview上显示接收到的string,所以activity需要继承fragment的接口,并且实现这个方法。
@Override
public void setContext(String msg) {
TextView tv = (TextView) findViewById(R.id.tv_content);
tv.setText(msg);
fragment2.setContext(msg);
}
那么fragment之间如果要依靠接口该怎么办呢?其实只要在之前的代码做一些修改就可以了。fragment之间传递信息的本质,还要需要fragment先传递给activity,在传递给另一个fragment。
所以,我们需要在activity中获取另一个fragment的实体,然后在修改一下之前activity实现的setContent
fragment2 = (NormalFragment2) getSupportFragmentManager().findFragmentById(R.id.normal_fragment2);
@Override
public void setContext(String msg) {
TextView tv = (TextView) findViewById(R.id.tv_content);
tv.setText(msg);
fragment2.setContext(msg);
}
完整代码:
点击下方的fragment的button,可以让activity还有上方的fragment中的textview内容变成1234567890
MainActivity.java
public class MainActivity extends FragmentActivity implements NormalFragment.OnUpdateContentListener {
private NormalFragment2 fragment2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_normal);
fragment2 = (NormalFragment2) getSupportFragmentManager().findFragmentById(R.id.normal_fragment2);
}
@Override
public void setContext(String msg) {
TextView tv = (TextView) findViewById(R.id.tv_content);
tv.setText(msg);
fragment2.setContext(msg);
}
}
注意继承接口。这里使用的fragment是v4,所以fragmentMananger要使用getSupportFragmentManager。
app的fragment支持3.0以后,v4可以兼容到1.6,xml中标签的使用没有区别,但如果activity中使用了v4的fragment,那么acvity必须继承FragmentActivity,否则,默认加载app的fragment。
NormalFragment.java(发送方)
public class NormalFragment extends Fragment {
private OnUpdateContentListener mCallback;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_normal, null);
Button btn = (Button) view.findViewById(R.id.btn_send);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mCallback.setContext("1234567890");
}
});
return view;
}
public interface OnUpdateContentListener {
void setContext(String msg);
}
/**
* 当Fragment与Activity发生关联时调用。
*
* @param activity
*/
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// 这是为了保证Activity容器实现了用以回调的接口。如果没有,它会抛出一个异常。
try {
// 实例化接口
mCallback = (OnUpdateContentListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnHeadlineSelectedListener");
}
}
}
NormalFragment2.java(接受信息的fragment)
public class NormalFragment2 extends Fragment {
private View mView;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_normal2, null);
mView = view;
return view;
}
// 名字随意
public void setContext(String msg) {
TextView tv = (TextView) mView.findViewById(R.id.tv_content2);
tv.setText(msg);
}
}
不需要继承接口。
ps:交流会分享这段遇到了一些问题,这里po一下提醒自己。
为什么使用handler来传递信息?
Android UI的操作必须由主线程来处理,Android在启动程序时就启动了一个主线程,使用单线程模式,通过限制来防止线程的不安全。也就是说Android的UI操作不是安全的,view的更新有两个函数,invalidate和postInvalidate,前者在UI线程中执行,后者在非UI线程中执行。Android的UI操作不是安全的,意味着invalidate不是安全的,如果在多线程中并发访问可能会导致UI控件处于不可预测的状态。而postInvalidate的本质,是使用handler去发送信息到队列,最后交给主线程处理。
那么为什么系统不对UI控件的访问加上锁机制呢?缺点有两个:首先加上上锁机制会让UI访问的逻辑变得复杂;其次锁机制会降低UI的访问效率,因为锁机制会阻塞某些进程的执行。鉴于这两个缺点,最简单且高效的方法就是采用单线程模型来处理UI操作。
至于强行在子线程更新UI会怎么,以及为什么,需要看源码,参考链接:http://www.cnblogs.com/lao-liang/p/5108745.html
hander帮助了多线程之间协同工作,帮助子线程通知主线程更新界面。
为什么使用接口?
上文中的fragment通信使用到了接口,我认为可能跟线程安全没有太大的联系。
但是接口有一点优势就是,你可以在事情没有发生之前就知道该怎么处理。比如说,如果你先写了A,然后再写处理A的B,那么你永远都不会知道接口的好处,但是如果你先写B,A还没有写,你就要处理他,或者将来有千千万万个和A相似的东西,你就知道接口或者说抽象的好处了。在A没有出现之前,如果他继承于BaseA或者实现一个接口,那么B就可以知道如何处理A,并且以后出现再多A,B也不需要修改。
还有一点就是规范化操作流程。平时我们玩游戏,会有很多的职业也好,英雄也好可以选择,这些对象一定会有共同点,比如他们一定会有攻击值,防御值,攻击这个动作,外观设定,这些都是共同的,如果我们继承了接口或者继承了对象,这些内容都需要实现,如果没有抽象,我们在创建新的角色时,很可能会漏掉点什么。
所以接口既能起到约束的作用,使开发流程清晰,也能让项目分工协作,不需要知道其他人具体做什么,就完成自己的部分。
EventBus相比较handler和接口的优势?
接口是不能控制线程问题的。但是规范了实现。
handler主要处理子线程通知主线程界面更新,但是实现上面,目前我们的项目中需要根据传入的标示来选择执行对应的内容。
eventBus独立出一个发布订阅模块
,调用者可以通过使用这个模块,屏蔽一些线程切换问题,简单地实现发布订阅功能。调用灵活,不用每次都注入context,父类对于通知的监听和处理可以继承给子类,通知具有优先级,可以保证Subscriber 关注最重要的通知;粘滞事件(sticky events)能够保证通知不会因 Subscriber 的不在场而忽略。使用也非常简单,快速而轻量,特别是3.0可以任意命名函数名之后,代码混淆的问题得到了解决。