【Android】OutOfMemoryと戦う【OOM】に触発されたので自分なりの対策をまとめます。主にAndroid2.3系の話です。
メモリリークとWeakReferenceの話は省きます。
ヒープ外にBitmapをロード
BitmapをBitmapFactoryでロードする際に、隠しAPIのinNativeAllocを使ってヒープ外にBitmapをロードすることが出来ます。(Android2系まで)
BitmapFactory.Options options = new BitmapFactory.Options();
try{
Field field = BitmapFactory.Options.getClass().getField("inNativeAlloc");
field.set(options, true);
}catch(Exception e){
}
ヒープの拡大も行われないことから、純粋にロード時間の短縮にもなります。注意点として、LinuxのOOM-Killerに殺される可能性が出てきます。その際は例外をスローせずに静かにプロセスが終了します。
ActivityやService単位でプロセスを分離
プロセス単位で最大のヒープサイズが割り当てられていますので、プロセスを分離することでActivity単位で最大のヒープサイズまで使用することが出来ます。
http://developer.android.com/intl/ja/guide/topics/manifest/activity-element.html
AndroidManifest.xmlのActivityタグにprocess属性を付けることで、プロセスの分離が可能です。
プロセスの分離を行う際には変数のスコープに要注意です。static変数は同一プロセスの同一のクラスローダーの範囲で有効スコープです。最初のActivityのstatic変数にセットした値が、次のActivityで取得できなくなります。
大きなメモリ確保を行う際にtry/catch
あまりオススメできる方法ではありませんが、try/catchでOutOfMemoryErrorを捕まえるのも方法としてありです。(実際にAndroidのAPIの実装にも見ることが出来ます。)
try{
//メモリを食う処理
}catch(OutOfMemoryError e){
//代替の処理
}
AsyncTaskを過信しない
リストビューで画像を表示する際などにAsyncTaskを使うと思いますが、AsyncTaskは並列数の制限が大きいため、通信が遅い状態でスクロールをすると沢山スレッドが作成されます。適切に通信やタスクをキャンセルするか、Executors.newFixedThreadPoolで同時処理数が固定のスレッドプールを使うのがオススメです。