Markdown版本筆記 | 我的GitHub首頁 | 我的部落格 | 我的微信 | 我的郵箱 |
---|---|---|---|---|
MyAndroidBlogs | baiqiantao | bqt20094 | [email protected] |
目錄
Fragment 懶加載
setUserVisibleHint 方法文檔
懶加載實作思路
整個加載過程解析
實作方案
BaseLazyFragment 版本一
實作類一
BaseLazyFragment 版本二
實作類二
回調過程與生命周期詳細分析
SimpleTabFragment
啟動後
滑到第二個頁面
滑到第一個頁面
再次滑到第二個頁面
最小化後再回到前台
@param isVisibleToUser true if this fragment's UI is currently visible to the user (default), false if it is not.
public void setUserVisibleHint(boolean isVisibleToUser) {}
Set a hint to the system about whether this fragment's UI is currently visible to the user. This hint defaults to true and is persistent across fragment instance state save and restore.
向系統提供有關此fragment的UI目前是否對使用者可見的提示。此提示預設為true,并且在fragment執行個體狀态儲存和還原期間保持不變。
An app may set this to false to indicate that the fragment's UI is scrolled out of visibility or is otherwise not directly visible to the user. This may be used by the system to prioritize operations such as fragment lifecycle updates or loader ordering behavior.
應用程式可能将此設定為false,以訓示fragment的UI滾動超出可見性,或者不直接對使用者可見。系統可以使用它來确定諸如fragment生命周期更新或加載器排序行為之類的操作的優先級。
Note: This method may be called outside of the fragment lifecycle and thus has no ordering guarantees with regard to fragment lifecycle method calls.
注意:此方法可以在fragment生命周期之外調用,是以對fragment生命周期方法調用沒有排序保證。
Note: Prior to Android N there was a platform bug that could cause setUserVisibleHint to bring a fragment up to the started state before its FragmentTransaction had been committed. As some apps relied on this behavior, it is preserved for apps that declare a targetSdkVersion of 23 or lower.
注意:在Android N之前,存在一個平台錯誤,可能導緻setUserVisibleHint在FragmentTransaction送出之前将fragment提升到started狀态。由于某些應用程式依賴于此行為,是以會為聲明targetSdkVersion為23或更低的應用程式保留該行為。
懶加載意思也就是當需要的時候才會去加載
那麼,為什麼Fragment需要懶加載呢,一般我們都會在onCreate()或者onCreateView()裡去啟動一些資料加載操作,比如從本地加載或者從伺服器加載。大部分情況下,這樣并不會出現什麼問題,但是當你使用
ViewPager + Fragment
的時候,問題就來了。
ViewPager為了讓滑動的時候可以有很好的使用者的體驗,也就是防止出現卡頓現象,是以它有一個緩存機制。預設情況下,ViewPager會提前建立好目前Fragment旁的兩個Fragment。如果Fragment加載時比較耗時或者需要加載圖檔等占用大量記憶體的對象,這時就應該考慮想想是否該實作懶加載。也就是,當我
打開
哪個Fragment的時候,它才會去執行加載資料、解析資料,更新UI等操作。
本來Fragment的 onResume() 表示的是目前Fragment處于可見且可互動狀态,但由于ViewPager的緩存機制,它已經失去了意義了,回調Fragment的onResume()和其
對使用者是否可見
不再具有關聯性。
此時,我們可以使用Fragment的
setUserVisibleHint()
方法判斷目前Fragment是否對使用者可見,setUserVisibleHint 這個方法優于 onCreate() 方法,它會通過 isVisibleToUser 告訴我們目前 Fragment 對使用者是否可見,我們可以在可見的時候再進行網絡加載等操作。
重要:懶加載一般和 setOffscreenPageLimit
配合使用:當使用者沒有點選某一個tab去檢視相應的Fragment時,此Fragment懶加載,一旦使用者檢視過某一Fragment,則把此Fragment緩存下來,以後不再重複建立。
下面兩種方案本質上是完全一緻的,其回調過程都符合以下規律。
啟動時(即預設顯示第一個頁面)
- 第1次回調第1個Fragment的setUserVisibleHint:此時其
為false,是以不會觸發其onLazyLoad()getUserVisibleHint
- 第1次回調第2個Fragment的setUserVisibleHint:此時其
getUserVisibleHint
- 第2次回調第1個Fragment的setUserVisibleHint:此時其
為true,但因為此時getUserVisibleHint
為false,是以仍不會觸發其onLazyLoad()isPrepared
你沒看錯,上面的調用過程是确切的!
- 回調第1個Fragment的onCreate、onCreateView、onActivityCreated等一些列方法:此時其
為true,getUserVisibleHint
被我們置為了true,isPrepared
仍是預設的false,是以會觸發其onLazyLoad()isLazyLoaded
- 回調第2個Fragment的onCreate、onCreateView、onActivityCreated等一些列方法:此時其
為false,是以不會觸發其onLazyLoad(),【但】其getUserVisibleHint
被置為了trueisPrepared
- 後續第1個Fragment因為其
被我們置為了true,是以永遠不會再觸發其onLazyLoad()isLazyLoaded
從第一個頁面滑到第二個頁面時
- 第1次回調第3個Fragment的setUserVisibleHint:此時其
getUserVisibleHint
-
為false,表明其不再對使用者可見了getUserVisibleHint
- 第2次回調第2個Fragment的setUserVisibleHint:此時其
為true,【并且】因為此前其getUserVisibleHint
被置為了true,isPrepared
isLazyLoaded
你沒看錯,上面的調用過程也是确切的,不要懷疑!
- 同樣,後續第2個Fragment因為其
isLazyLoaded
- 回調第3個Fragment的onCreate、onCreateView、onActivityCreated等一些列方法:此時其
為false,是以不會觸發其onLazyLoad(),【但】同樣其getUserVisibleHint
isPrepared
後面的邏輯就是類似的了。
public abstract class BaseLazyFragment extends Fragment {
protected boolean isLazyLoaded;//懶加載過,保證隻加載一次
private boolean isPrepared;//防止沒有inflateView以及findView之前操作View導緻空指針異常
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
lazyLoad();
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
isPrepared = true;
lazyLoad(); //必須調用,否則首次打開會顯示空白界面
}
private void lazyLoad() {
if (getUserVisibleHint() && isPrepared && !isLazyLoaded) {
onLazyLoad();//調用懶加載
isLazyLoaded = true;
}
}
@UiThread
public abstract void onLazyLoad();
}
public class SimpleTabFragment extends BaseLazyFragment {
private String name;
private View rootView;
public static final String ARGUMENT = "name";
public static SimpleTabFragment newInstance(String name) {
SimpleTabFragment fragment = new SimpleTabFragment();
Bundle bundle = new Bundle();
bundle.putString(ARGUMENT, name);
fragment.setArguments(bundle);
return fragment;
}
@Override
public void onLazyLoad() {
TextView tv = rootView.findViewById(R.id.tv);
new Thread(() -> {
SystemClock.sleep(500);//模拟耗時操作
getActivity().runOnUiThread(() -> {
tv.setText(name + ":" + new SimpleDateFormat("HH:mm:ss SSS", Locale.getDefault()).format(new Date()));
tv.setBackgroundColor(0xFF000000 + new Random().nextInt(0xFFFFFF));
});
}).start();
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle bundle = getArguments();
name = bundle != null ? bundle.getString(ARGUMENT) : "unknown";
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.fragment_tab, container, false);
return rootView;
}
}
public abstract class BaseLazyFragment extends Fragment {
protected View rootView;
protected boolean isLazyLoaded;//懶加載過,保證隻加載一次
private boolean isPrepared;//防止沒有inflateView以及findView之前操作View導緻空指針異常
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
lazyLoad();
}
/**
* 不要重寫onCreateView,請重寫getRootView()和lazyLoad()
*/
@Override
public final View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
rootView = getRootView(inflater, container, savedInstanceState);
isPrepared = true;
lazyLoad(); //必須調用,否則首次打開會顯示空白界面
return rootView;
}
private void lazyLoad() {
if (getUserVisibleHint() && isPrepared && !isLazyLoaded) {
onLazyLoad();//調用懶加載
isLazyLoaded = true;
}
}
public abstract void onLazyLoad();
protected abstract View getRootView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState);
}
public class SimpleTabFragment extends BaseLazyFragment {
private String name;
public static final String ARGUMENT = "name";
public static SimpleTabFragment newInstance(String name) {
SimpleTabFragment fragment = new SimpleTabFragment();
Bundle bundle = new Bundle();
bundle.putString(ARGUMENT, name);
fragment.setArguments(bundle);
return fragment;
}
@Override
public void onLazyLoad() {
TextView tv = rootView.findViewById(R.id.tv);
new Thread(() -> {
SystemClock.sleep(500);//模拟耗時操作
getActivity().runOnUiThread(() -> {
tv.setText(name + ":" + new SimpleDateFormat("HH:mm:ss SSS", Locale.getDefault()).format(new Date()));
tv.setBackgroundColor(0xFF000000 + new Random().nextInt(0xFFFFFF));
});
}).start();
}
@Override
protected View getRootView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_tab, container, false);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle bundle = getArguments();
name = bundle != null ? bundle.getString(ARGUMENT) : "unknown";
}
}
public class SimpleTabFragment extends Fragment {
private String name;
public static final String ARGUMENT = "name";
public static SimpleTabFragment newInstance(String name) {
SimpleTabFragment fragment = new SimpleTabFragment();
Bundle bundle = new Bundle();
bundle.putString(ARGUMENT, name);
fragment.setArguments(bundle);
return fragment;
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
Log.i("bqt", "【--------setUserVisibleHint:" + hashCode() + "-" + name + "-" + isVisibleToUser + "--------】");
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle bundle = getArguments();
name = bundle != null ? bundle.getString(ARGUMENT) : "unknown";
Log.i("bqt", "【onCreate:" + name + "】");
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Log.i("bqt", "【----onCreateView" + name + "----】");
View view = inflater.inflate(R.layout.fragment_tab, container, false);
TextView tv = view.findViewById(R.id.tv);
tv.setText(name + ":" + new SimpleDateFormat("HH:mm:ss SSS", Locale.getDefault()).format(new Date()));
tv.setBackgroundColor(0xFF000000 + new Random().nextInt(0xFFFFFF));
return view;
}
@Override
public void onResume() {
super.onResume();
Log.i("bqt", "【onResume:" + name + "-" + getUserVisibleHint() + "】");
}
@Override
public void onDestroyView() {
super.onDestroyView();
Log.i("bqt", "【----onDestroyView:" + name + "------】");
}
@Override
public void onDestroy() {
super.onDestroy();
Log.i("bqt", "【--------onDestroy:" + name + "--------】");
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
【----------------FragmentPagerAdapter-getItem:0----------------】
【--------setUserVisibleHint:215908580-null-false--------】
【----------------FragmentPagerAdapter-getItem:1----------------】
【--------setUserVisibleHint:173705549-null-false--------】
【--------setUserVisibleHint:215908580-null-true--------】
【onCreate:微信】
【onCreate:通訊錄】
【----onCreateView微信----】
【onResume:微信-true】
【----onCreateView通訊錄----】
【onResume:通訊錄-false】
剛開始setUserVisibleHint的參數為false,後來變為true
注意:兩次回調都發生在onCreate之前
【onPageSelected】1
【----------------FragmentPagerAdapter-getItem:2----------------】
【--------setUserVisibleHint:88702934-null-false--------】
【--------setUserVisibleHint:215908580-微信-false--------】
【--------setUserVisibleHint:173705549-通訊錄-true--------】
【onCreate:發現】
【----onCreateView發現----】
【onResume:發現-false】
建立第三個頁面時,目前隻回調了一次setUserVisibleHint,且參數為false
第一個頁面不可見了,同時第二個頁面可見了,是以這兩個Fragment的setUserVisibleHint各回掉了一次
【onPageSelected】0
【--------setUserVisibleHint:173705549-通訊錄-false--------】
【--------setUserVisibleHint:215908580-微信-true--------】
【----onDestroyView:發現------】
//【--------onDestroy:發現--------】//當使用FragmentStatePagerAdapter時會有這個回調
第三個頁面可見狀态内有發生變化,是以仍然沒有回調其setUserVisibleHint方法
【onPageSelected】1
//【----------------FragmentStatePagerAdapter-getItem:2----------------】//當使用*State*時會有這個回調
【--------setUserVisibleHint:88702934-發現-false--------】
【--------setUserVisibleHint:215908580-微信-false--------】
【--------setUserVisibleHint:173705549-通訊錄-true--------】
//【onCreate:發現】//當使用FragmentStatePagerAdapter時會有這個回調
【----onCreateView發現----】
【onResume:發現-false】
注意這裡第三個頁面回調了一次setUserVisibleHint方法
在第一個頁面最小化後再回到前台
【onResume:微信-true】
【onResume:通訊錄-false】
在第二個頁面最小化後再回到前台
【onResume:微信-false】
【onResume:通訊錄-true】
【onResume:發現-false】
2019-3-25
本文來自部落格園,作者:白乾濤,轉載請注明原文連結:https://www.cnblogs.com/baiqiantao/p/10603179.html