Android-Fragment中TextView.setFocusable(true)导致的内存泄露
来源:互联网 发布:广州网络远程教育 编辑:程序博客网 时间:2024/05/22 10:39
转载自:http://blog.csdn.net/goldenfish1919/article/details/38272305
转载请标明出处:http://blog.csdn.net/goldenfish1919/article/details/38272305
问题是这样的,页面中有EditText,为了让EditText失去焦点,只能让页面上的一个TextView获取焦点,因此设置了某个TextView的focusable和focusable都是true。但是很悲剧的是竟然出了内存泄露!复现代码:
MainActivity.java
- public class MainActivity extends FragmentActivity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- openFragmentFoo();
- }
- public void openFragmentFoo(){
- FragmentManager m = getSupportFragmentManager();
- FragmentTransaction ft = m.beginTransaction();
- ft.replace(R.id.fragment_container, new FooFragment());
- ft.commit();
- }
- public void openFragmentBar(){
- FragmentManager m = getSupportFragmentManager();
- FragmentTransaction ft = m.beginTransaction();
- ft.replace(R.id.fragment_container, new BarFragment());
- ft.addToBackStack(null);
- ft.commit();
- }
- public void openFragmentThird(){
- FragmentManager m = getSupportFragmentManager();
- FragmentTransaction ft = m.beginTransaction();
- ft.replace(R.id.fragment_container, new ThirdFragment());
- ft.addToBackStack(null);
- ft.commit();
- }
- }
main.xml:
- <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/fragment_container"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- />
- </FrameLayout>
- public class FooFragment extends Fragment {
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- View view = inflater.inflate(R.layout.fragment_foo, container, false);
- Button btn = (Button)view.findViewById(R.id.button1);
- btn.setOnClickListener(new OnClickListener(){
- @Override
- public void onClick(View v) {
- MainActivity main = (MainActivity)getActivity();
- main.openFragmentBar();
- }
- });
- return view;
- }
- }
fragment_foo.xml
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical" >
- <TextView
- android:id="@+id/textView1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="foo" />
- <Button
- android:id="@+id/button1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="button" />
- </LinearLayout>
arFragment.java:
- public class BarFragment extends Fragment {
- private static final String tag = "BarFragment";
- private TextView textview2;
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- View view = inflater.inflate(R.layout.fragment_bar, container, false);
- textview2 = (TextView) view.findViewById(R.id.textView2);
- <span style="color:#ff0000;">textview2.requestFocus();</span>
- Button btn = (Button) view.findViewById(R.id.button2);
- btn.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- MainActivity main = (MainActivity) getActivity();
- main.openFragmentThird();
- }
- });
- return view;
- }
- @Override
- public void onDestroyView() {
- // http://stackoverflow.com/questions/5038158/main-activity-is-not-garbage-collected-after-destruction-because-it-is-reference/23889598#23889598
- <span style="color:#ff0000;">fixInputMethodManager();</span>
- // ViewrootImpl:http://blog.csdn.net/gemmem/article/details/9967295
- <span style="color:#ff6666;">fixInputEventReceiver();</span>
- super.onDestroyView();
- }
- @Override
- public void onDestroy() {
- super.onDestroy();
- Log.e(tag, "BarFragment onDestroy");
- }
- @Override
- public void onDetach() {
- super.onDetach();
- Log.e(tag, "BarFragment onDetach");
- }
- private void fixInputEventReceiver() {
- View rootView = textview2.getRootView();//这个是PhoneWindow$DecorView
- ViewParent viewRootImpl = rootView.getParent();
- TypedObject param = new TypedObject(rootView, View.class);
- invokeMethodExceptionSafe(viewRootImpl, "clearChildFocus", param);
- }
- private void fixInputMethodManager() {
- final InputMethodManager imm = (InputMethodManager)this.getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
- final TypedObject windowToken = new TypedObject(this.getActivity().getWindow().getDecorView().getWindowToken(), IBinder.class);
- invokeMethodExceptionSafe(imm, "windowDismissed", windowToken);
- final TypedObject view = new TypedObject(null,View.class);
- invokeMethodExceptionSafe(imm, "startGettingWindowFocus", view);
- }
- public static final class TypedObject {
- private final Object object;
- private final Class<?> type;
- public TypedObject(final Object object, final Class<?> type) {
- this.object = object;
- this.type = type;
- }
- Object getObject() {
- return object;
- }
- Class<?> getType() {
- return type;
- }
- }
- public static void invokeMethodExceptionSafe(final Object methodOwner,final String method, final TypedObject... arguments) {
- if (null == methodOwner) {
- return;
- }
- try {
- final Class<?>[] types = null == arguments ? new Class[0]: new Class[arguments.length];
- final Object[] objects = null == arguments ? new Object[0]: new Object[arguments.length];
- if (null != arguments) {
- for (int i = 0, limit = types.length; i < limit; i++) {
- types[i] = arguments[i].getType();
- objects[i] = arguments[i].getObject();
- }
- }
- final Method declaredMethod = methodOwner.getClass().getDeclaredMethod(method, types);
- declaredMethod.setAccessible(true);
- declaredMethod.invoke(methodOwner, objects);
- } catch (final Throwable ignored) {
- }
- }
- }
- <?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/layout"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical" >
- <TextView
- android:id="@+id/textView2"
- android:layout_width="wrap_content"
- android:layout_height="100dp"
- android:text="bar"
- <span style="color:#ff0000;">android:focusable="true"
- android:focusableInTouchMode="true"</span>/>
- <Button
- android:id="@+id/button2"
- android:layout_width="wrap_content"
- android:layout_height="100dp"
- android:text="button2" />
- </LinearLayout>
ThirdFragment.java:
- public class ThirdFragment extends Fragment {
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- View view = inflater.inflate(R.layout.fragment_third, container, false);
- return view;
- }
- }
fragment_third.xml:
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical" >
- <TextView
- android:id="@+id/textView3"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="third" />
- <Button
- android:id="@+id/button3"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="button3" />
- </LinearLayout>
MainActivity首先是显示FooFragment,然后点击跳转到BarFragment,然后点击跳转到ThirdFragment,然后点击back->back->回到FooFragment,这个时候BarFragment和ThirdFragment肯定已经是被destroy了,控制台有输出。
假如在BarFragment的onDestroyView()中不调用fixInputMethodManager();和fixInputEventReceiver();,dump内存文件:
然后参考:http://stackoverflow.com/questions/5038158/main-activity-is-not-garbage-collected-after-destruction-because-it-is-reference/23889598#23889598
加入:fixInputMethodManager();
结果还是有:
同样的道理,继续反射之,加入:fixInputEventReceiver();终于不再泄露了。
看上去,TextView设置为focusable=true以后,InputMethodManager会把它记录为当前获取焦点的view,mNextServedView应该是点击next的时候获取焦点的view,但是,在Fragment销毁的时候,并没有通知InputMethodManager去删掉view的引用。WindowInputEventReceiver就比较坑爹了,它是ViewRootImpl的内部类,直接持有对外部类的引用,导致ViewRootImpl没有释放,而ViewRootImpl也会记录当前获取焦点的view,同样在fragment销毁的时候,引用没有被销毁!
- Android-Fragment中TextView.setFocusable(true)导致的内存泄露
- Android-Fragment中TextView.setFocusable(true)导致的内存泄露
- Android setFocusable(true)和requestFocus()的区别
- Android中Handler使用不当导致内存泄露的问题
- android开发中,可能会导致内存泄露的问题
- 【转】android开发中,可能会导致内存泄露的问题
- android开发中,可能会导致内存泄露的问题
- android开发中,可能会导致内存泄露的问题
- Android开发,中可能会导致内存泄露的问题
- Android InputMethodManager 导致的内存泄露
- Volley中listener导致的内存泄露
- Android中什么情况下会导致内存泄露
- fork 导致的内存泄露
- Android:Handler,内部类导致的可能内存泄露
- Android开发编码规范导致的内存泄露问题
- Android开发编码规范导致的内存泄露问题
- Android InputMethodManager 导致的内存泄露及解决方案
- Android InputMethodManager 导致的内存泄露及解决方案
- FFmpeg发送流媒体的命令(UDP,RTP,RTMP) - ozlargoco
- 自己改编的年会相声台词2015
- Redis研究(十一)—数据持久化
- 一个人的失败,98%死于"脾气"
- SQL Server 中对数据进行千分化,可以保留任何位数(money类型)
- Android-Fragment中TextView.setFocusable(true)导致的内存泄露
- 天祥 TX2440 UBOOT移植(3. 添加NANDFLASH有关操作)
- 用CSS美化你的滚动条
- [FAQ10145] 怎么去掉联系人、通话记录、拨号列表界面中的电话号码中间的空格?
- 天祥 TX2440 UBOOT移植(4. 支持网卡DM9000)
- 音视频的基础知识
- leetCode#168 Excel Sheet Column Title
- Mac下搭建cocos2d 和cocos2d -x 环境
- 数字三角形问题