懶加載的目的主要是應用于在裝有多個Fragment并進行切換的時候桅打,如viewPager。相信各位在寫項目的時候,肯定也有遇到過viewpager里的fragment進行了前后預加載漱受,即3個fragment的數(shù)據(jù)加載,要通過setOffscreenPageLimit來設置預加載的項目患整,不設置setOffscreenPageLimit拜效,則默認為1(設置0無效喷众,可以查看該方法源碼知道)各谚,即時設置了讓他預加載為0了,也會執(zhí)行多個預加載到千。這個很讓人頭疼昌渤,即大大降低了性能,又浪費初始化資源憔四。然而我們采用懶加載技術(shù)就能讓用戶看到的頁面才會加載他的數(shù)據(jù)膀息,大大提高效率。
懶加載的實現(xiàn)原理就是利用Fragment中的setUserVisibleHint()和onCreateView()了赵,主要是利用了setUserVisibleHint()對fragment的可見狀態(tài)監(jiān)測和在oncreateView()前執(zhí)行的優(yōu)點潜支。注意加載時,如果這個fragment不可見的加載柿汛,那setUserVisibleHint()沒有執(zhí)行冗酿。
當viewPager中fragment改變可見狀態(tài), 使用getUserVisibleHint() 可以返回fragment是否可見狀態(tài):從可見到不見,為false络断;從不可見切換到可見裁替,為true。
可見如圖:具體參考博客****http://blog.csdn.net/mr_immortalz/article/details/51015196
注意:滑動到第三個fragment時貌笨,第一個fragment移除了相關(guān)的視圖但未和活動解除關(guān)聯(lián)弱判。所以從3滑回2時,預加載的1的生命周期少一些執(zhí)行锥惋。
下面我們來看懶加載BaseLazyFragment的class代碼:
1昌腰、首先看setUserVisibleHint()方法的重寫开伏。
第一個判斷語句主要是兩個判斷條件
1)當isVisibleToUser 為true則進行數(shù)據(jù)加載,當isVisibleToUser為false則不進行數(shù)據(jù)加載2)判斷isInit值剥哑,對于已經(jīng)加載過數(shù)據(jù)的fragment硅则,再次被滑動到也不在進行加載數(shù)據(jù),也就是每個fragment僅做一次數(shù)據(jù)加載工作第二個判斷語句是針對以初始化的fragment株婴,再根據(jù)是否可見來判斷顯示怎虫、執(zhí)行生命周期。
2困介、然后回到onCreateView()上大审。在onCreateView()里,對要懶加載的fragment是添加FrameLayout座哩。
如我注釋所寫徒扶,應該比較好理解的吧?(一開始我看了幾篇文章還是很混根穷,直到看了(http://blog.csdn.net/mr_immortalz/article/details/51015196的生命周期演示后姜骡,就大體上有個了解了,各位可以看看屿良。)
3圈澈、然后是對fragment生命周期的處理,每個狀態(tài)下以Fragment的回調(diào)方法名字后面要加個Lazy的方法名命名尘惧,并依此調(diào)用康栈。
注意:onFragmentStopLazy()、onFragmentStartLazy()喷橙、onResumeLazy()在其他地方也有調(diào)用的啥么。
4、加載view贰逾,我們在onCreateView()里悬荣,對要懶加載的fragment是,添加FrameLayout,設置setContentView()是為了當我們將fragment移至可見疙剑,則能夠根據(jù)判斷氯迂,替換掉原來的FrameLayout布局。因為核芽,移至可見時囚戚,該fragment會再次調(diào)用onCreateView()方法嘛。
最后轧简,我貼一下我的代碼驰坊,代碼開頭注釋里講了整體的怎么懶加載思考思路。
package android.davidnba.com.davidnba_ywh.base;
import android.davidnba.com.davidnba_ywh.widget.LoadingDialog;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
/* Created by 仁昌居士 on 2017/2/6.
* 懶加載Fragment</h1> 只有創(chuàng)建并顯示的時候才會調(diào)用onCreateViewLazy方法
* 懶加載的原理onCreateView的時候Fragment有可能沒有顯示出來哮独。
* 但是調(diào)用到setUserVisibleHint(boolean isVisibleToUser),isVisibleToUser =
* true的時候就說明有顯示出來
* 但是要考慮onCreateView和setUserVisibleHint的先后問題所以才有了下面的代碼
* 注意:
* 《1》原先的Fragment的回調(diào)方法名字后面要加個Lazy拳芙,比如Fragment的onCreateView方法察藐, 就寫成onCreateViewLazy
* 《2》使用該LazyFragment會導致多一層布局深度
*/
/* fragment一共四種情況:
可見、初始化舟扎;
可見分飞、未初始化;
不可見睹限、初始化譬猫;
不可見、未初始化羡疗;
邏輯分析染服,假設setOffscreenPageLimit(1),共預加載2個fragment:
剛進來叨恨,1是可見柳刮,且要初始化的,2是不可見痒钝、未初始化秉颗,第三個3是不綁定到activity的。
于是送矩,1走途徑1,2走途徑2.
然后蚕甥,假設滑動了,1滑到2益愈。
此時梢灭,1不可見夷家,且已初始化了蒸其,2是可見,將要初始化库快,3綁定到activity上摸袁,為不可見、未初始化义屏。
于是靠汁,1由于預加載的原因,未解除與視圖的聯(lián)系闽铐。所以onPause()蝶怔、onStop()、onDestoryView()未執(zhí)行兄墅,
并且踢星,由于1不可見,setUserVisibleHint()執(zhí)行了隙咸。所以沐悦, isStart設置為false成洗,即不可見;并調(diào)用onFragmentStopLazy();
2可見,同理由于預加載原因藏否,處于可見狀態(tài)時瓶殃,不會再調(diào)用onCreateView(),而是會調(diào)用setUserVisibleHint(),
所以副签,isStart設置為true;onFragmentStartLazy();
3呢遥椿,調(diào)用onCreateView(),又由于3不可見且未初始化淆储,則不執(zhí)行修壕。所以,加載的是個FrameLayout
*/
public class BaseLazyFragment extends BaseFragment {
private boolean isInit = false;//真正要顯示的View是否已經(jīng)被初始化(正常加載)
private Bundle savedInstanceState;
public static final String INTENT_BOOLEAN_LAZYLOAD = "intent_boolean_lazyload";
private boolean isLazyLoad = true;
private FrameLayout layout;
public LoadingDialog mLoadingDialog;
private boolean isStart = false;//是否處于可見狀態(tài)遏考,in the screen
@Override
protected void onCreateView(Bundle saveInstanceState) {
super.onCreateView(saveInstanceState);
Bundle bundle = getArguments();
if (bundle != null) {
isLazyLoad = bundle.getBoolean(INTENT_BOOLEAN_LAZYLOAD, isLazyLoad);
}
//判斷是否懶加載
if (isLazyLoad) {
//一旦isVisibleToUser==true即可對真正需要的顯示內(nèi)容進行加載
if (getUserVisibleHint() && !isInit) {
//途徑1
this.savedInstanceState = saveInstanceState;
onCreateViewLazy(saveInstanceState);
isInit = true;
} else {
//途徑2
//如果不可見或者已經(jīng)初始化了
//進行懶加載
layout = new FrameLayout(getApplicationContext());
layout.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
super.setContentView(layout);
}
} else {
//途徑3
//這里是不可見慈鸠,但已經(jīng)初始化了
//不需要懶加載,開門江山灌具,調(diào)用onCreateViewLazy正常加載顯示內(nèi)容即可
onCreateViewLazy(saveInstanceState);
isInit = true;
}
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
//一旦isVisibleToUser==true即可進行對真正需要的顯示內(nèi)容的加載
//判斷fragment是否可見青团,是否還未被初始化,該fragment布局是否存在
//可見咖楣,但還沒被初始化督笆,且該fragment布局存在
if (isVisibleToUser && !isInit && getContentView() != null) {
isInit = true; //允許初始化
onCreateViewLazy(savedInstanceState);//調(diào)用懶加載
onResumeLazy();
}
//對于已經(jīng)加載過數(shù)據(jù)的fragment,再次被滑動到也不在進行加載數(shù)據(jù)诱贿,也就是每個fragment僅做一次數(shù)據(jù)加載工作
//如果已經(jīng)初始化了娃肿,且該fragment布局存在
if (isInit && getContentView() != null) {
if (isVisibleToUser) {
isStart = true;
onFragmentStartLazy();
} else {
isStart = false;
onFragmentStopLazy();
}
}
}
protected void onCreateViewLazy(Bundle savedInstanceState) {
Log.d("TAG", "onCreateViewLazy() called with: " + "savedInstanceState = [" + savedInstanceState + "]");
}
//當Fragment被滑到不可見的位置時,調(diào)用
protected void onFragmentStopLazy() {
Log.d("TAG", "onFragmentStopLazy() called with: " + "");
}
//當Fragment被滑到可見的位置時珠十,調(diào)用
protected void onFragmentStartLazy() {
Log.d("TAG", "onFragmentStartLazy() called with: " + "");
}
protected void onResumeLazy() {
Log.d("TAG", "onResumeLazy() called with: " + "");
}
protected void onPauseLazy() {
Log.d("TAG", "onPauseLazy() called with: " + "");
}
protected void onDestroyViewLazy() {
}
@Deprecated
@Override
public final void onStart() {
Log.d("TAG", "onStart() : " + "getUserVisibleHint():" + getUserVisibleHint());
super.onStart();
if (isInit && !isStart && getUserVisibleHint()) {
isStart = true;
onFragmentStartLazy();
}
}
@Deprecated
@Override
public final void onStop() {
super.onStop();
Log.d("TAG", "onStop() called: " + "getUserVisibleHint():" + getUserVisibleHint());
if (isInit && isStart && getUserVisibleHint()) {
isStart = false;
onFragmentStopLazy();
}
}
@Override
@Deprecated
public final void onResume() {
Log.d("TAG", "onResume() : " + "getUserVisibleHint():" + getUserVisibleHint());
super.onResume();
if (isInit) {
onResumeLazy();
}
}
@Override
@Deprecated
public final void onPause() {
Log.d("TAG", "onPause() : " + "getUserVisibleHint():" + getUserVisibleHint());
super.onPause();
if (isInit) {
onPauseLazy();
}
}
@Override
@Deprecated
public final void onDestroyView() {
Log.d("TAG", "onDestroyView() : " + "getUserVisibleHint():" + getUserVisibleHint());
super.onDestroyView();
if (isInit) {
onDestroyViewLazy();
}
isInit = false;
}
@Override
public void setContentView(int layoutResID) {
//判斷若isLazyLoad==true,移除所有l(wèi)azy view料扰,加載真正要顯示的view
if (isLazyLoad && getContentView() != null && getContentView().getParent() != null) {
layout.removeAllViews();
View view = inflater.inflate(layoutResID, layout, false);
layout.addView(view);
}
//否則,開門見山焙蹭,直接加載真正要顯示的view
else {
super.setContentView(layoutResID);
}
}
@Override
public void setContentView(View view) {
//判斷若isLazyLoad==true,移除所有l(wèi)azy view晒杈,加載真正要顯示的view
if (isLazyLoad && getContentView() != null && getContentView().getParent() != null) {
layout.removeAllViews();
layout.addView(view);
}
//否則,開門見山孔厉,直接加載真正要顯示的view
else {
super.setContentView(view);
}
}
}
參考博客:
[http://blog.csdn.net/mr_immortalz/article/details/51015196]
http://blog.csdn.net/sinat_15877283/article/details/51037987
http://www.reibang.com/p/58fd611260b5
http://www.open-open.com/lib/view/open1477033083443.html