android fragmentactivity fragment,Android:Activity与Fragment通信(99%)完美解决方案
前言
最近一直在想著能否有一種更好的方案來解決:Android中Activity與Fragment之間通信的問題,什么叫更好呢,就是能讓Fragment的復(fù)用性高,性能還有好(不用反射),代碼還要好維護(hù),不需要為每對Activity和Fragment之間定義接口而發(fā)愁。
先簡單說下Javascript這門語言吧,或許有人就會(huì)問:咱們不是聊Android的java問題嗎?怎么話題轉(zhuǎn)到JavaScript了。因?yàn)槲业慕鉀Q方案的啟發(fā)是從它來的,沒興趣的朋友可以略過。最近在學(xué)習(xí)javascript這門語言,同時(shí)自己搞Android(java)開發(fā)也有5年多時(shí)間了,所以在學(xué)習(xí)js的過程中,就會(huì)慣性的把這兩者進(jìn)行比較。
與java語言的 嚴(yán)謹(jǐn) 相比 Javascript是一門"放蕩不羈"、"不拘小節(jié)"(寬泛)的語言。
為什么要用"放蕩不羈"這個(gè)詞呢,下面是它的一個(gè)解釋:
放蕩不羈 [fàng dàng bù jī][解釋] 羈:約束。放縱任性,不加檢點(diǎn),不受約束。
因?yàn)槲矣X得這個(gè)詞更能充分的體現(xiàn)js弱類型的特點(diǎn)。
在給變量賦值時(shí) 可以這樣寫:
var a = 1;
還可以這樣寫:
var b = '123';
var o = new Object();
甚至還可以這樣寫:
var fun = new function(){};
fun1 = new function(){};
可以把任何類型的值賦給一個(gè)變量,也可以不加var關(guān)鍵字來聲明一個(gè)變量,是不是很任性,很不拘束啊。
"不拘小節(jié)"主要體現(xiàn)了JavaScript的語法更寬泛、更簡單的特點(diǎn): 比如:
js代碼:
//函數(shù)聲明不需要定義返回值,參數(shù)前面不需要有類型出現(xiàn),
//函數(shù)體里面就可以有返回值
function max(a,b){ return a > b? a:b; }
/* *可以傳遞任意多個(gè)參數(shù),在java里面根本不可以 */
function print(){
var len = arguments.length;
for(var i = 0; i < len; i++){
console.log(arguments[i]);
}
}
相應(yīng)java代碼:
int max(int a, int b){
return a> b? a:b;
}
/* *傳遞任意多個(gè)Object類型的參數(shù) */
void print(Object... args){
for (int i = 0; i < args.length; i++){
System.out.println(args[i]);
}
}
上面的代碼說明了JavaScript在聲明函數(shù)時(shí),不會(huì)有像java那么嚴(yán)格的規(guī)定,語法不拘小節(jié),語法更簡單(這里沒有說java不好的意思)。
啟發(fā)點(diǎn)
JavaScript中有一個(gè)重要的點(diǎn)(萬事萬物皆對象),函數(shù)也不列外,并且函數(shù)可以作為另外一個(gè)函數(shù)的參數(shù),如:
js代碼:
//遍歷一個(gè)數(shù)組如果是它是數(shù)組,就把它乘以10再輸出
var array = [1,2, '你好' , '不' ,31,15];
//數(shù)組的each方法接收一個(gè)函數(shù)
testArray.each( function( value ){
typeof value == 'number' ? alert( value *10 ):null;
}) ;
當(dāng)我看到上面JavaScript中函數(shù)的用法時(shí)我眼前一亮,為啥我不可以借鑒之來解決android中activity與fragment通信的問題呢?
Fragment的使命
先讓我們聊聊Fragment為什么出現(xiàn),這對于我們解決Activity與Fragment的通信有幫助。一個(gè)新事物的產(chǎn)生總是為了解決舊事物存在的問題,Fragment是android3.0的產(chǎn)物,在android3.0之前解決手機(jī)、平板電腦的適配問題是很頭疼的,對ActivityGroup有印象的朋友,應(yīng)該能深深的體會(huì)到ActivityGroup包裹的多個(gè)Activity之間切換等一系列的性能問題。由此Fragment誕生了。個(gè)人總結(jié)的Fragment的使命:
解決手機(jī)、平板電腦等各種設(shè)備的適配問題
解決多個(gè)Activity之間切換性能問題
模塊化,因?yàn)槟K化導(dǎo)致復(fù)用的好處
Fragment的使用
Fragment是可以被包裹在多個(gè)不同Activity內(nèi)的,同時(shí)一個(gè)Activity內(nèi)可以包裹多個(gè)Fragment,Activity就如一個(gè)大的容器,它可以管理多個(gè)Fragment。所有Activity與Fragment之間存在依賴關(guān)系。
Activity與Fragment通信方案
上文提到Activity與Fragment之間是存在依賴關(guān)系的,因此它們之間必然會(huì)涉及到通信問題,解決通信問題必然會(huì)涉及到對象之間的引用。因?yàn)镕ragment的出現(xiàn)有一個(gè)重要的使命就是:模塊化,從而提高復(fù)用性。若達(dá)到此效果,Fragment必須做到高內(nèi)聚,低耦合。
現(xiàn)在大家動(dòng)動(dòng)腳趾都能想到的解決它們之間通信的方案有:handler,廣播,EvnetBus,接口等(或許還有別的方案,請大家多多分享),那我們就聊下這些方案。
handler方案:
先上代碼
public class MainActivity extends FragmentActivity{
//聲明一個(gè)Handler
public Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
...相應(yīng)的處理代碼
}
}
...相應(yīng)的處理代碼
}
public class MainFragment extends Fragment{
//保存Activity傳遞的handler
private Handler mHandler;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
//這個(gè)地方已經(jīng)產(chǎn)生了耦合,若還有其他的activity,這個(gè)地方就得修改
if(activity instance MainActivity){
mHandler = ((MainActivity)activity).mHandler;
}
}
...相應(yīng)的處理代碼
}
該方案存在的缺點(diǎn):
Fragment對具體的Activity存在耦合,不利于Fragment復(fù)用
不利于維護(hù),若想刪除相應(yīng)的Activity,Fragment也得改動(dòng)
沒法獲取Activity的返回?cái)?shù)據(jù)
handler的使用個(gè)人感覺就很不爽(不知大家是否有同感)
廣播方案:
具體的代碼就不寫了,說下該方案的缺點(diǎn):
用廣播解決此問題有點(diǎn)大材小用了,個(gè)人感覺廣播的意圖是用在一對多,接收廣播者是未知的情況
廣播性能肯定會(huì)差(不要和我說性能不是問題,對于手機(jī)來說性能是大問題)
傳播數(shù)據(jù)有限制(必須得實(shí)現(xiàn)序列化接口才可以)
暫時(shí)就想到這些缺點(diǎn),其他的缺點(diǎn)請大家集思廣益下吧。
EventBus方案:
具體的EventBus的使用可以自己搜索下,個(gè)人對該方案的看法:
EventBus是用反射機(jī)制實(shí)現(xiàn)的,性能上會(huì)有問題(不要和我說性能不是問題,對于手機(jī)來說性能是大問題)
EventBus難于維護(hù)代碼
沒法獲取Activity的返回?cái)?shù)據(jù)
接口方案
我想這種方案是大家最易想到,使用最多的一種方案吧,具體上代碼:
//MainActivity實(shí)現(xiàn)MainFragment開放的接口
public class MainActivity extends FragmentActivity implements FragmentListener{
@override
public void toH5Page(){ }
...其他處理代碼省略
}
public class MainFragment extends Fragment{
public FragmentListener mListener;
//MainFragment開放的接口
public static interface FragmentListener{
//跳到h5頁面
void toH5Page();
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
//對傳遞進(jìn)來的Activity進(jìn)行接口轉(zhuǎn)換
if(activity instance FragmentListener){
mListener = ((FragmentListener)activity);
}
}
...其他處理代碼省略
}
這種方案應(yīng)該是既能達(dá)到復(fù)用,又能達(dá)到很好的可維護(hù)性,并且性能也是杠杠的。但是唯一的一個(gè)遺憾是假如項(xiàng)目很大了,Activity與Fragment的數(shù)量也會(huì)增加,這時(shí)候?yàn)槊繉ctivity與Fragment交互定義交互接口就是一個(gè)很頭疼的問題(包括為接口的命名,新定義的接口相應(yīng)的Activity還得實(shí)現(xiàn),相應(yīng)的Fragment還得進(jìn)行強(qiáng)制轉(zhuǎn)換)。 想看更好的解決方案請看下面章節(jié)。
大招來也
設(shè)計(jì)模式里經(jīng)常提到的一個(gè)概念就是封裝變化,同時(shí)受javascript中的函數(shù)的參數(shù)可以是函數(shù)對象的啟發(fā)下,我有了下面的想法,先上代碼:代碼地址
/** * + Created by niuxiaowei on 2016/1/20.
* 各種方法集合的類,可以把一個(gè)方法類以key-value的形式放入本類,
* 可以通過key值來調(diào)用相應(yīng)的方法 */
public class Functions {
//帶參數(shù)方法的集合,key值為方法的名字
private HashMap mFunctionWithParam ;
//無參數(shù)無返回值的方法集合,同理key值為方法名字
private HashMap mFunctionNoParamAndResult ;
/** * 基礎(chǔ)方法類 */
public static abstract class Function{
//方法的名字,用來做調(diào)用,也可以理解為方法的指針
public String mFunctionName;
public Function(String functionName){
this.mFunctionName = functionName;
}
}
/** * 帶有參數(shù)沒有返回值的方法
* @param 參數(shù) */
public static abstract class FunctionWithParam extends Function{
public FunctionWithParam(String functionName) {
super(functionName);
}
public abstract void function(Param param);
}
/** * 沒有參數(shù)和返回值的方法 */
public static abstract class FunctionNoParamAndResult extends Function{
public FunctionNoParamAndResult(String functionName) {
super(functionName);
}
public abstract void function();
}
/** * 添加帶參數(shù)的函數(shù)
* @param function {@link com.niu.myapp.myapp.view.util.Functions.FunctionWithParam}
* @return */
public Functions addFunction(FunctionWithParam function){
if(function == null){
return this;
}
if(mFunctionWithParam == null){
mFunctionWithParam = new HashMap<>(1);
}
mFunctionWithParam.put(function.mFunctionName,function);
return this;
}
/** * 添加帶返回值的函數(shù)
* @param function {@link com.niu.myapp.myapp.view.util.Functions.FunctionWithResult}
* @return */
public Functions addFunction(FunctionNoParamAndResult function){
if(function == null){ return this; }
if(mFunctionNoParamAndResult == null){
mFunctionNoParamAndResult = new HashMap<>(1);
}
mFunctionNoParamAndResult.put(function.mFunctionName,function);
return this;
}
/** * 根據(jù)函數(shù)名,回調(diào)無參無返回值的函數(shù)
* @param funcName */
public void invokeFunc(String funcName) throws FunctionException {
FunctionNoParamAndResult f = null;
if(mFunctionNoParamAndResult != null){
f = mFunctionNoParamAndResult.get(funcName);
if(f != null){ f.function(); }
}
if(f == null){ throw new FunctionException("沒有此函數(shù)"); }
}
/** * 調(diào)用具有參數(shù)的函數(shù)
* @param funcName
* @param param
* @param */
public void invokeFunc(String funcName,Param param)throws FunctionException{
FunctionWithParam f = null;
if(mFunctionWithParam != null){
f = mFunctionWithParam.get(funcName);
if(f != null){ f.function(param); }
}
}
}
設(shè)計(jì)思路:
1. 用一個(gè)類來模擬Javascript中的一個(gè)Function
Function就是此類,它是一個(gè)基類,每個(gè)Functioon實(shí)例都有一個(gè)mFuncName 既然是方法(或者函數(shù))它就有有參數(shù)和無參數(shù)之分
FunctionWithParam是Function的子類,代表有參數(shù)的方法類,方法參數(shù)通過泛型解決
FunctionNoParamAndResult是Function的子類,代表無參無返回值的方法類
2. 一個(gè)可以存放多個(gè)方法(或者函數(shù))的類
Functions類就是此類,下面簡單介紹下Functions有4個(gè)主要方法:
addFunction(FunctionNoParamAndResult function) 添加一個(gè)無參無返回值的方法類
addFunction(FunctionWithParam function) 添加一個(gè)有參無返回值的方法類
invokeFunc(String funcName) 根據(jù)funcName調(diào)用一個(gè)方法
invokeFunc(String funcName,Param param) 根據(jù)funcName調(diào)用有參無返回值的方法類
使用舉例:代碼地址
每個(gè)app都有的基礎(chǔ)activity(BaseActivity)
public abstract class BaseActivity extends FragmentActivity {
/**
* 為fragment設(shè)置functions,具體實(shí)現(xiàn)子類來做
* @param fragmentId */
public void setFunctionsForFragment(
int fragmentId){
}
}
其中的一個(gè)activity:
public class MainActivity extends BaseActivity {
@Override public void setFunctionsForFragment(int fragmentId) {
super.setFunctionsForFragment(fragmentId);
switch (fragmentId) {
case R.id.fragment_main:
FragmentManager fm = getSupportFragmentManager();
BaseFragment fragment = (BaseFragment) fm.findFragmentById(fragmentId);
//開始添加functions
fragment.setFunctions(new Functions()
.addFunction(new Functions.FunctionNoParamAndResult(MainFragment.FUNCTION_NO_PARAM_NO_RESULT) {
@Override
public void function() {
Toast.makeText(MainActivity.this, "成功調(diào)用無參無返回值方法", Toast.LENGTH_LONG).show();
}
}).
addFunction(new Functions.FunctionWithParam(MainFragment.FUNCTION_HAS_PARAM_NO_RESULT) {
@Override
public void function(Integer o) {
Toast.makeText(MainActivity.this, "成功調(diào)用有參無返回值方法 參數(shù)值=" + o, Toast.LENGTH_LONG).show(); } }));
}
}
}
每個(gè)app都會(huì)有的基礎(chǔ)fragment(BaseFragment)
public abstract class BaseFragment extends Fragment {
protected BaseActivity mBaseActivity;
/** * 函數(shù)的集合 */
protected Functions mFunctions;
/** * activity調(diào)用此方法進(jìn)行設(shè)置Functions
* @param functions */
public void setFunctions(Functions functions){
this.mFunctions = functions;
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
//呼叫activity進(jìn)行回調(diào)方法的設(shè)置
if(activity instanceof BaseActivity){
mBaseActivity = (BaseActivity)activity;
mBaseActivity.setFunctionsForFragment(getId());
}
}
}
MainActivity對應(yīng)的MainFragment
public class MainFragment extends BaseFragment {
/** * 沒有參數(shù)沒有返回值的函數(shù) */
public static final String FUNCTION_NO_PARAM_NO_RESULT = "FUNCTION_NO_PARAM_NO_RESULT";
/** * 有參數(shù)沒有返回值的函數(shù) */
public static final String FUNCTION_HAS_PARAM_NO_RESULT = "FUNCTION_HAS_PARAM_NO_RESULT";
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mBut1 = (Button) getView().findViewById(R.id.click1);
mBut3 = (Button) getView().findViewById(R.id.click3);
mBut1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
//調(diào)用無參無返回值的方法
mFunctions.invokeFunc(
FUNCTION_NO_PARAM_NO_RESULT);
} catch (FunctionException e) {
e.printStackTrace();
}
}
});
mBut3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
//調(diào)用有參無返回值的方法
mFunctions.invokeFunc(
FUNCTION_HAS_PARAM_NO_RESULT, 100);
} catch (FunctionException e) {
e.printStackTrace(); }
}
});
}
看到這您是不是覺得已經(jīng)結(jié)束了,當(dāng)然是沒有了,因?yàn)檫€有2個(gè)問題沒解決。方法返回值和方法接收多個(gè)參數(shù)的問題。
方法返回值的問題
上代碼:代碼地址
/** * 有返回值,沒有參數(shù)的方法
* @param */
public static abstract class FunctionWithResult extends Function{
public FunctionWithResult(String functionName) {
super(functionName);
}
public abstract Result function();
}
/** * 帶有參數(shù)和返回值的 方法
* @param
* @param */
public static abstract class FunctionWithParamAndResult extends Function{
public FunctionWithParamAndResult(String functionName) {
super(functionName);
}
public abstract Result function(Param data);
}
FunctionWithResult無參數(shù)有返回值的方法類
FunctionWithParamAndResult 有參數(shù)也有返回值的方法類
在Functions類中定義添加和調(diào)用這2種方法類的 相應(yīng)方法。
其次是方法含有多個(gè)參數(shù)的問題
在解決此問題時(shí)我想了很多辦法(比如怎樣引入多個(gè)泛型,但最終以失敗告終,希望有看了這篇文章的朋友可以多提下寶貴意見)。然后我就想到了用Bundle來解決多參數(shù)的問題,把多個(gè)參數(shù)放到Bundle中,但是在往Bundle中塞入數(shù)據(jù)時(shí)得有一個(gè)對應(yīng)的key值,生成key值以及記住key值(記住key值是為了從Bundle中取數(shù)據(jù))是一個(gè)繁瑣的事。同時(shí)Bundle不能傳遞非序列化對象。所以就封裝了一個(gè)FunctionParams類解決以上問題,請看類的實(shí)現(xiàn): 代碼地址
/** * 函數(shù)的參數(shù),當(dāng)函數(shù)的參數(shù)涉及到多個(gè)值時(shí),可以用此類,
* 此類使用規(guī)則:存參數(shù)與取參數(shù)的順序必須一致,
* 比如存參數(shù)順序是new
*FunctionParamsBuilder().putString("a").putString("b").putInt(100);
*取的順序也是: functionParams.getString(),
*functionParams.getString(), functionParams.getInt(); */
public static class FunctionParams {
private Bundle mParams = new Bundle(1);
private int mIndex = -1;
private Map mObjectParams = new HashMap(1);
FunctionParams(Bundle mParams,Map mObjectParams){
this.mParams = mParams;
this.mObjectParams = mObjectParams;
}
public Param getObject(Class p){
if(mObjectParams == null){ return null; }
return p.cast(mObjectParams.get((mIndex++) + "")); }
/** * 獲取int值
* @return */
public int getInt(){
if(mParams != null){
return mParams.getInt((mIndex++) + ""); } return 0;
}
/** * 獲取int值
* @param defalut
* @return */
public int getInt(int defalut){
if(mParams != null){
return mParams.getInt((mIndex++) + "");
}
return defalut;
}
/** * 獲取字符串
* @param defalut * @return */
public String getString(String defalut){
if(mParams != null){
return mParams.getString((mIndex++) + "");
}
return defalut;
}
/** * 獲取字符串 * @return */
public String getString(){
if(mParams != null){
return mParams.getString((mIndex++) + "");
} return null;
}
/** * 獲取Boolean值
* @return 默認(rèn)返回false */
public boolean getBoolean(){
if(mParams != null){
return mParams.getBoolean((mIndex++) + "");
} return false;
}
/** * 該類用來創(chuàng)建函數(shù)參數(shù) */
public static class FunctionParamsBuilder{
private Bundle mParams ;
private int mIndex = -1;
private Map mObjectParams = new HashMap(1);
public FunctionParamsBuilder(){ }
public FunctionParamsBuilder putInt(int value){
if(mParams == null){
mParams = new Bundle(2);
}
mParams.putInt((mIndex++) + "", value);
return this;
}
public FunctionParamsBuilder putString(String value){
if(mParams == null){
mParams = new Bundle(2);
}
mParams.putString((mIndex++) + "", value);
return this;
}
public FunctionParamsBuilder putBoolean(boolean value){
if(mParams == null){ mParams = new Bundle(2); }
mParams.putBoolean((mIndex++) + "", value);
return this;
}
public FunctionParamsBuilder putObject(Object value){
if(mObjectParams == null){
mObjectParams = new HashMap(1);
}
mObjectParams.put((mIndex++) + "", value);
return this;
}
public FunctionParams create(){
FunctionParams instance = new FunctionParams(mParams,mObjectParams); return instance;
}
}
}
FunctionParams封裝了取參數(shù)的功能,比如:
public Param getObject(Class p){
if(mObjectParams == null){ return null; }
return p.cast(mObjectParams.get((mIndex++) + ""));
}
取對象參數(shù)的功能,不需要傳人key值,只需要傳人需要即將取出來的類的Class實(shí)例即可
FunctionParamsBuilder類,看它的名字就知道是用了設(shè)計(jì)模式里的Builder(構(gòu)建)模式。該類是用來存放參數(shù)的,當(dāng)所有的參數(shù)都存放完畢后調(diào)用create()方法創(chuàng)建一個(gè)FunctionParams對象事物都是有兩面性的,有缺點(diǎn)就有優(yōu)點(diǎn),只不過是在某些場合下優(yōu)點(diǎn)大于缺點(diǎn),還是反之。
FunctionParams解決了以上提到的Bundle傳遞多參數(shù)種種不便的問題,但同時(shí)FunctionParams也有一個(gè)缺點(diǎn)就是存參數(shù)的順序與取參數(shù)的順序一定要一致,比如:
//存的順序 new
FunctionParamsBuilder().putString("1").putInt(2)
.putBoolean(true).create();
//取的順序
functionParams.getString();
functionParams.getInt();
functionParams.getBoolean();
但是這種缺點(diǎn)函數(shù)的定義來看也不是缺點(diǎn)。
Activity與Fragment之間的通信是通過Functions的,即把變化的部分封裝在Functions是類中,Functions起一個(gè)橋梁作用。
此方案優(yōu)點(diǎn):
Fragment與Activity的耦合性幾乎沒有
性能也好(沒用反射)
可以從Activity獲取返回?cái)?shù)據(jù)
擴(kuò)展性好(新增加的成對的Activity與Fragment之間的通信只需做以下幾步:
1.新增加Activity只需要覆蓋BaseActivity中的 setFunctionsForFragment(int fragmentId) 方法,把相應(yīng)的回調(diào)函數(shù)加入。
2.相應(yīng)的Fragment定義函數(shù)key值即可)
總結(jié)
簡單總結(jié)為以下幾點(diǎn):
Fragment的使命
Activity與Fragment之間通信的解決方案(handler,廣播,EventBus,接口)的優(yōu)缺點(diǎn)。
我自己關(guān)于Activity與Fragment之間通信的解決方案(Functions),其實(shí)解決的主要是Fragment調(diào)用Activity的方案。
希望大家能多提寶貴意見,多交流。代碼地址
本人微信:704451290
本人公眾賬號(hào)
總結(jié)
以上是生活随笔為你收集整理的android fragmentactivity fragment,Android:Activity与Fragment通信(99%)完美解决方案的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: automake linux,Linux
- 下一篇: android运行时状态,Android