2014年8月29日金曜日

【Android】「ホームボタンが押されてアプリがバックグラウンドに移ったとき」を検知するActivity.onUserLeaveHint()

Androidでは、アクティビティやフラグメントのライフサイクルを意識して処理の順番や初期化のタイミングなんかを整理しないといけない。

非同期処理やAPI接続など、複雑で長い処理(「一連の処理」のつながりが長い処理)は、途中で中断される可能性にも対応しなくてはならない。


「長い処理がどこまで進んだのか」を保持しておいて、再開時に続きから実行する、という処理が難しい場合、そもそも処理を初めからやり直させるほうが確実だしエラーが少ない。時間はかかるけど。

しかし、ユーザーが端末のHOMEボタンを押してHOME画面に遷移したとしても、アプリにとってはそれは「完全終了」扱いにならない。

通常だと、アクティビティもフラグメントも、ライフサイクル上のもっと短いサイクルを回るだけである。
HOMEボタンを押す→onPause()を通る
HOME画面から再度アプリに戻ってくる→onResume()を通る

そこで、「この処理が終わってない状態の時、HOMEボタンが押されたらきちんとアプリそのものを終了させて、再開時はライフサイクルの頭から再びきちんと処理をやり直させる」という仕組みがほしい。

Activityクラスには onUserLeaveHint() というメソッドがある。これはHOMEボタンを押すなど、「ユーザーの操作でアプリがバックグラウンドに移るときに呼ばれる」処理だということ。

http://developer.android.com/reference/android/app/Activity.html#onUserLeaveHint%28%29

Called as part of the activity lifecycle when an activity is about to go into the background as the result of user choice.
意訳:ユーザーの選択によってアクティビティがバックグラウンドに移る時、アクティビティのライフサイクルの一環として呼ばれる。

なので、該当アクティビティのonUserLeaveHint() メソッドに finish() を書いておけば、HOMEボタンが押された時に完全に終了させることができるようになる。
その処理は条件付きで行いたいという場合、プリファレンスなどを使って条件を参照できるようにしておき、「これこれの時に限っては、HOMEボタン押したらアプリの終了とするからね」という処理にするといい。

Activity.finish()は、アプリをキレイに終わらせるメソッドで、該当のアクティビティとそれに連動しているフラグメントたちはそれぞれきちんとonDestroy() まで通って終了してくれるようだ。

参考:
http://blog.livedoor.jp/tattyamm/archives/3640900.html

そうか、戻るボタンの挙動にも対応しないといけないのか…

2014年8月26日火曜日

【Android】AsyncTaskLoader と AsyncTask

WebAPIからデータを取得する処理。

通信はメインスレッドでやってはいけないので 、非同期処理を作ることになる。

AsyncTaskLoaderを使うのが推奨されているらしいので、それを使って実装していた。
AsyncTaskLoaderを継承したクラスを動作させるためにはLoaderManagerクラスのインスタンスを経由しないといけない。
LoaderManagerクラスのインスタンスを取得するには、Activityを主語にして getLoaderManager() を実行しないといけない。
→アクティビティが使える場所じゃないといけない。


今回、サービスから処理がスタートして、アプリ本体が画面に表示されていなくてもAPIに接続してデータを取得しそれをプリファレンスに書き込むという処理を作ることになった。
サービスからスタートしているし、画面にアプリ本体が表示されていなくても実行できないといけないので、アクティビティが使えない=AsyncTaskLoaderが使えない。

そこで、APIに接続する処理を AsyncTaskクラスを継承したクラスで行うようにした。
これならアクティビティを必要としない。

本番のAPIではなく、仮のURLに接続してテストしたところ、値が取得できたので動きそう。

2014年8月21日木曜日

【Android】JAVAでXMLを使う際の親子関係に注意


WebAPIなどから取得したXMLを解析したいことがよくある。

<key>value</key>
この「key」と「value」とは同じ世代のように見える。

node.getNodeName();  →「key」が取得できる時
node.getNodeValue();  →「value」が取得できそうに見える

しかしそうではない。

<親>子</親>
なんとこいつらの間にも階層がある。

node.getNodeName();
→「key」が取得できる時、

node.getFirstChild().getNodeValue();
→「value」を取得するならもうひと段階子孫に行かねばならない。

【Android】アプリが画面に表示されてない時に呼び出してはいけない処理

スプラッシュ画面などにおいて、タイマータスクでフラグメントを取り除くなどView要素を操作する場合。
画面にそのアプリが表示されていなければその操作は実行できないので、画面に表示中かどうかを確認した上で動作させるようにする。
または、タイマーをonPause()でキャンセルさせる。両方行うと良い。

View要素を操作するタイマーは、onResume()でセットして、onPause()でキャンセルさせるといいみたい。こうすればHOME画面の表示中に実行されることがない。

【Android】インナークラスから外側のクラスにアクセスする

インナークラスにおいて、「外側のクラス名.this」と書けば良い。

参考:
http://www.ne.jp/asahi/hishidama/home/tech/java/class_use.html

こんな感じ:
********************************************************

public class OuterClass{
    public void testMethod(){
        System.out.println("OuterClassのtestMethod()です。");
    }//function

    //////////////////////////////////
     /**
     * インナークラス
     */
    public class InnerClass{
        public void testMethodInner(){
            //ここで外側のクラスを主語にしたい時、

            //「外側のクラス名.this」とする。
            OuterClass.this.testMethod();
        }//function
    }//class

}//class

********************************************************

【Android】Serviceライフサイクルコピペ用

    //サービスクラスを作成したらマニフェストへの登録を忘れずに!

    ///////////////////////////////////////////////////////////
    //フィールド インナークラスでも使いたい変数はフィールドに
    ///////////////////////////////////////////////////////////

    ///////////////////////////////////////////////////////////
    //メソッド ライフサイクル順
    //サービスを開始させたスレッドで処理されるので、
    //重たい処理は非同期処理にする工夫をするのが良い
    ///////////////////////////////////////////////////////////

    ///////////////////////////////////////////////////////////
    /**
     * ライフサイクル01:onCreate()
     * はじめてサービスが起動される時
     * ※複数のサービスが起動されるときは初回だけ呼ばれる
     */

    ///////////////////////////////////////////////////////////
    /**
     * ライフサイクル02:onStartCommand(Intent intent, int flags, int startId)
     * 開始:startServiceメソッドでサービスが開始する時
     */

    ///////////////////////////////////////////////////////////
    /**
     * ライフサイクル03:onBind(Intent arg0)
     * バインド:bindServiceメソッドでサービスとバインドする時
     */

    ///////////////////////////////////////////////////////////
    /**
     * ライフサイクル04:onUnbind()
     * バインド解除:サービスとのバインドを解除する時
     */

    ///////////////////////////////////////////////////////////
    /**
     * ライフサイクル05:onRebind()
     * 再バインド:サービスと再度バインドする時
     */

    ///////////////////////////////////////////////////////////
    /**
     * ライフサイクル06:onDestroy()
     * 停止:停止状態から破棄される直前
     * アクティビティのstopServiceや、ServiceのstopSelf()メソッドが呼ばれたとき
     */


    ///////////////////////////////////////////////////////////
    //クラス内クラス
    //タイマー処理などとの連携用
    ///////////////////////////////////////////////////////////
    /**
     *
     *
     */


【Android】Fragmentライフサイクルコピペ用


    ///////////////////////////////////////////////////////////
    //フィールド インナークラスでも使いたい変数はフィールドに
    ///////////////////////////////////////////////////////////

    ///////////////////////////////////////////////////////////
    //メソッド コンストラクタ
    ///////////////////////////////////////////////////////////

    ///////////////////////////////////////////////////////////
    //メソッド ライフサイクル順
    ///////////////////////////////////////////////////////////

    ///////////////////////////////////////////////////////////
    /**
     * ライフサイクル01:onAttach()
     * フラグメントがアクティビティから最初に取り付けられた時に
     * 呼び出される。
     */

    ///////////////////////////////////////////////////////////
    /**
     * ライフサイクル02:onCreate()
     * システムがフラグメントを作成した時に呼び出される。
     */

    ///////////////////////////////////////////////////////////
    /**
     * ライフサイクル03:onCreateView()
     * フラグメントが画面描画をはじめて行ったタイミングで
     * 呼び出される。
     *
     * 【重要】
     * フラグメントを使用する時、クラスに記述するべき
     * 中心的メソッド。
     *
     * inflater.inflate()で取得するViewを戻す。
     * そのとき、リソースのレイアウトXMLを指定する。
     *
     * 戻り値がViewになっている。ここで返されたViewが描画される。
     */

    ///////////////////////////////////////////////////////////
    /**
     * ライフサイクル04:onActivityCreated()
     * 呼び出し元になるActivityのonCreateメソッドが完了したら
     * 呼び出される。
     */

    ///////////////////////////////////////////////////////////
    /**
     * ライフサイクル05:onViewStateRestored()
     * フラグメントのビュー階層の状態が復元されるときに
     * 呼び出される。
     */

    ///////////////////////////////////////////////////////////
    /**
     * ライフサイクル06:onStart()
     * フラグメントがユーザーに見えるように生成された
     * タイミングで呼び出される。
     *
     * アニメーションの開始など、表示に関わる初期化処理を行う。
     */

    ///////////////////////////////////////////////////////////
    /**
     * ライフサイクル07:onResume()
     * アクティビティがバックグラウンドからフォアグラウンドに
     * 移るタイミングで呼び出される。
     *
     * イベントリスナーの登録、タイマーの開始やデータの読み込みを行う。
     *
     * ホームボタンでホームに戻ってから改めてアプリに戻ってきた時もここは通る
     */

    ///////////////////////////////////////////////////////////
    /**
     * ライフサイクル08:onPause()
     * Activityがバックグラウンドに移ったか、もしくは
     * アクティビティ内のフラグメントを変更する操作を行うことで
     * ユーザーとの対話がされなくなった場合に呼び出される。
     *
     * ホームボタンでホームに戻るときもここは通る
     */

    ///////////////////////////////////////////////////////////
    /**
     * ライフサイクル09:onStop()
     * アクティビティが停止したか、もしくはアクティビティ内の
     * フラグメントを変更する操作を行うことでユーザーに表示され
     * なくなった場合に呼び出される。
     *
     * メモリを食うインスタンスの開放など
     */

    ///////////////////////////////////////////////////////////
    /**
     * ライフサイクル10:onDestroyView()
     * フラグメントのリソースをクリアする場合に呼び出される。
     */

    ///////////////////////////////////////////////////////////
    /**
     * ライフサイクル11:onDestroy()
     * フラグメントの状態が初期化される場合に呼び出される。
     */

    ///////////////////////////////////////////////////////////
    /**
     * ライフサイクル12:onDetach()
     * フラグメントがアクティビティから剥がされる直前に
     * 呼び出される。
     */


    ///////////////////////////////////////////////////////////
    //メソッド 非ライフサイクル
    //抽象クラスの継承やインターフェイスの実装により
    //オーバーライドが義務化されるメソッドなど
    ///////////////////////////////////////////////////////////
    /**
     *
     *
     */

    ///////////////////////////////////////////////////////////
    //クラス内クラス
    //タイマー処理などとの連携用
    //外側のクラスへのアクセス表記 : 外側のクラス名.this
    ///////////////////////////////////////////////////////////
    /**
     *
     *
     */

【Android】Activityライフサイクルコピペ用


    ///////////////////////////////////////////////////////////
    //フィールド
    ///////////////////////////////////////////////////////////

    ///////////////////////////////////////////////////////////
    //メソッド ライフサイクル順 アクティビティ用
    ///////////////////////////////////////////////////////////

    ///////////////////////////////////////////////////////////
    /**
     * ライフサイクル01:onCreate()
     * 最初に呼び出される。リソースの初期化処理を行う。
     */

    ///////////////////////////////////////////////////////////
    /**
     * ライフサイクル02:onRestart()
     * Activityの停止後、再開する直前に呼び出される。
     */

    ///////////////////////////////////////////////////////////
    /**
     * ライフサイクル03:onStart()
     * 画面が表示される直前に呼び出される。
     * アニメーションの開始など、表示に関わる初期化処理を行う。
     */

    ///////////////////////////////////////////////////////////
    /**
     * ライフサイクル04:onResume()
     * ユーザーからの入力が可能となる直前に呼び出される。
     * タイマーの開始やデータの読み込みを行う。
     *
     * ホームボタンでホームに戻ってからアプリに戻ってきた時もここは通る
     */

    ///////////////////////////////////////////////////////////
    /**
     * ライフサイクル05:onPause()
     * Activityから抜けようとした時に呼び出される。
     *
     * ホームボタンでホームに戻るときもここは通る
     */

    ///////////////////////////////////////////////////////////
    /**
     * ライフサイクル06:onStop()
     * Activityが非表示になった時に呼び出される
     */

    ///////////////////////////////////////////////////////////
    /**
     * ライフサイクル07:onDestroy()
     * Activityが破棄される時に呼び出される
     */


    ///////////////////////////////////////////////////////////
    /**
     * ライフサイクル追加:onUserLeaveHint()
     * ユーザーがHOMEボタンを押すなどして、アプリがバックグラウンドに
     * 移った時に呼び出される
     */
 


    ///////////////////////////////////////////////////////////
    //メソッド オプションメニュー用
    //onCreateOptionsMenu()
    //onOptionsItemSelected()など
    ///////////////////////////////////////////////////////////
    /**
     *
     *
     */


    ///////////////////////////////////////////////////////////
    //メソッド 非ライフサイクル
    //抽象クラスの継承やインターフェイスの実装により
    //オーバーライドが義務化されるメソッドなど
    ///////////////////////////////////////////////////////////
    /**
     *
     *
     */


    ///////////////////////////////////////////////////////////
    //クラス内クラス
    //タイマー処理やフラグメントなどとの連携用
    //外側のクラスへのアクセス表記 : 外側のクラス名.this
    ///////////////////////////////////////////////////////////
    /**
     *
     *
     */


【Android】データベースインスタンスはその場で作ってすぐに閉じる

Androidでデータベースを触らないといけない時。
データベースインスタンス(SQLiteDatabaseクラスなど)は、SQLを実行する直前で作成し、SQLを実行した直後にclose()してインスタンスの変数にもnullを入れてしまうこと。

データベースインスタンスはフィールドに入れて保持しておき、複数の場所で使い回す、ということをするべきではない。
非同期処理などで、同一のインスタンスが思わぬタイミングで使われたり閉じられたりするとエラーの原因になるためである。