ラベル メモ の投稿を表示しています。 すべての投稿を表示
ラベル メモ の投稿を表示しています。 すべての投稿を表示

2011年6月14日火曜日

【Android】 ACTION_EXTERNAL_APPLICATIONS_AVAILABLEはこねーよ!

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
photo: Original Update by Origamiancy

昨夜未明、私はSDカードへのインストールをサポートしたアプリに新規機能を追加していました。
その機能はAlarmManagerを使って色々登録するので、端末の再起動時にACTION_BOOT_COMPLETEDによってそれらをリストアしてやらんといけませんでした。
技術的には簡単だったので颯爽と実装を終えた私は意気揚々とテストに取り掛かりました。
さてリストアはうまくいったかな?

が・・・だめっ・・・!

何度端末を再起動しても、マニフェストファイルを見直してもダメ。
全く同じ設定の他アプリは受け取っているのにこの新機能を追加したアプリだけは受け取れません。

30分ほどハマってようやく気が付きました。

SDカードにインストールしてるアプリはACTION_BOOT_COMPLETEDを受け取れないんじゃね?

全くそのとおりでした。
長い夜の始まりでした。



SDカードにインストールされていた場合ACTION_BOOT_COMPLETEDは受け取れない


そうです、SDカードにアプリがインストールされていた場合ACTION_BOOT_COMPLETED(android.intent.action.BOOT_COMPLETED)は受け取れないんです。
確かに、SDカードのマウントより前に飛んでるイメージがありますね。
実際ドキュメントにも書かれています。

App Install Locationの"Applications That Should NOT Install on External Storage"に以下の記載があります。
Broadcast Receivers listening for "boot completed"
The system delivers the ACTION_BOOT_COMPLETED broadcast before the external storage is mounted to the device. If your application is installed on the external storage, it can never receive this broadcast.
訳:SDカードにインストールしてるアプリにACTION_BOOT_COMPLETEDはこねーよ! neverだよnever!

ではどーしたらええの?



ACTION_EXTERNAL_APPLICATIONS_AVAILABLEというアクションが追加されている


同ドキュメントにはまた、こんな記載もあります。
Your running Service will be killed and will not be restarted when external storage is remounted. You can, however, register for the ACTION_EXTERNAL_APPLICATIONS_AVAILABLE broadcast Intent, which will notify your application when applications installed on external storage have become available to the system again. At which time, you can restart your Service.
訳:Serviceもほんとはダメだけど、と、特別にACTION_EXTERNAL_APPLICATIONS_AVAILABLEを登録してたら再起動してあげるんだからね!

つまりACTION_EXTERNAL_APPLICATIONS_AVAILABLEを受け取るサービスを定義しておいてナニすればいいってことー!?



いやACTION_EXTERNAL_APPLICATIONS_AVAILABLEはSDカードにインストールされているアプリ側は受け取れないぽい


試す前に色々調べていると、以下のissueが。

Issue 8485: Documentation bug: ACTION_EXTERNAL_APPLICATIONS_AVAILABLE

ACTION_EXTERNAL_APPLICATIONS_AVAILABLEってSDカード内のアプリは受け取れなくねー?との事。
確かにリファレンスのACTION_EXTERNAL_APPLICATIONS_AVAILABLEの方では以下の記載が

ACTION_EXTERNAL_APPLICATIONS_AVAILABLE
Note that the packages in this list do not receive this broadcast. The specified set of packages are now available on the system.
訳:extraに乗ってくる利用可能リストに含まれるパッケージはこのbroadcastを受け取れないよー!(つまりSDカードがマウントされて有効化されるアプリ自身はこのbroadcastは受け取れない!)



ドキュメントバグじゃね


Issue 8485にこんなコメントが
This is a real bug. The apps installed on external storage will not receive ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.
訳:ACTION_EXTERNAL_APPLICATIONS_AVAILABLEはこねーよ!



明日があるさ


もしかしたらServiceで動的にACTION_EXTERNAL_APPLICATIONS_AVAILABLEを受け取るBroadcastReceiverをregisterしておくとシステムが空気読んで再起動してくれるのかもしれません。でもなんだか試すのが面倒なので諦めました。


全てを諦めた私はとうとうアプリを"internalOnly"にしました。


終わり

2011年5月30日月曜日

Windows Phone 7のアプリケーションの開発環境構築が死ぬほど簡単な件

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
Android,iPhoneと手を出したので、折角だしWindows Phoneも、と思って。
環境構築は超簡単でした。

手順

  1. APP HUBで開発ツールをダウンロード
  2. 開発ツールをインストール
  3. Microsoft Visual Studio 2010 Express for Windows Phoneを起動
  4. 新規プロジェクトを作成
  5. ちょっといじって実行してみる

1. APP HUBで開発ツールをダウンロード


開発ツールはWindows PhoneとXBOX 360のデベロッパーサイトに落ちています。
以下のページにアクセスし、「ツールをダウンロードしよう!」のページからインストーラ(vm_web.exe)をダウンロードする事が出来ます。
APP HUB







2. 開発ツールをインストール


ダウンロードしたvm_web.exeを実行して、ツールをインストール。ウィザードに従うだけ。
既にVisual Studio等の環境があっても必要なコンポーネントだけをインストールしてくれる様です。
全く何もない環境では「Microsoft Visual Studio 2010 Express for Windows Phone」がインストールされました。




3. Microsoft Visual Studio 2010 Express for Windows Phoneを起動


インストールされた開発ツールを起動するとこんな感じの画面が。




4. 新規プロジェクトを作成


「New Project」でアプリケーションを作成してみます。
ウィザードが表示され、様々なタイプのアプリケーションのテンプレートが出てきます。
とりあえず一番上の「Windows Phone Application」を選択します。
言語はC#ですがSilverlightも使えるみたいです。XNAはゲーム開発用のツールです。




5. ちょっといじって実行してみる


実際は新規プロジェクトを作ってすぐに実行してもいけます。
とりあえずデザイナでボタンとかブラウザを配置してみました。
Visual Studioはあまり使った事ないですが、.NETでやってる人なら恐らくいつもの感じでUIやロジックをコーディングしていけると思います。
Androidに比べてUIデザイナは圧倒的に生産性高いです。クリックイベント等も自動的に挿入してロジックだけ書けばいいので楽です、がIDE特有の「コードが散らかる」問題はありそうです。




実行するとエミュレータが起動されます。



そして作成したアプリケーションが立ち上がります。



ボタンを押すとWebBrowserのNavigateメソッドでGoogleのトップページを開くようにしています。



ソフトウェアキーボードはこんな感じ


数分もかからず作れました。
自分はC#や.NET類はほぼ経験がありませんが、大抵は触ったりググったりすればわかりそうです。
iPhoneはMac+Objective-Cだし、AndroidはJavaだし、でモバイルアプリを敬遠していたWindowsな方もWindows Phoneなら全くハードルが無いでしょう。
Windows Phoneが実際どの程度売れるのかはわかりませんが、とりあえず手を出しておくのはありなんじゃないでしょーか。

自分もちょっとAndroidアプリを移植しようかとか思いました。

2010年12月25日土曜日

android.util.Logのtag用にログ出力元のクラス名を取得する

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
android.util.Logの各種ログ出力メソッドの第一引数にはtagを指定しますが、
普通、どういった値を指定するものなのでしょうか?
現在のDDMSではログの絞込みにandやorや正規表現とか使えないので、
ある機能を満たすモジュール群で統一するとかいった感じでしょうか。

個人的には、「ログを実際出力させているクラス名」を常に使うようにしています。
毎回クラスの頭で
private final static String TAG = クラス.class.getSimpleName();
とやっています。
ところがこれ、クラス.class.getSimpleName()って書くのが地味にめんどくさいんですね。
なんとか統一的な書き方が出来ないものか、と思ってやってみました。

クラス名取得の為にスタックトレースを拝借する

とりあえず、tagの値を生成するUtilクラスを作る事にします。
Utilクラスは以下のメソッドを持ってます。
public static String tag()                        //クラスのフルネームを返す
public static String sTag()                     //クラスのパッケージ名を除いた名前を返す                
private static String getTag(int depth)     //クラスのフルネーム取得用メソッド

■getTag(int depth)
色々考えた結果、下記の用にスタックトレースでメソッドの呼び出し元のクラス名を引っ張る、てのを思いついたのでやりました。
引数にintを貰って、スタックトレースのどこまで遡るか決めます。直前の呼び出し元なら1を指定します。
private static String getTag(int depth) {
try {
throw new Exception();
} catch (Exception e) {
StackTraceElement[] es = e.getStackTrace();
if (es != null && es.length >= depth) {
return es[depth].getClassName();
}
}
return "DEFAULT";
}

■tag()
クラスのフルネームを取り出します。コールグラフは
-呼び出し元
    -tag()
        -getTag(int)
になるので、depthに2を指定しています。
/**
 * 呼び出し元のクラスのフルネームを返す.ex:jp.dip.sys1.android.aozora.util.Util
 * 
 * @return クラスのフルネーム
 */
public static String tag() {
return getTag(2);
}

■sTag()
クラスのフルネームからパッケージ名を取り除いてクラス名だけ返してます。
単純に最後の"."以降を返すようにしてるだけです。
/**
 * 呼び出し元のクラスのパッケージ名を除いた名前を返す.ex:Util
 * 
 * @return クラス名
 */
public static String sTag() {
String tag = getTag(2);
int i = tag.lastIndexOf(".");
return tag.substring(i == -1 ? 0 : i + 1);
}

使う&パフォーマンス

実際使ってみます。
■TestUtil.java
public void testTag() {
String correctString = "jp.dip.sys1.android.util.TestUtil";
String tag = Util.tag();
Log.d(tag, "testTag()");
assertTrue("is not match:" + tag, correctString.equals(tag));
}
public void testSTag() {
String correctString = "TestUtil";
String tag = Util.sTag();
Log.d(tag, "testSTag()");
assertTrue("is not match:" + tag, correctString.equals(tag));
}

いい感じで取り出せたっぽいですね

なんだかExceptionをいちいちthrowしてごにょごにょって何だか気持ち悪いなーと思う所なんですが、
まぁ
private final static String TAG = Util.sTag();
と統一して書けるからいいのかなぁ・・・と。
パフォーマンスとかどーなんだろう、と思って、正しい計測の仕方かどうかよくわかって無いんで是非ご指導頂きたいんですが
1000回ずつログを出してかかった時間を比較する以下の様なテストコードで測ってみました。
Util.startCountTimeとかUtil.endCountTimeはSystem.currentTimeMillis()で取った時間を保存しておいて後で引く感じになってます。内部的に。
private final static String TAG = TestUtil.class.getSimpleName();
public void testTagPerformance() {
Util.startCountTime(this);
for (int i = 0; i < 1000; i++) {
Log.d(TAG, "test" + i);
}
Log.d("test", "time1:" + Util.endCountTime(this));
Util.startCountTime(this);
for (int i = 0; i < 1000; i++) {
Log.d(Util.tag(), "test" + i);
}
Log.d("test", "time2:" + Util.endCountTime(this));
}

結果
TAG: 752ms
Util.getTag():6768ms

9倍。かなり遅い。けど、

private final static String TAG = Util.sTag();
の書き方ならクラスローディングの時一回実行されるだけだしいいんじゃないのかなーと。

どうなんでしょ。

※2010/12/25 1:49 追記
@zaki50さんから一瞬でアドバイスが!

Eclipseの設定でクラス作るときにクラスのbody部分にテンプレートを定義できるとの事!

Eclipseのメニューの
[Window]-[Preference]
で設定を開き、
[Java]-[Code Style]-[Code Templates]-[Code]-[Class body]

private static final String TAG = ${type_name}.class.getSimpleName();
を追加する。

そして、
クラスを新規で作成してみると・・・


おおおおおお!
超楽。Eclipse万歳!
Eclipseもっと活用しないと・・・。本買うか・・・!

@zaki50さんありがとうございました!!