1. 程式人生 > >android之fragment活動棧單例

android之fragment活動棧單例

本文主要記錄一些零碎的東西

最近參加了一個做地圖的公司的面試,怎麼說呢,反正問的我都不會,問題感覺偏向記憶體的顯示優化方向,比如Bitmap佔記憶體大小啦之流,只怪自己學業不精。

專案還要繼續,在寫專案時,發現fragment的活動棧有些問題,回想起面試官問過這個問題 ,研究一下。

說一下問題的出現,我的fragment,從A--->(transaction.addToBackStack)B--->(transaction.addToBackStack)C--->B--->(transaction.addToBackStack)C,然後在這是按返回按鈕,回退到B,再次按回退按鈕,介面沒有反應,在頂部的導航欄中將當前的佈局檔案的內容切換成另外一個fragment,沒有壓入棧,這時就會發現介面上亂套了,兩個介面都顯示著,新切換的介面在上C介面在下,問題就出現啦。

這裡解決也簡單,在C--->B是加上transaction.addToBackStack,就沒有問題了,但是又會出現一個問題,沒有做單例,如果我一直C--->B--->C--->B--->C...活動棧中存在多個重複的B和C物件。

那如何做單例呢,activity裡做單例可以在mainfest.xml檔案裡的activity 裡配置android:launchMode,有四個引數

standard  每次都會新建,每個Task都可以有,且每個Task都可以有多個例項(每個Task都可以有,且可以有多個)
singleTop 當前例項如果在棧頂,就不新建例項,呼叫其OnNewIntent。 如不在棧頂,則新建例項 

(每個Task都可以有,且可以有多個,在棧頂時可複用)
singleTask 新建一個Task,如果已經有其他的Task並且包含該例項,那就直接呼叫那個Task的例項。(只有一個Task中會有)
singleInstance 新建一個Task,且在該Task中只有它的唯一一個例項。 (只有一個Task會有,且該Task中只有它)

但是上面這麼好的東西,但是不是我想要的效果,其實我每次C--->B--->C--->B--->C...時資料都是變化的,我想實現的是使用一個物件,但是每次replace時不僅要切換介面,還要更新顯示的資料,目前還沒有想到很好的解決方案。

- - - - - - - - - - - - -  - - - - - - -更新 2016-04-13  - -- - - - - - -- - - - - - - - - - - - - 

想著fragment切換時不傳資料,把要更新的資料寫在一個單獨的類中有get/set方法,新的fragment直接get新資料

 發現 replace()這個方法只是在上一個Fragment不再需要時採用的簡便方法。
正確的切換方式是add(),切換時hide(),add()另一個Fragment;再次切換時,只需hide()當前,show()另一個。
這樣就能做到多個Fragment切換不重新例項化,

有點投機取巧的意思,呼叫onResume方法,但是基本上我實現了返回棧裡只有物件的單例的,同時也重新整理的fragment上的資料,

看下測試執行結果


上傳一張fragment的生命週期


主要程式碼:

先看看DataUtil,簡單模擬一下資料

/**
 * Created by Administrator on 2016/4/13.
 */
public class DataUtil {
    public static String getNum() {
        return (new Random()).nextInt()+"";
    }

}
main_activity 佈局檔案
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">

    <FrameLayout
        android:id="@+id/change"
        android:layout_width="match_parent"
        android:layout_height="match_parent"></FrameLayout>
</RelativeLayout>
fragment佈局檔案,
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent" android:layout_height="match_parent">

    <TextView
        android:background="#0ff"
        android:id="@+id/show"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:textSize="40sp"
        android:text="111"/>
</LinearLayout>
mainactivity
package android.addre.com.fragmenttest;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;

public class MainActivity extends FragmentActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        switchContent(null,FragmetntA.getFragmetntA());

    }
    public void switchContent(Fragment from, Fragment to) {
        FragmentTransaction transactio = getSupportFragmentManager().beginTransaction();
            if (!to.isAdded()) { // 先判斷是否被add過,沒有新增就新增
                if(from != null){
                    transactio.hide(from);
                    transactio.addToBackStack(null);
                }
                transactio.add(R.id.change, to);
                transactio.commit(); // 隱藏當前的fragment,add下一個到Activity中
            } else {
                if(from != null){
                    transactio.hide(from);
                }
                to.onResume();//這裡使用者更新資料,不加這句就可以實現單例了,但是不會呼叫任何生命週期裡的方法,不會更新資料
                transactio.show(to).commit(); // 隱藏當前的fragment,顯示下一個
            }

    }
}
fragmentA

import android.content.Context;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentTransaction;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

/**
 * Created by Administrator on 2016/4/13.
 */
public class FragmetntA extends Fragment {

    private  View view;
    private TextView textView;
    private static FragmetntA fragmetntA;
    public static FragmetntA getFragmetntA(){
        if(fragmetntA == null){
            fragmetntA = new FragmetntA();
        }
        return fragmetntA;
    }


    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        Log.i("slack", "onAttach...A");
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.i("slack", "onCreate...A");
    }
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        Log.i("slack", "onCreateView...A");
        view = inflater.inflate(R.layout.fragmentlayout, null);
        textView =(TextView)view.findViewById(R.id.show);
        view.findViewById(R.id.show).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.i("slack", "onClick...A");
                //切換介面
                /*FragmentTransaction transaction = getActivity().getSupportFragmentManager().beginTransaction().replace(R.id.change, FragmetntB.getFragmetntB());
                transaction.addToBackStack(null);
                transaction.commit();*/
                ((MainActivity)getActivity()).switchContent(fragmetntA, FragmetntB.getFragmetntB());
            }
        });
        return view;
    }


    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        Log.i("slack", "onActivityCreated...A");
    }

    @Override
    public void onStart() {
        super.onStart();
        Log.i("slack", "onStart...A");
    }

    @Override
    public void onResume() {
        super.onResume();
        Log.i("slack", "onResume...A");
        textView.setText("A:" + DataUtil.getNum());
    }

    @Override
    public void onPause() {
        super.onPause();
        Log.i("slack", "onPause...A");
    }

    @Override
    public void onStop() {
        super.onStop();
        Log.i("slack", "onStop...A");
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        Log.i("slack", "onDestroyView...A");
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.i("slack", "onDestroy...A");
    }
    @Override
    public void onDetach() {
        super.onDetach();
        Log.i("slack", "onDetach...A");
    }

}

fragmentB

import android.content.Context;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

/**
 * Created by Administrator on 2016/4/13.
 */
public class FragmetntB extends Fragment {

    private  View view;
    private TextView textView;

    private static FragmetntB fragmetntB;
    public static FragmetntB getFragmetntB(){
        if(fragmetntB == null){
            fragmetntB = new FragmetntB();
        }
        return fragmetntB;
    }
    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        Log.i("slack", "onAttach...B");
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.i("slack", "onCreate...B");
    }
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        Log.i("slack", "onCreateView...B");
        View view = inflater.inflate(R.layout.fragmentlayout,null);
        textView =(TextView)view.findViewById(R.id.show);
        view.findViewById(R.id.show).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.i("slack", "onClick...B");
                //切換介面
                /*FragmentTransaction transaction = getActivity().getSupportFragmentManager().beginTransaction().replace(R.id.change, FragmetntA.getFragmetntA());
                transaction.addToBackStack(null);
                transaction.commit();*/
                ((MainActivity) getActivity()).switchContent(fragmetntB, FragmetntA.getFragmetntA());
            }
        });
        return view;
    }



    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        Log.i("slack", "onActivityCreated...B");
    }

    @Override
    public void onStart() {
        super.onStart();
        Log.i("slack", "onStart...B");
    }

    @Override
    public void onResume() {
        super.onResume();
        Log.i("slack", "onResume...B");
        textView.setText("B:" + DataUtil.getNum());
    }

    @Override
    public void onPause() {
        super.onPause();
        Log.i("slack", "onPause...B");
    }

    @Override
    public void onStop() {
        super.onStop();
        Log.i("slack", "onStop...B");
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        Log.i("slack", "onDestroyView...B");
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.i("slack", "onDestroy...B");
    }
    @Override
    public void onDetach() {
        super.onDetach();
        Log.i("slack", "onDetach...B");
    }

}

跳轉都寫成一個方法,在main_activity裡switchContent,引數的意思是當前是顯示那個fragment,要顯示哪個新的fragment,

新增時先判斷一下,要顯示的是否已被新增過,未被新增則隱藏當前,新增新的,如果已被新增,則隱藏當前,顯示棧裡的對應物件

public void switchContent(Fragment from, Fragment to) {
        FragmentTransaction transactio = getSupportFragmentManager().beginTransaction();
            if (!to.isAdded()) { // 先判斷是否被add過,沒有新增就新增
                if(from != null){
                    transactio.hide(from);
                    transactio.addToBackStack(null);
                }
                transactio.add(R.id.change, to);
                transactio.commit(); // 隱藏當前的fragment,add下一個到Activity中
            } else {
                if(from != null){
                    transactio.hide(from);
                }
                to.onResume();//這裡使用者更新資料,不加這句就可以實現單例了,但是不會呼叫任何生命週期裡的方法,不會更新資料
                transactio.show(to).commit(); // 隱藏當前的fragment,顯示下一個
            }

    }
如果是已被新增的,在呼叫show之前先呼叫resume方法更新一下資料
 @Override
    public void onResume() {
        super.onResume();
        Log.i("slack", "onResume...A");
        textView.setText("A:" + DataUtil.getNum());
    }