Android Fragment使用接口通信

前段时间完成的项目,fragment之间的通信都是都是使用handler传送Message。然后学习了EventBus的一些内容,现在反过来看传统的接口调用,做一些笔记。


步骤:

1、在发送方定义一个接口

2、在接收方实现这个接口

3、传递信息


定义接口

这里我们假如一个fragment想要给他的activity发送一个string信息,那么,首先要在发送的fragment定义一个接口。

    public interface OnUpdateContentListener {
        void setContext(String msg);
    }

这里我们定义了一个OnUpdateContentListener的接口 ,Fragment在onAttach()回调函数中获取接口的具体实现的对象。之后,fragment就可以调用接口中的方法实现与Activity的通信。onAttach()是Fragment生命周期中第一个会调用的函数,紧接着是OnCreate和OncreateView。

    /**
     * 当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);
    }

这样,一个完整的流程就结束了,点击button,activity就可以显示我们发送的1234567890


那么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);
    }

fragment2的方法名不一定要叫做接口内申明的名字,可以随便起,但是activtiy的是继承了接口的,不要命名错了。


完整代码:

点击下方的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);
    }
}

不需要继承接口。


示意图:

Android Fragment使用接口通信_第1张图片         Android Fragment使用接口通信_第2张图片


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可以任意命名函数名之后,代码混淆的问题得到了解决。

你可能感兴趣的:(Android)