1

Androidタブレットに保存された画像を開き、ビットマップにデコードしてからbase64文字列に変換して、SQLite DB内に保存できるようにする次のコードを研究しています。

私の知る限り、このトランザクションから 2MB ものデータがリークされており、この関数を複数回呼び出すと、正しくガベージ コレクションされていないメモリがますます消費されます。

origin_photo が読み込まれると約 49kb を占め、base64 に変換されると約 65kb を占めます。

photo.insert 関数は、base64 の単純な SQLite 挿入といくつかの小さな情報です。それが問題の一部であるとは思いません。

この関数が完了すると、log cat に「57 フレームをスキップしました! アプリケーションはメイン スレッドで多くの作業を行っている可能性があります」というメッセージも受け取ります (これには、コードの速度を低下させるブレーク ポイントがありません)。

私は間違っている可能性があり、リークを引き起こしているのはこのコードの近くで実行されている別のセクションですが、これが最も可能性の高い候補のようです。どんな助けでも大歓迎です。

更新: base64.encodeBytes 関数はから取得されます

http://iharder.net/base64

public void savePhoto() 
{
    try
    {
        String[] projection = { MediaStore.Images.Media.DATA };
        //Get last captured image in db
        String capturedImageFilePath = null;
        try
        {
            //NOTE: The warning on activity.managedQuert states you must NOT close the cursor it creates yourself 
            Cursor cursor = activity.managedQuery(mCapturedImageURI, projection, null, null, null); 
            int column_index_data = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); 
            cursor.moveToFirst(); 
            //Get file path from last stored photo
            capturedImageFilePath = new String(cursor.getString(column_index_data));
            //cursor.close();
        }
        catch(IllegalArgumentException e)
        {
            ErrorCodes.CreateError(ErrorCodes.DCDF_SAVE_PHOTO_GET_IMAGE_FILE_PATH, this.getActivity());
        }
        catch(NullPointerException e)
        {
            ErrorCodes.CreateError(ErrorCodes.DCDF_SAVE_PHOTO_GET_IMAGE_FILE_PATH, this.getActivity());
        }
        catch(Exception e) 
        {
            ErrorCodes.CreateError(ErrorCodes.DCDF_SAVE_PHOTO_GET_IMAGE_FILE_PATH, this.getActivity()); 
        }

        int orientation = -1;
        //Get Exif data from current image and store orientation
        //Note exif data will be stripped when this filepath is turned
        //into a bitmap
        try 
        {
            ExifInterface e = new ExifInterface(capturedImageFilePath);
            orientation = e.getAttributeInt("Orientation", -1);
        }
        catch (IOException ioe) 
        {
            ioe.printStackTrace();
            ErrorCodes.CreateError(ErrorCodes.DCDF_SAVE_PHOTO_EXIF_DATA_IO, this.getActivity());
        }
        catch(Exception e) { ErrorCodes.CreateError(ErrorCodes.DCDF_SAVE_PHOTO_EXIF_DATA_GENERAL, this.getActivity()); }

        //Decode current photo into a Bitmap
        Bitmap b = BitmapFactory.decodeFile(capturedImageFilePath);

        if(b == null)
        {
            Log.d("activitycrossellcalls", "error open " + capturedImageFilePath);
            ErrorCodes.CreateError(ErrorCodes.DCDF_SAVE_PHOTO_NULL_BITMAP, this.getActivity());
        }
        else 
        {
            int width = -1;
            int height = -1;

            width = b.getWidth();
            height = b.getHeight();
            Log.d("activitycrossellcalls", "w: "+width+", h:"+height);
            //Scale down if too big
            int max = (width > height)?width:height;
            float ratio = 1;
            if(max > MAX_IMAGE_SIZE)
                ratio = (float)max/(float)MAX_IMAGE_SIZE;
            width /= ratio;
            height /= ratio;

            b = Photos.getResizedBitmap(b, height, width);

            Log.i("activitycrossellcalls", "new w: " + width + ", h: " + height);

            // Encode
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            b.compress(Bitmap.CompressFormat.JPEG, 70, baos);
            b.recycle();


            byte[] origin_photo = null;

            origin_photo = baos.toByteArray();

            // Insert               
            Photo photo = null;
            try
            {
                photo = new Photo();
                photo.base64 = Base64.encodeBytes(origin_photo);
                photo.call = DailyCallsDetailsActivity.call.id;
                photo.tag_id = TaggingActivity.currentTag.id;
                photo.orientation = orientation;
            }
            catch(Exception e) { ErrorCodes.CreateError(ErrorCodes.DCDF_SAVE_PHOTO_INIT, this.getActivity()); }

            photo.insert();
        }
    }
    catch(Exception e) { ErrorCodes.CreateError(ErrorCodes.DCDF_SAVE_PHOTO, this.getActivity()); }
}        

.

public static Bitmap getResizedBitmap(Bitmap bm, int newHeight, int newWidth) 
{
    try
    {
        int width = bm.getWidth();
        int height = bm.getHeight();
        float scaleWidth = ((float) newWidth) / width;
        float scaleHeight = ((float) newHeight) / height;
        // CREATE A MATRIX FOR THE MANIPULATION
        Matrix matrix = new Matrix();
        // RESIZE THE BIT MAP
        matrix.postScale(scaleWidth, scaleHeight);
        // RECREATE THE NEW BITMAP
        Bitmap resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, false);
        bm.recycle();
        return resizedBitmap;
    }
    catch(Exception e) { ErrorCodes.ReportError(ErrorCodes.PHOTS_GET_RESIZED_BITMAP); return null; }
}
4

3 に答える 3

3

見つけたと思う

Bitmap b = BitmapFactory.decodeFile(capturedImageFilePath);
...
b = Photos.getResizedBitmap(b, height, width);

元の b をリサイクルしていません。これは永続的なリークではありませんが、GC がスラッシングする原因となることは間違いありません。

また、LRUCache を使用してビットマップを自動的に循環させることを検討することもできます。

于 2013-07-03T11:21:42.037 に答える
2

おそらく大きい画像をデコードしています。savePhoto() の次の行の後のビットマップ b のサイズを確認します。

    Bitmap b = BitmapFactory.decodeFile(capturedImageFilePath);

その後、縮小すると、データベースに保存すると小さく表示されます。私の推測では、ソース ファイルが大きいため、予想よりも多くのメモリが使用されていると思われます。

また、logcat からの「スキップされたフレーム」メッセージについては、通常、UI スレッドで長時間実行されている操作を実行しているときに発生します。メソッドが UI スレッドから呼び出されていることを確認してくださいAsyncTask

于 2013-07-03T11:06:46.970 に答える
0

カーソルを閉じていません。唯一の問題ではないかもしれませんが、その1つです。

于 2013-07-03T11:01:08.583 に答える