關於一個fragment之間,以及activity與fragment的消息傳遞方式的問題?

熟悉android應該知道,fragment與fragment之間的通信需要間接地使用宿主activity,主要有 1.在一個fragment中通過使用getacvtivty獲得宿主從而獲得宿主擁有的另一個fragment,進而操作該fragment的元素,甚至信息。另一個是 通過在fragment中編寫介面,讓activity 實現該介面,在fragment中把宿主activity當成該介面使用。請問這兩種方式是什麼關係,後一種方式 的價值在哪裡


建議你用EventBus,看起來清爽很多


如果要討論第二種方式的意義在哪,在就要從設計模式從頭開始說起。設計模式中的一個基本原則是開閉原則。開閉原則是說模塊應該對擴展開放,而對修改關閉。模塊應該盡量不修改代碼的情況下進行擴展。

那麼說第一種方式,第一種方式雖然行得通,但是違反了設計模式的開閉原則。fragment天生是一種獨立的開發組件,如果fragment要與自己的宿主activity交互,那麼就需要知道activity是如何工作的,這本身就破壞了fragment的獨立性。也就是說這個fragment只能在這個activity里使用,不能再另一個activity里使用。

而第二種方式,在fragment里定義了介面,由宿主的activity來實現借口處理任務。如果想換一個宿主activity,只需要在新的activity中實現借口就好,fragment始終不需要進行改變。這樣就滿足了設計模式的開閉原則。

其實第二種的實現方式,就是設計模式中的代理模式。多一個代理類出來,替原對象進行一些操作。這種使用方式在iOS開放中也是基本方式,iOS中的delegate和第二種方式思想基本一致。


後一種主要是解耦。

你在第二個Fragment裡面的時候,不應該關心第一個Fragment,反過來也是!


說說我自己的理解,不喜請輕噴。

1.使用介面的好處就是不用關心具體的實現,以後不管這個Fragment用在哪裡,Activity如果對相應的動作感興趣,只要實現該介面即可,這樣就很好地實現了解耦;

2.其實現在Android Studio中新建Fragment的話,已經有很好的一個範例了,我覺得這種推廣best practice的方式非常好,示例如下:

/**

* A simple {@link Fragment} subclass.

* Activities that contain this fragment must implement the

* {@link BlankFragment.OnFragmentInteractionListener} interface

* to handle interaction events.

* Use the {@link BlankFragment#newInstance} factory method to

* create an instance of this fragment.

*/

public class BlankFragment extends Fragment {

// TODO: Rename parameter arguments, choose names that match

// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER

private static final String ARG_PARAM1 = "param1";

private static final String ARG_PARAM2 = "param2";

// TODO: Rename and change types of parameters

private String mParam1;

private String mParam2;

private OnFragmentInteractionListener mListener;

/**

* 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 BlankFragment.

*/

// TODO: Rename and change types and number of parameters

public static BlankFragment newInstance(String param1, String param2) {

BlankFragment fragment = new BlankFragment();

Bundle args = new Bundle();

args.putString(ARG_PARAM1, param1);

args.putString(ARG_PARAM2, param2);

fragment.setArguments(args);

return fragment;

}

public BlankFragment() {

// Required empty public constructor

}

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

if (getArguments() != null) {

mParam1 = getArguments().getString(ARG_PARAM1);

mParam2 = getArguments().getString(ARG_PARAM2);

}

}

@Override

public View onCreateView(LayoutInflater inflater, ViewGroup container,

Bundle savedInstanceState) {

// Inflate the layout for this fragment

return inflater.inflate(R.layout.fragment_blank, container, false);

}

// TODO: Rename method, update argument and hook method into UI event

public void onButtonPressed(Uri uri) {

if (mListener != null) {

mListener.onFragmentInteraction(uri);

}

}

@Override

public void onAttach(Activity activity) {

super.onAttach(activity);

try {

mListener = (OnFragmentInteractionListener) activity;

} catch (ClassCastException e) {

throw new ClassCastException(activity.toString()

+ " must implement OnFragmentInteractionListener");

}

}

@Override

public void onDetach() {

super.onDetach();

mListener = null;

}

/**

* This interface must be implemented by activities that contain this

* fragment to allow an interaction in this fragment to be communicated

* to the activity and potentially other fragments contained in that

* activity.

* &

* See the Android Training lesson & * "Communicating with Other Fragments"

* &>Communicating with Other Fragments& for more information.

*/

public interface OnFragmentInteractionListener {

// TODO: Update argument type and name

public void onFragmentInteraction(Uri uri);

}

}

裡面除了下面這段之外,其它的其實都不錯了:

try {

mListener = (OnFragmentInteractionListener) activity;

} catch (ClassCastException e) {

throw new ClassCastException(activity.toString()

+ " must implement OnFragmentInteractionListener");

}

其實這裡完全可以用instanceof先進行判斷的,不然只要宿主Activity沒有實現該介面就會拋出異常。或者改用setListener的方法也可以,只要判斷其是否為null即可了;

其它的注釋裡面都解釋得非常詳細,我就不再啰嗦了;

3.EventBus作為一個事件分發和訂閱的框架,也是一個不錯的選擇,只是要注意它跟大多數框架不太一樣的地方,比如它其實不是Singleton模式,而且PostThread, MainThread 和BackgroundThread 模式下不適合進行耗時操作,即使不是在UI線程

4.Fragment雖好,但是濫用也不好,我曾經見過只在一個頁面用到的收藏按鈕,竟然被人做成了Fragment,當時感覺真是日了狗了。


使用第二種方式,如果更改了宿主Activity,Fragment的代碼不需要更改,只需要讓宿主Activity實現對應的介面就行。就像 @Gracker 前輩說的,又在一定程度上解耦和。如果使用第一種,如果更改了宿主Activity,那麼Fragment中跟Activity交互的代碼就要進行一定程度的修改,如果這個Fragment在多個地方使用,那麼使用起來就很麻煩了。


已經解釋的很多了 不再解釋了。

但如果題主已經搞明白了,那我就建議好好研究下 "訂閱者" 模式,好用的框架有 EventBus 和 Otto,可以讓你忘了你所提問的那兩種方式或對它們的理解更進一步。

可以直接閱讀相關文檔,或者看看網上的視頻教程也可以,http://www.jikexueyuan.com/course/933.html

其實最重要的,還是要理解學會 "訂閱者" 模式,這是內功。


推薦閱讀:

BAT哪家Android的APP 水準最高?
一加與CM停止合作,還有必要買一加手機或者期待一加2嗎?
最好的PC端Android模擬器是哪個軟體?
該如何讓現有的安卓 App 設計風格適配為 Android L 風格?
電阻屏為什麼被淘汰了?

TAG:Android開發 | Android |