Android

Android開発TIPS

投稿日:2017年4月22日 更新日:

android開発TIPSをまとめました。

 

androidのswitch文で「定数式が必要です」エラーが発生する対応

android開発で、switchのcase文にR.idを入れたらエラーになる場合があり、メニューの分岐処理等でよく発生する現象です。ライブラリプロジェクトにおいて、リソースフィールドを条件分岐に使用する場合は、switch ではなく if-else を使う必要があります。「ライブラリプロジェクト」とは Project → Properties → Android で Is Library にチェックを入れたプロジェクトのことです。

参考ページ:『Non-constant Fields in Case Labels

 

setTargetFragmentを使った時、落ちた時の対応

(現象)
Activity→PreferenceBaseFragment→DialogFragmentで表示している時、1回目 or 2回目の回転で落ちてしまう現象です。最初、落ちる原因が不明だったが、1つずつ処理を出し入れして原因追究すると、回転対応の処理でsetTargetFragmentを使用していると落ちる事が分かりました。setTargetFragment処理部をコメントにすると、とりあえずは落ちなくなりましたが、setTargetFragmentを使用した本来の目的は達成できなくなってしまいます。

(原因)
ActivityのonCreateでPreferenceBaseFragmentのフラグメントを作成している事が問題でした。onCreateでFragmentの生成処理を記述しているため、回転の度にFragmentが生成され競合しているため、問題が発生しているようです。

参考ページ『PreferenceFragmentからDialogFragmentを表示し画面回転で例外発生』『FragmentをActivityに貼付ける

(対応)
ActivityのonCreateで初回だけFragmentを生成するように変更しました。

 

AsyncTaskでプログレスダイアログで進捗表示する時について

AsyncTaskでProgressDialogを使用する時、onPreExecuteメソッド内でProgressDialogをインスタン化(new)してshow()メソッドを実行する場合が多いと思います(WEBページ上にあるほとんどのサンプルもこの通りだと思います)。ProgressDialogをインスタン化する時、ProgressDialogのコンストラクタにContextを与えますが、このContextは呼出しの元アクティビティのContextを与える必要があります。ActivityのContextを取得するには、(Activityを継承してるクラスで)thisを呼ぶ事になります。

 

AsyncTaskLoaderについて

AsyncTaskLoaderはAsyncTaskをLoaderの仕組みの中で管理し、処理結果を受け取るコールバックの管理の煩雑さを軽減しようとしてAsyncTaskをLoaderでラップしただけです。実行される非同期タスクとLoaderは別物として考える必要があります。基本的にプロセスがKILLされない限り実行中の非同期タスクは実行を続けるようです。よって、Loaderが破棄されても(resetイベントが発生しても)、非同期タスクは実行を続けているようです。Loaderが破棄された場合、実行中の非同期タスクは最後まで実行されますが、Loaderが破棄されているためonLoadFinishedが実行されない状況になるようです。開発者向けオプションの「アクティビティを保持しない」に設定した時、Loader実行中にホームボタンを押下した時にこの状況になるようです。

システムによる破棄処理とLoaderとの関係は、①回転等によるconfiguration changeの場合はLoaderと非同期タスクともに破棄されず引き継ぎ可能です、②Low MemoryによるプロセスKILLはプロセス死滅なので、さすがにLoaderも非同期タスクも破棄されるようです、③開発者向けオプションの「アクティビティを保持しない」に設定した時のアクティビティの破棄はLoaderのみが破棄されて非同期タスクは実行を続けます。項②と項③の場合に、Loaderによる非同期タスクは実行を開始したが、Loader破棄によりonLoadFinishedが実行されない状況になるようです。

AsyncTaskLoaderの非同期タスクにおいて、開始Loader(非同期タスクも含む)と対になる非同期タスクの完了を検知して、onLoadFinishedを確実に実行させる方法はないようです。例えば、同じ処理のタスクを重複して実行させたくない場合などで、タスクの実行中のステータス管理をonLoadFinishedで行っている場合などは注意が必要です。タスクの進捗状況を表示している場合もonLoadFinishedの確実なコールは期待できないようです。いろいろなケースにおいてonLoadFinishedがコールされるか否かを検討するのであれば、EventBusなどを使って非同期タスク処理の中で完了状況を伝達した方が、onLoadFinishedより確実性が増すと思われます。しかし、プロセスKILLによる非同期タスクの破棄は非同期タスクの完了を捉える事が出来ないので、結果的には、確実に非同期タスクの完了を検知する事は出来ない事になりそうです。Low MemoryによるプロセスKILLの場合も、Loaderも非同期タスクも途中で破棄されるので、onLoadFinishedは実行されない。どうしても、onLoadFinishedが確実に発生させるべき処理であれば、システムの破棄処理によるLoader(非同期タスク)の引き継ぎはあきらめて、破棄された以前のLoaderや非同期タスクは無視するとして、onCreate発生都度にLoader(非同期タスク)を新規作成して、onLoadFinishedが発生してくれるのを待つしかない。

AsyncTaskもAsyncTaskLoaderにおいて、Activity破棄による非同期タスクを引き継ぐケースを考えた場合、①非同期タスク終了のコールバック処理(onLoadFinished)はコールされないケースが存在する事を検討した方が良い。②よって、UI側のコールバック処理でなく、なるべく非同期タスク(loadInBackground)の終了で終了処理を行った方が良い、③非同期処理は実行されるとプロセス破棄まで実行を継続するので影響がないようにする(同じ処理の重複実行など)、④UI側の通知(終了も含めて)はEventBusなどを使ってUI側のコールバック処理(onLoadFinished)より確実性を上げる。⑤もしくは、引き継ぐ事をあきらめて、以前の非同期タスクは無視して、都度、新規タスクを発生させる事が可能な仕様とする。進捗状況を表示したり、極端に重い処理だったり、タスク実行中のステータス管理などした場合は、Activity破棄による非同期タスクを引き継ぐ必要があるので、処理が難しくなる。

onCreateメソッドにおけるinitLoaderやrestartLoaderの記述パターン

  1. 初期の画面表示処理など: 画面処理時間が極端に長くなければ、configuration changeもLow Memoryの場合も同様に扱う事が可能で、以前のタスク実行は関係なく、新規の作成したLoaderとタスクが実行中、及び、実行後に新しいUIスレッドを通信できれば良いと思います。onCreateメソッドの実行都度、新規にLoaderを作成しても良いのではないかと思います。onLoadFinishedで必ず処理をさせたいのであれば、onCreateメソッドでrestartLoaderを実行させて、都度、新規にLoaderを再作成するしかないと思います。。
  2. 画面表示後の操作処理(操作ボタンによる処理):最初からやり直しても良い処理であれば、上記と同様に、以前のタスク実行は関係なく、新規にLoderを作成しても良いと思います(restartLoderメソッド)。また、再操作してもらえる仕様なら、onCreateで新規にLoaderを作成する事も不要と思います。進捗経過などを表示している場合や処理が重い場合などで、以前の処理を引き継ぎたい場合は、onCreateのinitLoaderメソッドでローダ(非同期タスク)を引き継ぐ事を検討して、同時にonLoadFinishedが実行されない対応を検討する必要があると思われます。

 

Toolbar使用の注意点

ToobarはAndroid 5.0 (Lollipop)と同時に追加されたActionBarの代替となるコンポーネントで、アクティビティのテーマに影響します。
Toobarを使用するアクティビティに対して、AndroidManifest.xmlのactivityタグで「android:theme="@style/AppTheme.NoActionBar"」の指定を行う必要があります。

 

EventBus使用の注意点

Activityでイベントを受信するなら、onResume(もしくはonStart)でEventBus.getDefault().register(this);とすることでイベントの購読を行います。この際、onPause(もしくはonStop)でEventBus.getDefault().unregister(this);で購読解除を忘れないようにします。両者の設定をせず、@Subscribeアノテーションのメソッドのみをイベントハンドラとして登録している場合は、Activityの起動でエラーになるようです。

 

JAVAの4種類の内部クラスについて

(1)非staticな内部クラス
非staticな内部クラスは外部クラスへの暗黙的な参照を持ちます。その証拠として内部クラスのメソッド内から外部クラスのインスタンス変数にアクセス可能です。暗黙的な参照のためメモリリークの原因になることがあり、なるべく使用しないようにします。

(2)staticな内部クラス
staticな内部クラスは外部クラスへの参照を持ちません。そのため、内部クラスのメソッド内から外部クラスのインスタンス変数をアクセスすることは出来ません。また、staticな内部クラスは外部に定義したクラスとほぼ同様に扱うことが出来ますので、通常、内部クラスを定義する場合は、staticな内部クラスを使用した方が安全です。

(3)ローカルクラス

(4)無名クラス

 

Androidエミュレータについて

Androidエミュレータとしては、「Genymotion」が良いように感じました。

  • Oracle VirtualBoxで実現している(Oracle VirtualBoxをインストールする必要があります)。
  • Windows、Mac、Linuxで利用可能できます。
  • 高速に動作すると感じます。
  • 個人利用なら完全無料のようです。

(参考資料)

Genymotionでソフトキーボードを使う

GenymotionにGoogle Playストアをインストールする方法

 

開発者向けオプションの「アクティビティを保持しない」について

このオプションを有効にすることで、常に最前面以外のアクティビティはすぐに破棄されるようになります。アクティビティ保持の厳格モードとして使用します。開発中は「アクティビティを保持しない」をONにする事で、アクティビティの破棄テストを常に行う事が可能になります。ただ、このアクティビティの破棄ですが、回転などによるconfiguration changeのような動きでもなく、また、Low MemoryによるプロセスKILLの動きでもないようです。configuration changeのような動きであれば、Loaderも実行中タスクも破棄されませんし、Low MemoryによるプロセスKILLであれば、当然、Loaderも実行中タスクも破棄されます。当該アクティビティのみの破棄なので、Loaderは破棄されますが、実行中タスクは破棄されず、実行タスクの終了でonLoadFinishedが実行されない状況になるようです。

 

 

スポンサーリンク

スポンサーリンク

-Android

執筆者:

関連記事

Android開発Fragmentについてのまとめ

Android開発のFragmentについての注意事項をまめました。いくつかの守るべきルールや既に確立されたコーディングスタイルがあるようです。それを守らないとある程度は動いているんだけど、動作をさせ ...

Android開発のリストビューに関するTIPS

android開発のリストビューに関するTIPSをまとめました。   ListViewの使い方 業務アプリによるListViewの使い方のポイント 基本的に表示するだけの一覧表示するだけの機 ...

Android StudioのTIPS

Android StudioのTIPSをまとめました。   Android Studioで解決できないコンパイルエラーが発生した時の対応 今まで問題がなかったプロジェクトが、急にコンパイルエ ...