Я написал приложение для чтения книг, которое использует пейджер представления с FragmentStatePagerAdapter. С ним нет очевидной ошибки времени выполнения. Тем не менее, использование памяти продолжает увеличиваться при прокрутке страниц. Поэтому я протестировал простую версию установки и протестировал ее на Nexus 7. Пользователь должен чередовать несколько проверок и несколько поворотов. После этого несколько раз, из журнала видно, что использование кучи продолжает расти. Я думаю, что это наводит на мысль о утечке памяти. Однако я не могу определить причину этого. Прикреплены код, журнал GC и два подозреваемых в утечке из MAT. Любое полезное понимание будет оценено по достоинству. Благодарю.просмотр анализатор памяти пейджера
MainActivity.java:
package com.example.leakypager;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.view.ViewPager;
import android.util.Log;
public class MainActivity extends FragmentActivity {
public static final String TAG = "MainActivity";
public static final String EXTRA_argument = "pageNumber";
private static final int viewPagerId = 1;
private static final int totalPages = 10;
private int mPageNumber;
private ViewPager mViewPager;
private int mTotalPages;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set a default value to page 1
mPageNumber = getIntent().getIntExtra(EXTRA_argument, 1);
Log.i(TAG, "Called onCreate() on page number " + mPageNumber);
mViewPager = new ViewPager(this);
mViewPager.setId(viewPagerId);
setContentView(mViewPager);
mTotalPages = totalPages;
FragmentManager fragmentManager;
fragmentManager = getSupportFragmentManager();
mViewPager.setAdapter(new FragmentStatePagerAdapter(fragmentManager) {
@Override
public Fragment getItem(int pos) {
// The page number is one greater than the pager index.
Fragment pageFragment;
pageFragment = PageFragment.newInstance(pos+1);
return pageFragment;
}
@Override
public int getCount() {
return mTotalPages;
}
@Override
public int getItemPosition(Object item) {
return POSITION_NONE;
}
});
if (mPageNumber >= 1 && mPageNumber <= mTotalPages) {
// The pager item index is one less than the page number.
mViewPager.setCurrentItem(mPageNumber-1);
}
}
}
PageFragment.java:
package com.example.leakypager;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.EditText;
import android.widget.TextView;
public class PageFragment extends Fragment {
private static final String EXTRA_int = "argument";
private static final String TAG = "PageFragment";
private int mPageNumber;
private String mData;
private String mNote = "";
private TextView mDataView;
private EditText mNoteView;
public static PageFragment newInstance(int pagenumber) {
Bundle args = new Bundle();
args.putInt(EXTRA_int, pagenumber);
PageFragment fragment = new PageFragment();
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPageNumber = (Integer) getArguments().getInt(EXTRA_int);
mData = "This is page " + mPageNumber;
}
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
Log.i(TAG, "onCreateView() on page " + mPageNumber);
View v;
v = inflater.inflate(R.layout.fragment_page, parent, false);
mDataView = (TextView) v.findViewById(R.id.fragmentPage_data_id);
mNoteView = (EditText) v.findViewById(R.id.fragmentPage_note_id);
mNoteView.addTextChangedListener(new TextWatcher() {
@Override
public void afterTextChanged(Editable arg0) {}
@Override
public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {}
@Override
public void onTextChanged(CharSequence c, int start, int before, int count) {
if (c==null) c=""; // do not allow c to be null
mNote = c.toString().trim();
}
});
getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
mDataView.setText(mData);
mNoteView.setText(mNote);
return v;
} // end to implementing onCreateView() for PageFragment
}
Файл макета содержит только один TextView и один редактировать текст внутри LinearLayout. (Не воспроизводится здесь, если не запрашивается).
Бревно с GC фильтром показывает следующий результат:
12-06 00:07:03.205: D/dalvikvm(23276): GC_FOR_ALLOC freed 232K, 4% free 7758K/8052K, paused 16ms, total 18ms
12-06 00:07:10.095: D/dalvikvm(23276): GC_FOR_ALLOC freed 272K, 5% free 7989K/8324K, paused 19ms, total 19ms
12-06 00:07:20.945: D/dalvikvm(23276): GC_FOR_ALLOC freed 460K, 7% free 8043K/8564K, paused 21ms, total 22ms
12-06 00:07:23.805: D/dalvikvm(23276): GC_FOR_ALLOC freed 511K, 7% free 8043K/8616K, paused 17ms, total 17ms
12-06 00:07:42.535: D/dalvikvm(23276): GC_FOR_ALLOC freed 421K, 6% free 8134K/8616K, paused 18ms, total 21ms
12-06 00:08:03.925: D/dalvikvm(23276): GC_FOR_ALLOC freed 537K, 7% free 8109K/8708K, paused 20ms, total 20ms
12-06 00:08:11.665: D/dalvikvm(23276): GC_FOR_ALLOC freed 537K, 8% free 8083K/8708K, paused 27ms, total 30ms
12-06 00:08:15.265: D/dalvikvm(23276): GC_FOR_ALLOC freed 456K, 7% free 8138K/8708K, paused 18ms, total 18ms
12-06 00:08:20.015: D/dalvikvm(23276): GC_FOR_ALLOC freed 313K, 5% free 8315K/8708K, paused 25ms, total 26ms
12-06 00:08:24.685: D/dalvikvm(23276): GC_FOR_ALLOC freed 448K, 6% free 8378K/8888K, paused 18ms, total 19ms
12-06 00:08:27.955: D/dalvikvm(23276): GC_FOR_ALLOC freed 330K, 5% free 8560K/8952K, paused 28ms, total 28ms
12-06 00:08:36.055: D/dalvikvm(23276): GC_FOR_ALLOC freed 449K, 6% free 8625K/9136K, paused 37ms, total 37ms
12-06 00:08:42.015: D/dalvikvm(23276): GC_FOR_ALLOC freed 438K, 6% free 8699K/9200K, paused 60ms, total 63ms
12-06 00:08:45.775: D/dalvikvm(23276): GC_FOR_ALLOC freed 454K, 6% free 8760K/9276K, paused 27ms, total 30ms
12-06 00:08:51.185: D/dalvikvm(23276): GC_FOR_ALLOC freed 491K, 6% free 8771K/9328K, paused 36ms, total 36ms
12-06 00:08:57.845: D/dalvikvm(23276): GC_FOR_ALLOC freed 382K, 5% free 8866K/9328K, paused 28ms, total 30ms
12-06 00:09:03.885: D/dalvikvm(23276): GC_FOR_ALLOC freed 491K, 6% free 8887K/9440K, paused 26ms, total 27ms
12-06 00:09:06.905: D/dalvikvm(23276): GC_FOR_ALLOC freed 442K, 6% free 8955K/9460K, paused 26ms, total 27ms
12-06 00:09:11.215: D/dalvikvm(23276): GC_FOR_ALLOC freed 408K, 5% free 9058K/9528K, paused 23ms, total 25ms
12-06 00:09:19.355: D/dalvikvm(23276): GC_FOR_ALLOC freed 445K, 6% free 9130K/9640K, paused 47ms, total 47ms
12-06 00:09:24.965: D/dalvikvm(23276): GC_FOR_ALLOC freed 479K, 6% free 9208K/9748K, paused 29ms, total 30ms
12-06 00:09:30.735: D/dalvikvm(23276): GC_FOR_ALLOC freed 456K, 6% free 9330K/9848K, paused 35ms, total 36ms
12-06 00:09:39.765: D/dalvikvm(23276): GC_FOR_ALLOC freed 473K, 6% free 9477K/10012K, paused 37ms, total 37ms
12-06 00:09:54.795: D/dalvikvm(23276): GC_FOR_ALLOC freed 344K, 4% free 9803K/10208K, paused 36ms, total 36ms
12-06 00:10:05.605: D/dalvikvm(23276): GC_FOR_ALLOC freed 478K, 6% free 10104K/10644K, paused 28ms, total 28ms
12-06 00:10:17.165: D/dalvikvm(23276): GC_FOR_ALLOC freed 499K, 6% free 10483K/11044K, paused 40ms, total 41ms
12-06 00:10:28.855: D/dalvikvm(23276): GC_FOR_ALLOC freed 679K, 7% free 10811K/11552K, paused 44ms, total 46ms
12-06 00:10:43.815: D/dalvikvm(23276): GC_FOR_ALLOC freed 873K, 8% free 11054K/11988K, paused 47ms, total 47ms
12-06 00:11:02.155: D/dalvikvm(23276): GC_FOR_ALLOC freed 891K, 8% free 11360K/12312K, paused 41ms, total 41ms
12-06 00:11:22.725: D/dalvikvm(23276): GC_FOR_ALLOC freed 807K, 7% free 11852K/12720K, paused 58ms, total 58ms
12-06 00:11:46.165: D/dalvikvm(23276): GC_FOR_ALLOC freed 932K, 8% free 12382K/13376K, paused 45ms, total 45ms
Вот два интересных подозреваемых утечки сообщенные MAT:
Suspect 1.
49 instances of "android.support.v4.app.Fragment$SavedState",
loaded by "dalvik.system.PathClassLoader @ 0x420ebce8" occupy
1,892,544 (16.76%) bytes. These instances are referenced from
one instance of "java.lang.Object[]", loaded by "<system class loader>"
Keywords
java.lang.Object[]
android.support.v4.app.Fragment$SavedState
dalvik.system.PathClassLoader @ 0x420ebce8
Suspect 2.
19 instances of "com.example.leakypager.MainActivity", loaded by
"dalvik.system.PathClassLoader @ 0x420ebce8" occupy 1,419,320 (12.57%) bytes.
Biggest instances:
com.example.leakypager.MainActivity @ 0x422adfa0 - 246,896 (2.19%) bytes.
com.example.leakypager.MainActivity @ 0x4238a430 - 237,880 (2.11%) bytes.
com.example.leakypager.MainActivity @ 0x420edc60 - 160,600 (1.42%) bytes.
Keywords
dalvik.system.PathClassLoader @ 0x420ebce8
com.example.leakypager.MainActivity
(Следующее обновление добавляется дек 17, 2013) После нескольких исследований с большой помощью Мартина (спасибо!), Я узнал, что есть два преступника. Первый - TextWatcher (TextChangedListener), который не был выпущен. Это легко исправить. Второй виновник очень противный.
Второй виновник исходит от EditText, который по-прежнему сохраняет ссылку (mContext) на уничтоженную деятельность. Эта проблема возникает только тогда, когда приложение протестировано на Nexus 7 под управлением Android 4.3. Это не происходит в Samsung TF, работающем под управлением 4.1.3. MAT показывает следующие входящие ссылки с существующей деятельности в мертвую деятельность:
com.example.leakySimplePager.MainActivity @ 0x4213b220 (dead)
'- mContext android.widget.EditText @ 0x42114ec8
'- mTextView android.widget.Editor @ 0x4214b4b0
'- this$0 android.widget.Editor$EasyEditSpanController @ 0x420fafa0
'- [2] java.lang.Object[13] @ 0x420e4a08
'- mSpans android.text.SpannableString @ 0x42129b90
'- text android.widget.TextView$SavedState @ 0x42129b68
'- [2] java.lang.Object[13] @ 0x42129b20
'- mValues android.util.SparseArray @ 0x42129ab8
'- value java.util.HashMap$HashMapEntry @ 0x4210e420
'- [2] java.util.HashMap$HashMapEntry[4] @ 0x4210e3f8
'- table java.util.HashMap @ 0x4210e3c0
'- mMap android.os.Bundle @ 0x4210e398
'- mState android.support.v4.app.Fragment$SavedState @ 0x42129bb0
'- [0] java.lang.Object[12] @ 0x42123300
'- array java.util.ArrayList @ 0x42106830
'- mSavedState com.example.leakySimplePager.MainActivity$ViewPagerAdapter @ 0x42114e90
'- mViewPagerAdapter com.example.leakySimplePager.MainActivity @ 0x42140910 (alive)
'- mContext com.android.internal.policy.impl.PhoneWindow$DecorView @ 0x420efc78
'- mCurRootView android.view.inputmethod.InputMethodManager @ 0x42102ee8
Связь между живой активностью через SavedState каждый TextView (EditText является TextView), которая автоматически сохраняется системой при представление имеет изменения. (Ссылка: http://developer.android.com/guide/components/activities.html#SavingActivityState) Этот несчастный SavedState ссылается на FragmentStatePagerAdapter как часть его работы. В отличие от слушателя, я не могу освободить FragmentStatePagerAdapter из ViewPager, когда действие уничтожено.
Я заметил, что эта проблема возникает не только с помощью EditText, но и с TextView, которая поддерживает изменение его текстовых форматов/цветов и т. Д. Так или иначе, поскольку TextView не может освободить свой mContext, мой текущий поворот явно запретить его сохранение своего состояния, вызвав mNoteView.setSaveEnabled (false); когда создается представление. Это не повредит моему фактическому приложению, так как я уже сохраняю, загружаю/перезагружаю данные EditText всякий раз, когда фрагмент возвращается к активному. Однако документация разработчиков Android, похоже, не поощряет такой подход. Возможно, есть лучший способ сделать это. Любые новые идеи или советы экспертов будут приветствоваться!
Зачем вам делать ... @Override public int getItemPosition (объект объекта) { return POSITION_NONE; }, что очень неэффективно. –
В исходном приложении мне нужно обновлять позиции каждый раз, потому что на всех страницах, которые могут измениться, когда пользователь выбирает определенные параметры из меню, есть дополнительные функции, такие как фигура, макет экрана и т. Д. Эти функции не включены в этот простой код, потому что я хочу сосредоточиться на тестировании использования памяти в viewpager. – Ahui
Понял. Я все еще не видел ничего странного здесь ... разве вы, ради тестирования, пытались временно переключиться на FragmentPagerAdapter вместо государственной версии? –