【Android開発】Bitmapの取扱でOutOfMemoryErrorが解決出来なかった事象

自作アプリのJpopPVを元に、Android開発時に解決出来なかった事象の解決方法を記載したいと思います。


JpopPVアプリではBitmapを大量に取り扱っています。このBitmap取り扱い時、なぜかOutOfMemoryErrorの解決が長い事出来ませんでした。
なんとか解決出来た様子ですので、同じ事象に悩まれている方の参考になれば幸いです。
WEBでもBitmap取り扱い参考例はあるのですが、解決に至る記事は発見出来ませんでした。

OutOfMemoryErrorの事象

お気に入りに動画を追加した際、お気に入り一覧にて動画のサムネイルを作成します。
サムネイル作成数が大量に発生すると、OutOfMemoryErrorが発生してアプリが落ちます。

サムネイル作成フローは以下の画像。

事象が発生しつずけていたVersionのサムネイル作成コーディング。
ハイライトしている箇所でOutOfMemoryErrorが発生します。

    	if(objIconFile.exists() == false &&
    	   this.isCancelled() == false){

    		// 画像作成中フラグ設定
    		ThumbnailImageLoader.mNowCreatingFlg = true;

			// アイコンファイルが存在しない場合、作成
			// APIレベルにより、タスク処理を変更する
			if(Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD){
				//APIレベル9以前の機種の場合の処理
				retBitmap = ThumbnailUtils.createVideoThumbnail(this.mMoviePath, Thumbnails.MINI_KIND);

			}else if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD_MR1){
				//APIレベル10以降の機種の場合の処理
				MediaMetadataRetriever objMediaMeta = new MediaMetadataRetriever();
				objMediaMeta.setDataSource(this.mMoviePath);
				//秒単位で指定
				retBitmap = objMediaMeta.getFrameAtTime(1000 * 1000 * 5);
				//サムネイルを任意のサイズにリサイズ
				retBitmap = ThumbnailUtils.extractThumbnail(retBitmap, 512, 384,ThumbnailUtils.OPTIONS_RECYCLE_INPUT);
				objMediaMeta.release();
			}

			try {
				objFileOutputStream = new FileOutputStream(objIconFile);
				// jpegで保存
				retBitmap.compress(CompressFormat.JPEG, 100, objFileOutputStream);

			} catch (FileNotFoundException e) {
				e.printStackTrace();
			}finally{
				if(objFileOutputStream != null){
					try {
						objFileOutputStream.close();
						objFileOutputStream = null;
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}

    		// 画像作成済みフラグ設定
			ThumbnailImageLoader.mNowCreatingFlg = false;
    	}

原因を調査した所、恐らく、MediaMetadataRetrieverクラスから取得したBitmapがリサイクルされず、メモリに残ったままどんどんサムネイルを作成していく為、OutOfMemoryErrorが発生していると思われます。

OutOfMemoryErrorの回避

■以下のように処理を変更
・サムネイルが存在する場合でもディスクIOをシリアル処理とする
・サムネイル作成処理も同様にシリアル処理とする
・MediaMetadataRetrieverから取得したBitmapをrecycleする

以下が安定したVersionのサムネイル作成コーディング。
ハイライトしている箇所が変更箇所です。

    	if(this.isCancelled() == false){

			// アイコンファイルが存在しない場合、作成
			// APIレベルにより、タスク処理を変更する
			if(Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD){
				//APIレベル9以前の機種の場合の処理
				retBitmap = ThumbnailUtils.createVideoThumbnail(argMoviePath, Thumbnails.MINI_KIND);

			}else if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD_MR1){
				//APIレベル10以降の機種の場合の処理
				MediaMetadataRetriever objMediaMeta = new MediaMetadataRetriever();
				objMediaMeta.setDataSource(argMoviePath);
				//秒単位で指定
				objFrameAtBitmap = objMediaMeta.getFrameAtTime(1000 * 1000 * 5);
				//サムネイルを任意のサイズにリサイズ
				retBitmap = ThumbnailUtils.extractThumbnail(objFrameAtBitmap, 512, 384,ThumbnailUtils.OPTIONS_RECYCLE_INPUT);
				if(objFrameAtBitmap != null){
					// この処理が無いとOut of Memoryが起きる
					objFrameAtBitmap.recycle();
					objFrameAtBitmap = null;
				}
				if(objMediaMeta != null){
					objMediaMeta.release();
					objMediaMeta = null;
				}
			}

			try {
	        	objIconFile = new File(argIconPath);

				objCacheOutputStream = new FileOutputStream(objIconFile);

				if(retBitmap != null &&
				    objCacheOutputStream != null){
					// jpegで保存
					retBitmap.compress(CompressFormat.JPEG, 100, objCacheOutputStream);
				}

			} catch (FileNotFoundException e) {
				e.printStackTrace();
	 	    } finally {
	 	        if (objCacheOutputStream != null) {
	 	            try {
	 	            	objCacheOutputStream.flush();
	 	            	objCacheOutputStream.getFD().sync();
	 	            	objCacheOutputStream.close();
	 	            	objCacheOutputStream = null;
	 	            } catch (IOException e) {
	 	            }
	 	        }
	 	    	objIconFile = null;
	 	    }
    	}

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です


*