Viewpager + Fragmentpageradapter, when we often use a pair of partners, the actual application of the code is very simple, but there are some easy to ignore the place, This time we're going to discuss the cache application of Fragmentpageradapter to fragment.
We can first look at the simplest implementation, the custom adapter as follows:
[Code]java Code:?
01020304050607080910111213141516171819202122232425 |
public class CustomPagerAdapter
extends FragmentPagerAdapter{
private List<fragment> mFragments;
public CustomPagerAdapter(FragmentManager fm, List<fragment> fragments) {
super
(fm);
this
.mFragments = fragments;
fm.beginTransaction().commitAllowingStateLoss();
}
@Override
public Fragment getItem(
int position) {
return this
.mFragments.get(position);
}
@Override
public int getCount() {
return this
.mFragments.size();
}
@Override
public long getItemId(
int position) {
return position;
}
}</fragment></fragment>
|
The code is simple, it is not explained, and then the adapter is used in the activity:
[Code]java Code:?
01020304050607080910111213141516 |
ViewPager pager = (ViewPager) findViewById(R.id.view_pager);
List<fragment> fragmentList =
new ArrayList<>()
TestFragment fragmentOne =
new TestFragment();
fragmentOne.setText(
"One"
);
TestFragment fragmentTwo =
new TestFragment();
fragmentTwo.setText(
"Two"
);
TestFragment fragmentThree =
new TestFragment();
fragmentThree.setText(
"Three"
); fragmentList.add(fragmentOne);
fragmentList.add(fragmentTwo);
fragmentList.add(fragmentThree);
CustomPagerAdapter adapter =
new CustomPagerAdapter(getSupportFragmentManager(), fragmentList);
pager.setAdapter(adapter);</fragment>
|
This completes the most basic application of a fragmentpageradapter. Now, everything seems to be what we want, but is there really no problem?
Next, let's simulate when the program runs in the background, the Android system because of memory tension, kill our program process situation:
- First run the program to the foreground
- Next, click the Home button to return to the desktop, and our program is returned to the background to run.
- Go to Android Studio, click on Android Monitor tab, select the current device and select the process name of our program.
- Click Terminal Application This Little Red fork button, such as:
- This time the background process has been killed, but the application history of our application is still in, so long press the home button, and select our program, let it back to the foreground.
- You will see that the program does revert to the previous page. But strangely enough, there is only hello on the page, where did we go before we passed in?
Fragment code is also relatively simple, through the log, we find that when recovering, the Mtext field is empty. Therefore, the corresponding TextView on the page cannot be displayed.
[Code]java Code:?
0102030405060708091011121314151617 |
public class TestFragment
extends Fragment {
private String mText;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_test, container,
false
);
TextView textView = (TextView) view.findViewById(R.id.center_text_view);
textView.setText(mText);
return view;
}
public void setText(String text) {
this
.mText = text;
}
}
|
As we know, the above is the process of killing the back-end app while simulating the memory tension of the Android system. In addition, when the user installs a similar 360 security butler and other applications, choose to clean up the memory, will also trigger the above situation.
Then when the above process occurs, the activity's onsaveinstancestate will be called so that we can save the current user data/page state and so on. When recovering, at OnCreate, we can fetch the previously stored data and then rebind it to the view with the Savedinstancestate parameter.
This process can be understood, but back to our activity code:
[Code]java Code:?
010203040506070809101112 |
TestFragment fragmentOne =
new TestFragment();
fragmentOne.setText(
"One"
);
TestFragment fragmentTwo =
new TestFragment();
fragmentTwo.setText(
"Two"
);
TestFragment fragmentThree =
new TestFragment();
fragmentThree.setText(
"Three"
);
fragmentList.add(fragmentOne);
fragmentList.add(fragmentTwo);
fragmentList.add(fragmentThree); CustomPagerAdapter adapter =
new CustomPagerAdapter(getSupportFragmentManager(), fragmentList);
pager.setAdapter(adapter);
|
This code is called in the OnCreate method. When the app resumes from the background, the code is completely executed. In that case, the three fragment have been recreated and the corresponding text values have been set, so why is the Mtext field in fragment still empty?
Is it that the fragment on the screen, and the fragment we instantiate in OnCreate, are already not the same instance?
To verify this idea, add the following log in the OnCreate:
[Code]java Code:?
123 |
TestFragment fragmentOne = new TestFragment(); fragmentOne.setText( "One" ); Log.i( "test" , "++++fragmentOne++++:" + fragmentOne.toString()); |
Also note the log in the Oncreateview method of Testfragment:
[Code]java Code:?
1 |
Log.i( "test" , "++++current fragment++++:" + this .toString()); |
First run, yes, no problem. Both create and run the same instance 534ed278
[Code]java Code:?
12 |
I/test: ++++fragmentOne++++:TestFragment{534ed278} I/test: ++++current fragment++++:TestFragment{534ed278 # 0 id= 0x7f0c0066 android:switcher: 2131492966 : 0 } |
Next, we are going to kill the process again and restore the process. The log output is:
[Code]java Code:?
12 |
I/test: ++++fragmentOne++++:TestFragment{534c5c30} I/test: ++++current fragment++++:TestFragment{534d10d4 # 0 id= 0x7f0c0066 android:switcher: 2131492966 : 0 } |
Amount: Sure enough, this time we created the fragment, and actually passed the Oncreateview fragment. Not the same one (534c5c30/534d10d4).
It seems, or to seek the truth from the source code, open Fragmentpageradapter source, in the Instantiateitem method found the following paragraph:
[Code]java Code:?
01020304050607080910111213 |
// Do we already have this fragment?
String name = makeFragmentName(container.getId(), itemId);
Fragment fragment = mFragmentManager.findFragmentByTag(name);
if (fragment !=
null
) {
if (DEBUG) Log.v(TAG,
"Attaching item #" + itemId +
": f=" + fragment);
mCurTransaction.attach(fragment);
}
else {
fragment = getItem(position);
if (DEBUG) Log.v(TAG,
"Adding item #" + itemId +
": f=" + fragment);
mCurTransaction.add(container.getId(), fragment,
makeFragmentName(container.getId(), itemId));
}
|
The Makefragmentname method is as follows:
[Code]java Code:?
123 |
private static String makeFragmentName( int viewId, long id) { return "android:switcher:" + viewId + ":" + id; } |
Originally, in the instantiation of fragment, Fragmentpageradapter will first through the makefragmentname return of the tag, to Fragmentmanager to find whether there is the current fragment cache, if any , it restores the previous fragment back and uses it, and instead uses the new instance we passed in.
The makefragmentname generated tag is only affected by the return value of the Getitemid () method that we override, and the current container view's Id,container.getid ().
Here, the problem is clear, because Fragmentpageradapter will take the initiative to fetch the cache of the fragment, so the recovery back after the fragment instance is not the same problem.
As for why the Mtext field is empty, and how to solve the situation, we'll discuss ^_^ in the next chapter.
Fragmentpageradapter cache for fragment in Android (i)