2ntブログ

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

Androidアプリ開発 (ガベージコレクション戦争編)

こんばんわ。フィブです。


前回OpenGLの描画に切り替えて画面表示を行ったところ一応表示はできました。
が、問題発生。
3秒に1回ほどなぜか一瞬画面が固まる。

OpenGLの実装方法ミスったのかと思ったけどそんなこともなく。
まさかOpenGLでやっても重い?! そもそも処理が重すぎたの!?
と焦ったところ見つけた情報。

「GC(ガベージコレクション)が発生すると全スレッドが100ms~300ms停止します」

はい、原因はこいつでした。ガベージ。
ご存じのとおりJavaは不要になったメモリを勝手に解放してくれます。これがガベージコレクション。
メモリ領域を勝手に掃除してくれちゃうのです。そのせいで止まると・・・。

Logcatで確認。
確かにLog上でGCなんちゃらって書かれた後に画面が一時停止してる。
絶対これだ。なんて迷惑な・・・。

いろんな記事を漁っていると、ゲームのループ処理に入ったら極力GCが発生しないようにプログラムを組め!
必要なものはループの前に全部メモリ確保すること。
など、結構注意事項が広まっておりました。
あたしはGCなんて意識したことないよ・・・。

実行中のメモリの割り当て解放状態は、DDMSのAllocation Trackerという機能で見れるらしいので見てみる。
確認するとあるわあるわ・・・。すごく頻繁にやりまくってる。

ソース上で確認。AllocationTrackerを確認しつつ以下一つ一つを修正していきます。

①new を絶対にしない!
  ローカル変数でもnewしたらメモリ割りあて→解放が発生します。
 必要なものはクラスのメンバ変数に突っ込んで使いまわすように修正しました。

②foreach文を使わない
  foreachはイテレータを生成して処理をするらしいのでメモリ割り当て解放が発生するようです。
  あたしのソースはArrayListを全部foreachで処理してたので全部以下のように書き直しました。
  凄い時間かかった・・・。
 
元:for( UserObject obj : mObjectList)
     ↓
  現:for( int i=0; i< mObjectList.size(); i++)

③enumクラスを使わない。
  enumクラスもメモリ割り当て→解放が頻発するらしいのでやめました。
  enumクラスはすべて定数(public static final 型) に書き換え。

④描画時にcreateBufferしない。
  前回導入したOpenGLで描画するクラスのとこですが、
  描画前にAllocationBufferでFloat型のバッファを作成しておりました。
  これがかなりメモリ食ってました。
  いいのか悪いのかわかりませんが、これも描画用のオブジェクトにFloatBuffer変数をメンバ変数に定義し
  使いまわすように修正。

さて、デバッグ実行してAllocationTrackerとにらめっこ。
明らかにメモリ解放頻度が減ってます。目に見えて減ってます。
LogcatもGC発生頻度が激減し、画面も止まらなくなりました!


しかし調査、対応で2,3日かかった・・・。疲れた・・・。
まさかガベージコレクションと戦う日がこようとは思いませんでした。
これからは敵視していきましょうっ

Android開発(何か終了時にエラー出る編)

こんばんわ、フィブです。


かなり短いメモ書き程度ですが・・・

あの後思ったように速度が出ず、ちょろっといじくってたら
戻るボタン押下の終了時にエラーが発生するようになりました。

Logcat上だと FATAL EXCEPTION main と表示され
なんかNullpointerExceptionが発生している。
Detachなんちゃらってところ。

調べてみたところ、英語サイトに答えがありました。

A:GLSurfaceViewではRendererオブジェクトがない状態でDetachが呼ばれちゃいけないらしい。
  
もともとのサンプルはSurfaceViewだったのでエラーを起こさなかったんだけど
期待した速度が出ず、継承クラスをGLSurfaceViewに変えての起動実験してました。
(結局求める速度は出ませんでしたが・・・)

その時の行動は単純にSurfaceViewの部分をGLSurfaceViewと書き換えただけ。
当然Rendererなんて設定してない。SurfaceViewに直すことによってエラーが解消しました・・・。

GLSurfaceViewはSurfaceViewとの機能の違い、実装方法の違い、などがよくわかっていないので
とりあえず置いておきます。

Androidアプリ開発(OpenGL描画導入編)

こんばんは、フィブです。


今まで作成してたプログラムですが、問題発生。
フリック処理が激重い!

今まで順調だった開発が背景スクロール実装した途端FPSが60→30台に。
フリック処理中は20台にまで落ち込むことがありハッキリ言ってとても使い物にならない。

速度を求めていろいろ調べたところやっぱりOpenGLでの描画が早いらしい。
というわけでOpenGLを用いた実装に変更していきました。

下記サイトを参考にしつつサーフェスが表示できるまで変更していきます
http://www.saturn.dti.ne.jp/~npaka/android/Graphics2DEx/index.html

いらないところは削りつつ、欲しいところはもらいつつソースを読んでいきましょう。
かなり苦労しました。
昔DirectXを学習したとき3Dの概念があまり理解できず一度投げたんですよ。
まさかこんなところで基礎知識不足というツケが回ってくるとは思いませんでした・・・。

以下よりあってるか間違ってるかわからないですが、簡単メモ書き。
※多分こうだろうってことがばっかり書いてるので全然違うかもしれません。

・OpenGLで描画するにはまずEGLというオブジェクトが必要(EGL10クラスとか)
・EGLはコンテキスト、コンフィグ、サーフェスなどをガリガリ設定して登録しておく
・OpenGLで実際に描画するにはGLオブジェクトが必要。(GL10クラスとか)くら
 →これは言うなればOpenGLで描画する際のCanvasクラスみたいなもんだと思っとく
   Canvas.drawBitmapで描画するように、GL.draw_textureで実際描画するし。

・GLクラスはビューポート変換、プロジェクション変換、モデリング変換、他ブレンド設定とかUV設定とかやっとく。
・テクスチャは2のべき乗のサイズに修正する。
 バイトバッファに画像情報をしこしこ格納していく(単純に画像のRGBA情報をバイト配列に詰めなおしてる)
 glGenTexturesで、テクスチャに管理番号を割り当てる。(GL側が勝手に管理する番号がもらえる)
 ※各テクスチャを張るときはこの管理番号で判別するため自分のオブジェクトに生成した番号を保存しておこう
 
 glBindTextureで、今つけた管理番号のテクスチャを使用しますよって連絡する
 glTexImage2Dで、画像情報が入ったバイトバッファなどを渡し「今の管理番号はこの画像と関連付けるから!」って命令する
 
 ここまでやってようやくテクスチャ一枚が使えるようになる。

・メインループ内では、今の管理番号をGLオブジェクトに渡して描画する。
・全部終わったら、EGLのeglSwapBuffersで画面に表示する!
 (あたしはここでハマりました。 これやらなかったからずっと画面真っ黒だった・・・)

以上。疲れた。。。
えーっと簡単にまとめると
-------------------------------------------------------
プログラムの開始

EGL作成・設定

GL作成・設定

テクスチャ読み込みとかする

メインループ
  ↓
  GLを使って描画
  ↓
  EGLで画面を更新!
  ↓
メインループに戻る・・・
-------------------------------------------------------

この手順を意識してやれば多分いけます・・・。
さて・・・色々疑問が浮かぶこともありましたが。

Q.INTバッファにカラー情報格納ってやってるけどこのINTバッファの処理の必要性がイマイチわからない。
 テクスチャ読む際は画像情報を自前でバッファ配列に突っ込まなきゃいけないの?
 なんかOpenGL標準読み込みメソッドとかそういうのないのかな・・・
 予想としては2つ。

 ①OpenGLにはそういう機能がないからこっちがやらなきゃいけない。
 ②こうしたほうが速い。

 バイトバッファでなんかやったほうが速いと思っとこう・・・。

Q.なんで画像読み込み時に画像作りなおしてるの?
A.これはOpenGLが画像のサイズが2のべき乗のものしか扱えないから。
  サイズがまちまちだったりするとこまるから2のべき乗の数のサイズで画像を作りなおしてるのね。

Q.テクスチャ描画する際に、引数Yがマイナスされてるのなんで?
A.これ、OpenGLの3D座標の問題で。OpenGLはY座標の値が上に伸びるから。

 画面は
 ・→X
 ↓
 Y

 だけどOpenGLは

 Y
 ↑
 ・→X

 って感じで、画面とY値の方向が逆なの。


以上、3Dに明るい人は「当たり前だろ!」とか言い出しそうな話。
あんまり詳しくない人ようにあえて書きましたが、真偽は定かでは・・。

Androidアプリ開発(描画編)

おはようございます。ふぃぶです。
Bitmap描画における移動、回転、画像切り出しの話。


AndroidでBitmap描画を行う際、CanvasのdrawBitmapを使うんですが
今回やりたいのは画像ファイルを任意の位置から切り出し、回転を加えて描画するってこと。

で、うまい方法はないかと詰まりました。

①画像切り出し表示の場合
Canvas.drawBitmap( bmp, src,dst,paint) ;

srcの部分に切り出しする位置とサイズ情報を持ったRectオブジェクトを渡すことで可能。

②回転をかけて表示する場合
Canvas.drawBitmap( bmp, matrix, paint);

matrixの部分に回転角情報を持たせたMatrixオブジェクトを渡すことで可能。


で、こいつの機能両方をもったものが欲しいんだがどうにもなさそうなので困ってます。
回転をかける際どうしても ②のメソッドを使用してしまうが、
このメソッド画像の切り出し情報を持たせる場所がないのです。
Translate(画面表示位置情報)はできるんだけど・・・。

一応考えた実装方法としては、
①テクスチャから画像を切り出し新たにBitmapを生成する
②Matrixに回転角と移動値情報を設定
③上記①で生成したテクスチャと②のMatrix情報を使用して表示。

以下コーディング例
----------------------------------------------------------
//描画するメソッド (私が勝手につくった)
public void doDraw(Canvas canvas){

//①テクスチャから画像を切り出し新たにBitmapを生成する
// mTextureには既に読み込んであるBitmapデータがあるよ
// 今回は画像上の(10,10)の位置から(20,20)の大きさで切り出します。
Bitmap tex = Bitmap.createBitmap(mTexture,10,10,20,20);

//②画像描画情報の設定

//Matrixに設定されいる情報を初期化します
mMatrix.reset();

//回転角を設定します.
// mAngleには回転角が数値で入ってるよ(1~360)
// tex.getWidth() / 2で画像の中央を指定。
// tex.getHeight() / 2で画像の中央を指定。
// 画像の中央を中心にmAngleの角度で回転させます
mMatrix.setRotate(mAngle,tex.getWidth()/2,tex.getHeight()/2);

//mPosXは画面上の表示位置が設定されてるよ
//mPosYは画面上の表示位置が設定されてるよ
mMatrix.postTranslate(mPosX, mPosY);

//①で生成した切り出し済み画像情報(tex)を
//②で設定したMatrix情報を使用して表示します。
canvas.drawBitmap( tex , mMatrix,null );
}


一応これで実現可能なんですけど、問題が。
そう、毎回newしてるので絶対重いよね!
1フレームにつき1回はnewが発生します。
しかもオブジェクトの数だけ発生します。
fps60だと考えたら秒間60*オブジェクト数回だけnewが発生する!

絶対重いでしょこれ・・・・

もっとうまい方法ないのかしら。

Androidアプリ開発(環境セットアップ編)

こんばんは、フィブです。
最近話題のAndroidアプリ開発に着手しようと
ゆっくりゆっくり作業を進めています。

さて、色々エラーも起こりまくったので自分用にメモ書き。
もちろん同じようなことで困ってる人のためにも書くんですが、
ここR-18サイトですよ?どんな技術者が見るんですか・・・。

さて、今回インストールの参考にしたのはここです。
http://gihyo.jp/dev/serial/01/androidapp/0002?page=1

簡単に手順をまとめると・・・
1.AndroidSDKを公式からDL(あたしの場合インストーラー)→インストーラーでインストール
2.環境変数の設定。(変数)Pathの最後に「;[AndroidSDKのインストール先]\tools」を追加
3.JDKのインストール(あたしのPCではインストール済みだったのでスキップ)
4.Eclipceのダウンロード→解凍→Cドライブの直下に移動
5.Eclipceの日本語適用(上記サイトの手順ではエラーだったのでスキップしました)
6.Eclipceを使用してADTプラグインのインストール→促されるままにEclipce再起動
7.Eclipceの[ウィンドウ]メニュ→設定→AndroidのSDKロケーションに
  [AndroidSDKのインストール先]を設定。
  なんかこの時エラーできたしたけど気にしない。
8.[ウィンドウ]メニュ→AndroidSDK及びAVDマネージャを選択。
  Available packagesに表示されもノを全部インストール。
  ※インストール超長いです!2時間あれば入りますっ。
9.エミュレータ設定をして、エミュレータの起動を確認。

ここで、エミュレータ起動しないトラブル発生。
色々調べてるうちにどうやらWindowsのユーザ名に全角文字が設定されているとダメみたい。

9のエミュレータの設定を行うと、
C:\Documents and Settings\[ユーザ名のフォルダ]に「.android」ってフォルダが作成されます。
このなかの\.android\avd\の中にあるiniファイルを開きましょう。
path=C:\Documents and Settings\【文字化け】.android\avd\~~
ってなってると思うんだけど、この文字化けがダメみたい。
2バイト文字だめなみたいなの・・・。
ちなみにアドレス書き換えても2バイト文字入ってたらダメみたいです。

ので、次の手順を踏みます。
~~\.android\avd以下のフォルダをCドライブ直下に移動。
さっき開いたiniファイルに描いてあるpathを移動先のパスに書き換える。

これで再度Eclipceからエミュレータ起動かけたらエミュレータが起動掛かりました。

■ここから本題。


インストール完了翌日(今日)。
Androidのエミュレータが起動しなくなりました。

■症状
1.Eclipceを起動すると直後に「android sdk content loader」とエラーが出る。

2.AndroidSDK及びAVDマネージャを選択すると以下のエラーが発生。
 「Location of the Android SDK had not been setup in the preference.」
3.Eclipceの[ウィンドウ]メニュ→設定→AndroidのSDKロケーションの下の
  SDKターゲットリストがなにも表示されない。

■ネットでみつけた対応でダメだったもの
・[プロジェクト]→自動的にビルドのチェックを外す。(効果なし)
・SDKロケーションパスの再設定。(効果なし)


■現在で見る感触
 まず、1はよくわからないのですが、2は「SDKロケーションが設定されないです」
 って言ってて、3は何かロケーションが認識できてなさそう。原因分からない・・・。
 要は3が上手く行ってないから2がダメなんじゃないかと。
 何で・・・?

■やってみたこと。
・AndroidSDKのzip版を落としなおしてSDKロケーションに設定(効果なし)
・ADTプラグインのインストール再確認(全部されてる)
・環境変数の設定しなおし(効果なし)
・Eclipceの再起動(効果なし)
・ネトゲを終了させてEclipce再起動(効果なし)
・昨日編集したiniファイルの変更。(効果なし)

 何やっても原因がわかりませんでした。
 だって昨日起動確認→今日起動でエラー だもん。何が原因なんよ・・・っ!


■対応
全部消して再インストール!

・・・最悪の対応な気がする;;
でもまぁほら、どんな手使っても動く環境作るのが大事です!


■アンインストール
・タスクマネージャのプロセスから「adb.exe」を終了させる。
 (これが動いてると使用中といわれフォルダが消せない)
・AndroidSDKをアンインストーラーでアンインストール
・EclipceとWorkフォルダを削除。
・C:\Documents and Settings\[ユーザ名のフォルダ].androidフォルダを削除。
・Cドライブ直下に移動したAndroidのフォルダを削除

■インストール。
1.AndroidSDKを公式からDL(あたしの場合インストーラー)→インストーラーでインストール
  このとき、Program Fileフォルダでなく「C:\Android\android-sdk」にインストール
2.環境変数の設定。(変数)Pathの最後に「;[AndroidSDKのインストール先]\tools」を追加
3.Eclipceのダウンロード→解凍→Cドライブの直下に移動
4.Eclipce日本語化プラグインをダウンロード反映( http://mergedoc.sourceforge.jp/ )
4.Eclipceのショートカットを作成。-creanオプションを設定し起動は全部そこからするようにした
6.Eclipceを使用してADTプラグインのインストール→促されるままにEclipce再起動
7.Eclipceの[ウィンドウ]メニュ→設定→AndroidのSDKロケーションに
  [AndroidSDKのインストール先]を設定。
  なんかこの時エラーできたしたけど気にしない。
8.[ウィンドウ]メニュ→AndroidSDK及びAVDマネージャを選択。
  Available packagesに表示されもノを全部インストール。

 ※ここで一旦Eclipceを再起動。SDKロケーションがちゃんと表示されることを確認。

9.エミュレータ設定をして、エミュレータのエラーを確認。
10.C:\Documents and Settings\[ユーザ名のフォルダ]\.android\avdの下のフォルダを
  Cドライブ直下に移動。
11.移動したフォルダ内のiniファイルに記載されているpathの値を変更。
12.SDKロケーションの表示を確認。正常に表示されてる。
  ※下全部同じなんですが↑確認しないで「AndroidSDK及びAVDマネージャを選択する」を
   押すと「Location of the Android SDK~~」エラーが出ます。
   一度SDKロケーションを確認すればエラーは解消されました。

  エミュレータ起動。起動確認。
13.Eclipceを再起動させ、SDKロケーションの表示を確認。正常に表示されてる。
  エミュレータ起動。起動確認。
14.PCを再起動し、Eclipce起動。SDKロケーションの表示を確認。正常に表示されてる。
  エミュレータ起動。起動確認。


な・・・何とか動いている模様。
でも何がいけなかったんだろうか・・・。

■前回と違うところ
・Eclipceを日本語化
・SDKのインストール先を変更(パスに半角スペースとか入ってない場所にした)
・Eclipceの起動を-creanオプションがついたショートカットから起動するようにした。


う~ん、これくらいだよなぁ・・・。
Android謎が多い機能だ。PC再起動しても動いてるし大丈夫かな。
2,3日様子をみつつ開発していこうかな。

そんなわけで、同じ症状でお困りの方。
一度入れなおしてみるのも有かと。
でも原因が結局分からず・・・。

誰か教えてくださいな。