1. 程式人生 > >[Android]Fragment與Activity之間的通訊方案

[Android]Fragment與Activity之間的通訊方案

Fragment依賴於Activity,那麼它們之間的通訊就必須要涉及。通過自己的尋找和實踐,知道了Fragment和Activity之間的通訊方案大概有5種,各有優缺點,用哪個方案看需求吧。

  1. setArguments(Bundle args)
  2. Handler
  3. Broadcast廣播
  4. EventBus
  5. 介面回撥

上程式碼,開始分析。自己寫了個小Demo:Fragment通訊Demo ,裡邊有上邊前4種通訊方式。  

demo中1、2、3為activity->fragment,2、3若要fragment->activiy則相關程式碼調換位置即可。4包含雙向通訊。

一、 setArguments(Bundle args)

 此方式還挺簡單的,貌似是谷歌官方推薦的一種資料傳遞方式,如果遇到記憶體重啟,系統會自動儲存資料,和Activity中的Intent一個原理。

用Android Studio新建一個Fragment時,會自帶一個函式:

private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";

private String mParam1;
private String mParam2;

/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* @param param1 Parameter 1.
* @param param2 Parameter 2.
* @return A new instance of fragment Fragment1.
*/
// TODO: Rename and change types and number of parameters
public static Fragment1 newInstance(String param1, String param2) {
	Fragment1 fragment = new Fragment1();
    Bundle args = new Bundle();
    args.putString(ARG_PARAM1, param1);
    args.putString(ARG_PARAM2, param2);
    //儲存資料
    fragment.setArguments(args);
    return fragment;
}

在Activity中新建Fragment例項的時候,直接傳入想要傳送的資料。

//1.直接傳入引數 setArguments() 傳遞資料
Fragment1 fragment1 = Fragment1.newInstance(number1, number2);

取出資料在Fragment的OnCreat()中,用getArguments()函式取出。

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    //獲取activity傳遞的資料
    if (getArguments() != null) {
        mParam1 = getArguments().getString(ARG_PARAM1);
        mParam2 = getArguments().getString(ARG_PARAM2);
    }
}

二、Handler方式

比如Activity向Fragment傳遞資料,首先,保證進行通訊的Activity和Fragment使用同一個Handler例項物件。Activity中設定全域性 變數hander,並給出其Setter方法。在響應事件中,封裝資料到message中,然後進行傳送。

//2.使用handler傳遞資料
//在handler傳送資料之前,注意需要先開啟fragment。
//否則handler變數一直為空 此handler變數是fragment呼叫setHandler()傳來的。
if (handler == null) {
    return;
}
Message message = new Message();

bundle.putString(SaveData1, number1);
bundle.putString(SaveData2, number2);
message.setData(bundle);
handler.sendMessage(message);

Activity中暴露的Setter方法:

//設定handler物件  由fragment呼叫
public void setHandler(Handler handler) {
    this.handler = handler;
}

Fragment中要接收資料,所以要註冊一個Handler來接收Message。同時,還要將handler物件設定給Activity。

onAttach中的activity是在基類BaseFragment中設定的,子類直接用了。

activity = (FrgmentActivity) context; //防止getActivity()空指標
@Override
public void onAttach(Context context) {
    super.onAttach(context);
    //設定Activity的handler物件
    //為的是 activity和fragment使用同一個handler物件 才能進行handler通訊
    activity.setHandler(handler);
}

//定義handler 用於接收訊息 接收activity傳遞來的資料
private Handler handler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);

        Bundle bundle = msg.getData();
        String number1 = bundle.getString("number1");
        String number2 = bundle.getString("number2");

        //進行資料操作
        textResult.setText(doAddition(number1, number2));
    }
};

因為要在Fragment的生命週期onAttach中設定Activity的handler物件,所以傳遞資料傳送訊息,一定要在Fragment載入之後,即生命週期從onAttach走到onResume。否則Activity中的handler物件始終為null。


三、Broadcast廣播

廣播有很多用處,當然也可以用在這裡。Activity向Fragment通訊,則在Activity中傳送廣播,在Fragment中接收廣播。

Activity中傳送廣播:

//3.使用廣播傳遞資料
Intent intent = new Intent();
intent.setAction(ACTION_NAME);
intent.putExtra(SaveData1, number1);
intent.putExtra(SaveData2, number2);

//sendBroadcast(intent);
//使用本地廣播更加安全
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);

Fragment中接收廣播:

public class Fragment3 extends BaseFragment {

    private MyReceiver myReceiver;

    @Override
    public void onResume() {
        super.onResume();
        myReceiver = new MyReceiver();
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(activity.ACTION_NAME);
        //註冊廣播
        //activity.registerReceiver(myReceiver, intentFilter);
        LocalBroadcastManager.getInstance(activity).registerReceiver(myReceiver, intentFilter);
    }

    @Override
    public int setFragmentLayoutID() {
        return R.layout.fragment_fragment3;
    }

    //建立廣播接收例項
    class MyReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            //獲取資料
            String number1 = intent.getStringExtra(activity.SaveData1);
            String number2 = intent.getStringExtra(activity.SaveData2);
            //進行資料操作
            textResult.setText(doAddition(number1, number2));
        }
    }

    @Override
    public void onPause() {
        super.onPause();
        //取消註冊廣播
        //activity.unregisterReceiver(myReceiver);
        LocalBroadcastManager.getInstance(activity).unregisterReceiver(myReceiver);
    }
}

四、EventBus

EventBus是一款針對Android優化的釋出/訂閱事件匯流排。簡化了應用程式內各元件間、元件與後臺執行緒間的通訊。 

EventBus詳細介紹請移步: Android事件匯流排(一)EventBus3.0用法全解析 

首先新增依賴:

implementation 'org.greenrobot:eventbus:3.0.0'

Activity向Fragment中傳遞資料:(Fragment->Activity調換位置即可)

Fragment中要接收資料,首先要在Fragment中註冊和登出EventBus:

@Override
public void onResume() {
    super.onResume();
    //註冊EventBus,注意引數是this,傳入activity會報錯
    EventBus.getDefault().register(this);
}

@Override
public void onPause() {
    super.onPause();
    //取消EventBus註冊
    EventBus.getDefault().unregister(this);
}

還應該有一個接收資料,處理資料的函式。

//EventBus的處理事件函式  該方法可以隨意取名 必須為public 必須添加註解並指定執行緒模型
//之後EventBus會自動掃描到此函式,進行資料傳遞
@Subscribe(threadMode = ThreadMode.MAIN)
public void getData(Bundle bundle) {
    String number1 = bundle.getString(activity.SaveData1);
    String number2 = bundle.getString(activity.SaveData2);

    //進行資料操作
    textResult.setText(doAddition(number1, number2));
}

Activity在響應事件中打包資料,資料複雜的話可以專門封裝一個實體類,我的資料簡單,直接用Bundle封裝了。

//4.使用EventBus 向fragment傳遞資料
//利用bundle打包資料,也可自己封裝Event事件
bundle.putString(SaveData1, number1);
bundle.putString(SaveData2, number2);
//傳送資料
EventBus.getDefault().post(bundle);

是不是很簡單。


五、介面回撥

介面的方式懶得寫了,就是Fragment宣告一個介面,然後Activity實現介面。(反之亦然)


各個方式優缺點以及大神封裝的介面框架,參考:Android:Activity與Fragment通訊(99%)完美解決方案 

~~年底了,公司over了,突然就下崗了(# ̄~ ̄#),希望自己能找到更好的工作。

待隨時補充,修正。