0

AndroidがAndroid3.0でビットマップの新しいメモリ管理を導入したため(ビットマップデータはDalvikヒープ内に割り当てられるようになりました)、このOutOfMemoryErrorエラーが発生してアプリがクラッシュします。

基本的に私は約のListViewを持っています。アセットフォルダからのビットマップを保持する1000個のImageView(解像度:300 x 200ピクセル)。

JellyBeanを実行しているHTCDesireでこの問題を検出しました。高速にスクロールすると、アプリのProバージョンがクラッシュします。(無料版のリストには150個のアイテムしかありません)エミュレーターでエラーを再現しようとしましたが、Androidバージョン3.0以降のDDMSのガベージコレクションメッセージが、古い2.3.6スマートフォンのようにGC_EXTERNAL_ALLOCではないことがわかりました。 (これは問題なくアプリを実行します)。したがって、大量のデータのメモリサイズは十分ではありません。ListViewが実際にすべてのビットマップをメモリに保持しているとは思いませんが、実際には150アイテムと1000アイテムの間に違いがあるはずです。

リストアダプタでGetView()をAsyncTaskしようとしましたが、AsyncTaskスレッドでOutOfMemoryが発生します。だから私はこれでどこにも行きません...

誰かがこの問題を解決するためのアイデアを持っていますか?

Googleの開発ガイドは私を助けませんでした

編集:

わかりました。APPをMAT分析しましたが、問題の原因はListViewではなく、ViewFlipperのようです。

MainMenu、SearchScreen、SearchResults(ListViewとGridView-切り替え可能)、およびDetailedViewはすべて、ViewFlipperから離れた単一の「ページ」です。

MainMenu、SearchScreen、DetailedViewは私のメモリの約20MBを占めます。コードからList-とGridviewを抽出しましたが、それらは8MBから最大13MBまでの形式をとっているようです(スクロールの実行速度によって異なります)。

Android SDKエミュレーター(4.2をエミュレート)のVMヒープサイズは32MBです。したがって、最初は機能します。高速にスクロールすると、メモリが不足し、エラーメッセージが表示されます。

skia decoder->decode returned false

さまざまなBitmap-decode-OutOfMemoryの問題が続きます。

だから私はViewFlipperのページをアンロード/キャッシュする必要があると思います-そうですか?どうやってするか???

これは、ListView/GridView実装の抽出されたソースです。

public class GridTestActivity extends Activity {

    GridView gv;
    ListView lv;
    int lvScreenWidth;
    int lvScreenHeight;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        this.requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_grid_test);

        Display display = getWindowManager().getDefaultDisplay(); 
        this.lvScreenWidth  =  display.getWidth();
        this.lvScreenHeight  =  display.getHeight();

        MyAdapter adp = new MyAdapter(this);

        gv = (GridView)findViewById(R.id.gridView1);
        gv.setAdapter(adp);
        gv.setFastScrollEnabled(true);

        lv = (ListView)findViewById(R.id.listView1);
        lv.setAdapter(adp);
        lv.setFastScrollEnabled(true);


        ActivityManager am = (ActivityManager)this.getSystemService(ACTIVITY_SERVICE);
        Toast.makeText(this, "DALVIK HEAP SIZE: " + am.getMemoryClass() + "MB", 5).show();


    }

    static class ViewHolder{
         TextView text;
         ImageView icon;
    }


    public class MyAdapter extends BaseAdapter{

        private LayoutInflater mInflater;

        public MyAdapter(Context context) {
            mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        }


        @Override
        public int getCount() {
            return 1000;
        }

        @Override
        public Object getItem(int arg0) {
            return null;
        }

        @Override
        public long getItemId(int position) {
            return 0;
        }

        @Override
        public boolean hasStableIds() {
            return true;
        }


        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder holder;

            if (convertView == null) {
                convertView = mInflater.inflate(R.layout.list_item_icon_text, parent, false);
                holder = new ViewHolder();
                holder.text = (TextView) convertView.findViewById(R.id.text);
                holder.icon = (ImageView) convertView.findViewById(R.id.icon);

                convertView.setTag(holder);
            } else {
                holder = (ViewHolder) convertView.getTag();
            }

            holder.text.setText("pos:" + position);

            // best way to load an asset-image???
            try {
                InputStream ims = getAssets().open("assetpic_" + position + ".jpg");
                BitmapDrawable d = (BitmapDrawable) Drawable.createFromStream(ims, null);
                ims.close();
                holder.icon.setImageDrawable(d);


                if (parent.equals(lv)){   
                    int oldWidth = d.getIntrinsicWidth();
                    int oldHeight= d.getIntrinsicHeight();
                    holder.icon.getLayoutParams().height = lvScreenWidth * oldHeight/oldWidth;
                }


            }
            catch(IOException ex) {
                Log.i("MyAdapter" , "Could not load 'assetpic_" + position + ".jpg' from Assets-folder");
            }

            return convertView;
        }

    }

}

よくわかりませんが、コードの詳細が不足している可能性がありますか?

フルソース(25MB)

4

3 に答える 3

1

使用しないときは、ビットマップをリサイクルする必要があります。

bitmap.recycle();

Universal Image Loader を使用できます。https://github.com/nostra13/Android-Universal-Image-Loader .

画像の遅延読み込みを使用できます。https://github.com/thest1/LazyList .

どちらもキャッシュを使用します。

http://www.youtube.com/watch?v=_CruQY55HOk . 講演は、メモリ管理、ビットマップのリサイクル、および MAT アナライザーを使用してメモリ リークを検出する方法についてです。

http://developer.android.com/training/improving-layouts/smooth-scrolling.html . スムーズなスクロールにはビュー ホルダーを使用します。

http://www.youtube.com/watch?v=wDBM6wVEO70。トークは、ビューホルダーとリストビューのパフォーマンスについてです。

あなたが言及したように、ビットマップを効率的に表示することはすでに経験しています。

于 2013-03-22T14:15:54.553 に答える
0

これは適切な解決策ではないかもしれませんが、今のところ私が提供しなければならないのはそれだけです。マニフェストを追加します (アプリケーション タグ内)

android:largeHeap="true"

それはあなたのメモリ不足の問題を限界まで解決するかもしれません.アダプタとリストビューのペアを見せれば、もっと正当な解決策を教えてくれるかもしれません.

于 2013-03-22T14:17:16.163 に答える
0

問題は、AsyncTask は少数の画像に対してのみ有効であり、数百 (または数千) の画像ではうまく処理できないことです。

確かにキャッシングは絶対に不可欠です。あなたが投稿したGoogleリンクには、RAMとディスクキャッシングの両方の説明があります。

このような大規模なデータ セットの場合、実際には 2 つのオプションがあります。

  1. ライブラリ (Universal Image Loader や Lazy List など) を使用すると、完全ではありませんが、ほとんどの場合うまく機能します。

  2. 独自の実装を作成します。私は以前にそれらを実装したことがあり、オープンソースにしたかったので、それらが会社の IP の一部ではないことを望みました。ただし、1 つのキャッシュから、または別のキャッシュから、またはオンラインからロードするスレッドに対して ( Executorsを使用して) Threads のいくつかのグループを実装し、結果を送り返し、その ImageView がリサイクルされている場合は、送り返しをキャンセルすることを忘れないでください。これは長くて複雑なプロセスですが、正しく行えば、バターのように滑らかなスクロールが得られます。

于 2013-03-22T14:42:02.750 に答える