2013/04/27

[Android]AsyncTaskLoaderを簡単に扱えるAbstractAsyncLoader

AsyncTaskLoader って AsyncTask を並列実行できるだけのやつでしょ、
なんだかよくわからないし FragmentActivity 使わないといけないしで敬遠していたけど
カメラプレビュー連射して保存を Thread で並列実行したら余裕で OOM が出るので
使うことにしました。
Fragment のライフサイクルや AsyncTaskLoader の処理の流れも知らなかったので
AsyncTaskLoader | Android Developersのサンプルコードを読んでみるも
Web でのソースコードを読むのが辛く、関係ない処理も含まれている感じで
よくわかりませんでした。

ということでいくつかのページを参考に AsyncTaskLoader を理解していきました。
AsyncTaskLoaderを使ってみる | Developers.IO
主に動作イメージなんかがわかりやすかったです。
AsyncTaskLoaderに手を出してみる - ナカザンドットネット
ソースコードのコメントが簡潔でわかりやすかったです。
技術見聞録 - AsyncTaskLoaderの動作をソースから知る
AsyncTaskLoader の挙動がちょっとわかるかもしれません。
LoaderManager#enableDebugLogging(boolean) | Android Developersというものがあるらしいです。
AsyncTaskLoaderのあるActivityに戻ってきたときに再度loadInBackgroundが呼ばれる問題 - digital matter
ココからリンクされている以下の Stack Overflow にたどり着くことができたページ。
android - onLoadFinished not called after coming back from a HOME button press - Stack Overflow
神、いわゆるゴッド。
さすが Stack Overflow 神!俺達にできないことを平然とやってのける!そこにシビれる!あこがれるゥ!
import android.content.Context;
import android.support.v4.content.AsyncTaskLoader;

public abstract class AbstractAsyncLoader<D> extends AsyncTaskLoader<D> {

    private D mData = null;

    public AbstractAsyncLoader(Context context) {
        super(context);
    }

    /**
     * {@link #startLoading()}が呼び出された時に呼び出される。
     * データが有る場合は結果を送り、ない場合は新規のデータをロードする。
     * 
     * @UiThread
     */
    @Override
    protected void onStartLoading(){
        // すでにデータが有る場合は結果を送る
        if(mData != null){
            deliverResult(mData);
        }
        // loader停止中にコンテンツが変わった、またはデータがない
        if(takeContentChanged() || mData == null){
            // #startLoading()とは異なり、以前のデータを無視して新規のデータをロードする
            forceLoad();
        }
    }

    /**
     * 非同期処理を行い、その結果を返す。
     * 
     * @Background
     */
    @Override
    public D loadInBackground(){
        return mData;
    }

    /**
     * {@link #stopLoading()}が呼び出された時に呼び出される。ロードをキャンセルする。
     * 
     * @UiThread
     */
    @Override
    protected void onStopLoading(){
        // 可能な場合、ロードをキャンセルする
        cancelLoad();
    }

    /**
     * {@link #reset()}が呼び出された時に呼び出される。ロードをキャンセルし、データを破棄する。
     * 
     * @UiThread
     */
    @Override
    protected void onReset(){
        super.onReset();
        stopLoading();
        mData = null;
    }

    /**
     * onLoadCompleteリスナーに結果を送る。
     * メインスレッドから呼び出す。
     * 
     * @param 結果
     */
    @Override
    public void deliverResult(D data){
        if(isReset()){
            // Loader停止中に呼び出された
            return;
        }
        mData = data;
        super.deliverResult(data);
    }

}
まだちょっとしか使ってないから適切かどうか自信がないけど
だいたいこんな感じで処理を組めば良いという目安にしておきたいと思う。

ジョジョの奇妙な冒険 Vol.1 (第1部オリジナルサウンドトラック、全巻購入特典フィギュア応募券付き)(初回限定版) [Blu-ray]
ジョジョの奇妙な冒険 Vol.1 (第1部オリジナルサウンドトラック、全巻購入特典フィギュア応募券付き)(初回限定版) [Blu-ray]
2013/04/26

[Android]ディスプレイを常時ONにするバックライトを消さない

バックライトを消さないようにして常時ディスプレイをONにしておく方法です。
もう今更って感じのネタですが記事に書いたことがなかったので書いておきます。

コードからでもレイアウトからでも設定できるので
どちらか好きな方を設定すれば良いです。

Activity でコードから設定

Window#addFlags(int) | Android Developers
WindowManager.LayoutParams#FLAG_KEEP_SCREEN_ON | Android Developers
上記のフラグとメソッドを使って以下のように設定します。
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
解除するときは Window#clearFlags(int) | Android Developersを使えば良いらしいです。

XML でレイアウトから設定

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:keepScreenOn="true" >
</RelativeLayout>
たぶんこれ↓
View(android:keepScreenOn) | Android Developers
コードから View にセットすることもできるようです。
View#setKeepScreenOn(boolean) | Android Developers
2013/04/22

[Subversion]svn:ignoreなど設定値のみコミットする

Subversion 面倒臭い @wada811 です。
なんか Android で Subversion 使っていたら
bin とか gen とかコンフリクトするんですけど。
めんどくさいやめてください force commit とかないんですか。

調べてみるとまずコミットするべきではないようです。
xsharing | 自動生成されるファイル/ディレクトリをコミットすんじゃねーよ

じゃあ svn:ignore というやつを設定してやろうではないかということで
svn propedit svn:ignore --editor-cmd vi とかやって bin とか gen とか 追加するわけですよ。
んじゃあ確認してみるかと
svn proplist -v とかやってみるといないわけですね。
なんだと!と思ってググってみるとどうやらコミットしてやらなければいけないらしい。面倒臭い…。

安西先生…。 git が使いたいです…。

どうせ学ぶなら git が学びたいのですよ。

話しそれたけど設定値もコミットしないといけないのでコミットするかと思うけど
まだソースコードはコミットしたくないのでどうしようかと思ったら
StackOverflow神は言った。
設定値だけコミットできるよ、と。
svn commit --depth empty . -m "Modify svn externals definition only."
svn - Commit only property changes on root of repo, not files - Stack Overflow

ところで Android で Subversion 使う時コミットに入れないのは bin と gen だけでいいんです?誰か教えてー
2013/04/21

[Subversion]指定ディレクトリ以下の「.svn」フォルダを全て削除する

Subversion というやつはトラブルとなんだかよくわからず面倒くさいので
バックアップを残して再度チェックアウトすることが度々あるのだけど、
チェックアウトしてきたプロジェクトにバックアップから
変更とか新規のファイルを持って行くと「.svn」フォルダが残ってたりして
これまた面倒臭いことになるので「.svn」フォルダを全滅させたいとよく思います。
僕は Subversion に面倒臭い思いしかさせられていないので。

そんな時は以下のコマンドを実行して貴様らの命はあと僅かだ!とか心の中で呟きながら
find . -type d -name ".svn"
以下のコマンドで「.svn」フォルダを殲滅してやりましょう。
find . -type d -name ".svn" -print0 | xargs -0 rm -rf

解説

find . -type d -name ".svn": カレントディレクトリ内の「.svn」というディレクトリを検索する
find . -type d -name ".svn" -print0 | xargs -0 rm -rf
-print0: パス名にヌル文字をつけて標準出力に出力する
xargs -0: 区切り文字をスペースや改行からヌル文字に変更して以降のコマンドを実行する
rm -rf: 通常ディレクトリの削除でエラーになるところを強制的にディレクトリごと削除する
2013/04/20

[Eclipse]AndroidとSupportPackageのソースをアタッチする

久々に本格的に Android アプリ開発をしようとしているので
効率良く開発できるようにと色々環境整備を進めている @wada811 です。

今回は Android と SupportPackage のソースを Eclipse で見れるようにします。
コレをやっておくとなんでアプリが落ちたとかわかりやすくなるかもしれません。
Android のソースを追って理解できるスキルがないと意味が無いかもしれないけど…。
SupportPackage のソースをアタッチしておけば JavaDoc も見れるので便利です。

Android SDK Manager から Source for Android SDK と
Android Support Library をダウンロードする

もうすでにダウンロードしているかもしれませんが確認しといてください。

Source for Android SDK のソースをアタッチする

プロジェクト名の所で右クリック
Properties > Java Buil Path > Libraries > Android x.x.x > android.jar > source attachment
で Edit ボタンを押す。
External Folder で [Android SDK Path]/sources があると思うからそのフォルダを指定する。
後は OK を押して適当に Activity など Android SDK 由来のキーワードの上で F3 を押せば
定義元ジャンプして Android SDK のソースに飛んで読むことができる。

Android Support Library をアタッチする

同様にプロジェクト名の所で右クリック
Properties > Java Buil Path > Libraries > android-support-v4.jar > source attachment
で Edit ボタンを押す。
External Folder で [Android SDK Path]/extras/android/support があると思うからそのフォルダを指定する。
Order and Export で android-support-v4.jar が Android Dependencies の上に来るまで Up ボタンを押す。

以上!

これでソースが読めなくて困ることはないし、
Android Support Library の JavaDoc も表示されます!

参考
穀風: Android のソースコードを Eclipse から使用できるようにする
Android 開発者必須!SupportPackage で SDK バージョン間の実装の違いを解消しよう | Developers.IO
2013/04/15

[Android]Logクラスをもっと使いやすく!クラス名、メソッド名、行数を表示するLogUtilクラス

久しぶりにAndroidアプリ開発してたら色々面倒臭くてUtilクラスばっかり作ってます。
今回はその一環で作ったLogUtilについてです。

ベースは以下のgistのLoggerクラスで、
Androidでログを吐くためのクラス。クラス名とメソッド名はStackTraceから取ってきてるので、Log.d(LOG_TAG, "関数名, 文字列"); って書かなくても Logger.d("文字列"); でいける
クラス名、メソッド名、行数の取得をこちらに切り替え、
AndroidのLogCatのTagにクラス名、メソッド名、行番号を表示するクラス | DEVLAB
クラス名の分割をこっちの方法でやりました。
琴線探査: AndroidのLogCatのTagで「クラス名.メソッド名:行番号」と表示するには?

import android.util.Log;

public class LogUtil {

    private static final String  TAG     = "LogUtil";
    private static final boolean isDebug = true;

    public static void v(){
        if(isDebug){
            Log.v(TAG, getMetaInfo());
        }
    }

    public static void v(String message){
        if(isDebug){
            Log.v(TAG, getMetaInfo() + null2str(message));
        }
    }

    public static void d(){
        if(isDebug){
            Log.d(TAG, getMetaInfo());
        }
    }

    public static void d(String message){
        if(isDebug){
            Log.d(TAG, getMetaInfo() + null2str(message));
        }
    }

    public static void i(){
        if(isDebug){
            Log.i(TAG, getMetaInfo());
        }
    }

    public static void i(String message){
        if(isDebug){
            Log.i(TAG, getMetaInfo() + null2str(message));
        }
    }

    public static void w(String message){
        if(isDebug){
            Log.w(TAG, getMetaInfo() + null2str(message));
        }
    }

    public static void w(String message, Throwable e){
        if(isDebug){
            Log.w(TAG, getMetaInfo() + null2str(message), e);
            printThrowable(e);
            if(e.getCause() != null){
                printThrowable(e.getCause());
            }
        }
    }

    public static void e(String message){
        if(isDebug){
            Log.e(TAG, getMetaInfo() + null2str(message));
        }
    }

    public static void e(String message, Throwable e){
        if(isDebug){
            Log.e(TAG, getMetaInfo() + null2str(message), e);
            printThrowable(e);
            if(e.getCause() != null){
                printThrowable(e.getCause());
            }
        }
    }

    public static void e(Throwable e){
        if(isDebug){
            printThrowable(e);
            if(e.getCause() != null){
                printThrowable(e.getCause());
            }
        }
    }

    private static String null2str(String string){
        if(string == null){
            return "(null)";
        }
        return string;
    }

    /**
     * 例外のスタックトレースをログに出力する
     * 
     * @param e
     */
    private static void printThrowable(Throwable e){
        Log.e(TAG, e.getClass().getName() + ": " + e.getMessage());
        for(StackTraceElement element : e.getStackTrace()){
            Log.e(TAG, "  at " + LogUtil.getMetaInfo(element));
        }
    }

    /**
     * ログ呼び出し元のメタ情報を取得する
     * 
     * @return [className#methodName:line]
     */
    private static String getMetaInfo(){
        // スタックトレースから情報を取得 // 0: VM, 1: Thread, 2: LogUtil#getMetaInfo, 3: LogUtil#d など, 4: 呼び出し元
        final StackTraceElement element = Thread.currentThread().getStackTrace()[4];
        return LogUtil.getMetaInfo(element);
    }

    /**
     * スタックトレースからクラス名、メソッド名、行数を取得する
     * 
     * @return [className#methodName:line]
     */
    public static String getMetaInfo(StackTraceElement element){
        // クラス名、メソッド名、行数を取得
        final String fullClassName = element.getClassName();
        final String simpleClassName = fullClassName.substring(fullClassName.lastIndexOf(".") + 1);
        final String methodName = element.getMethodName();
        final int lineNumber = element.getLineNumber();
        // メタ情報
        final String metaInfo = "[" + simpleClassName + "#" + methodName + ":" + lineNumber + "]";
        return metaInfo;
    }

}
BuildConfig.DEBUG を使っていないので公開時には手動で false にしないといけないので注意。
これでログが凄く使いやすくなりました。

タグ(RSS)