Fragment这个东西相信大家并不会陌生,最近在考虑Fragment的懒加载模式,顺便研究了Fragment的生命周期。
首先,先扔出这两张图
1、图示:
图一
图二
2 生命周期方法探究
通过上图我们可以清楚的看到Fragment的生命周期方法有:onAttach()、onCreate()、onCreateView()、onActivityCreated()、onStart()、onResume()、
onPause()、onStop()、onDestoryView()、onDestory()、onDetach()。
下面我们通过一个简单的例子来验证Fragment生命周期方法的执行顺序。这里我写了一个demo下面来看一下代码
(1)MainActivity.java
package com.fragment.admin.fragmenttext;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
import com.fragment.admin.fragmenttext.fragment.Fragment01;
import com.fragment.admin.fragmenttext.fragment.Fragment02;
import com.fragment.admin.fragmenttext.fragment.Fragment03;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private ViewPager viewpager;
private List<Fragment> list=new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
initViews();
}
private void initViews() {
viewpager= (ViewPager) findViewById(R.id.viewpager);
list.add(new Fragment01());
list.add(new Fragment02());
list.add(new Fragment03());
viewpager.setAdapter(new viewPagerAdapter(getSupportFragmentManager()));
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
private class viewPagerAdapter extends FragmentPagerAdapter
{
public viewPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
return list.get(position);
}
@Override
public int getCount() {
return list.size();
}
}
}
代码很简单不多说了,我们以Fragment01 做为研究对象。
(2)Fragment01.java
package com.fragment.admin.fragmenttext.fragment;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.fragment.admin.fragmenttext.R;
/**
* Created by admin on 2016/11/1.
*/
public class Fragment01 extends BaseFragment {
private static final String TAG = "Fragment01";
private TextView tv_FragmentName;
// /**
// * 用于判断当前的Fragment是否可见
// * @param isVisibleToUser
// */
// @Override
// public void setUserVisibleHint(boolean isVisibleToUser) {
// super.setUserVisibleHint(isVisibleToUser);
// if (isVisibleToUser)
// {
// Log.e(TAG,"Fragment01可见");
//
// return;
// }
// Log.e(TAG,"Fragment01不可见");
//
// }
@Override
public void onAttach(Context context) {
super.onAttach(context);
Log.e(TAG, "onAttach");
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
Log.e(TAG, "onCreateView");
View view = inflater.inflate(R.layout.item, container, false);
initViews(view);
return view;
}
@Override
public void onLoad() {
Log.e(TAG, TAG);
}
private void initViews(View view) {
tv_FragmentName = (TextView) view.findViewById(R.id.tv_FragmentName);
tv_FragmentName.setText(TAG);
}
@Override
public void onPause() {
super.onPause();
Log.e(TAG, "onPause");
}
@Override
public void onStop() {
super.onStop();
Log.e(TAG, "onStop");
}
@Override
public void onDestroyView() {
super.onDestroyView();
Log.e(TAG, "onDestroyView");
}
@Override
public void onDestroy() {
super.onDestroy();
Log.e(TAG, "onDestroy");
}
@Override
public void onDetach() {
super.onDetach();
Log.e(TAG, "onDetach");
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.e(TAG, "onCreate");
}
@Override
public void onStart() {
super.onStart();
Log.e(TAG, "onStart");
}
@Override
public void onResume() {
super.onResume();
Log.e(TAG, "onResume");
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Log.e(TAG, "onActivityCreated");
}
}
Fragment01的代码也相当简单,只是重写了其生命周期方法。
(3)测试
第一步:启动app(相当与是启动了Fragment01)
11-08 11:57:44.650 26181-26181/com.fragment.admin.fragmenttext E/Fragment01: onAttach
11-08 11:57:44.650 26181-26181/com.fragment.admin.fragmenttext E/Fragment01: onCreate
11-08 11:57:44.652 26181-26181/com.fragment.admin.fragmenttext E/Fragment01: onCreateView
11-08 11:57:44.661 26181-26181/com.fragment.admin.fragmenttext E/Fragment01: onActivityCreated
11-08 11:57:51.292 26181-26181/com.fragment.admin.fragmenttext E/Fragment01: onStart
11-08 11:57:51.293 26181-26181/com.fragment.admin.fragmenttext E/Fragment01: onResume
第二步:滑动到Fragment02
logcat没有任何变化
第三步:滑动到Fragment03
11-08 11:57:44.650 26181-26181/com.fragment.admin.fragmenttext E/Fragment01: onAttach
11-08 11:57:44.650 26181-26181/com.fragment.admin.fragmenttext E/Fragment01: onCreate
11-08 11:57:44.652 26181-26181/com.fragment.admin.fragmenttext E/Fragment01: onCreateView
11-08 11:57:44.661 26181-26181/com.fragment.admin.fragmenttext E/Fragment01: onActivityCreated
11-08 11:57:51.292 26181-26181/com.fragment.admin.fragmenttext E/Fragment01: onStart
11-08 11:57:51.293 26181-26181/com.fragment.admin.fragmenttext E/Fragment01: onResume
11-08 11:59:32.999 26181-26181/com.fragment.admin.fragmenttext E/Fragment01: onPause
11-08 11:59:32.999 26181-26181/com.fragment.admin.fragmenttext E/Fragment01: onStop
11-08 11:59:32.999 26181-26181/com.fragment.admin.fragmenttext E/Fragment01: onDestroyView
第四步:退出到桌面
11-08 13:37:13.507 24861-24861/com.fragment.admin.fragmenttext E/Fragment01: onAttach
11-08 13:37:13.507 24861-24861/com.fragment.admin.fragmenttext E/Fragment01: onCreate
11-08 13:37:13.510 24861-24861/com.fragment.admin.fragmenttext E/Fragment01: onCreateView
11-08 13:37:13.516 24861-24861/com.fragment.admin.fragmenttext E/Fragment01: onActivityCreated
11-08 13:37:13.516 24861-24861/com.fragment.admin.fragmenttext E/Fragment01: onStart
11-08 13:37:13.516 24861-24861/com.fragment.admin.fragmenttext E/Fragment01: onResume
11-08 13:37:20.868 24861-24861/com.fragment.admin.fragmenttext E/Fragment01: onPause
11-08 13:37:20.868 24861-24861/com.fragment.admin.fragmenttext E/Fragment01: onStop
11-08 13:37:20.868 24861-24861/com.fragment.admin.fragmenttext E/Fragment01: onDestroyView
11-08 13:37:30.751 24861-24861/com.fragment.admin.fragmenttext E/Fragment01: onDestroy
11-08 13:37:30.751 24861-24861/com.fragment.admin.fragmenttext E/Fragment01: onDetach
总结:以上四步可以完整的体现Fragment的生命周期,值得注意的是当调用onDestroyView方法时只是将Fragment中的View销毁掉了,当宿主Activity被销毁时Fragment才会被销毁。
3 Fragment懒加载模式实现以及原理
在项目中我们经常会使用ViewPager+Fragment的组合,默认的情况ViewPager会提前加载当前页的下一页以备不时之需。但是这样往往是比较消耗用户流量的,所以我们要选择懒加载模式
也就是所谓的需要的再来加载的模式。
(1)方法一:个人认为这个方法说了和没说是一样的,但是为了让我们知道一些关于ViewPager预加载模式的小九九我们还是说一下吧,不过看完以后小伙伴们别打我。
ViewPager里有一个setOffscreenPageLimit(int pageCount)方法,这个方法的作用就是设置预加载页的页数,参数pageCount默认是1。那么有人就想到了
我把参数设置为0不久ok了?没错,我之前也是这么想的,但是你测试就会发现并没有起作用。原因是~~~~
来来来让我们看一下源码
private static final int DEFAULT_OFFSCREEN_PAGES = 1;
/**
* Set the number of pages that should be retained to either side of the
* current page in the view hierarchy in an idle state. Pages beyond this
* limit will be recreated from the adapter when needed.
*
* <p>This is offered as an optimization. If you know in advance the number
* of pages you will need to support or have lazy-loading mechanisms in place
* on your pages, tweaking this setting can have benefits in perceived smoothness
* of paging animations and interaction. If you have a small number of pages (3-4)
* that you can keep active all at once, less time will be spent in layout for
* newly created view subtrees as the user pages back and forth.</p>
*
* <p>You should keep this limit low, especially if your pages have complex layouts.
* This setting defaults to 1.</p>
*
* @param limit How many pages will be kept offscreen in an idle state.
*/
public void setOffscreenPageLimit(int limit) {
if (limit < DEFAULT_OFFSCREEN_PAGES) {
Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to " +
DEFAULT_OFFSCREEN_PAGES);
limit = DEFAULT_OFFSCREEN_PAGES;
}
if (limit != mOffscreenPageLimit) {
mOffscreenPageLimit = limit;
populate();
}
}
limit是我们传入的页数DEFAULT_OFFSCREEN_PAGES是默认的页数,然而你会发现无论limit是几只要小与1,最终只能等于1。所以这个方法将失效!又有人说我们可以通过改变jar包将DEFAULT_OFFSCREEN_PAGES 的值改为0,这个方法我信。。。。。但是我们还是要说说方法二!
方法二:说到这个方法我们必须了解一个方法setUserVisbleHint(boolean),老规矩源码伺候
/**
* 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.
*
* <p>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.</p>
*
* @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) {
if (!mUserVisibleHint && isVisibleToUser && mState < STARTED) {
mFragmentManager.performPendingDeferredStart(this);
}
mUserVisibleHint = isVisibleToUser;
mDeferStart = !isVisibleToUser;
}
关于这个方法的作用注视写的很清楚,大概意思是这样的
此处省去100字。。。
简而言之这个方法是告诉系统,Fragment的ui是否是用户可见的。如果是可见的那么isVisibleToUser是true否则是false。
为了方便我写了一个BaseFragment,让三个Fragment都继承它。
BaseFragment.java
package com.fragment.admin.fragmenttext.fragment;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
/**
* Created by admin on 2016/11/1.
*/
public abstract class BaseFragment extends Fragment {
private static final String TAG = "BaseFragment";
private boolean isVisable=false;
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
Log.e(TAG, "setUserVisibleHint");
if (isVisibleToUser) {
Log.e(TAG, "BaseFragment 可见");
isVisable=true;
visable();
return;
}
Log.e(TAG, "BaseFragment 不可见");
isVisable=false;
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
protected void visable()
{
onLoad();
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return super.onCreateView(inflater, container, savedInstanceState);
}
public abstract void onLoad();
}
这个类中写了一个抽象方法onLoad()用于是子类的网络加载。由以上代码可以看出当Fragment处于可见状态时才回去调用onLoad()方法,从而实现了懒加载模式。