スポンサーサイト

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

APKファイルの場所の確認方法

Androidの実機にインストールされているアプリのAPKファイルの場所の確認方法です。
adbのシェルでパッケージマネージャを使用し確認します。

■前提
Android SDKの設定が完了している事。
実機がadbから認識できる事(ドライバー等のインストールが完了している)。

1.実機をUSBで接続して、以下のコマンドでADB Shellを起動します。

adb shell

2.パッケージマネージャをコマンドから起動し、インストールしているAPKファイルを一覧表示します。

$ pm list packages -f

上記のコマンドでパッケージ名とAPKファイルの場所が一覧で表示されます。
予めパッケージ名等が分かっている場合は、grepコマンドをパイプすればフィルタリングできます。

$ pm list packages -f | grep <<パッケージ名の一部等>>


以上です。
スポンサーサイト

AndroidのDialog表示(API13以降)

本日はAndroidのActivityでダイアログを表示する方法を紹介します。
API13以前は、ActivityのonCreateDialog()メソッドでDialog表示を行ったりしていましたが、どうやらAPI13以降ではdeprecatedの扱いになっているようなので、Googleさんがお勧めしているやり方でDialogを表示します。

1. DialogFragmentの実装
先ずはDialogの作成を担当するクラスとしてDialogFragmentを継承したサブクラスを実装します。
そしてonCreateDialog()メソッドをオーバライドして、その中でDialogを作成して返却するように実装します。

public class CustomDialogFragment extends DialogFragment {

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle(R.string.title_delete_all_history);
builder.setMessage(R.string.msg_delete_all_history)
.setPositiveButton(R.string.msg_yes, new DialogInterface.OnClickListener() {

@Override
public void onClick(DialogInterface dialog, int which) {
// ダイアログのOKボタンをクリックした時の処理
}
})
.setNegativeButton(R.string.msg_no, new DialogInterface.OnClickListener() {

@Override
public void onClick(DialogInterface dialog, int which) {
// ダイアログのCANCELボタンをクリックした時の処理
}
});
return builder.create();
}
}

ダイアログの作成は従来と変わらずAlertDialog.Builderで行います。
今回のケースだとsetPositiveButton()、setNegatiiveButton()の2つを設定しているので、OK/CANCELの2つのボタンが存在するダイアログを作成しています。
別クラスを作成する場合は、Activity(Context)をパラメータでもらってフィールドに保持するような作りをしたりしますが、DialogFragmentにはgetActivity()というメソッドが存在するのでフィールドに持つ必要はありません(ここは少し奇麗に書けるようになった部分かな)。
話が少しそれますが、このBuilderの実装は少し前にはやった流れるインターフェースというやつですね。今回は最後のbuilder.create()で一呼吸おいてますので若干流れが悪くなってますが。

2.ダイアログの表示
続いてダイアログを表示するコードです。
先ほど作成したDialogFragmentのサブクラスを生成して、show()メソッドを呼び出します。

public void showDialog() {
DialogFragment dialog = new CustomDialogFragment();
dialog.show(getFragmentManager(), "CustomDialogFragment");
}

show()メソッドの2番目の引数はダイアログを一意に特定する識別子で任意の文字列を設定できます。
ここに設定した文字列でfindFragmentByTag()メソッド等でダイアログを探す事が出来たりするようです。
getFragmentManager()でFragmentManagerのインスタンスを取得していますが、Android3.0より前のコードの場合はFragmentActivity#getSupportFragmentManager()メソッドでFragmentManagerを取得します。

ダイアログの表示だけなら、ここまでで実装完了です。
しかし、このままだとDialogのボタンをクリックした後の処理をDialogFragmentのサブクラスで実装しなければ行けなくなってしまいます。

そこでこの後は、ダイアログのボタンをクリックした後のコールバックをダイアログの表示元(ホスト)のActivityに返すための実装を追加します。

3.イベントリスナの定義
先ほど作成したDialogFragmentのサブクラスに内部インターフェースとしてイベントリスなを定義し、定義したインタフェースを自体をフィールドとして宣言します。
onCreateDialog()メソッドの中で作成したボタンに設定したイベントリスナ(OnClickListener)の中で、先ほど内部インターフェースとして宣言し、かつフィールドとして宣言したイベントリスナの適切なメソッドを呼び出します。
また、FragmenのライフサイクルイベントでコールバックされるonAttach()メソッドをオーバライドし、getActivity()で取得したActivity(すなわちダイアログの表示元)をイベントリスナの型にキャストしてフィールドに設定します。
ここまで、くれば次にやる事も大体分かりますね。


public class CustomDialogFragment extends DialogFragment {

// イベントリスナの定義
public interface NoticeDialogListener {
public void onDialogPositiveClick(DialogFragment dialog);
public void onDialogNegativeClick(DialogFragment dialog);
}

// イベントリスナをフィールドに宣言
NoticeDialogListener mListener;

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle(R.string.title_delete_all_history);
builder.setMessage(R.string.msg_delete_all_history)
.setPositiveButton(R.string.msg_yes, new DialogInterface.OnClickListener() {

@Override
public void onClick(DialogInterface dialog, int which) {
// イベントリスナのメソッド呼び出し
mListener.onDialogPositiveClick(DeleteHistoryDialogFragment.this);
}
})
.setNegativeButton(R.string.msg_no, new DialogInterface.OnClickListener() {

@Override
public void onClick(DialogInterface dialog, int which) {
// イベントリスナのメソッド呼び出し
mListener.onDialogNegativeClick(DeleteHistoryDialogFragment.this);
}
});
return builder.create();
}

@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
// キャストしてフィールドに設定
mListener = (NoticeDialogListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement NoticeDialogListener");
}
}
}


4.Activityにイベントリスナを実装
続いてActivityにイベントリスナを実装し、必要なメソッドをオーバライドします。
イベントリスナーを実装しないと、先ほどのDialogFragmentのサブクラスでオーバライドしたonAttach()メソッドでキャスト例外が発生します。
オーバライドしたonDialogPositiveClick()、onDialogNegativeClick()がダイアログのボタンを選択した時にコールバックされるので、これらのメソッドの中でボタンクリック後に行いたい処理を実装します。

public class ExampleActivity extends Activity
implements DeleteHistoryDialogFragment.NoticeDialogListener {

@Override
public void onDialogPositiveClick(DialogFragment dialog) {
// ダイアログのOKボタンをクリックした後に行いたい処理を実装
}

@Override
public void onDialogNegativeClick(DialogFragment dialog) {
// ダイアログのCANCELボタンをクリックした後に行いたい処理を実装
}
:

やっている事は自作でイベントリスナを実装するのと大して変わりないかと思います。

参考:
公式ドキュメント API Guides - Dialogs
公式ドキュメント API Guides - Fragments

AndroidアプリをURLから起動

今回はAndroidアプリをHTMLファイルの中のURLから起動する方法を紹介します。

■実装方法
1.インテントフィルタの設定
『AndroidManifest.xml』ファイルにインテントフィルターを宣言し、ブラウザからの起動に応答できるようにします。

AndroidManifext.xml

<activity
android:name="com.example.myapplication.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="myapp" android:host="test" android:path="/sample"/>

</intent-filter>
</activity>

カテゴリとして「android.intent.category.BROWSABLE」を設定する事でブラウザからの起動に応答できるようにします。
そのしたの「<data android:~」は起動する時のURLの設定です。

android:scheme … 起動時のURLのスキーム(必須)
android:host … 起動時のURLのホスト(必須)
android:pathPrefix … 起動時のURLのパス(任意)

上記の内容だと以下のURLでアプリが起動する事となります。

myapp://test/sample

2.htmlページ作成
アプリ起動用のhtmlファイルを作成します。今回はアプリ起動時にパラメータを渡すようにクエリを設定しています。

sample.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>アプリケーション起動サンプル</title>
</head>
<body>
<a href="myapp://test/sample?param1=パラメータ1&param2=パラメータ2">アプリケーションを起動</a>
</body>
</html>

上記URLの「param1=パラメータ1&param2=パラメータ2」がパラメータとなります。
こちらのhtmlをAndroid端末のローカルストレージに保存、もしくは適当なサーバに保存します。
(ローカルストレージに保存した場合は、エクスプローラを使ってファイル開く)

3.アプリの実装
URLから起動された時の処理を記述します。
今回は、パラメータを取得してTextViewに設定する処理を行っています。

@Override
protected void onResume() {
super.onResume();

Intent intent = getIntent();
String action = intent.getAction();

if (Intent.ACTION_VIEW.equals(action)) {
Uri uri = intent.getData();
if (uri != null) {
String param1 = uri.getQueryParameter("param1");
String param2 = uri.getQueryParameter("param2");
TextView txtView1 = (TextView)findViewById(R.id.textView);
TextView txtView2 = (TextView)findViewById(R.id.textView2);
txtView1.setText(param1);
txtView2.setText(param2);
}
}
}

intent.getData()でアプリを起動したURLが取得できます。
uri.getQueryParameter("パラメータ名")でクエリ―に設定したパラメータが取得し、こちらをTextViewに設定しています。

以上です。

ちなみに、AndroidのChromeブラウザのバージョン25以降からは、iframeのsrc属性でのアプリ起動が出来なくなっているらしく、intent://というスキームを使用するように変更になっているようです。

参考URL:
Android Intents with Chrome
Enabling Deep Links for App Content

AsyncTaskをJUnitでテストする方法

明けましておめでとうございます。
久しぶりに更新します。

今回はAndroidのAsyncTaskをJUnitでテストする方法を紹介します。

AsynkTaskはUIThreadでないと動作しません。またバックグランド処理が
完了した後にコールバックされるonPostExecute(Result)は、普通にテストケースを
実装してしまうとテストケースのスレッドではonPostExecute(Result)の実行をまってくれず
処理が終了してしまいます。
このような理由でテストケースを実装するにはコードにひと工夫が必要です。
以下がそのソースコードとなります。

1.まずはAsynkTaskの実装から(かなり適当)
-----
public class TestableAsyncTask extends AsyncTask {

    private Activity activity;
    private TextView textView;
    
    public TestableAsyncTask(Activity activity) {
        this.activity = activity;
    }
    
    @Override
    protected String doInBackground(String... args) {
        return args[0];
    }

    /* (non-Javadoc)
     * @see android.os.AsyncTask#onPostExecute(java.lang.Object)
     */
    @Override
    protected void onPostExecute(String result) {
        textView.setText("AsynkTask Executed Count:" + result);
    }

    /* (non-Javadoc)
     * @see android.os.AsyncTask#onPreExecute()
     */
    @Override
    protected void onPreExecute() {
        textView = (TextView)activity.findViewById(R.id.txtview);
    }

}
-----
わざわざ非同期にする必要がないような処理ですが気にせず(;^_^A
ちなみにonPreExecute()はdoInBackground()の前に実行されるコールバックでUIThreadで動作します。

2.テストケースのサンプル
-----
public class TestableAsyncTaskTestCase extends ActivityInstrumentationTestCase2<AsyncTaskSampleActivity>
 :
    public void testTestableAsyncTask() throws Throwable {
        // create CountDownLatch.
        final CountDownLatch signal = new CountDownLatch(1);
        
        // create subclass of test target asynctask.
        final AsyncTask testTask
            = new TestableAsyncTask(mActivity) {

                /* (non-Javadoc)
                 * @see com.makochi.asynctasksample.logic.TestableAsyncTask#onPostExecute(java.lang.String)
                 */
                @Override
                protected void onPostExecute(String result) {
                    super.onPostExecute(result);
                    
                    // unlock testcase's thread.
                    signal.countDown();
                }
            
        };
        
        // execute asynctask.
        runTestOnUiThread(new Runnable() {
            
            @Override
            public void run() {
                testTask.execute("1");
            }
        });
        
        // wait for asynctask.
        signal.await(30, TimeUnit.SECONDS);
        
        assertEquals("TextViewのText", "AsynkTask Executed Count:1", mTextView.getText());
    }
}
-----
テストケースは「ActivityInstrumentationTestCase2」のサブクラスで実装しています。
このクラスはテストケースでアクティビティを使いたい場合は、UIThreadでテストメソッド内の
処理を実行させたい場合に利用します(詳しい実装方法はandroid deveopers参照)

そして今回のポイントは赤色で示したところです。

まず、AsyncTaskのコールバックの実行を待機するために「java.util.concurrent.CountDownLatch」の
インスタンスを生成します。
このクラスは他のスレッドで実行されている処理を待機する為のクラスで生成する時のコンストラクタ
で指定した回数分だけcountDown()メソッドが呼ばれるまで、await()メソッドを呼び出した箇所で待機します。
今回はAsyncTaskのバックグランド処理の完了であるonPostExecute()メソッドが呼び出されるまで待機します。

続いてテストをしたいAsyncTaskのサブクラスを実装します。
その際、onPostExecute()メソッドをオーバライドして、super.onPostExecute()を呼び出した後に
CountDownLatchのcountDown()メソッドを呼び出します。
(具象クラスも生成と同時にメソッドオーバライド可能なんですね)
ここでcountDown()メソッドを呼びことにより、テストケースのスレッド待機が解除されます。
(今回はCountDownLatchの生成時に1を指定している為、countDown()も1回でスレッドブロックが解除)

次のコードは実際にAsyncTaskを実行する部分です。
runTestOnUiThread()メソッドの中でAsyncTaskを実行します。

最後がawait()メソッドでAsyncTaskの実行完了を待機します。
第一引数に指定した30秒経過するか、指定回数countDown()メソッドが呼び出されるまで
このテストケースのスレッドはこの箇所で待機します。

以上です。


■参考
・android developers - Hello, Testing
http://developer.android.com/resources/tutorials/testing/helloandroid_test.html

・android developers - ActivityInstrumentationTestCase2
http://developer.android.com/reference/android/test/ActivityInstrumentationTestCase2.html

・JSE6 API Reference - CountDownLatch
http://java.sun.com/javase/ja/6/docs/ja/api/java/util/concurrent/CountDownLatch.html

Androidのapkファイルのデコンパイル

今日は仕事でapkファイルをデコンパイルする機会があったので、その方法をまとめます。

【前提条件】
JRE1.6
Android SDK(http://developer.android.com/intl/ja/sdk/index.html
~android-sdk-windows\platform-toolsがPATHに設定されている

1.デコンパイル用のツールをダウンロード
以下のURLから以下の2つのファイルをダウンロード。

・apktool-install-windows-r04-brut1.tar.bz2(Windowsの場合)
・apktool1.4.1.tar.bz2

【URL】
https://code.google.com/p/android-apktool/

2.解凍
「apktool-install-windows-r04-brut1.tar.bz」を解凍したディレクトリと
同じ場所に「apktool1.4.1.tar.bz2」を解凍

解凍後のディレクトリ構成は以下。

~apktool-install-windows-r04-brut1
├aapt.exe
├apktool.bat
└apktool.jar(apktool1.4.1.tar.bz2を解凍したファイル)

3.コマンドを実行
「apktool-install-windows-r04-brut1」の解凍ディレクトリまで移動して以下のコマンドを実行。

apktool d <apkファイルのパス>

実行するとツールのディレクトリと同じ場所にフォルダが出来てその中にデコンパイルされたソースが出力されます。

プロフィール

まこち

Author:まこち
スマートフォンのアプリ開発やWebサイト構築等を仕事や趣味でやっています。
最近はグラフデータベースも始めました。

最新記事
最新コメント
最新トラックバック
月別アーカイブ
カテゴリ
検索フォーム
RSSリンクの表示
リンク
ブロとも申請フォーム

この人とブロともになる

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