2

この問題に 3 日間費やしましたが、私は困惑しています。

私のアプリは、フラグメント マネージャーを含む単一のアクティビティを使用します。現在の状態では、一度に 1 つのフラグメントのみがアタッチされるように設計されています。電話する:

getSupportFragmentManager().beginTransaction().replace(R.id.main_fragment, mCurrent).commit();

既存のフラグメントを削除し、新しいフラグメントに置き換えます。フラグメントは他の場所に保存されていません。(次のフラグメントが開かれたときに置き換えられる mCurrent とトランザクション マネージャーのみ)

発生する問題は OutOfMemory 例外ではなく、デバイス自体がメモリ不足になるため、アプリ全体が再起動します。ログには、DeadObjectException のようなもの以外は何も表示されません。

9-17 11:34:14.742: W/InputDispatcher(341): channel ~ Consumer closed input channel or an error occurred.  events=0x9

09-17 11:34:14.742: E/InputDispatcher(341): channel ~ Channel is unrecoverably broken and will be disposed!

これらは、アプリのログではなく、デバイスのログからのものです。そして、何が原因かを追跡するのに十分な情報を提供してくれません。アプリのメモリが解放されることはなく、タブレットはその時点から使用できなくなります。

メモリリークが何であるかを分析するために利用できるすべてのツールを使用しましたが、何も目立ちません。MAT (MemoryAnalyzerTool) と Eclipse DDMS ツールを使用して、何が問題なのかを突き止めました。多くの異なるフラグメントにアクセスした後、MAT は 8MB ~ 16MB の合計メモリ使用量をレポートします。Leaks Report を見ると、異常はないように思えます。Imageviews で約 3MB、Bitmaps で 3MB、その他で 8MB です。DDMS では、アプリの割り当てサイズの約 50% を使用しているとヒープに表示されます。実行中のプロセスの設定を見ると、アクセスするフラグメントごとにアプリのメモリ量が 30 ~ 140MB の間で変動しますが、デバイス自体のメモリが減少することはないため、デバイスのメモリが不足します。アプリを完全に閉じたり、終了したり、破棄したりしても、メモリが解放されることはありません。

私の推測では、メモリ内のフラグメント自体を保持しているか、アプリがそうでなくてもこれらのフラグメント内に含まれている何かを保持していると考えられます。フラグメントをスワップするたびに GC を呼び出します。

Samsung Galaxy Tab 2 10.1" を使用していますが、Motorola Xoom でも同じ問題が発生します。

質問:

  1. この種の行動を経験した人はいますか?
  2. メモリリークの可能性を調査するのに役立つ他のツールについて、誰かが私に指示を与えることができますか?
  3. デバイス メモリがどのように割り当てられているかを詳細に確認できるシステム プロセスはありますか?

御時間ありがとうございます。

4

3 に答える 3

0

メモリリークの主な原因を見つけました。Process オブジェクトを作成し、入力ストリームと出力ストリームを開くことによって、シェル コマンドを実行することが中心でした。これを使用して、ルート化されたデバイスを検出しました。これがなぜリークを引き起こしているのかを説明するには、私は Process クラスの専門家ではありません (今から調べます)。 .

public static enum SHELL_COMMAND
{
    check_su_binary(new String[] { "/system/xbin/which", "su" }), ;
    String[] command;

    SHELL_COMMAND(String[] command)
    {
        this.command = command;
    }
}

/**
 * @param shellCommand
 * @return
 */
public List<String> executeCommand(SHELL_COMMAND shellCommand)
{
    //this code was causing my memory leak
    try
    {
        String line = null;
        List<String> fullResponse = new ArrayList<String>();
        Process localProcess = Runtime.getRuntime().exec(shellCommand.command);

        OutputStreamWriter writer = new OutputStreamWriter(localProcess.getOutputStream());
        BufferedWriter bufferedWriter = new BufferedWriter(writer);

        InputStreamReader reader = new InputStreamReader(localProcess.getInputStream());
        BufferedReader in = new BufferedReader(reader);

        while ((line = in.readLine()) != null)
        {
            fullResponse.add(line);
        }
    }
    catch (Exception e)
    {
        e.printStackTrace();
    }

    try
    {
        bufferedWriter.close();
        bufferedWriter = null;
        writer.close();
        writer = null;

        in.close();
        in = null;
        reader.close();
        reader = null;
    }
    catch (IOException e)
    {
        e.printStackTrace();
    }

    return fullResponse;
}
于 2013-12-12T16:36:55.290 に答える
0

さらに分析した結果、メモリ リークには 3 つの主な問題があったと推測できます。

  1. 適切な場所 (onStart と onCreateView) が不明だったため、最初はすべての UI 作成コードを Overridden onStart メソッドに配置しました。すべての UI コードを onCreateView に移動しました (this.getView() に関連するものはすべて -> onCreateView でインフレータを使用します)。これにより、アプリを連続して開いたり閉じたりするたびに発生していたメモリ リークが解消されたようです。推奨事項は次のとおりです。

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        View v = inflater.inflate(R.layout.fragment_one, container, false);
        //ALL UI code here using "v"
        return v;
    }
    
  2. ViewPager を使用してすべてのフラグメントを格納していました (これは、特定の種類のナビゲーションが必要だったためです)。これを、カスタム フラグメント管理ソリューションの使用に切り替えました。フラグメント トランザクション マネージャーの使用をお勧めします (当初、フラグメント マネージャーがリークの原因であると考えていましたが、そうではないようです)。これはリークの原因ではありませんでしたが、大量のメモリを消費していました。

  3. WebView は、メモリ リークの重大な原因でもあります (インターネット全体で十分に文書化されているため)。WebView をコンテナーにラップし、WebView とそのコンテナーの両方を破棄するという投稿されたソリューションを使用してみました (コードは次のとおりです)。

    @Override
    public void onDestroy()
    {
        super.onDestroy();
        if (mWebContainer != null)
            mWebContainer.removeAllViews();
        if (mWebView != null)
        {
            mWebView.loadUrl("about:blank");
            mWebView.destroy();
            mWebView = null;
        }
    }
    

これは私の webview の問題を解決しませんでしたが、他の誰かの問題を解決するかもしれません。問題を探し続けています。私のシナリオには、複数のフラグメントが含まれており、それぞれに Web ビューを持つ追加のフラグメントを開くことができる Web ビューがあります。

これが他の人に役立つことを願っています。最初は多くの原因があるとは考えていなかったため、リークを引き起こすさまざまなシナリオをすべてデバッグするのに1か月以上立ち往生していました。幸運を。いつものように SO コミュニティに感謝します。

于 2013-10-07T21:23:48.003 に答える