Android 如何清理阵列适配器?

Android 如何清理阵列适配器?,android,out-of-memory,android-arrayadapter,Android,Out Of Memory,Android Arrayadapter,当我使用阵列适配器并更改屏幕方向时,内存不足。我制作了最小的完整工作代码来展示这个问题 package com.foobar.test; import java.util.ArrayList; import java.util.List; import android.app.Activity; import android.content.Context; import android.content.pm.ActivityInfo; import android.os.Bundle; i

当我使用阵列适配器并更改屏幕方向时,内存不足。我制作了最小的完整工作代码来展示这个问题

package com.foobar.test;

import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;

public class TestAppActivity extends Activity {
    protected ArrayList<ListItem> mModel = new ArrayList<ListItem>();
    protected ListAdapter mAdapter = null;
    private static Context mApplicationContext = null;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        System.gc();
        setContentView(R.layout.main);

        if (mApplicationContext == null) { mApplicationContext = getApplicationContext(); }

        ListView lv = (ListView) findViewById(R.id.ListList);
        lv.setItemsCanFocus(true);
        mAdapter = new ListAdapter(mApplicationContext, lv, mModel);
        lv.setAdapter(mAdapter);

        if (savedInstanceState == null) {
            Log.i("test", "@@ start");
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        boolean change = true;
                        Thread.sleep(4 * 1000);
                        for (int ii = 0; ii < 200; ii++) {
                            setRequestedOrientation(change ? ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE : ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
                            Thread.sleep(4 * 1000);
                            change = !change;
                            Log.i("test", "@@ change: " + (ii+1));
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        Log.i("test", "@@ complete!");
                    }
                }
            }).start();
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

// doesn't make any difference.
//        ListView lv = (ListView) findViewById(R.id.ListList);
//        lv.setAdapter(null);
//        adapter.clear();
//        adapter = null;

        unbindDrawables(findViewById(R.id.ListList));
        System.gc();
    }

    private void unbindDrawables(View view) {
        if (view == null) { return; }
        if (view.getBackground() != null) { view.getBackground().setCallback(null); }

        if (view instanceof ViewGroup) {
            for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) { unbindDrawables(((ViewGroup) view).getChildAt(i)); }
            if (!(view instanceof AdapterView)) { ((ViewGroup) view).removeAllViews(); }
        }
    }

    public static class ListItem {
        public int placeholder;
    }

    public static class ListAdapter extends ArrayAdapter<ListItem> implements View.OnClickListener {
        public ListAdapter(Context context, ListView lv, List<ListItem> model) {
            super(context, android.R.layout.simple_list_item_1, model);
        }

        @Override
        public void onClick(View v) { }
    }
}
package com.foobar.test;
导入java.util.ArrayList;
导入java.util.List;
导入android.app.Activity;
导入android.content.Context;
导入android.content.pm.ActivityInfo;
导入android.os.Bundle;
导入android.util.Log;
导入android.view.view;
导入android.view.ViewGroup;
导入android.widget.AdapterView;
导入android.widget.ArrayAdapter;
导入android.widget.ListView;
公共类TestAppActivity扩展了活动{
受保护的ArrayList mModel=新的ArrayList();
受保护的ListAdapter mAdapter=null;
私有静态上下文mApplicationContext=null;
@凌驾
创建时的公共void(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
gc();
setContentView(R.layout.main);
如果(mApplicationContext==null){mApplicationContext=getApplicationContext();}
ListView lv=(ListView)findViewById(R.id.ListList);
lv.setItemsCanFocus(真);
mAdapter=newlistadapter(mApplicationContext、lv、mModel);
低压设置适配器(mAdapter);
如果(savedInstanceState==null){
Log.i(“测试”,“启动”);
新线程(newrunnable()){
@凌驾
公开募捐{
试一试{
布尔变化=真;
线程。睡眠(4*1000);
对于(int ii=0;ii<200;ii++){
setRequestedOrientation(更改?ActivityInfo.SCREEN\u方向\u横向:ActivityInfo.SCREEN\u方向\u纵向);
线程。睡眠(4*1000);
改变=!改变;
Log.i(“测试”,“更改:”+(ii+1));
}
}捕获(例外e){
e、 printStackTrace();
}最后{
Log.i(“测试”,“完成!”);
}
}
}).start();
}
}
@凌驾
受保护的空onDestroy(){
super.ondestory();
//没什么区别。
//ListView lv=(ListView)findViewById(R.id.ListList);
//lv.setAdapter(空);
//适配器。清除();
//适配器=空;
不可绑定的文件(findViewById(R.id.ListList));
gc();
}
私有void未绑定可提取项(视图){
如果(view==null){return;}
如果(view.getBackground()!=null){view.getBackground().setCallback(null);}
if(视图组的视图实例){
对于(int i=0;i<((视图组)视图).getChildCount();i++){unbindDrawables(((视图组)视图).getChildAt(i));}
如果(!(AdapterView的视图实例)){((视图组)视图).removeAllViews();}
}
}
公共静态类ListItem{
公共int占位符;
}
公共静态类ListAdapter扩展ArrayAdapter实现View.OnClickListener{
公共ListAdapter(上下文上下文、ListView lv、列表模型){
super(context,android.R.layout.simple\u list\u item\u 1,model);
}
@凌驾
公共void onClick(视图v){}
}
}
在大约70次方向改变之后,我得到了一个内存不足的错误(因为我有一个特别大的背景图像)。我的XML如下所示:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical"
    android:background="@drawable/background">

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/hello" />

    <ListView
        android:id="@+id/ListList"
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:choiceMode="singleChoice"
        android:cacheColorHint="#00000000"
        android:dividerHeight="3dp"
        android:overScrollFooter="@null" />

</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="50dp"
    android:orientation="horizontal">
</LinearLayout>

我的简单背景如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical"
    android:background="@drawable/background">

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/hello" />

    <ListView
        android:id="@+id/ListList"
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:choiceMode="singleChoice"
        android:cacheColorHint="#00000000"
        android:dividerHeight="3dp"
        android:overScrollFooter="@null" />

</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="50dp"
    android:orientation="horizontal">
</LinearLayout>

这让我抓狂,因为这是难以置信的简单,似乎这应该奏效;这是适配器代码中的错误吗

蒂亚


我将我的代码简化为:

package com.foobar.test;

import java.util.ArrayList;

import android.app.Activity;
import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ListView;

public class TestAppActivity extends Activity {
    protected ArrayList<ListItem> mModel = new ArrayList<ListItem>();
    protected ListAdapter mAdapter = null;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        ListView lv = (ListView) findViewById(R.id.ListList);
        lv.setItemsCanFocus(true);
        mAdapter = new ListAdapter(this, lv, mModel);
        lv.setAdapter(mAdapter);

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        View root = findViewById(R.id.RootView);
        ((BitmapDrawable) root.getBackground()).getBitmap().recycle();

        // this doesn't seem to have any affect.
        ListView lv = (ListView) findViewById(R.id.ListList);
        lv.setAdapter(null);
        mAdapter.clear();
        mAdapter = null;

        unbindDrawables(root);
        System.gc();
    }

    private void unbindDrawables(View view) {
        if (view == null) { return; }
        if (view.getBackground() != null) { view.getBackground().setCallback(null); }

        if (view instanceof ViewGroup) {
            for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) { unbindDrawables(((ViewGroup) view).getChildAt(i)); }
            if (!(view instanceof AdapterView)) { ((ViewGroup) view).removeAllViews(); }
        }
    }

}
package com.foobar.test;
导入java.util.ArrayList;
导入android.app.Activity;
导入android.graphics.drawable.BitmapDrawable;
导入android.os.Bundle;
导入android.view.view;
导入android.view.ViewGroup;
导入android.widget.AdapterView;
导入android.widget.ListView;
公共类TestAppActivity扩展了活动{
受保护的ArrayList mModel=新的ArrayList();
受保护的ListAdapter mAdapter=null;
@凌驾
创建时的公共void(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ListView lv=(ListView)findViewById(R.id.ListList);
lv.setItemsCanFocus(真);
mAdapter=新的ListAdapter(this、lv、mModel);
低压设置适配器(mAdapter);
}
@凌驾
受保护的空onDestroy(){
super.ondestory();
视图根=findviewbyd(R.id.RootView);
((BitmapDrawable)root.getBackground()).getBitmap().recycle();
//这似乎没有任何影响。
ListView lv=(ListView)findViewById(R.id.ListList);
lv.setAdapter(空);
mAdapter.clear();
mAdapter=null;
不可绑定(根);
gc();
}
私有void未绑定可提取项(视图){
如果(view==null){return;}
如果(view.getBackground()!=null){view.getBackground().setCallback(null);}
if(视图组的视图实例){
对于(int i=0;i<((视图组)视图).getChildCount();i++){unbindDrawables(((视图组)视图).getChildAt(i));}
如果(!(AdapterView的视图实例)){((视图组)视图).removeAllViews();}
}
}
}
如果我运行该代码,然后手动更改屏幕方向20次,然后查看Eclipse内存分析器工具,它会显示我的活动的20个实例


有趣的是,如果我在3.x设备上执行此操作,我就不会有这个问题-我猜这是在蜂巢中修复的问题?

适配器中的数据集有多大?另外,如果你可以的话,为什么不自己处理方向的改变,而不是让android重新启动你的活动