2012年5月19日土曜日

【Android】BitmapによるOut of Memoryについて

今回はBitmapが原因で起きるOut of Memory Errorについて書きたいと思います。
これは筆者が作ったアプリがOS2.3まではサクサク動いていたのに4.0のGalaxy Nexusを使用した際にロードが極端に遅くなりOut of Memory Errorで落ちる現象が起き、色々調べた結果わかったことを書きます。

もちろんBitmapをロードしたりしたらちゃんとbitmap.recycle();を使ったほうがいいですし、その直後に強制的にSystem.gc();を使って強制的にGarbage Collectionを呼んだらいいとよく書かれています。それらをやっても落ちることがあります。

Androidのリソースフォルダの中には色々なdrawableで始まるフォルダがあります。
デフォルトはdrawableですが、プロジェクトを新たに作るときは(対応OSバージョンにもよりますが)drawable-ldpi, drawable-mdpi, drawable-hdpi, drawable-xhdpiとあります。

これらは端末の解像度(正確には画面の密度)によって選択するリソースを変えるためにあります。本来の使い方であればそれぞれのフォルダに違うサイズに切り分けた同じ名前の画像ファイルを入れます。ただし、ファイルが大きくなってしまうので画像を1枚だけ用意してデフォルトのdrawableやmdpiのフォルダに入れる人も多いかと思います。筆者もそうでした。

Androidのシステムはまずリソースから画像を取って来いといわれたら端末の解像度に一番あったフォルダから探します。内場合は徐々に下っていき、最終的にはデフォルトのフォルダから引っ張ってきます。もし端末の解像度に合ったフォルダから取ってきた場合はその画像を拡大・縮小してロードします。

もしdrawable-mdpiのフォルダに画像を入れていて端末がxhdpiの端末の場合画像をかなり拡大してロードされます。特に大きな画像(背景など)はかなりのダメージを受けます。
そして筆者のミスはdrawableフォルダに画像を入れていたことでした。drawableのフォルダは実はdrawable-mdpiと同じ扱いを受けるのです。

例えば720x1280の背景画像を使うとしましょう。これをdrawable, drawable-mdpiなどのフォルダに入れるとxhdpiやhdpiの端末では拡大してロードされます。hdpiでは約1.5倍、xhdpiでは約2倍のサイズでロードされます。

なので、画像はそれぞれのサイズに合ったフォルダに入れましょう。720x1280の画像をxhdpiのフォルダに入れればxhdpiの端末ではそのままロードされます。そしてmdpiの端末では縮小してロードされます。

ただし、あまりにも拡大・縮小率が高くなるとやはり画像は荒くなります。
そこで、drawable-nodpiというフォルダをつくり、画像を入れると、どの端末でも画像そのままのサイズで使ってくれます。ただし、なんでもかんでも全部これに入れると、メモリの少ない端末などではOOMを起こしてしまうようになるので気をつけましょう。また逆に拡大・縮小してくれるとありがたい素材などもあります(ボタンなど)。

なので、なるべく画像はその画像のサイズに適したフォルダにいれるようにしましょう!また画像を用意するときにサイズをよく考えて作りましょう。
もし1サイズしか用意できないのであれば恐らくhdpi辺りのサイズの画像がいいでしょう。2サイズ用意できるのであれば大きめと小さめなどを用意しておくといいかもしれません。

とにかく大事なのは、Androidではシステムが自動でリソース画像を拡大・縮小するという事を知っておくことです。Androidは色々な解像度の端末があり、それに対応するための便利なシステムではありますが、同時にちゃんと理解していないとメモリの無駄遣いやOOMエラーに繋がります。

ちなみに筆者のアプリは90%~98%のメモリusageだったところが画像をフォルダ移動しただけで50%台まで落ちました。(これは3.0以降の端末となります。3.0まではBitmapはDalvik heapではなくnative heapに保存されていたのでDDMSのheap dumpでは見れませんでした。見る方法はありますが・・・)

というわけでOOMエラーに悩まされている方は一度リソースを置いているフォルダをもう一度確認してみてください!

英文ですが、詳しくは下のAndroid Developersのサイトに詳しく書かれています。
http://developer.android.com/guide/practices/screens_support.html

2012年5月13日日曜日

【Android】Androidで袋文字(Outlined Text)の表示

題名通り、今回はAndroidでの袋文字の表示の仕方について書きたいと思います。

フォントファイルを使用することも可能ですが、日本語だとフォントファイルが大きくなったりで、容量の少ないアプリなどを作る際には割りに合わないというケースも多いと思います。

そこで、キャンバスを使用して、袋文字を作る方法を今回は紹介したいと思います。筆者も色々と研究してこれが多分一番シンプルでわかりやすいだろうと思った手法を紹介したいと思います。

ではまずコードから。


public void draw(Canvas canvas) {
  Paint paint = new Paint();
  
  int outlineColor = Color.WHITE;     //袋文字のふちの色
  int outlineWidth = 4;               //袋文字のふちの幅
  String text = "表示したいテキスト";
  int textColor = Color.BLACK;        //テキストの色
  int start = 0;                      //表示するテキストの開始位置
  int end = text.length();            //表示するテキストの終了位置+1
  int x = 0;                          //canvasに描画したいx座標
  int y = 0;                          //canvasに描画したいy座標
  
 //アンチエイリアスを設定(これをしないとギザギザになる)
 paint.setAntiAlias(true);
 
 //ふちの部分を描画
  paint.setColor(outlineColor);
  paint.setStyle(Paint.Style.STROKE);
  paint.setStrokeWidth(outlineWidth);
  canvas.drawText(text, start, end, x, y, paint);
  
 //実際のテキストを描画
  paint.setColor(textColor);
  paint.setStyle(Paint.Style.FILL);
  canvas.drawText(text, start, end, x, y, paint);
 }

という感じでやると袋文字が完成します。
最初はtextPathというのを使ってやってみたんですが、途中でずれだしたりして、clipを使用する方法も考えましたが、4.0からはHardware Accelerationを外さないとclipは使えないのでこれが一番シンプルでいいんじゃないかと思います。

いくつか注意点としては:

AntiAliasは必ず設定してください。文字がギザギザになってしまいます。

paint.setAntiAlias(true);

他にはstartとendは必要ない場合は使わなくても大丈夫です。drawTextにはstartとendを必要としないバージョンも存在するので。
canvas.drawText(text, start, end, x, y, paint);

canvas.drawText(text, x, y, paint);
でも大丈夫です。

あと、実際に使用する際はPaintオブジェクトや各種設定用変数は他で指定するなりしたほうがいいです。あくまでサンプルコードとしてとらえていただければと思います。

筆者はこれをカスタマイズしたTextViewに組み込んでTextViewと同じ用に使えるようにしています。カスタマイズしたTextViewの書き方はまた後日紹介したいと思います。

2012年5月12日土曜日

初投稿

初投稿です。宜しくお願い致します。

このブログではAndroid開発に関することを中心に書いていこうと思っています。
日々の開発で気づいたことや、質問があったことなど、他の人にも役立ちそうな情報を提供できたらなと思っています。
また、要望があれば英語の記事や、フォーラムなどの翻訳も時間の許す限り行っていきたいと思っています。

筆者は米国の工科大を卒業後、日本に帰国し現在アンドロイドアプリ開発のプログラマーとして働いています。

更新は不定期になることが予想されますが、ちょっとずつ記事を増やし、ブログも強化していこうと思っています。

またここに掲載されるコードは全て商用、私用問わず、自由に使っていただいてかまいません。

ブログ初心者なので至らないところも多々あるかと思いますが、宜しくお願い致します。