1

私のアプリには、カメラのプレビューとして機能するアクティビティがあります。画面全体に広がるSurfaceViewと、その上にオーバーレイされた多数のアイテムを使用しますが、それほど複雑なものはありません。このアクティビティは他のアクティビティを起動して、カメラのプレビューに戻ることができます。

リソースのクリーンアップ、ビットマップのリサイクル、メモリリークの回避などに細心の注意を払っています。このアプリを実行して、狂ったようにテストできますが、スマートフォンがしばらくオンになっていて、他のアプリがメモリにあると、カメラプレビューを保持するアクティビティを再開または作成すると、サイレントシャットダウンが発生します。クラッシュを再現するための一般的なテストケースは、アプリの使用、写真のスナップ(処理をトリガーする)、サブアクティビティの起動などです。アプリを終了し、リソースやグラフィックが重いものを起動してから、アプリを再開します。

クラッシュ時のlogcat出力は次のとおりです。

03-29 14:20:02.109: ERROR/dalvikvm(6368): externalAllocPossible(): footprint 2756592 + extAlloc 15831356 + n 8640000 >= max 22409232 (space for 3821284)
03-29 14:20:02.109: ERROR/dalvikvm-heap(6368): 8640000-byte external allocation too large for this process.
03-29 14:20:02.109: ERROR/dalvikvm(6368): Out of memory: Heap Size=3835KB, Allocated=2835KB, Bitmap Size=15460KB, Limit=21884KB
03-29 14:20:02.109: ERROR/dalvikvm(6368): Trim info: Footprint=5383KB, Allowed Footprint=5383KB, Trimmed=1548KB
03-29 14:20:02.109: ERROR/GraphicsJNI(6368): VM won't let us allocate 8640000 bytes

私のアクティビティはすべてのステップでログに記録されるため、これは、super.onCreateを呼び出してからコンテキストビューをxmlレイアウトに設定するまでの間にActivity.onCreateで発生します。私が最初に考えたのは、SurfaceHolderを取得するプロセス、またはSurfaceHolderメソッドで発生するプロセスは、メモリが不足している状況では多すぎる可能性があるということでした。xmlレイアウトを解析し、Viewオブジェクトを構築しているときに、setContentViewで発生しているようです。

私のカメラプレビューコードは、本や記事で見つけた例から取られているので、surfaceDestroyedで行う必要のある追加のクリーンアップがあるかどうか疑問に思っていますか?その時点でガベージコレクションをトリガーする必要がありますか?この考え方の理由は、メモリ内のアプリが少ない状況でアプリを実行するのに十分なメモリがシステムにあるためです。これは、自分のアプリが十分にクリーンアップされていないか、システムが自分のアプリに十分な速度でメモリを再利用できないことに関係しています。私が理解していないのは、setContentView中に多くの新しいメモリを割り当てようとしている理由です。

これが私のSurfaceコールバックコードとアクティビティで行われていることの言い換えです

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.camera_preview);

    // crash occurs here

    // ...other stuff

    initControls();
}

private void initControls()
{       
    previewHolder = preview.getHolder();
    previewHolder.addCallback(surfaceCallback);
    previewHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

    // ...other stuff
}


SurfaceHolder.Callback surfaceCallback = new Callback() {

    public void surfaceDestroyed(SurfaceHolder holder) {
        Log.d(ApplicationEx.LogTag, "surfaceDestroyed");
        camera.stopPreview();
        camera.release();
        camera = null;
        isPreviewRunning = false;
    }

    public void surfaceCreated(SurfaceHolder holder) {
        Log.d(ApplicationEx.LogTag, "surfaceCreated");
        camera = Camera.open();

        try
        {
            camera.setPreviewDisplay(previewHolder);
        }
        catch(Throwable t)
        {

        }
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
    {
        Log.d(ApplicationEx.LogTag, "surfaceChanged");
        if (isPreviewRunning)
        {
            Log.d(ApplicationEx.LogTag, "preview is running, stop preview");
            camera.stopPreview();
            isPreviewRunning = false;
        }
        Camera.Parameters parameters = camera.getParameters();
        setPreviewAndPictureSize(parameters, width, height);
        parameters.setPictureFormat(PixelFormat.JPEG);
        parameters.setJpegQuality(85);
        camera.setParameters(parameters);
        camera.startPreview();
        isPreviewRunning = true;
        Log.d(ApplicationEx.LogTag, "end surfaceChanged");

    }
};
4

1 に答える 1

2

でプレビューを停止してカメラを離しonPause()、 で取得しonResume()ます。他に何があっても、現時点では、カメラを拘束して、ユーザーが HOME を押したときに他のアプリケーションがカメラを使用できないようにします。

(ところで、私はこれを長い間間違っていて、過去数か月にわたって自分の本で修正しました)

確かではありませんが、これがあなたの記憶状況に役立つ可能性があります. 8640000 バイトの割り当て (これは非常に大きい) を正確に求めているものを絞り込むことができれば、それが役立つかもしれません。おそらく、レイアウトの背景画像ですか?

于 2011-03-29T19:38:32.497 に答える