0

厄介な問題が
あります。サーバーから大量の GeoJSON データを取得しています。これは、16MB のヒープでも、アプリを数回起動および停止しても機能します。メモリ消費量は一定であり、それを超えることはありません。しかし、16MB のヒープを超える場合があります。簡単に説明したいと思います:
アプリは使用され、ホーム ボタンで「終了」するため、アプリはバックグラウンドに存在し、まだ破棄されていません。アプリが再開されると、アプリの一部である「コントローラー」が新しい GeoJSON データをチェックします。GeoJSON データの更新がある場合、アプリはそれをダウンロードして処理し、ここから問題が始まります。):

private synchronized String readUrlData(HttpURLConnection urlConnection) throws IOException {       
    Log.i(TAG, "Start reading data from URL");
    urlConnection.setReadTimeout(1000 * 45); //45 sec
    Reader reader = new InputStreamReader(urlConnection.getInputStream());

    StringBuilder sb = new StringBuilder(1024 * 16);
    char[] chars = new char[1024 * 16]; //16k
    int len;
    while((len = reader.read(chars)) >= 0) {
        sb.append(chars, 0, len);
    }

    reader.close();

    Log.i(TAG, "Finished reading data from URL");

    return sb.toString();
}

append()またはtoString()で OutOfMemory を取得します。明らかに、以前に何らかの形で使用された場合、アプリはこれにほとんどまたは多くのメモリを必要としません。上記のコードに対して、よりリソースに優しい方法を既に見つけようとしましたが、解決策はありません。繰り返しますが、アプリを新規から開始する場合、問題はありません。そして、私はメモリリークがないことを絶対に確信しています。

  1. この部分などを MAT で確認したところ、1.6MB を超えることはありませんでした (これが GeoJSON データです)。
  2. このユース ケースを 24MB のヒープ サイズで連続して数回実行しました。

メモリリークがあれば24MBのヒープで3回目から4回目でクラッシュするところだったのですが、問題なく動きました。

クラッシュを回避する方法を知っています。利用可能な新しい GeoJSON データがあり、アプリを再起動する必要があることをユーザーに知らせる AlertDialog をユーザーに表示できます。しかし、落とし穴があります。アプリケーションがアクティビティのfinish()によって「終了」された場合、アプリケーションはまだメモリに残っているため、再起動すると、メモリの割り当てが解除されないため、クラッシュが再び発生します(少なくとも、ほとんどの場合、それに頼ることはできません)。私はすでにそのSystem.exit(0);を理解しました。終了の代わりに、アプリ全体を強制終了するため、すべてのメモリが解放されるため、新しい GeoJSON データで再起動した後にクラッシュは発生しません。しかし、私はこれが良い解決策ではないことを知っています。私はすでにSystem.gc()を試しました重要な部分ですが、これも機能しません。この問題に対処する方法はありますか? おそらく、使用済みメモリをすべて解放してアプリを再起動するようなものが必要です。

別の解決策として、上記のコードを再設計することもできますが、これでより多くの MB を取得できるとは思いません。

これに対する合理的な解決策が見つからない場合は、ヒープが 16MB のときにSystem.exit(0)を使用して (それを確認する方法があると思います)、アプリを再起動します。

4

2 に答える 2

0

回答ありがとうございます。最後に、この 1 つの特別なケースで System.exit(0) を許可することにしました。データが更新されることはほとんどなく、たとえ誰かがアプリをバックグラウンドで実行しているときに発生するのは偶然に違いないからです。

于 2012-12-26T17:56:53.040 に答える
0

ここにいくつかのアイデアがあります:

  1. 読み取るものがあることがわかったらすぐに、古いデータを null に設定して、GC されるようにします。

  2. このタスクを実行するサービスを使用します。これは別のプロセスで実行されます (したがって、このタスクのためだけに少なくとも 16 mb が必要です)。データを処理したら、必要なコンポーネントに何らかの方法で移動し、前に古いデータをnullにします。

  3. データをすべて取得してからデコードするのではなく、取得したデータをデコードします。

  4. デコード中にデータを圧縮し、途中で解凍します。

  5. Google のプロトコル バッファや独自のカスタマイズされたデータ型など、json の代替を使用します。

  6. そこにあるデバイスのほとんどは、ヒープ メモリに 16 MB を超えています。最小 SDK バージョンを 8 または 10 に設定するだけで済みます。これよりも多くのメモリを搭載したほとんどのデバイスは、より高い API も備えているためです。

于 2012-12-25T20:06:31.493 に答える