2010年12月29日水曜日



Gingerbreadの新機能の一つとして「StrictMode」が導入されましたので、実際に使用して検証してみました。


StrictModeとは?




  • アプリケーションの動作をもっさりさせる原因となる、 ディスクやネットワークへのアクセスを検知するための仕組み。

  • 「スレッドや仮想マシンのポリシー」という形で、何を検知するかを決めることができます。

  • デフォルトでは下記のようなものが検知できます。


    • ディスクの読み込み、書き込み

    • ネットワークの使用

    • 違反:ログ、クラッシュ、dropbox、邪魔なダイアログ



  • 検知は、ディスク(java.io.*, android.database.sqlite.*, etc)やネットワーク(java.net.*)をフックすることで行われます。


StrictModeの利点




  • パフォーマンスの劣化につながる要素(ディスクアクセス、ネットワークアクセス、データベースカーソルのリークなど)を特定することができます。

  • 特定した結果をlogcatで表示することができ、特殊なログ解析ツール等は不要。

  • 次期バージョン「Honeycomb」ではメイン(UI)スレッドでネットワークのリクエストを行うとfatalエラー(例えターゲットがHoneycomb以前だとしても)となります。したがって、StrictModeは次期バージョンへアプリを対応させる上で、重要なツールとなります。


StrictModeの欠点




  • パフォーマンスを劣化させる全てを検知できる訳ではない。

  • 正常なアクセスもポリシー違反として検知されることがある。

  • 将来において、より多くの種類の検知を行うようになるため、リリース時には手動で無効化しておく手間がかかる。


StrictModeの使いどころ




  • ユーザエクスペリエンスに問題がある(もっさりしすぎている)と感じた場合にのみ、使うと良いです。

  • 将来的にはコンパイルエラーを特定するためのツールの一つになる可能性があります(UIスレッドでアクセスしている部分の特定など)。




StrictMode関連クラス・メソッド一覧


StrictModeクラス:




  • enableDefaults() - おすすめのStrictModeのデフォルト設定、ポリシー違反はログに出力される。

  • allowThreadDiskReads():getThreadPolicy() -> ディスク読み込みの許可 -> setThreadPolicy() までやってくれるラッパー。

  • allowThreadDiskWrites():getThreadPolicy() -> ディスク読み込み&書き込みの許可 -> setThreadPolicy() までやってくれるラッパー。

  • getThreadPolicy():現在のスレッドのポリシーを取得する。

  • getVmPolicy():現在のVMのポリシーを取得する。

  • setThreadPolicy(StrictMode.ThreadPolicy policy):現在のスレッドに指定のポリシーを適用する。

  • setVmPolicy(StrictMode.VmPolicy policy):現在のVMに指定のポリシーを適用する。


StrictMode.ThreadPolicy.Builderクラス - スレッドに適用されるポリシー




  • build() : ThreadPolicyのインスタンスを生成する。

  • detectAll():潜在的に疑いのある全ての事象を検知する。

  • detectDiskReads():ディスクの読み込みを検知する

  • detectNetwork():ネットワーク操作を検知する

  • penaltyDeath():違反したプロセス全体をクラッシュする。

  • penaltyDialog():違反として検知した煩わしいダイアログを表示する。

  • penaltyDropBox():ポリシーに違反したDropboxのスタックトレースとタイミングデータのログを有効にする。

  • penaltyLog():システムログに違反検知ログを出力する。

  • permitAll():全ての検知を無効にする。

  • permitDiskReads():ディスクの読み込み検知を無効にする。

  • permitDiskWrites():ディスクの書き込み検知を無効にする。

  • permitNetwork():ネットワークの操作検知を無効にする。


StrictMode.VmPolicy.Builderクラス - VMプロセス中の全てのスレッドに適用されるポリシー




  • build() :VMのインスタンスを生成する。

  • detectAll():潜在的に疑いのある全ての事象を検知する。

  • detectLeakedSqlLiteObjects():SQLiteCursor または 他のSQLiteのオブジェクトがクローズされずにファイナライズされた場合に検知する。

  • penaltyDeath():違反したプロセス全体をクラッシュする。

  • penaltyDropBox():ポリシーに違反したDropboxのスタックトレースとタイミングデータのログを有効にする。

  • penaltyLog():システムログに違反検知ログを出力する。


StrictModeで実行してみる


下記のようにonCreateで設定するだけでOK。結果はLogcatで確認。



// StrictModeはデバッグ時のみ使用すること
private final boolean DEVELOPER_MODE = true;

@Override
public void onCreate(Bundle savedInstanceState) {
if (DEVELOPER_MODE) {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyLog()
.build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectAll()
.penaltyLog()
.penaltyDeath()
.build());
}
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
  :
��以下略)


または



// StrictModeはデバッグ時のみ使用すること
private final boolean DEVELOPER_MODE = true;

@Override
public void onCreate(Bundle savedInstanceState) {
if (DEVELOPER_MODE) {
StrictMode.enableDefaults();
}
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
  :
��以下略)


実行結果

※個人的に随分昔に作成した非常に残念で恥ずかしいコードを利用



12-18 23:30:34.588: DEBUG/StrictMode(337): StrictMode policy violation; ~duration=6604 ms: android.os.StrictMode$StrictModeNetworkViolation: policy=23 violation=4
12-18 23:30:34.588: DEBUG/StrictMode(337): at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:758)
12-18 23:30:34.588: DEBUG/StrictMode(337): at java.net.InetAddress.lookupHostByName(InetAddress.java:488)
12-18 23:30:34.588: DEBUG/StrictMode(337): at java.net.InetAddress.getAllByNameImpl(InetAddress.java:294)
12-18 23:30:34.588: DEBUG/StrictMode(337): at java.net.InetAddress.getAllByName(InetAddress.java:256)
12-18 23:30:34.588: DEBUG/StrictMode(337): at org.apache.harmony.luni.internal.net.www.protocol.http.HttpConnection.<init>(HttpConnection.java:68)
12-18 23:30:34.588: DEBUG/StrictMode(337): at org.apache.harmony.luni.internal.net.www.protocol.http.HttpConnection.<init>(HttpConnection.java:48)
12-18 23:30:34.588: DEBUG/StrictMode(337): at org.apache.harmony.luni.internal.net.www.protocol.http.HttpConnection$Address.connect(HttpConnection.java:298)
12-18 23:30:34.588: DEBUG/StrictMode(337): at org.apache.harmony.luni.internal.net.www.protocol.http.HttpConnectionPool.get(HttpConnectionPool.java:89)
12-18 23:30:34.588: DEBUG/StrictMode(337): at org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl.getHttpConnection(HttpURLConnectionImpl.java:285)
12-18 23:30:34.588: DEBUG/StrictMode(337): at org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl.makeConnection(HttpURLConnectionImpl.java:267)
12-18 23:30:34.588: DEBUG/StrictMode(337): at org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl.retrieveResponse(HttpURLConnectionImpl.java:1018)
12-18 23:30:34.588: DEBUG/StrictMode(337): at org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:510)
12-18 23:30:34.588: DEBUG/StrictMode(337): at java.net.URL.openStream(URL.java:645)
12-18 23:30:34.588: DEBUG/StrictMode(337): at jp.naoki.seto.ResultList$ViewItemAdapter.getView(ResultList.java:141)
12-18 23:30:34.588: DEBUG/StrictMode(337): at android.widget.AbsListView.obtainView(AbsListView.java:1418)
12-18 23:30:34.588: DEBUG/StrictMode(337): at android.widget.ListView.makeAndAddView(ListView.java:1745)
12-18 23:30:34.588: DEBUG/StrictMode(337): at android.widget.ListView.fillDown(ListView.java:670)
12-18 23:30:34.588: DEBUG/StrictMode(337): at android.widget.ListView.fillFromTop(ListView.java:727)
12-18 23:30:34.588: DEBUG/StrictMode(337): at android.widget.ListView.layoutChildren(ListView.java:1598)
12-18 23:30:34.588: DEBUG/StrictMode(337): at android.widget.AbsListView.onLayout(AbsListView.java:1248)
12-18 23:30:34.588: DEBUG/StrictMode(337): at android.view.View.layout(View.java:7175)
12-18 23:30:34.588: DEBUG/StrictMode(337): at android.widget.FrameLayout.onLayout(FrameLayout.java:338)
12-18 23:30:34.588: DEBUG/StrictMode(337): at android.view.View.layout(View.java:7175)
12-18 23:30:34.588: DEBUG/StrictMode(337): at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1254)
12-18 23:30:34.588: DEBUG/StrictMode(337): at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1130)
12-18 23:30:34.588: DEBUG/StrictMode(337): at android.widget.LinearLayout.onLayout(LinearLayout.java:1047)
12-18 23:30:34.588: DEBUG/StrictMode(337): at android.view.View.layout(View.java:7175)
12-18 23:30:34.588: DEBUG/StrictMode(337): at android.widget.FrameLayout.onLayout(FrameLayout.java:338)
12-18 23:30:34.588: DEBUG/StrictMode(337): at android.view.View.layout(View.java:7175)
12-18 23:30:34.588: DEBUG/StrictMode(337): at android.view.ViewRoot.performTraversals(ViewRoot.java:1140)
12-18 23:30:34.588: DEBUG/StrictMode(337): at android.view.ViewRoot.handleMessage(ViewRoot.java:1859)
12-18 23:30:34.588: DEBUG/StrictMode(337): at android.os.Handler.dispatchMessage(Handler.java:99)
12-18 23:30:34.588: DEBUG/StrictMode(337): at android.os.Looper.loop(Looper.java:123)
12-18 23:30:34.588: DEBUG/StrictMode(337): at android.app.ActivityThread.main(ActivityThread.java:3647)
12-18 23:30:34.588: DEBUG/StrictMode(337): at java.lang.reflect.Method.invokeNative(Native Method)
12-18 23:30:34.588: DEBUG/StrictMode(337): at java.lang.reflect.Method.invoke(Method.java:507)
12-18 23:30:34.588: DEBUG/StrictMode(337): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
12-18 23:30:34.588: DEBUG/StrictMode(337): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
12-18 23:30:34.588: DEBUG/StrictMode(337): at dalvik.system.NativeStart.main(Native Method)


パフォーマンスのチューニング方法


ログを解析する


StrictMode policy violation; ~duration=6604 ms: android.os.StrictMode$StrictModeNetworkViolation: policy=23 violation=4
at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:758)


ネットワーク関連の処理で、処理時間が(duration)が6604ms = 6.6sec !? もかかっている・・・



DEBUG/StrictMode(337): at java.net.URL.openStream(URL.java:645)
DEBUG/StrictMode(337): at jp.naoki.seto.ResultList$ViewItemAdapter.getView(ResultList.java:141)


問題の場所。どうやら、ResultList.java 141行目のViewItemAdapterクラス内のgetViewでURL.openStreamを実行している。つまり、UIスレッド上に展開されたリストビューで、ネットワークアクセスをしているようだ。


コードを見直す

ResultList.java



89: // リストビューアイテム表示アダプタ
90: private static class ViewItemAdapter extends BaseAdapter {
91:
��前略)
119:
120: public View getView(int position, View convertView, ViewGroup parent) {
121:
��中略)
139: try {
140: url = new URL(result.get(position).get("smallImageUrl").toString());
141: is = url.openStream();
142: } catch (MalformedURLException e) {


確かにソースを確認すると、メインスレッド上のgetViewで(つまり、リストビューの1行表示毎に)実行しており、パフォーマンスを著しく落としている原因になっている。


このように、ログからパフォーマンスを著しく落とす原因(上記の場合、メインスレッド上のネットワークアクセスを行っている箇所)を特定し、これらをバックグラウンドプロセス等に置き換えることで、アプリのパフォーマンスを向上させることができると思われる。


※ちなみに、このコードはHoneycombではfatal errorとなって、コンパイルすら出来なくなるでしょう。


Android端末はフラッシュメモリを内蔵しているので、ディスクアクセスは遅くないのではないか?という疑問


大抵の場合(容量が非常に限られた)内蔵フラッシュメモリを使用している間は、ディスクアクセスは非常に早いです。但し、他のプロセスからバックグラウンドでI/O処理が発生した場合など、劇的に遅くなるケースも多々存在します。したがって、「ディスクアクセスは非常に遅い」と仮定した設計が望ましいと思われます。


StrictModeはセキュリティのメカニズムではない


StrictModeはセキュリティのメカニズムではないので、全てのディスクやネットワークへのアクセスを検知する訳ではなく、保証もされません。Binderの呼び出し時にプロセス境界の向こう側に状態を伝播しますが、それでもベストエフォート型メカニズムに過ぎません。特にJNIからのディスクやネットワークアクセスは必ずしも検知されません。


パフォーマンス改善のためにStrictMode以外で考慮すべきこと


使用するAPIを変えることでパフォーマンスを改善出来ることがあります。例えば、新しいメソッドである SharedPreferences.Editor.apply() は 戻り値を必要としない場合は、同クラスの commit() よりもパフォーマンスが良くなります。



文責:技術部 瀬戸 直喜(前・日本Androidの会四国支部長/情報セキュリティスペシャリスト)





StrictModeでパフォーマンスをチューニングする



Gingerbreadの新機能の一つとして「StrictMode」が導入されましたので、実際に使用して検証してみました。


StrictModeとは?




  • アプリケーションの動作をもっさりさせる原因となる、 ディスクやネットワークへのアクセスを検知するための仕組み。

  • 「スレッドや仮想マシンのポリシー」という形で、何を検知するかを決めることができます。

  • デフォルトでは下記のようなものが検知できます。


    • ディスクの読み込み、書き込み

    • ネットワークの使用

    • 違反:ログ、クラッシュ、dropbox、邪魔なダイアログ



  • 検知は、ディスク(java.io.*, android.database.sqlite.*, etc)やネットワーク(java.net.*)をフックすることで行われます。


StrictModeの利点




  • パフォーマンスの劣化につながる要素(ディスクアクセス、ネットワークアクセス、データベースカーソルのリークなど)を特定することができます。

  • 特定した結果をlogcatで表示することができ、特殊なログ解析ツール等は不要。

  • 次期バージョン「Honeycomb」ではメイン(UI)スレッドでネットワークのリクエストを行うとfatalエラー(例えターゲットがHoneycomb以前だとしても)となります。したがって、StrictModeは次期バージョンへアプリを対応させる上で、重要なツールとなります。


StrictModeの欠点




  • パフォーマンスを劣化させる全てを検知できる訳ではない。

  • 正常なアクセスもポリシー違反として検知されることがある。

  • 将来において、より多くの種類の検知を行うようになるため、リリース時には手動で無効化しておく手間がかかる。


StrictModeの使いどころ




  • ユーザエクスペリエンスに問題がある(もっさりしすぎている)と感じた場合にのみ、使うと良いです。

  • 将来的にはコンパイルエラーを特定するためのツールの一つになる可能性があります(UIスレッドでアクセスしている部分の特定など)。




StrictMode関連クラス・メソッド一覧


StrictModeクラス:




  • enableDefaults() - おすすめのStrictModeのデフォルト設定、ポリシー違反はログに出力される。

  • allowThreadDiskReads():getThreadPolicy() -> ディスク読み込みの許可 -> setThreadPolicy() までやってくれるラッパー。

  • allowThreadDiskWrites():getThreadPolicy() -> ディスク読み込み&書き込みの許可 -> setThreadPolicy() までやってくれるラッパー。

  • getThreadPolicy():現在のスレッドのポリシーを取得する。

  • getVmPolicy():現在のVMのポリシーを取得する。

  • setThreadPolicy(StrictMode.ThreadPolicy policy):現在のスレッドに指定のポリシーを適用する。

  • setVmPolicy(StrictMode.VmPolicy policy):現在のVMに指定のポリシーを適用する。


StrictMode.ThreadPolicy.Builderクラス - スレッドに適用されるポリシー




  • build() : ThreadPolicyのインスタンスを生成する。

  • detectAll():潜在的に疑いのある全ての事象を検知する。

  • detectDiskReads():ディスクの読み込みを検知する

  • detectNetwork():ネットワーク操作を検知する

  • penaltyDeath():違反したプロセス全体をクラッシュする。

  • penaltyDialog():違反として検知した煩わしいダイアログを表示する。

  • penaltyDropBox():ポリシーに違反したDropboxのスタックトレースとタイミングデータのログを有効にする。

  • penaltyLog():システムログに違反検知ログを出力する。

  • permitAll():全ての検知を無効にする。

  • permitDiskReads():ディスクの読み込み検知を無効にする。

  • permitDiskWrites():ディスクの書き込み検知を無効にする。

  • permitNetwork():ネットワークの操作検知を無効にする。


StrictMode.VmPolicy.Builderクラス - VMプロセス中の全てのスレッドに適用されるポリシー




  • build() :VMのインスタンスを生成する。

  • detectAll():潜在的に疑いのある全ての事象を検知する。

  • detectLeakedSqlLiteObjects():SQLiteCursor または 他のSQLiteのオブジェクトがクローズされずにファイナライズされた場合に検知する。

  • penaltyDeath():違反したプロセス全体をクラッシュする。

  • penaltyDropBox():ポリシーに違反したDropboxのスタックトレースとタイミングデータのログを有効にする。

  • penaltyLog():システムログに違反検知ログを出力する。


StrictModeで実行してみる


下記のようにonCreateで設定するだけでOK。結果はLogcatで確認。



// StrictModeはデバッグ時のみ使用すること
private final boolean DEVELOPER_MODE = true;

@Override
public void onCreate(Bundle savedInstanceState) {
if (DEVELOPER_MODE) {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyLog()
.build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectAll()
.penaltyLog()
.penaltyDeath()
.build());
}
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
  :
��以下略)


または



// StrictModeはデバッグ時のみ使用すること
private final boolean DEVELOPER_MODE = true;

@Override
public void onCreate(Bundle savedInstanceState) {
if (DEVELOPER_MODE) {
StrictMode.enableDefaults();
}
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
  :
��以下略)


実行結果

※個人的に随分昔に作成した非常に残念で恥ずかしいコードを利用



12-18 23:30:34.588: DEBUG/StrictMode(337): StrictMode policy violation; ~duration=6604 ms: android.os.StrictMode$StrictModeNetworkViolation: policy=23 violation=4
12-18 23:30:34.588: DEBUG/StrictMode(337): at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:758)
12-18 23:30:34.588: DEBUG/StrictMode(337): at java.net.InetAddress.lookupHostByName(InetAddress.java:488)
12-18 23:30:34.588: DEBUG/StrictMode(337): at java.net.InetAddress.getAllByNameImpl(InetAddress.java:294)
12-18 23:30:34.588: DEBUG/StrictMode(337): at java.net.InetAddress.getAllByName(InetAddress.java:256)
12-18 23:30:34.588: DEBUG/StrictMode(337): at org.apache.harmony.luni.internal.net.www.protocol.http.HttpConnection.<init>(HttpConnection.java:68)
12-18 23:30:34.588: DEBUG/StrictMode(337): at org.apache.harmony.luni.internal.net.www.protocol.http.HttpConnection.<init>(HttpConnection.java:48)
12-18 23:30:34.588: DEBUG/StrictMode(337): at org.apache.harmony.luni.internal.net.www.protocol.http.HttpConnection$Address.connect(HttpConnection.java:298)
12-18 23:30:34.588: DEBUG/StrictMode(337): at org.apache.harmony.luni.internal.net.www.protocol.http.HttpConnectionPool.get(HttpConnectionPool.java:89)
12-18 23:30:34.588: DEBUG/StrictMode(337): at org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl.getHttpConnection(HttpURLConnectionImpl.java:285)
12-18 23:30:34.588: DEBUG/StrictMode(337): at org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl.makeConnection(HttpURLConnectionImpl.java:267)
12-18 23:30:34.588: DEBUG/StrictMode(337): at org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl.retrieveResponse(HttpURLConnectionImpl.java:1018)
12-18 23:30:34.588: DEBUG/StrictMode(337): at org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:510)
12-18 23:30:34.588: DEBUG/StrictMode(337): at java.net.URL.openStream(URL.java:645)
12-18 23:30:34.588: DEBUG/StrictMode(337): at jp.naoki.seto.ResultList$ViewItemAdapter.getView(ResultList.java:141)
12-18 23:30:34.588: DEBUG/StrictMode(337): at android.widget.AbsListView.obtainView(AbsListView.java:1418)
12-18 23:30:34.588: DEBUG/StrictMode(337): at android.widget.ListView.makeAndAddView(ListView.java:1745)
12-18 23:30:34.588: DEBUG/StrictMode(337): at android.widget.ListView.fillDown(ListView.java:670)
12-18 23:30:34.588: DEBUG/StrictMode(337): at android.widget.ListView.fillFromTop(ListView.java:727)
12-18 23:30:34.588: DEBUG/StrictMode(337): at android.widget.ListView.layoutChildren(ListView.java:1598)
12-18 23:30:34.588: DEBUG/StrictMode(337): at android.widget.AbsListView.onLayout(AbsListView.java:1248)
12-18 23:30:34.588: DEBUG/StrictMode(337): at android.view.View.layout(View.java:7175)
12-18 23:30:34.588: DEBUG/StrictMode(337): at android.widget.FrameLayout.onLayout(FrameLayout.java:338)
12-18 23:30:34.588: DEBUG/StrictMode(337): at android.view.View.layout(View.java:7175)
12-18 23:30:34.588: DEBUG/StrictMode(337): at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1254)
12-18 23:30:34.588: DEBUG/StrictMode(337): at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1130)
12-18 23:30:34.588: DEBUG/StrictMode(337): at android.widget.LinearLayout.onLayout(LinearLayout.java:1047)
12-18 23:30:34.588: DEBUG/StrictMode(337): at android.view.View.layout(View.java:7175)
12-18 23:30:34.588: DEBUG/StrictMode(337): at android.widget.FrameLayout.onLayout(FrameLayout.java:338)
12-18 23:30:34.588: DEBUG/StrictMode(337): at android.view.View.layout(View.java:7175)
12-18 23:30:34.588: DEBUG/StrictMode(337): at android.view.ViewRoot.performTraversals(ViewRoot.java:1140)
12-18 23:30:34.588: DEBUG/StrictMode(337): at android.view.ViewRoot.handleMessage(ViewRoot.java:1859)
12-18 23:30:34.588: DEBUG/StrictMode(337): at android.os.Handler.dispatchMessage(Handler.java:99)
12-18 23:30:34.588: DEBUG/StrictMode(337): at android.os.Looper.loop(Looper.java:123)
12-18 23:30:34.588: DEBUG/StrictMode(337): at android.app.ActivityThread.main(ActivityThread.java:3647)
12-18 23:30:34.588: DEBUG/StrictMode(337): at java.lang.reflect.Method.invokeNative(Native Method)
12-18 23:30:34.588: DEBUG/StrictMode(337): at java.lang.reflect.Method.invoke(Method.java:507)
12-18 23:30:34.588: DEBUG/StrictMode(337): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
12-18 23:30:34.588: DEBUG/StrictMode(337): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
12-18 23:30:34.588: DEBUG/StrictMode(337): at dalvik.system.NativeStart.main(Native Method)


パフォーマンスのチューニング方法


ログを解析する


StrictMode policy violation; ~duration=6604 ms: android.os.StrictMode$StrictModeNetworkViolation: policy=23 violation=4
at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:758)


ネットワーク関連の処理で、処理時間が(duration)が6604ms = 6.6sec !? もかかっている・・・



DEBUG/StrictMode(337): at java.net.URL.openStream(URL.java:645)
DEBUG/StrictMode(337): at jp.naoki.seto.ResultList$ViewItemAdapter.getView(ResultList.java:141)


問題の場所。どうやら、ResultList.java 141行目のViewItemAdapterクラス内のgetViewでURL.openStreamを実行している。つまり、UIスレッド上に展開されたリストビューで、ネットワークアクセスをしているようだ。


コードを見直す

ResultList.java



89: // リストビューアイテム表示アダプタ
90: private static class ViewItemAdapter extends BaseAdapter {
91:
��前略)
119:
120: public View getView(int position, View convertView, ViewGroup parent) {
121:
��中略)
139: try {
140: url = new URL(result.get(position).get("smallImageUrl").toString());
141: is = url.openStream();
142: } catch (MalformedURLException e) {


確かにソースを確認すると、メインスレッド上のgetViewで(つまり、リストビューの1行表示毎に)実行しており、パフォーマンスを著しく落としている原因になっている。


このように、ログからパフォーマンスを著しく落とす原因(上記の場合、メインスレッド上のネットワークアクセスを行っている箇所)を特定し、これらをバックグラウンドプロセス等に置き換えることで、アプリのパフォーマンスを向上させることができると思われる。


※ちなみに、このコードはHoneycombではfatal errorとなって、コンパイルすら出来なくなるでしょう。


Android端末はフラッシュメモリを内蔵しているので、ディスクアクセスは遅くないのではないか?という疑問


大抵の場合(容量が非常に限られた)内蔵フラッシュメモリを使用している間は、ディスクアクセスは非常に早いです。但し、他のプロセスからバックグラウンドでI/O処理が発生した場合など、劇的に遅くなるケースも多々存在します。したがって、「ディスクアクセスは非常に遅い」と仮定した設計が望ましいと思われます。


StrictModeはセキュリティのメカニズムではない


StrictModeはセキュリティのメカニズムではないので、全てのディスクやネットワークへのアクセスを検知する訳ではなく、保証もされません。Binderの呼び出し時にプロセス境界の向こう側に状態を伝播しますが、それでもベストエフォート型メカニズムに過ぎません。特にJNIからのディスクやネットワークアクセスは必ずしも検知されません。


パフォーマンス改善のためにStrictMode以外で考慮すべきこと


使用するAPIを変えることでパフォーマンスを改善出来ることがあります。例えば、新しいメソッドである SharedPreferences.Editor.apply() は 戻り値を必要としない場合は、同クラスの commit() よりもパフォーマンスが良くなります。



文責:技術部 瀬戸 直喜(前・日本Androidの会四国支部長/情報セキュリティスペシャリスト)







12/26(日)、先月開催されたFeliCaハッカソン(日経BPさん主催)が、大阪でも開催されました。


f:id:bs-android:20101227232826j:image


内容は前回と同じく、AndroidでFeliCaチップが初搭載されたIS03をターゲットに、FeliCa APIを使ったアプリを一日かけて開発します。


資料や成果物(コード)はGoogle Codeにアップされ、開催模様はUSTREAMにて録画されています。


参加メンバー


クリスマス明けで急に冷え込んだ寒い朝にも関わらず、16名もの開発者の方々がご参加くださいました。


その他、スタッフとしてはKDDIさん、SHARPさん、フェリカネットワークスさん、日経BPさん、ブリリアントサービスの5社から各3~4名を加えて総勢30数名のハッカソンとなりました。


f:id:bs-android:20101227232829j:image


f:id:bs-android:20101227232830j:image


はじめのセッション


今回はアイディアソン、ハッカソンの時間を確保することを目的として、日経BPさん、KDDIさん、シャープさんの挨拶もそこそこに、フェリカネットワークスさんからFeliCa APIの解説だけ行われました。


f:id:bs-android:20101228000864j:image


午前のアイディアソン


チーム分けで時間が取られると思いきや、すぐに3~4名のグループが出来上がりあれよと言う間にホワイトボードが埋められていきました。


f:id:bs-android:20101227232827j:image


今回は、ターゲットの端末が既発売であったり前回の成果物などあったり、事前に必要な情報がある程度揃っていたからでしょうか。


アイディアプレゼン


昼食込みのアイディアソンの後、各チームで何を作るかの簡単なプレゼンが行われました。


f:id:bs-android:20101228000865j:image


f:id:bs-android:20101228000866j:image


その時のメモはこちらです。


午後のハッカソン


アイディアソンから引き続き、大きく4グループに分かれ、グループによっては複数のアプリの開発が行われました。


こちらも早い段階でアイディアが固まっていたからか、皆さん早々にもくもくと開発を進めておられました。


f:id:bs-android:20101227232837j:image


一時的に一部ネットワークが不通になる事はあったものの、その他はこれといったトラブルもなく、かなりしっかりした環境をご用意して頂けたのだと思います。


夕方の成果物発表


チームはやべん(3名)『FeliCaでWi-Fi』

FeliCaが接触させるほどの近距離で通信を行う点をセキュリティに有利と考案したアプリ。


アクセスポイントとなる端末から、WEPキーを送信して対向の端末で受け取ります。


一度に送信可能なデータが180Byteに満たない事もあり、最初のキーとなる情報のやり取りをFeliCaで行うというファーストコネクションの提案でした。


f:id:bs-android:20101227232836j:image


チーム神戸(3名)『FeliCaで短歌』

一方で上の句を作成して相手方へ送信、受け取った側では下の句を作成して返す。


そして最後に両者で詠み上げる(実際には表示のみです)というアプリを作成されていました。


送る対象が日本語のため、そのままURLエンコードすると1文字9バイト必要となり、


上の句の17文字を送ることに苦心されたそうです。


f:id:bs-android:20101227232835j:image


チーム神戸 分離して チームぼっち(1人)『MusicFeliCaIn』

Webベースのオーディオファイル管理システムAmpacheとクライアントの端末があったとして、


その端末から別の端末じぇFeliCaによる認証キーの転送を行い、オーディオのストリーミング再生を


行おうとされていました。時間と諸々の関係で残念ながら完成には至らずでした。


f:id:bs-android:20101227232834j:image


チームオールコム(4名)『AllCom(出席&名刺交換)』

勉強会などで照会が大変な出欠確認を、FeliCaを行って補助する仕組み。


出席者が会場に用意された端末にタッチすることで出席をとることができます。


また、その場でタッチしあった人同士で名刺交換や挨拶をし、どの人に会ったかの確認もできます。


この時間でサーバーの環境まで構築されたというのは驚きの一言。


f:id:bs-android:20101227232833j:image


チームばらばら(5名)『ア◯ック25のようなスタンプラリーのようなアプリ』

複数に分割されたパネルと同数の端末を用意し、パネルと端末に同じ番号を割り振ります。


2台をタッチさせることでお互いの番号に合わせたパネルが消えていき、例えば下にある画像がアンロックされていく仕組みです。


応用すればスタンプラリーや宝探しのようなゲームに使えるのでは?というアプリでした。


f:id:bs-android:20101227232832j:image


結果発表


1位はチーム神戸の「FeliCaで短歌」、2位は惜しくも1票差でチームオールコムの「AllCom」でした。


f:id:bs-android:20101227232831j:image


また、景品としてソニー(&フェリカネットワークス)さんからFeliCaリーダーPaSoRiと、日経BPさんからはIS03用特製ケースが提供、授与されました。


最後に


主催された日経BPさんを初め、各社スタッフとしてご参加いただいた方々、クリスマスの寒い中お集まり頂いた開発者の皆さんありがとうございました。そしておつかれさまでした。
今回は前回の実績があるため、ある意味やり辛いかな?と思っていましたが全くの杞憂でした。開発者の皆さんの発想力にはいつも驚かされます。
今後もこのようなイベントに関わり続けていきたいと思います。(文責:中山雅也)





Android FeliCa-thon 2010 in Osaka が開催されました



12/26(日)、先月開催されたFeliCaハッカソン(日経BPさん主催)が、大阪でも開催されました。


f:id:bs-android:20101227232826j:image


内容は前回と同じく、AndroidでFeliCaチップが初搭載されたIS03をターゲットに、FeliCa APIを使ったアプリを一日かけて開発します。


資料や成果物(コード)はGoogle Codeにアップされ、開催模様はUSTREAMにて録画されています。


参加メンバー


クリスマス明けで急に冷え込んだ寒い朝にも関わらず、16名もの開発者の方々がご参加くださいました。


その他、スタッフとしてはKDDIさん、SHARPさん、フェリカネットワークスさん、日経BPさん、ブリリアントサービスの5社から各3~4名を加えて総勢30数名のハッカソンとなりました。


f:id:bs-android:20101227232829j:image


f:id:bs-android:20101227232830j:image


はじめのセッション


今回はアイディアソン、ハッカソンの時間を確保することを目的として、日経BPさん、KDDIさん、シャープさんの挨拶もそこそこに、フェリカネットワークスさんからFeliCa APIの解説だけ行われました。


f:id:bs-android:20101228000864j:image


午前のアイディアソン


チーム分けで時間が取られると思いきや、すぐに3~4名のグループが出来上がりあれよと言う間にホワイトボードが埋められていきました。


f:id:bs-android:20101227232827j:image


今回は、ターゲットの端末が既発売であったり前回の成果物などあったり、事前に必要な情報がある程度揃っていたからでしょうか。


アイディアプレゼン


昼食込みのアイディアソンの後、各チームで何を作るかの簡単なプレゼンが行われました。


f:id:bs-android:20101228000865j:image


f:id:bs-android:20101228000866j:image


その時のメモはこちらです。


午後のハッカソン


アイディアソンから引き続き、大きく4グループに分かれ、グループによっては複数のアプリの開発が行われました。


こちらも早い段階でアイディアが固まっていたからか、皆さん早々にもくもくと開発を進めておられました。


f:id:bs-android:20101227232837j:image


一時的に一部ネットワークが不通になる事はあったものの、その他はこれといったトラブルもなく、かなりしっかりした環境をご用意して頂けたのだと思います。


夕方の成果物発表


チームはやべん(3名)『FeliCaでWi-Fi』

FeliCaが接触させるほどの近距離で通信を行う点をセキュリティに有利と考案したアプリ。


アクセスポイントとなる端末から、WEPキーを送信して対向の端末で受け取ります。


一度に送信可能なデータが180Byteに満たない事もあり、最初のキーとなる情報のやり取りをFeliCaで行うというファーストコネクションの提案でした。


f:id:bs-android:20101227232836j:image


チーム神戸(3名)『FeliCaで短歌』

一方で上の句を作成して相手方へ送信、受け取った側では下の句を作成して返す。


そして最後に両者で詠み上げる(実際には表示のみです)というアプリを作成されていました。


送る対象が日本語のため、そのままURLエンコードすると1文字9バイト必要となり、


上の句の17文字を送ることに苦心されたそうです。


f:id:bs-android:20101227232835j:image


チーム神戸 分離して チームぼっち(1人)『MusicFeliCaIn』

Webベースのオーディオファイル管理システムAmpacheとクライアントの端末があったとして、


その端末から別の端末じぇFeliCaによる認証キーの転送を行い、オーディオのストリーミング再生を


行おうとされていました。時間と諸々の関係で残念ながら完成には至らずでした。


f:id:bs-android:20101227232834j:image


チームオールコム(4名)『AllCom(出席&名刺交換)』

勉強会などで照会が大変な出欠確認を、FeliCaを行って補助する仕組み。


出席者が会場に用意された端末にタッチすることで出席をとることができます。


また、その場でタッチしあった人同士で名刺交換や挨拶をし、どの人に会ったかの確認もできます。


この時間でサーバーの環境まで構築されたというのは驚きの一言。


f:id:bs-android:20101227232833j:image


チームばらばら(5名)『ア◯ック25のようなスタンプラリーのようなアプリ』

複数に分割されたパネルと同数の端末を用意し、パネルと端末に同じ番号を割り振ります。


2台をタッチさせることでお互いの番号に合わせたパネルが消えていき、例えば下にある画像がアンロックされていく仕組みです。


応用すればスタンプラリーや宝探しのようなゲームに使えるのでは?というアプリでした。


f:id:bs-android:20101227232832j:image


結果発表


1位はチーム神戸の「FeliCaで短歌」、2位は惜しくも1票差でチームオールコムの「AllCom」でした。


f:id:bs-android:20101227232831j:image


また、景品としてソニー(&フェリカネットワークス)さんからFeliCaリーダーPaSoRiと、日経BPさんからはIS03用特製ケースが提供、授与されました。


最後に


主催された日経BPさんを初め、各社スタッフとしてご参加いただいた方々、クリスマスの寒い中お集まり頂いた開発者の皆さんありがとうございました。そしておつかれさまでした。
今回は前回の実績があるため、ある意味やり辛いかな?と思っていましたが全くの杞憂でした。開発者の皆さんの発想力にはいつも驚かされます。
今後もこのようなイベントに関わり続けていきたいと思います。(文責:中山雅也)





2010年12月22日水曜日



f:id:bs-android:20101221182358p:image


このトピックは「Google Analytics SDK for Android」と「Android Developer’sのブログ記事」を参考にしています。


SDKの概要


Google Analytics SDK for Androidは、Google Analytics for Mobile Apps SDKの一つで、モバイルアプリの操作を追跡し、それをGoogle Analyticsへレポートするインタフェースを提供します。SDKを利用すると主に下記のものが測定可能です。




  • 訪問者数

  • セッションの滞在時間

  • バウンスレート(直帰率:ウェブサイトを訪問し、サイト内をじっくり閲覧せずに離れた訪問者の割合のこと)

  • ユニーク訪問者数


モバイルアプリケーションの追跡は、Webサイトのページの追跡モデルに対して、若干の構造的な違いがあります。SDKはウェブサイトの訪問者を追跡し、Webページのウィジェットとやりとりを行うモデルを使用しています。したがって、以下で使用される用語は従来のウェブサイトの追跡モデルを、モバイルアプリケーションの追跡として置き換えたものとなります。


このSDKがどのように機能するかを理解するため、Analyticsで追跡する方法に詳しくなるべきです。Analyticsとやりとりするアプリを作るには、以下のモバイル追跡SDKを使用してください:


ページビュー追跡

ページビューは、伝統的なWebサイトのトラフィック量を図る標準的な指標です。モバイルアプリはHTMLページを含んでいないので、ページビューのリクエストを「いつ」「どれくらい」発生させるかを決定する必要があります。また、ページビューのリクエストはディレクトリの構造に基づいたレポートとして設計されています。そのため、Analyticsのコンテンツレポート中の、ページのパス名を使用するため、明示的な要求用の名前を用意する必要があります。選択された名前は実際のHTMLのページがなくとも、ページパスとしてAnalyticsのレポート中に設定されます。


イベント追跡

Analyticsでは、イベントはページビューのリクエストから、ユーザーのウェブページの要素とのやりとりを明確に追跡するように設計されています。Google Analyticsのイベント追跡機能を利用して、Analyticsのレポートインタフェースのイベント追跡セクション中にレポートされる呼び出しを、追加で利用できます。イベントはカテゴリを利用してグループ化され、また、イベント毎のラベルも利用でき柔軟性の高いレポートを提供します。例えば、マルチメディアのアプリはビデオのカテゴリとビデオ名毎のラベルに対して、再生/停止/一時停止を行う事ができます。Google Analyticsレポートはビデオのカテゴリにタグ付けされた全てのイベントを収集します。イベント追跡の情報については、イベントトラッキングガイドを参照して下さい。


カスタム変数

カスタム変数は名前と値のペアのタグで、Google Analyticsの追跡を強化するために追跡コード中に挿入することができます。使用方法に関する情報は、カスタム変数ガイドを参照して下さい。




はじめよう


システム要件

Google Analyticsの追跡システムにAndroidアプリを統合するには、以下のものが必要です:




セットアップ



  • libGoogleAnalytics.jar をプロジェクトの /libs ディレクトリに追加します。

  • プロジェクトのAndroidManifest.xmlに以下のパーミッションを追加します:


    • <uses-permission android:name="android.permission.INTERNET" />

    • <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />




SDKに含まれているサンプルのアプリケーションは、セットアップが成功したらプロジェクトがどのような構成となっているべきかを示すものとなっています。Analyticsが統合された自身のアプリのテンプレートとして、自由に利用しても構いません。


SDKを利用する


SDKを利用する前に、www.google.com/analyticsでフリーのアカウントを最初に取得し、適当な(偽の)WebサイトのURL(例:http://mymobileapp.mywebsite.com)を使ったアカウントに、Webサイトプロフィールを作成しなければなりません。プロフィールを生成したら、WebプロパティIDを書き写すかコピーを保管してください。



WebプロパティIDはまた、追跡コードである「UAナンバー」として知られており「UA-xxxxx-yy」のような形式となっています。xとyは各プロフィールのユニークな番号を示します。追跡オブジェクトをインスタンス化する際はWebプロパティIDを明示する必要があります。詳しくはWebプロパティを御覧ください。



SDKの使用の際には、ユーザに対して、アプリケーション自身や利用規約で、匿名でアプリケーション中のユーザの活動を追跡し、レポートする権利を有していることを示す必要があります。さらに、Google Analytics SDKの使用に関して、アカウントのサインアップ時にGoogle Analytics利用規約に同意する必要があります。


追跡を開始する

GoogleAnalyticsTracker.getInstance()をコールして、シングルトンのトラッカーを取得します。startメソッドをコールすると、WebプロパティIDが渡されて、アクティビティの追跡が開始されます。アクティビティのonCreateメソッド中で直接このメソッドをコールすると良いでしょう。例:



package com.google.android.apps.analytics.sample;

import com.google.android.apps.analytics.GoogleAnalyticsTracker;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class TestActivity extends Activity {

GoogleAnalyticsTracker tracker;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

tracker = GoogleAnalyticsTracker.getInstance();

// 手動のディスパッチモードで追跡を開始する...
tracker.start("UA-YOUR-ACCOUNT-HERE", this);

// あるいは、ディスパッチ間隔(秒)で指定して追跡を開始することも可能
//tracker.start("UA-YOUR-ACCOUNT-HERE", 20, this);

setContentView(R.layout.main);
Button createEventButton = (Button)findViewById(R.id.NewEventButton);
createEventButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
tracker.trackEvent(
"Clicks", // カテゴリ
"Button", // アクション
"clicked", // ラベル
77); // 値
}
});

Button createPageButton = (Button)findViewById(R.id.NewPageButton);
createPageButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// "Medium"名、"MobileApp"の値、セッションレベルのスコープで、このページビューにカスタム変数を追加する
// パラメータは順に、「Index (1~5)」「名前」「値」「スコープ」
// スコープの定義:訪問者=1, セッション=2, ページ=3(デフォルト)
tracker.setCustomVar(1, "Navigation Type", "Button click", 2);

// ページビューを追跡します。これはおそらく、アプリケーションを追跡する方法として良い方法でよく使われるでしょう。
// 例えば、
// tracker.trackPageView("/help");      > ヘルプ画面を誰が見たか追跡する
// tracker.trackPageView("/level2");     > 誰がゲームのレベル2に達したかを追跡する
// tracker.trackPageView("/uploadScreen"); > 誰がアップロード画面を利用したかを追跡する
tracker.trackPageView("/testApplicationHomeScreen");
}
});

Button quitButton = (Button)findViewById(R.id.QuitButton);
quitButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});

Button dispatchButton = (Button)findViewById(R.id.DispatchButton);
dispatchButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// 手動でディスパッチを開始する。一定間隔でディスパッチする方法で開始した場合は不要。
tracker.dispatch();
}
});
}

@Override
protected void onDestroy() {
super.onDestroy();
// これ以上必要のない追跡を停止する
tracker.stop();
}
}


カスタム変数のスコープの定義

カスタム変数に定義できるスコープは次の3種類が存在します。




  • 訪問者スコープ(=1):端末でアプリケーションが最初に動作する時のみ呼び出します。同じインデックスでカスタム変数を生成、または、最初の変数を上書きしてはいけません。どのバージョンのアプリが使用されたか、どの種類の電話か、ライトとフルバージョンのアプリとの比較、または、アプリケーションのインストールから全く変化しない何らかのデータを送信するのに便利です。

  • セッションスコープ(=2):アクティビティが開始する毎に1度だけ呼び出します。同じインデックスで異なるカスタム変数を生成しない場合、アクティビティのライフサイクル中の全てのページビューやイベントに対して適用されます。

  • ページスコープ(=3):trackEventやtrackPageViewの直前にカスタム変数を適用する必要がある場合に呼び出します。メソッドが呼び出される度に動作します。スコープが指定されていない場合、これがデフォルトになります。


ページビューとイベントの追跡

ページビューの追跡は簡単です:ページビューを起動したい時に都度トラッカーオブジェクトの trackPageView をコールするだけです。イベントを記録するためには trackEvent をコールします。ページビューとイベントの詳細は、上記のSDKの概要を参照して下さい。


カスタム変数を利用する

カスタム変数の追加もまた簡単です:モバイルSDKによって提供される setCustomVar メソッドを利用するだけです。それぞれのカスタム変数のインデックスがどのようにマッピングされるかを前もって計画するだけで、既に存在する変数の上書きはしません。


カスタム変数の詳細な情報は、カスタム変数ガイドを参照して下さい。setCustomVar メソッドは直接、自身のデータをAnalyticsに送らないことに注意して下さい。次のページビューまたはイベント発生時に併せて送信されます(ピギーバックと呼ばれる方式です)。次の送信までの間はローカルのSQLITEデータベースに追跡情報が保存されます。ページビューやイベントの追跡前に setCustomVar をコールする必要があります。


バッチ処理で統計情報をアップロードする

接続が維持され続ける事やバッテリーの過負荷を防ぐため、追跡のリクエストをバッチ処理することをお勧めします。バッチリクエストを行いたければいつでも、追跡オブジェクトの dispatch メソッドをコールすることができ、手動でも一定間隔毎でもどちらでも実行することができます。



Tip: アプリケーション内で発生する他のHTTPリクエストにトラッカーをバンドルし、ディスパッチすることで、オーバーヘッドを低減することができます。



既知の不具合

不正確なタイムスタンプの可能性:タイムスタンプは Google Analyticsへアプリケーションからディスパッチされた時点で記録されますので、ユーザが長期間に渡りオフラインであった場合、タイムスタンプは100%正確ではないかもしれません。


リファラーの追跡


Android 1.6のOSリリースで、Androidマーケットへのダウンロードリンク中の、URLパラメータのリファラーがサポートされました。アプリケーション向けGoogle Analytics中の参照/キャンペーン情報を自動的に保持するため、Google Analytics SDK for Androidはこのパラメータを使用します。これは、(例えば、アプリの特定広告の有効性を測るといった場合に役立つような)ページビューやイベントを記録し関連付けるために、アプリケーションのソースをインストールすることを可能にします。



警告:アプリケーション中に生存する間はリファラーが残り続けるため、このリファラー操作は従来の訪問レベルのリファラーとは異なります。Androidでのリファラーのセットアップについては、原文のAndroidマーケットのリファラー追跡を御覧ください。



追跡を動作させるため、プロジェクトのAndroidManifest.xmlに、以下のブロードキャストレシーバを追加する必要があります。




<receiver android:name="com.google.android.apps.analytics.AnalyticsReceiver" android:exported="true">
<intent-filter>
<action android:name="com.android.vending.INSTALL_REFERRER" />
</intent-filter>
</receiver>


Androidマーケットのリファラー追跡

Androidマーケットを通じてGoogle Analyticsのリファラー追跡をセットアップするには、リファラーリンクを生成するURLビルダーを利用します。Analytics SDKは自動的にリファラー情報をパースして記録し、Analyticsのレポートとして公開します。リファラーリンクを生成するには、下記のフィールドを埋めてください。*1


パッケージ名、キャンペーンソース、キャンペーンミディアムとキャンペーン名は必須です。それぞれのパラメータの詳細な説明は、下記のテーブルを参照して下さい。


リファラーリンクパラメータ







































パラメータ必須説明
utm_sourceキャンペーンソース:サーチエンジン、ニュースレター、その他のソースの特定に使用します。utm_source=google
utm_mediumキャンペーン媒体:eメールやコストパークリック(cpc)のような媒体の特定に使用します。utm_medium=cpc
utm_termキャンペーン用語:広告のために提供するキーワードを有料の検索で使用します。utm_term=running+shoes
utm_contentキャンペーンのコンテンツ:A/Bテストや、同じURLが指定された異なる広告やリンクのための、コンテンツをターゲットとした広告に使用します。 utm_content=logolink
utm_content=textlink
utm_campaignキャンペーン名:特別な製品プロモーションや戦略的なキャンペーンを特定するために、キーワードの解析に使用されます。utm_campaign=spring_sale


アプリケーションのリファラー情報を渡すためのマーケットへのリンクは、下記の通りです:


http://market.android.com/search?q=pname:<package>&referrer=<referral>


<package>はアプリケーションのパッケージ名、<referral>はAnalyticsのキャンペーン情報のリストをエンコードしたURLです。URLを生成するツールが下記にあります。


http://code.google.com/mobile/analytics/docs/android/#android-market-tracking


利用シーンの例


Androidに特徴的な例をあげると、ユーザーがフルバージョンのアプリを持っているかどうかに依存するステータス、"Full"または"Lite"の状態を有する"アプリケーションタイプ(AppType)"が挙げられるでしょう。AnalyticsのWebインタフェースを使って、"Lite"ユーザだけを確認したり、特定のバージョンがどの位使用されているかを見たり、"Lite"と"Full"セグメントとの違いを見ることができるでしょう。


利用規約を守る――個人情報は追跡してはならない!


Google Analyticsはそれ自身の利用規約に基づいているため、それらを読み、守ることは非常に重要です。特にAndroidアプリケーションの中にこれらの情報は存在するため、Analyticsサーバに個人を特定できる情報を送信してはいけません。つまり、訪問者レベルのカスタム変数に「電話番号」「名前」「eメールアドレス」を含む事はできないということです。直感的ではないですが、しかしながらこれは重要で、アプリケーションがWebアプリケーションのクライアントであれば(例えば、CRMやショッピングサイト)、Analyticsに店舗の情報(ユーザIDやトランザクションIDといった、Webバックエンドに格納される個人が特定できるものと結合可能な情報)を格納できないことを意味します。


実行結果


今回はSDKに付属するサンプルアプリを使用しました。


事前にGoogle Analyticsのアカウントを作成

f:id:bs-android:20101221174445p:image


Google Analyticsのサイトに移動し、適当なURLとアカウントを入力。URLとアカウントは実在しないドメインでも良いです。





f:id:bs-android:20101221174446p:image


連絡先情報を入力(誰が連絡してくるのだろう・・・)





f:id:bs-android:20101221174447p:image


利用規約を読んで同意するにチェック。この規約はSDKを利用するアプリにも適用されます。





f:id:bs-android:20101221174448p:image


Web用のコードスニペットが表示されるので、そこに記載されている「UA-XXXXX-Y」の文字列を保存しておく。





SDKを搭載したアプリを操作する

f:id:bs-android:20101221173337p:image


アプリ自体は至ってシンプルです。ボタンクリックによって、ページビューやイベントの追跡が発生します。





Google Analytics上のレポート例



  • レポートは初めて追跡を開始してから数時間(最大24時間)を経過しなければ表示されません。


f:id:bs-android:20101221174444p:image


f:id:bs-android:20101222004309p:image


f:id:bs-android:20101222004310p:image


f:id:bs-android:20101222004311p:image


このように、アプリから送信されたページビューやイベントの追跡情報が、Google Analyticsに表示されます。カテゴリ毎、ラベル毎、アクション毎など、様々な切り口から分析することができます。


まとめ


AndroidとGoogle Analyticsを使用すると、上記のように様々なユーザの操作などを情報を追跡する事ができます。このあたりの連携はさすがGoogle、といったところでしょうか。これらの追跡情報から、ユーザにとって使いやすいUIの配置を理論的に検討することもできるでしょう。


しかしながら、使い方によっては、ユーザーにとって知られたくない情報も収集されてしまう可能性もありますので、やはりアプリケーション内でこのSDKを使用している場合は、インストール時などにユーザーに対して情報を追跡している旨を伝え、事前の承諾を得る形で使用するべきでしょう(いわゆる、"オプトイン"方式)。



文責:技術部 植物工場研究G 瀬戸 直喜






Google Analytics SDK for Androidでユーザーのアプリ操作を追跡する



f:id:bs-android:20101221182358p:image


このトピックは「Google Analytics SDK for Android」と「Android Developer’sのブログ記事」を参考にしています。


SDKの概要


Google Analytics SDK for Androidは、Google Analytics for Mobile Apps SDKの一つで、モバイルアプリの操作を追跡し、それをGoogle Analyticsへレポートするインタフェースを提供します。SDKを利用すると主に下記のものが測定可能です。




  • 訪問者数

  • セッションの滞在時間

  • バウンスレート(直帰率:ウェブサイトを訪問し、サイト内をじっくり閲覧せずに離れた訪問者の割合のこと)

  • ユニーク訪問者数


モバイルアプリケーションの追跡は、Webサイトのページの追跡モデルに対して、若干の構造的な違いがあります。SDKはウェブサイトの訪問者を追跡し、Webページのウィジェットとやりとりを行うモデルを使用しています。したがって、以下で使用される用語は従来のウェブサイトの追跡モデルを、モバイルアプリケーションの追跡として置き換えたものとなります。


このSDKがどのように機能するかを理解するため、Analyticsで追跡する方法に詳しくなるべきです。Analyticsとやりとりするアプリを作るには、以下のモバイル追跡SDKを使用してください:


ページビュー追跡

ページビューは、伝統的なWebサイトのトラフィック量を図る標準的な指標です。モバイルアプリはHTMLページを含んでいないので、ページビューのリクエストを「いつ」「どれくらい」発生させるかを決定する必要があります。また、ページビューのリクエストはディレクトリの構造に基づいたレポートとして設計されています。そのため、Analyticsのコンテンツレポート中の、ページのパス名を使用するため、明示的な要求用の名前を用意する必要があります。選択された名前は実際のHTMLのページがなくとも、ページパスとしてAnalyticsのレポート中に設定されます。


イベント追跡

Analyticsでは、イベントはページビューのリクエストから、ユーザーのウェブページの要素とのやりとりを明確に追跡するように設計されています。Google Analyticsのイベント追跡機能を利用して、Analyticsのレポートインタフェースのイベント追跡セクション中にレポートされる呼び出しを、追加で利用できます。イベントはカテゴリを利用してグループ化され、また、イベント毎のラベルも利用でき柔軟性の高いレポートを提供します。例えば、マルチメディアのアプリはビデオのカテゴリとビデオ名毎のラベルに対して、再生/停止/一時停止を行う事ができます。Google Analyticsレポートはビデオのカテゴリにタグ付けされた全てのイベントを収集します。イベント追跡の情報については、イベントトラッキングガイドを参照して下さい。


カスタム変数

カスタム変数は名前と値のペアのタグで、Google Analyticsの追跡を強化するために追跡コード中に挿入することができます。使用方法に関する情報は、カスタム変数ガイドを参照して下さい。




はじめよう


システム要件

Google Analyticsの追跡システムにAndroidアプリを統合するには、以下のものが必要です:




セットアップ



  • libGoogleAnalytics.jar をプロジェクトの /libs ディレクトリに追加します。

  • プロジェクトのAndroidManifest.xmlに以下のパーミッションを追加します:


    • <uses-permission android:name="android.permission.INTERNET" />

    • <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />




SDKに含まれているサンプルのアプリケーションは、セットアップが成功したらプロジェクトがどのような構成となっているべきかを示すものとなっています。Analyticsが統合された自身のアプリのテンプレートとして、自由に利用しても構いません。


SDKを利用する


SDKを利用する前に、www.google.com/analyticsでフリーのアカウントを最初に取得し、適当な(偽の)WebサイトのURL(例:http://mymobileapp.mywebsite.com)を使ったアカウントに、Webサイトプロフィールを作成しなければなりません。プロフィールを生成したら、WebプロパティIDを書き写すかコピーを保管してください。



WebプロパティIDはまた、追跡コードである「UAナンバー」として知られており「UA-xxxxx-yy」のような形式となっています。xとyは各プロフィールのユニークな番号を示します。追跡オブジェクトをインスタンス化する際はWebプロパティIDを明示する必要があります。詳しくはWebプロパティを御覧ください。



SDKの使用の際には、ユーザに対して、アプリケーション自身や利用規約で、匿名でアプリケーション中のユーザの活動を追跡し、レポートする権利を有していることを示す必要があります。さらに、Google Analytics SDKの使用に関して、アカウントのサインアップ時にGoogle Analytics利用規約に同意する必要があります。


追跡を開始する

GoogleAnalyticsTracker.getInstance()をコールして、シングルトンのトラッカーを取得します。startメソッドをコールすると、WebプロパティIDが渡されて、アクティビティの追跡が開始されます。アクティビティのonCreateメソッド中で直接このメソッドをコールすると良いでしょう。例:



package com.google.android.apps.analytics.sample;

import com.google.android.apps.analytics.GoogleAnalyticsTracker;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class TestActivity extends Activity {

GoogleAnalyticsTracker tracker;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

tracker = GoogleAnalyticsTracker.getInstance();

// 手動のディスパッチモードで追跡を開始する...
tracker.start("UA-YOUR-ACCOUNT-HERE", this);

// あるいは、ディスパッチ間隔(秒)で指定して追跡を開始することも可能
//tracker.start("UA-YOUR-ACCOUNT-HERE", 20, this);

setContentView(R.layout.main);
Button createEventButton = (Button)findViewById(R.id.NewEventButton);
createEventButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
tracker.trackEvent(
"Clicks", // カテゴリ
"Button", // アクション
"clicked", // ラベル
77); // 値
}
});

Button createPageButton = (Button)findViewById(R.id.NewPageButton);
createPageButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// "Medium"名、"MobileApp"の値、セッションレベルのスコープで、このページビューにカスタム変数を追加する
// パラメータは順に、「Index (1~5)」「名前」「値」「スコープ」
// スコープの定義:訪問者=1, セッション=2, ページ=3(デフォルト)
tracker.setCustomVar(1, "Navigation Type", "Button click", 2);

// ページビューを追跡します。これはおそらく、アプリケーションを追跡する方法として良い方法でよく使われるでしょう。
// 例えば、
// tracker.trackPageView("/help");      > ヘルプ画面を誰が見たか追跡する
// tracker.trackPageView("/level2");     > 誰がゲームのレベル2に達したかを追跡する
// tracker.trackPageView("/uploadScreen"); > 誰がアップロード画面を利用したかを追跡する
tracker.trackPageView("/testApplicationHomeScreen");
}
});

Button quitButton = (Button)findViewById(R.id.QuitButton);
quitButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});

Button dispatchButton = (Button)findViewById(R.id.DispatchButton);
dispatchButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// 手動でディスパッチを開始する。一定間隔でディスパッチする方法で開始した場合は不要。
tracker.dispatch();
}
});
}

@Override
protected void onDestroy() {
super.onDestroy();
// これ以上必要のない追跡を停止する
tracker.stop();
}
}


カスタム変数のスコープの定義

カスタム変数に定義できるスコープは次の3種類が存在します。




  • 訪問者スコープ(=1):端末でアプリケーションが最初に動作する時のみ呼び出します。同じインデックスでカスタム変数を生成、または、最初の変数を上書きしてはいけません。どのバージョンのアプリが使用されたか、どの種類の電話か、ライトとフルバージョンのアプリとの比較、または、アプリケーションのインストールから全く変化しない何らかのデータを送信するのに便利です。

  • セッションスコープ(=2):アクティビティが開始する毎に1度だけ呼び出します。同じインデックスで異なるカスタム変数を生成しない場合、アクティビティのライフサイクル中の全てのページビューやイベントに対して適用されます。

  • ページスコープ(=3):trackEventやtrackPageViewの直前にカスタム変数を適用する必要がある場合に呼び出します。メソッドが呼び出される度に動作します。スコープが指定されていない場合、これがデフォルトになります。


ページビューとイベントの追跡

ページビューの追跡は簡単です:ページビューを起動したい時に都度トラッカーオブジェクトの trackPageView をコールするだけです。イベントを記録するためには trackEvent をコールします。ページビューとイベントの詳細は、上記のSDKの概要を参照して下さい。


カスタム変数を利用する

カスタム変数の追加もまた簡単です:モバイルSDKによって提供される setCustomVar メソッドを利用するだけです。それぞれのカスタム変数のインデックスがどのようにマッピングされるかを前もって計画するだけで、既に存在する変数の上書きはしません。


カスタム変数の詳細な情報は、カスタム変数ガイドを参照して下さい。setCustomVar メソッドは直接、自身のデータをAnalyticsに送らないことに注意して下さい。次のページビューまたはイベント発生時に併せて送信されます(ピギーバックと呼ばれる方式です)。次の送信までの間はローカルのSQLITEデータベースに追跡情報が保存されます。ページビューやイベントの追跡前に setCustomVar をコールする必要があります。


バッチ処理で統計情報をアップロードする

接続が維持され続ける事やバッテリーの過負荷を防ぐため、追跡のリクエストをバッチ処理することをお勧めします。バッチリクエストを行いたければいつでも、追跡オブジェクトの dispatch メソッドをコールすることができ、手動でも一定間隔毎でもどちらでも実行することができます。



Tip: アプリケーション内で発生する他のHTTPリクエストにトラッカーをバンドルし、ディスパッチすることで、オーバーヘッドを低減することができます。



既知の不具合

不正確なタイムスタンプの可能性:タイムスタンプは Google Analyticsへアプリケーションからディスパッチされた時点で記録されますので、ユーザが長期間に渡りオフラインであった場合、タイムスタンプは100%正確ではないかもしれません。


リファラーの追跡


Android 1.6のOSリリースで、Androidマーケットへのダウンロードリンク中の、URLパラメータのリファラーがサポートされました。アプリケーション向けGoogle Analytics中の参照/キャンペーン情報を自動的に保持するため、Google Analytics SDK for Androidはこのパラメータを使用します。これは、(例えば、アプリの特定広告の有効性を測るといった場合に役立つような)ページビューやイベントを記録し関連付けるために、アプリケーションのソースをインストールすることを可能にします。



警告:アプリケーション中に生存する間はリファラーが残り続けるため、このリファラー操作は従来の訪問レベルのリファラーとは異なります。Androidでのリファラーのセットアップについては、原文のAndroidマーケットのリファラー追跡を御覧ください。



追跡を動作させるため、プロジェクトのAndroidManifest.xmlに、以下のブロードキャストレシーバを追加する必要があります。




<receiver android:name="com.google.android.apps.analytics.AnalyticsReceiver" android:exported="true">
<intent-filter>
<action android:name="com.android.vending.INSTALL_REFERRER" />
</intent-filter>
</receiver>


Androidマーケットのリファラー追跡

Androidマーケットを通じてGoogle Analyticsのリファラー追跡をセットアップするには、リファラーリンクを生成するURLビルダーを利用します。Analytics SDKは自動的にリファラー情報をパースして記録し、Analyticsのレポートとして公開します。リファラーリンクを生成するには、下記のフィールドを埋めてください。*1


パッケージ名、キャンペーンソース、キャンペーンミディアムとキャンペーン名は必須です。それぞれのパラメータの詳細な説明は、下記のテーブルを参照して下さい。


リファラーリンクパラメータ







































パラメータ必須説明
utm_sourceキャンペーンソース:サーチエンジン、ニュースレター、その他のソースの特定に使用します。utm_source=google
utm_mediumキャンペーン媒体:eメールやコストパークリック(cpc)のような媒体の特定に使用します。utm_medium=cpc
utm_termキャンペーン用語:広告のために提供するキーワードを有料の検索で使用します。utm_term=running+shoes
utm_contentキャンペーンのコンテンツ:A/Bテストや、同じURLが指定された異なる広告やリンクのための、コンテンツをターゲットとした広告に使用します。 utm_content=logolink
utm_content=textlink
utm_campaignキャンペーン名:特別な製品プロモーションや戦略的なキャンペーンを特定するために、キーワードの解析に使用されます。utm_campaign=spring_sale


アプリケーションのリファラー情報を渡すためのマーケットへのリンクは、下記の通りです:


http://market.android.com/search?q=pname:<package>&referrer=<referral>


<package>はアプリケーションのパッケージ名、<referral>はAnalyticsのキャンペーン情報のリストをエンコードしたURLです。URLを生成するツールが下記にあります。


http://code.google.com/mobile/analytics/docs/android/#android-market-tracking


利用シーンの例


Androidに特徴的な例をあげると、ユーザーがフルバージョンのアプリを持っているかどうかに依存するステータス、"Full"または"Lite"の状態を有する"アプリケーションタイプ(AppType)"が挙げられるでしょう。AnalyticsのWebインタフェースを使って、"Lite"ユーザだけを確認したり、特定のバージョンがどの位使用されているかを見たり、"Lite"と"Full"セグメントとの違いを見ることができるでしょう。


利用規約を守る――個人情報は追跡してはならない!


Google Analyticsはそれ自身の利用規約に基づいているため、それらを読み、守ることは非常に重要です。特にAndroidアプリケーションの中にこれらの情報は存在するため、Analyticsサーバに個人を特定できる情報を送信してはいけません。つまり、訪問者レベルのカスタム変数に「電話番号」「名前」「eメールアドレス」を含む事はできないということです。直感的ではないですが、しかしながらこれは重要で、アプリケーションがWebアプリケーションのクライアントであれば(例えば、CRMやショッピングサイト)、Analyticsに店舗の情報(ユーザIDやトランザクションIDといった、Webバックエンドに格納される個人が特定できるものと結合可能な情報)を格納できないことを意味します。


実行結果


今回はSDKに付属するサンプルアプリを使用しました。


事前にGoogle Analyticsのアカウントを作成

f:id:bs-android:20101221174445p:image


Google Analyticsのサイトに移動し、適当なURLとアカウントを入力。URLとアカウントは実在しないドメインでも良いです。





f:id:bs-android:20101221174446p:image


連絡先情報を入力(誰が連絡してくるのだろう・・・)





f:id:bs-android:20101221174447p:image


利用規約を読んで同意するにチェック。この規約はSDKを利用するアプリにも適用されます。





f:id:bs-android:20101221174448p:image


Web用のコードスニペットが表示されるので、そこに記載されている「UA-XXXXX-Y」の文字列を保存しておく。





SDKを搭載したアプリを操作する

f:id:bs-android:20101221173337p:image


アプリ自体は至ってシンプルです。ボタンクリックによって、ページビューやイベントの追跡が発生します。





Google Analytics上のレポート例



  • レポートは初めて追跡を開始してから数時間(最大24時間)を経過しなければ表示されません。


f:id:bs-android:20101221174444p:image


f:id:bs-android:20101222004309p:image


f:id:bs-android:20101222004310p:image


f:id:bs-android:20101222004311p:image


このように、アプリから送信されたページビューやイベントの追跡情報が、Google Analyticsに表示されます。カテゴリ毎、ラベル毎、アクション毎など、様々な切り口から分析することができます。


まとめ


AndroidとGoogle Analyticsを使用すると、上記のように様々なユーザの操作などを情報を追跡する事ができます。このあたりの連携はさすがGoogle、といったところでしょうか。これらの追跡情報から、ユーザにとって使いやすいUIの配置を理論的に検討することもできるでしょう。


しかしながら、使い方によっては、ユーザーにとって知られたくない情報も収集されてしまう可能性もありますので、やはりアプリケーション内でこのSDKを使用している場合は、インストール時などにユーザーに対して情報を追跡している旨を伝え、事前の承諾を得る形で使用するべきでしょう(いわゆる、"オプトイン"方式)。



文責:技術部 植物工場研究G 瀬戸 直喜






2010年12月21日火曜日



前回エントリーで告知しました「第2回 SHARPハッカソン」が、12月18・19日に東広島で開催されました。


西は福岡、東は栃木まで各地から開発者がSHARP東広島工場に集まりました。
開発対象機種としてSHARP2010年秋冬モデル(IS03/GALAPAGOS SoftBank 003SH/LYNX 3DSH-03C)を使用し、開発者がチームを組んで2日間に渡りアプリ開発が行うイベントとなりました。


ハッカソン前の説明


朝11時、まずSHARPさんから端末の説明とSHARP APIの概要の説明がありました。
SHARP APIに関しての詳しい内容は、SHARPさんのSH Developers Squareサイトにリファレンスが上がっているので、そちらを確認してください。
f:id:bs-android:20101218112351j:image


次に司会をさせて頂いた弊社からGoogleCodeの案内と、Twitterハッシュタグの説明、USTがされる事の説明を行いました。




アイディアソン


貸し出された端末で何か面白いが出来ないかと、それぞれ何人かで固まってブレーンストーミングが行われました。
APIに詳しいSHARPの方々も混じって、APIの利用法を説明しながら和気あいあいとアイディア出しが行われました。
アイディアソン直後の各チーム毎のアイディアはこちらに書かれています。
f:id:bs-android:20101218124735j:image
f:id:bs-android:20101218124709j:image
f:id:bs-android:20101218124728j:image


ハッカソン1日目


午後はひたすらもくもくと開発する時間です。
f:id:bs-android:20101218142629j:image
f:id:bs-android:20101218142640j:image
f:id:bs-android:20101218142655j:image
f:id:bs-android:20101218142708j:image
f:id:bs-android:20101218164844j:image
一日目は17:30に終了。


ハッカソン2日目


二日目は10時スタート。ハッカソン終了の14時までどのチームもラストスパートを掛けていました。


発表


なまけもの/チームアバター

1日1回歩数を取得して、歩数に応じたキャラクターをウィジェットに表示するアプリ。
今後、Twitterと連携させる予定。
f:id:bs-android:20101219141720j:image
f:id:bs-android:20101219142048j:image





チーム3GamesCreators

3つのゲームアプリを開発。




  1. NGワードゲーム
    NGワードが表示された端末をおでこに置いて、お互いにヒントを言い合ってNGワードを言われたら負けるゲーム。

  2. 早打ちガンマン
    ゲーム参加端末で一斉に5カウントされ、0になった時に早く撃った人が勝ちのゲーム。

  3. ビーチ
    基本ルールはビーチフラッグ。一定歩数歩くと問題が出題され、その問題に早く正解した人が勝ちのゲーム。


このチームのメンバーが書かれたブログ記事です。
第2回 SHARPハッカソン@東広島に参加しました。 - stachibanaさん
第2回 SHARPハッカソン@東広島 - Toro_kunさん
android hackathon at sharp - lycheeさん


f:id:bs-android:20101219142214j:image
f:id:bs-android:20101219142400j:image
f:id:bs-android:20101219142610j:image




チーム寝坊/チーム3D

2つのアプリを開発。




  1. 寝坊しました
    ひたすら寝坊しましたと言う音声が再生され続ける。

  2. 1shotで3D画象をつくっちゃうぜ
    二台の端末を使って写真撮影して、撮影したら2つの画像を合わせて3D表示させる。


このチームのメンバーが書かれたブログ記事です。
android hackathon at sharp - lycheeさん
f:id:bs-android:20101219143125j:image
f:id:bs-android:20101219143425j:image




歩meji

simeji + 歩数計
歩いた歩数によって入力する文字が選ばれる。
これを使えば健康的且つハンズフリー。
f:id:bs-android:20101219143614j:image
f:id:bs-android:20101219143828j:image




歩数計マッシュルーム

歩いた歩数 + 単位を入力するマッシュルーム。
歩いた歩数によって、単位が「メェ」や「ウホ」と変わっていく。
f:id:bs-android:20101219144314j:image


チーム文鎮



  • SHARP秋モデルを文鎮化させる。

  • 3Dでのリモートデスクトップアプリの開発。


・・・を予定していたが、我々が文鎮になってしまった。
f:id:bs-android:20101219144808j:image
f:id:bs-android:20101219144851j:image




こども銀行

FeliCaを利用してお金のやり取りをシミュレートするアプリ
FeliCaAPIを利用すると、1トランザクションでの受領確認が困難であるため、サーバを用意する必要がある等実装が困難。


このチームのプレゼン資料です。
こども銀行 - keiji_ariyamaさん
f:id:bs-android:20101219145023j:image
f:id:bs-android:20101219145256j:image


結果発表


順位は以下のようになりました!


1位:チーム3GamesCreators
2位:歩meji
3位:チーム寝坊
4位:こども銀行
5位:チームアバター
6位:チーム文鎮
7位:歩数計マッシュルーム


1位のチームにはEdy搭載リストバンドが渡されました!
ブービーの方にはSHARPロゴの入ったフューチャーフォン用三脚。
他の参加者にはDroid君人形が配られました。


記念撮影


参加者全員の記念撮影
f:id:bs-android:20101219153353j:image


最後に


主催されたSHARPさん、全国各地から集まられた開発者の皆さん、ありがとうございました&お疲れさまでした。


このような面白いイベントの運営&司会をさせて頂き、裏方と言う立場ながら非常に楽しめたイベントとなりました。
これからも面白いイベントに関わっていきたく思います。


また、記事に使わせて頂いた写真は4gemaruさんから提供頂きました。ありがとうございます。


情報




なお、USTは結果発表時は録画に失敗していたため上がっておりません。
GoogleCodeは Apache License 2.0 を容認されたプロジェクトのみ上げられています。





第2回 SHARPハッカソン@東広島が開催されました



前回エントリーで告知しました「第2回 SHARPハッカソン」が、12月18・19日に東広島で開催されました。


西は福岡、東は栃木まで各地から開発者がSHARP東広島工場に集まりました。
開発対象機種としてSHARP2010年秋冬モデル(IS03/GALAPAGOS SoftBank 003SH/LYNX 3DSH-03C)を使用し、開発者がチームを組んで2日間に渡りアプリ開発が行うイベントとなりました。


ハッカソン前の説明


朝11時、まずSHARPさんから端末の説明とSHARP APIの概要の説明がありました。
SHARP APIに関しての詳しい内容は、SHARPさんのSH Developers Squareサイトにリファレンスが上がっているので、そちらを確認してください。
f:id:bs-android:20101218112351j:image


次に司会をさせて頂いた弊社からGoogleCodeの案内と、Twitterハッシュタグの説明、USTがされる事の説明を行いました。




アイディアソン


貸し出された端末で何か面白いが出来ないかと、それぞれ何人かで固まってブレーンストーミングが行われました。
APIに詳しいSHARPの方々も混じって、APIの利用法を説明しながら和気あいあいとアイディア出しが行われました。
アイディアソン直後の各チーム毎のアイディアはこちらに書かれています。
f:id:bs-android:20101218124735j:image
f:id:bs-android:20101218124709j:image
f:id:bs-android:20101218124728j:image


ハッカソン1日目


午後はひたすらもくもくと開発する時間です。
f:id:bs-android:20101218142629j:image
f:id:bs-android:20101218142640j:image
f:id:bs-android:20101218142655j:image
f:id:bs-android:20101218142708j:image
f:id:bs-android:20101218164844j:image
一日目は17:30に終了。


ハッカソン2日目


二日目は10時スタート。ハッカソン終了の14時までどのチームもラストスパートを掛けていました。


発表


なまけもの/チームアバター

1日1回歩数を取得して、歩数に応じたキャラクターをウィジェットに表示するアプリ。
今後、Twitterと連携させる予定。
f:id:bs-android:20101219141720j:image
f:id:bs-android:20101219142048j:image





チーム3GamesCreators

3つのゲームアプリを開発。




  1. NGワードゲーム
    NGワードが表示された端末をおでこに置いて、お互いにヒントを言い合ってNGワードを言われたら負けるゲーム。

  2. 早打ちガンマン
    ゲーム参加端末で一斉に5カウントされ、0になった時に早く撃った人が勝ちのゲーム。

  3. ビーチ
    基本ルールはビーチフラッグ。一定歩数歩くと問題が出題され、その問題に早く正解した人が勝ちのゲーム。


このチームのメンバーが書かれたブログ記事です。
第2回 SHARPハッカソン@東広島に参加しました。 - stachibanaさん
第2回 SHARPハッカソン@東広島 - Toro_kunさん
android hackathon at sharp - lycheeさん


f:id:bs-android:20101219142214j:image
f:id:bs-android:20101219142400j:image
f:id:bs-android:20101219142610j:image




チーム寝坊/チーム3D

2つのアプリを開発。




  1. 寝坊しました
    ひたすら寝坊しましたと言う音声が再生され続ける。

  2. 1shotで3D画象をつくっちゃうぜ
    二台の端末を使って写真撮影して、撮影したら2つの画像を合わせて3D表示させる。


このチームのメンバーが書かれたブログ記事です。
android hackathon at sharp - lycheeさん
f:id:bs-android:20101219143125j:image
f:id:bs-android:20101219143425j:image




歩meji

simeji + 歩数計
歩いた歩数によって入力する文字が選ばれる。
これを使えば健康的且つハンズフリー。
f:id:bs-android:20101219143614j:image
f:id:bs-android:20101219143828j:image




歩数計マッシュルーム

歩いた歩数 + 単位を入力するマッシュルーム。
歩いた歩数によって、単位が「メェ」や「ウホ」と変わっていく。
f:id:bs-android:20101219144314j:image


チーム文鎮



  • SHARP秋モデルを文鎮化させる。

  • 3Dでのリモートデスクトップアプリの開発。


・・・を予定していたが、我々が文鎮になってしまった。
f:id:bs-android:20101219144808j:image
f:id:bs-android:20101219144851j:image




こども銀行

FeliCaを利用してお金のやり取りをシミュレートするアプリ
FeliCaAPIを利用すると、1トランザクションでの受領確認が困難であるため、サーバを用意する必要がある等実装が困難。


このチームのプレゼン資料です。
こども銀行 - keiji_ariyamaさん
f:id:bs-android:20101219145023j:image
f:id:bs-android:20101219145256j:image


結果発表


順位は以下のようになりました!


1位:チーム3GamesCreators
2位:歩meji
3位:チーム寝坊
4位:こども銀行
5位:チームアバター
6位:チーム文鎮
7位:歩数計マッシュルーム


1位のチームにはEdy搭載リストバンドが渡されました!
ブービーの方にはSHARPロゴの入ったフューチャーフォン用三脚。
他の参加者にはDroid君人形が配られました。


記念撮影


参加者全員の記念撮影
f:id:bs-android:20101219153353j:image


最後に


主催されたSHARPさん、全国各地から集まられた開発者の皆さん、ありがとうございました&お疲れさまでした。


このような面白いイベントの運営&司会をさせて頂き、裏方と言う立場ながら非常に楽しめたイベントとなりました。
これからも面白いイベントに関わっていきたく思います。


また、記事に使わせて頂いた写真は4gemaruさんから提供頂きました。ありがとうございます。


情報




なお、USTは結果発表時は録画に失敗していたため上がっておりません。
GoogleCodeは Apache License 2.0 を容認されたプロジェクトのみ上げられています。





2010年12月20日月曜日



日経BPさん主催の Android FeliCa-thon が大阪で開催されることになりました。


Android FeliCa-thonって?


KDDIさんのFeliCa対応機器「IS03」上で、FeliCa対応アプリケーションをグループまたは個人で開発するハッカソンイベントです。


今回のハッカソンは下記の特徴があります。




  • グループ間で開発するコンテンツとプレゼンテーションを競い合います

  • 優勝作品には賞品付きで表彰されます

  • 参加費は無料で、先着順での受付となります


前回に引き続き、今回も弊社社員がチューターを担当させて頂きます!


前回(東京開催)の様子






応募するには


日経BPさんの下記のサイトからご応募下さい。受付は先着順となります。


FeliCa-thon 応募ページ





ちなみに、ハッカソンって・・・?


”ハッカソン"とは「ハッカー+マラソン」のスラング(特定の人々の間で使われる言葉)で、プログラマーが一同に介して共同でプログラミングを行うイベントのことです。SunのマーケティングチームとOpenBSDの開発者から生まれたとされ、1999年のJavaOne conferenceがその起源とされています。


ハッカソンはただ特定のコードをハックするだけでなく、プログラマーがやってみたいと考える内容について、特にプログラミングの方向性やゴールに対して制約を受けることなく行うこともあります。ハッカソンは数日から数週間に渡って開催されることもあります。


また、ハッカソンは、開催期間や団体によって、別の呼び方になることもあります。




  • Sprint(スプリント):数日で終わる短期間のハッカソン

  • codefest(コードフェスト:code + festival):主要なLinuxユーザの間で呼ばれている


(以上、Wikipediaより引用)


皆様のご参加をお待ちしております!





Android FeliCa-thon 2010 in Osakaが開催されます



日経BPさん主催の Android FeliCa-thon が大阪で開催されることになりました。


Android FeliCa-thonって?


KDDIさんのFeliCa対応機器「IS03」上で、FeliCa対応アプリケーションをグループまたは個人で開発するハッカソンイベントです。


今回のハッカソンは下記の特徴があります。




  • グループ間で開発するコンテンツとプレゼンテーションを競い合います

  • 優勝作品には賞品付きで表彰されます

  • 参加費は無料で、先着順での受付となります


前回に引き続き、今回も弊社社員がチューターを担当させて頂きます!


前回(東京開催)の様子






応募するには


日経BPさんの下記のサイトからご応募下さい。受付は先着順となります。


FeliCa-thon 応募ページ





ちなみに、ハッカソンって・・・?


”ハッカソン"とは「ハッカー+マラソン」のスラング(特定の人々の間で使われる言葉)で、プログラマーが一同に介して共同でプログラミングを行うイベントのことです。SunのマーケティングチームとOpenBSDの開発者から生まれたとされ、1999年のJavaOne conferenceがその起源とされています。


ハッカソンはただ特定のコードをハックするだけでなく、プログラマーがやってみたいと考える内容について、特にプログラミングの方向性やゴールに対して制約を受けることなく行うこともあります。ハッカソンは数日から数週間に渡って開催されることもあります。


また、ハッカソンは、開催期間や団体によって、別の呼び方になることもあります。




  • Sprint(スプリント):数日で終わる短期間のハッカソン

  • codefest(コードフェスト:code + festival):主要なLinuxユーザの間で呼ばれている


(以上、Wikipediaより引用)


皆様のご参加をお待ちしております!





2010年12月17日金曜日



SHARP様 主催で第2回 SHARPハッカソンが広島で開催されます。


イベント内容はSHARP2010年秋冬モデル(IS03/GALAPAGOS SoftBank 003SH/LYNX 3DSH-03C)を使用し、参加者でチームを組んでアプリ開発を行うイベントとなっております。


参加者は、既にインビテーションされた開発者の方々となり、全国各地から集まります。
運営は、前回の第1回 SHARPハッカソンでDK-01を使ってのハッカソンに引き続き、弊社が担当させて頂きます。


また、参加されない方でもハッシュタグやUSTを確認頂くと、当日の模様が分かるかと思います。
ハッシュタグ:#shthon2010w
UST:当日行われ、URLに関しては当日にハッシュタグ付きでtweetされる予定です。





参加者の方は、当日参加される事や、当日どのような事をされたいかハッシュタグ付きでtweetされると事前に他の参加者の事や、どのようなアイディアを持たれているかが共有出来て当日やりやすくなるかもしれません。





12月18・19日に第2回 SHARPハッカソン@東広島が開催されます



SHARP様 主催で第2回 SHARPハッカソンが広島で開催されます。


イベント内容はSHARP2010年秋冬モデル(IS03/GALAPAGOS SoftBank 003SH/LYNX 3DSH-03C)を使用し、参加者でチームを組んでアプリ開発を行うイベントとなっております。


参加者は、既にインビテーションされた開発者の方々となり、全国各地から集まります。
運営は、前回の第1回 SHARPハッカソンでDK-01を使ってのハッカソンに引き続き、弊社が担当させて頂きます。


また、参加されない方でもハッシュタグやUSTを確認頂くと、当日の模様が分かるかと思います。
ハッシュタグ:#shthon2010w
UST:当日行われ、URLに関しては当日にハッシュタグ付きでtweetされる予定です。





参加者の方は、当日参加される事や、当日どのような事をされたいかハッシュタグ付きでtweetされると事前に他の参加者の事や、どのようなアイディアを持たれているかが共有出来て当日やりやすくなるかもしれません。





2010年12月14日火曜日



f:id:bs-android:20101212111045p:image


原文はこちら


概要


layoutoptは、アプリケーションのレイアウトとレイアウトヒエラルキーを最適化するためのツールです。レイアウトファイルやリソースファイルを原因とする、アプリケーションのパフォーマンスに影響を及ぼす非効率な、あるいはその他のタイプの問題を素早くチェックすることができます。


ツールを実行するには、ターミナルを開いて SDKのtools/ディレクトリから layout <リソース名> を起動します。コマンド引数には、解析したい未コンパイルのリソースXMLファイルかディレクトリを指定します。


実行時にツールは指定のXMLファイルを読み込み、事前に定義されたルールセットに従ってレイアウトの構造を解析します。問題を発見した場合は、問題、ファイル名、行番号、問題の概要に関する情報と共に、推定される解決方法のいくつかを出力します。




使い方


lyaoutopt を実行するには、引数にレイアウトリソースのリスト(スペース区切りで複数可)を指定します:



layoutopt <XMLファイルのリスト または ディレクトリ>


例:



$ layoutopt res/layout-land
$ layoutopt res/layout/main.xml res/layout-land/main.xml


実行結果の例


$ layoutopt samples/
samples/compound.xml
7:23 The root-level <FrameLayout/> can be replaced with <merge/>
11:21 This LinearLayout layout or its FrameLayout parent is useless
samples/simple.xml
7:7 The root-level <FrameLayout/> can be replaced with <merge/>
samples/too_deep.xml
-1:-1 This layout has too many nested layouts: 13 levels, it should have <= 10!
20:81 This LinearLayout layout or its LinearLayout parent is useless
24:79 This LinearLayout layout or its LinearLayout parent is useless
28:77 This LinearLayout layout or its LinearLayout parent is useless
32:75 This LinearLayout layout or its LinearLayout parent is useless
36:73 This LinearLayout layout or its LinearLayout parent is useless
40:71 This LinearLayout layout or its LinearLayout parent is useless
44:69 This LinearLayout layout or its LinearLayout parent is useless
48:67 This LinearLayout layout or its LinearLayout parent is useless
52:65 This LinearLayout layout or its LinearLayout parent is useless
56:63 This LinearLayout layout or its LinearLayout parent is useless
samples/too_many.xml
7:413 The root-level <FrameLayout/> can be replaced with <merge/>
-1:-1 This layout has too many views: 81 views, it should have <= 80!
samples/useless.xml
7:19 The root-level <FrameLayout/> can be replaced with <merge/>
11:17 This LinearLayout layout or its FrameLayout parent is useless


実行結果の読み方

実行結果は下記の順で出力されています。




  • 解析対象のXMLファイル名

  • 開始行番号:終了行番号

  • 問題の内容 または 最適化された内容

  • 解決のためのヒント


注意点


コマンドを実行しても、リソースファイルが自動で変換されることはありません。あくまでも診断ツールです。


対応バージョン


SDK Tools Rev3(Android 1.5)以降


まとめ


Androidアプリ開発者として、レイアウトの複雑化に伴うパフォーマンスの低下には関心を持たざるを得ないので試してみましたが、特に複雑なレイアウトを持つアプリケーションに対して使ってみる価値があると感じました。


また、実行結果の例では、レイアウトのタグが置き換えられていたり、使われていないレイアウトが指摘されていることは容易に想像できたのですが、何気にネストできるレイアウトのレベルは10までであったり、レイアウトが保持できるビューの数が80までであることが記載されていたりして、良い勉強になりました。



文責:技術部 瀬戸 直喜(前・日本Androidの会四国支部長/情報セキュリティスペシャリスト)





便利な開発ツール:レイアウトを最適化する layoutopt (Android Developers - Dev Guide和訳)



f:id:bs-android:20101212111045p:image


原文はこちら


概要


layoutoptは、アプリケーションのレイアウトとレイアウトヒエラルキーを最適化するためのツールです。レイアウトファイルやリソースファイルを原因とする、アプリケーションのパフォーマンスに影響を及ぼす非効率な、あるいはその他のタイプの問題を素早くチェックすることができます。


ツールを実行するには、ターミナルを開いて SDKのtools/ディレクトリから layout <リソース名> を起動します。コマンド引数には、解析したい未コンパイルのリソースXMLファイルかディレクトリを指定します。


実行時にツールは指定のXMLファイルを読み込み、事前に定義されたルールセットに従ってレイアウトの構造を解析します。問題を発見した場合は、問題、ファイル名、行番号、問題の概要に関する情報と共に、推定される解決方法のいくつかを出力します。




使い方


lyaoutopt を実行するには、引数にレイアウトリソースのリスト(スペース区切りで複数可)を指定します:



layoutopt <XMLファイルのリスト または ディレクトリ>


例:



$ layoutopt res/layout-land
$ layoutopt res/layout/main.xml res/layout-land/main.xml


実行結果の例


$ layoutopt samples/
samples/compound.xml
7:23 The root-level <FrameLayout/> can be replaced with <merge/>
11:21 This LinearLayout layout or its FrameLayout parent is useless
samples/simple.xml
7:7 The root-level <FrameLayout/> can be replaced with <merge/>
samples/too_deep.xml
-1:-1 This layout has too many nested layouts: 13 levels, it should have <= 10!
20:81 This LinearLayout layout or its LinearLayout parent is useless
24:79 This LinearLayout layout or its LinearLayout parent is useless
28:77 This LinearLayout layout or its LinearLayout parent is useless
32:75 This LinearLayout layout or its LinearLayout parent is useless
36:73 This LinearLayout layout or its LinearLayout parent is useless
40:71 This LinearLayout layout or its LinearLayout parent is useless
44:69 This LinearLayout layout or its LinearLayout parent is useless
48:67 This LinearLayout layout or its LinearLayout parent is useless
52:65 This LinearLayout layout or its LinearLayout parent is useless
56:63 This LinearLayout layout or its LinearLayout parent is useless
samples/too_many.xml
7:413 The root-level <FrameLayout/> can be replaced with <merge/>
-1:-1 This layout has too many views: 81 views, it should have <= 80!
samples/useless.xml
7:19 The root-level <FrameLayout/> can be replaced with <merge/>
11:17 This LinearLayout layout or its FrameLayout parent is useless


実行結果の読み方

実行結果は下記の順で出力されています。




  • 解析対象のXMLファイル名

  • 開始行番号:終了行番号

  • 問題の内容 または 最適化された内容

  • 解決のためのヒント


注意点


コマンドを実行しても、リソースファイルが自動で変換されることはありません。あくまでも診断ツールです。


対応バージョン


SDK Tools Rev3(Android 1.5)以降


まとめ


Androidアプリ開発者として、レイアウトの複雑化に伴うパフォーマンスの低下には関心を持たざるを得ないので試してみましたが、特に複雑なレイアウトを持つアプリケーションに対して使ってみる価値があると感じました。


また、実行結果の例では、レイアウトのタグが置き換えられていたり、使われていないレイアウトが指摘されていることは容易に想像できたのですが、何気にネストできるレイアウトのレベルは10までであったり、レイアウトが保持できるビューの数が80までであることが記載されていたりして、良い勉強になりました。



文責:技術部 瀬戸 直喜(前・日本Androidの会四国支部長/情報セキュリティスペシャリスト)





2010年12月13日月曜日



f:id:bs-android:20101212110400p:image


マーケットの変更により、アプリのサイズが最大50MBまで拡張されることになりました。今後、端末本体にアプリケーションを保存できない状況が考えられるため、端末本体以外の場所にアプリを保存する方法を知っておきましょう。


原文はこちら


要点




  • アプリケーションをデバイスの外部ストレージにインストールすることができます

  • いくつかの種類のアプリケーションは外部ストレージにインストールを許可すべきではありません

  • システムに密接に関わらない大きいアプリケーションは、外部ストレージにインストールすることが理想です(大抵はゲーム)


概要


APIレベル8(Android 2.2)から、アプリケーションを外部ストレージにインストールすることが可能となりました(例えばSDカード)。これは、マニフェストの属性に android:installLocation を定義することで利用可能となるオプションの機能です。もし、この属性を定義していないなら、内部ストレージにのみインストールすることが可能で、外部ストレージへ移すことはできません。


外部ストレージにアプリケーションをインストールすることをシステムに許可するためには、<manifest>要素内の android:installLocation 属性が"preferExternal"か"auto"のいずれかの値を含めるようにマニフェストファイルを修正します。


例:



<manifest xmlns:android="http://schemas.android.com/apk/res/android"
android:installLocation="preferExternal"
... >





"preferExternal"を定義した場合、外部ストレージにアプリケーションをインストールすることを要求しますが、システムは外部ストレージにアプリケーションをインストールすることを保証しません。もし、外部ストレージが一杯であれば、システムはアプリを内部のストレージにインストールするでしょう。ユーザーはまた、アプリケーションを2つの場所の間で移動することができます。


"auto"を定義した場合、外部ストレージにアプリケーションがインストールされるかもしれないことを示しますが、インストールの場所を選択することはできません。システムはいくつかの要素に基づいて、アプリケーションのインストール場所を決定します。ユーザはまた、2つの場所の間でアプリケーションを移動することができます。


アプリケーションが外部ストレージにインストールされた時:




  • 外部ストレージがデバイスにマウントされる限り、アプリケーションのパフォーマンスには全く影響がありません。

  • .apkファイルは外部ストレージに保存されますが、全てのプライベートなユーザデータ、データベース、最適化された.dexファイル、展開されたネイティブコードは内部デバイスメモリに保存されます。

  • 格納されたアプリケーションのユニークなコンテナは、オリジナルなデバイスでのみ復号可能なランダムに生成されたキーで暗号化されます。そのため、SDカードにインストールされたアプリケーションは、特定の一つのデバイスでのみ動作します。

  • ユーザーはシステム設定を通して、アプリケーションを内部ストレージに移動させることができます。



警告:ユーザーがコンピュータとファイルを共有するためにUSBマスストレージを有効にした時、あるいは、システム設定でSDカードをアンマウントした時は、外部ストレージはデバイスからアンマウントされ、外部ストレージで実行中のアプリケーションは直ちにkillされます。



下位互換性


外部ストレージにアプリケーションをインストールする機能は、APIレベル8 (Android 2.2) 以上が動作するデバイスでのみ機能します。APIレベル8より前でビルドされた既に存在するアプリケーションは、いつも内部ストレージにインストールされ、外部ストレージに移動することはできません(例えAPIレベル8をサポートするデバイスの上であっても)。しかしながら、8より低いAPIレベルをサポートするようにアプリケーションがデザインされていて、APIレベル8以上のデバイスに対してこの機能をサポートするように選択することは可能で、APIレベルが8未満のデバイスに対しても互換性を維持することができます。


外部ストレージへのインストールを許可し、APIレベル8未満のバージョンとの互換性を維持するために:




  1. "auto" または "preferExternal" を有する android:installLocation 属性を<manifest>要素に含めます。

  2. android:minSdkVersion属性を残したままで(いくつかは "8" より低いでしょう)、アプリケーションのコードがそのレベルと互換性のあるAPIを使用することを確実にして下さい。

  3. アプリケーションをコンパイルするため、ビルドターゲットをAPIレベル8に変更して下さい。これは、古いAndroidライブラリが android:installLocation属性を理解できないためであり、これがあるとコンパイルできないためです。


APIレベル8未満のデバイスにアプリケーションをインストールした時、android:installLocation属性は無視され、アプリケーションは内部ストレージにインストールされます。



警告:古いプラットフォームによってXMLのマークアップが無視されますが、下位互換性を提供する必要が無い場合を除いて、minSdkVersionを8未満にするのであれば、APIレベル8で導入されたAPIを利用しないように気をつけなければなりません。アプリケーションコードの下位互換性のビルドに関する情報は、下位互換性の記事を参照して下さい。






外部ストレージにインストールすべきでないアプリケーション


ユーザーがコンピュータとファイルを共有するためにUSBマスストレージを有効にした時(または、外部ストレージをアンマウントか除去した場合)、外部ストレージにインストールされて動作しているアプリケーションはkillされます。システムはマスストレージが無効化しデバイスに再マウントされるまで、事実上アプリケーションを認識しなくなります。アプリケーションをkillしユーザーが使用出来なくなること以外に、より深刻な方法でいくつかの種類のアプリケーションを壊す事があります。


アプリケーションに予想可能な一貫した振る舞いをさせるため、下記に示す機能を利用している場合は、外部ストレージにアプリケーションをインストールさせるべきではありません。


外部ストレージをアンマウントした時、当たり前に起こりうること:


サービス(Services)

実行中のサービスはkillされ、外部ストレージが再マウントされてもリスタートされません。しかしながら、アプリケーションが外部ストレージにインストールされ、システムが再度利用可能となったアプリケーションが通知する ACTION_EXTERNAL_APPLICATIONS_AVAILABLE ブロードキャストインテントを登録すれば使用可能です。この時、サービスを再起動することができます。


アラームサービス(Alarm Services)

AlarmManagerによって登録されていたアラームはキャンセルされます。外部ストレージが再マウントされた時に手動でアラームを再登録しなければなりません。


入力メソッドエンジン(Input Method Engines)

IMEはデフォルトのIMEに置き換えられます。外部ストレージが再マウントされた時、ユーザーはシステム設定からIMEを再び有効にすることができます。


ライブウォールペーパー(Live Wallpapers)

動作中のLive WallpaperはデフォルトのLive Wallpaperに置き換えられます。外部ストレージが再マウントされた時、ユーザーはLive Wallpaperを再度選択することができます。


ライブフォルダ(Live Folders)

ホーム画面からライブフォルダが削除されます。外部ストレージが再マウントされた時、ユーザーは再びホーム画面にライブフォルダを追加することができます。


ウィジェット(App Widgets)

ホーム画面からウィジェットが削除されます。外部ストレージが再マウントされても、システムがホームアプリをリセットするまで、ユーザーはウィジェットを選択して使用することができません(一般的に、システムが再起動されるまで使用できません)。


アカウントマネージャ(Account Managers)

外部ストレージが再マウントされるまで、AccountManagerによって生成されたアカウントは非表示になります。


同期アダプタ(Sync Adapters)

外部ストレージが再マウントされるまで、AbstractThreadedSyncAdapterと全ての同期機能は利用できません。


デバイス管理(Device Administrators)

DeviceAdminReceiverと管理者機能は無効化されます。外部ストレージが再マウントされた後でも存続するという、デバイス機能について予期できない結果となるでしょう。


"起動完了"を待つブロードキャストレシーバ(Broadcast Receivers listening for "boot completed")

外部ストレージがデバイスにマウントされる前に、システムは ACTION_BOOT_COMPLETED ブロードキャストを配信します。外部ストレージにアプリケーションがインストールされている場合は、このブロードキャストを受信することができません。


アプリケーションが上記リストの機能を利用する場合、外部ストレージにアプリケーションをインストールすべきではありません。デフォルトでは、システムは外部のストレージにアプリケーションをインストールすることを許可しませんので、既に存在するアプリケーションについて心配する必要はありません。しかし、外部ストレージにアプリケーションを決してインストールさせないようにするためには、android:installLocationの値を"internalOnly"として明確に定義する必要があるでしょう。デフォルトの振る舞いに変更はありませんが、これは「内部ストレージにのみインストールさせるべきであり、あえてそのようにしている決定をした」と自身や他の開発者に対して明確に述べることになるでしょう。





外部ストレージにインストールすべきアプリケーション


簡単に言えば、前節の上記リストの機能を利用していなければ外部ストレージにインストールしても安全です。ゲームは非アクティブになった際に特に追加のサービスを提供しないため、大きなゲームは外部ストレージにインストールを許可すべき典型的なアプリケーションです。外部ストレージが使用不可となりゲームプロセスがkillされた時、ストレージが再び利用可能となってユーザーがゲームをリスタートした時に、目に見える効果があるべきではありません(ゲームプロパティが通常のアクティビティのライフサイクル中に状態を保存していると仮定しています)。


アプリケーションがAPKファイルに数メガバイトを要求する場合、ユーザーが内部ストレージのスペースを確保するために、外部ストレージにインストールすることを有効にするかどうか、注意深く検討すべきでしょう。



文責:技術部 瀬戸 直喜(前・日本Androidの会四国支部長/情報セキュリティスペシャリスト)





アプリケーションのインストール領域を考える (Dev Guild - App Install Location 和訳)



f:id:bs-android:20101212110400p:image


マーケットの変更により、アプリのサイズが最大50MBまで拡張されることになりました。今後、端末本体にアプリケーションを保存できない状況が考えられるため、端末本体以外の場所にアプリを保存する方法を知っておきましょう。


原文はこちら


要点




  • アプリケーションをデバイスの外部ストレージにインストールすることができます

  • いくつかの種類のアプリケーションは外部ストレージにインストールを許可すべきではありません

  • システムに密接に関わらない大きいアプリケーションは、外部ストレージにインストールすることが理想です(大抵はゲーム)


概要


APIレベル8(Android 2.2)から、アプリケーションを外部ストレージにインストールすることが可能となりました(例えばSDカード)。これは、マニフェストの属性に android:installLocation を定義することで利用可能となるオプションの機能です。もし、この属性を定義していないなら、内部ストレージにのみインストールすることが可能で、外部ストレージへ移すことはできません。


外部ストレージにアプリケーションをインストールすることをシステムに許可するためには、<manifest>要素内の android:installLocation 属性が"preferExternal"か"auto"のいずれかの値を含めるようにマニフェストファイルを修正します。


例:



<manifest xmlns:android="http://schemas.android.com/apk/res/android"
android:installLocation="preferExternal"
... >





"preferExternal"を定義した場合、外部ストレージにアプリケーションをインストールすることを要求しますが、システムは外部ストレージにアプリケーションをインストールすることを保証しません。もし、外部ストレージが一杯であれば、システムはアプリを内部のストレージにインストールするでしょう。ユーザーはまた、アプリケーションを2つの場所の間で移動することができます。


"auto"を定義した場合、外部ストレージにアプリケーションがインストールされるかもしれないことを示しますが、インストールの場所を選択することはできません。システムはいくつかの要素に基づいて、アプリケーションのインストール場所を決定します。ユーザはまた、2つの場所の間でアプリケーションを移動することができます。


アプリケーションが外部ストレージにインストールされた時:




  • 外部ストレージがデバイスにマウントされる限り、アプリケーションのパフォーマンスには全く影響がありません。

  • .apkファイルは外部ストレージに保存されますが、全てのプライベートなユーザデータ、データベース、最適化された.dexファイル、展開されたネイティブコードは内部デバイスメモリに保存されます。

  • 格納されたアプリケーションのユニークなコンテナは、オリジナルなデバイスでのみ復号可能なランダムに生成されたキーで暗号化されます。そのため、SDカードにインストールされたアプリケーションは、特定の一つのデバイスでのみ動作します。

  • ユーザーはシステム設定を通して、アプリケーションを内部ストレージに移動させることができます。



警告:ユーザーがコンピュータとファイルを共有するためにUSBマスストレージを有効にした時、あるいは、システム設定でSDカードをアンマウントした時は、外部ストレージはデバイスからアンマウントされ、外部ストレージで実行中のアプリケーションは直ちにkillされます。



下位互換性


外部ストレージにアプリケーションをインストールする機能は、APIレベル8 (Android 2.2) 以上が動作するデバイスでのみ機能します。APIレベル8より前でビルドされた既に存在するアプリケーションは、いつも内部ストレージにインストールされ、外部ストレージに移動することはできません(例えAPIレベル8をサポートするデバイスの上であっても)。しかしながら、8より低いAPIレベルをサポートするようにアプリケーションがデザインされていて、APIレベル8以上のデバイスに対してこの機能をサポートするように選択することは可能で、APIレベルが8未満のデバイスに対しても互換性を維持することができます。


外部ストレージへのインストールを許可し、APIレベル8未満のバージョンとの互換性を維持するために:




  1. "auto" または "preferExternal" を有する android:installLocation 属性を<manifest>要素に含めます。

  2. android:minSdkVersion属性を残したままで(いくつかは "8" より低いでしょう)、アプリケーションのコードがそのレベルと互換性のあるAPIを使用することを確実にして下さい。

  3. アプリケーションをコンパイルするため、ビルドターゲットをAPIレベル8に変更して下さい。これは、古いAndroidライブラリが android:installLocation属性を理解できないためであり、これがあるとコンパイルできないためです。


APIレベル8未満のデバイスにアプリケーションをインストールした時、android:installLocation属性は無視され、アプリケーションは内部ストレージにインストールされます。



警告:古いプラットフォームによってXMLのマークアップが無視されますが、下位互換性を提供する必要が無い場合を除いて、minSdkVersionを8未満にするのであれば、APIレベル8で導入されたAPIを利用しないように気をつけなければなりません。アプリケーションコードの下位互換性のビルドに関する情報は、下位互換性の記事を参照して下さい。






外部ストレージにインストールすべきでないアプリケーション


ユーザーがコンピュータとファイルを共有するためにUSBマスストレージを有効にした時(または、外部ストレージをアンマウントか除去した場合)、外部ストレージにインストールされて動作しているアプリケーションはkillされます。システムはマスストレージが無効化しデバイスに再マウントされるまで、事実上アプリケーションを認識しなくなります。アプリケーションをkillしユーザーが使用出来なくなること以外に、より深刻な方法でいくつかの種類のアプリケーションを壊す事があります。


アプリケーションに予想可能な一貫した振る舞いをさせるため、下記に示す機能を利用している場合は、外部ストレージにアプリケーションをインストールさせるべきではありません。


外部ストレージをアンマウントした時、当たり前に起こりうること:


サービス(Services)

実行中のサービスはkillされ、外部ストレージが再マウントされてもリスタートされません。しかしながら、アプリケーションが外部ストレージにインストールされ、システムが再度利用可能となったアプリケーションが通知する ACTION_EXTERNAL_APPLICATIONS_AVAILABLE ブロードキャストインテントを登録すれば使用可能です。この時、サービスを再起動することができます。


アラームサービス(Alarm Services)

AlarmManagerによって登録されていたアラームはキャンセルされます。外部ストレージが再マウントされた時に手動でアラームを再登録しなければなりません。


入力メソッドエンジン(Input Method Engines)

IMEはデフォルトのIMEに置き換えられます。外部ストレージが再マウントされた時、ユーザーはシステム設定からIMEを再び有効にすることができます。


ライブウォールペーパー(Live Wallpapers)

動作中のLive WallpaperはデフォルトのLive Wallpaperに置き換えられます。外部ストレージが再マウントされた時、ユーザーはLive Wallpaperを再度選択することができます。


ライブフォルダ(Live Folders)

ホーム画面からライブフォルダが削除されます。外部ストレージが再マウントされた時、ユーザーは再びホーム画面にライブフォルダを追加することができます。


ウィジェット(App Widgets)

ホーム画面からウィジェットが削除されます。外部ストレージが再マウントされても、システムがホームアプリをリセットするまで、ユーザーはウィジェットを選択して使用することができません(一般的に、システムが再起動されるまで使用できません)。


アカウントマネージャ(Account Managers)

外部ストレージが再マウントされるまで、AccountManagerによって生成されたアカウントは非表示になります。


同期アダプタ(Sync Adapters)

外部ストレージが再マウントされるまで、AbstractThreadedSyncAdapterと全ての同期機能は利用できません。


デバイス管理(Device Administrators)

DeviceAdminReceiverと管理者機能は無効化されます。外部ストレージが再マウントされた後でも存続するという、デバイス機能について予期できない結果となるでしょう。


"起動完了"を待つブロードキャストレシーバ(Broadcast Receivers listening for "boot completed")

外部ストレージがデバイスにマウントされる前に、システムは ACTION_BOOT_COMPLETED ブロードキャストを配信します。外部ストレージにアプリケーションがインストールされている場合は、このブロードキャストを受信することができません。


アプリケーションが上記リストの機能を利用する場合、外部ストレージにアプリケーションをインストールすべきではありません。デフォルトでは、システムは外部のストレージにアプリケーションをインストールすることを許可しませんので、既に存在するアプリケーションについて心配する必要はありません。しかし、外部ストレージにアプリケーションを決してインストールさせないようにするためには、android:installLocationの値を"internalOnly"として明確に定義する必要があるでしょう。デフォルトの振る舞いに変更はありませんが、これは「内部ストレージにのみインストールさせるべきであり、あえてそのようにしている決定をした」と自身や他の開発者に対して明確に述べることになるでしょう。





外部ストレージにインストールすべきアプリケーション


簡単に言えば、前節の上記リストの機能を利用していなければ外部ストレージにインストールしても安全です。ゲームは非アクティブになった際に特に追加のサービスを提供しないため、大きなゲームは外部ストレージにインストールを許可すべき典型的なアプリケーションです。外部ストレージが使用不可となりゲームプロセスがkillされた時、ストレージが再び利用可能となってユーザーがゲームをリスタートした時に、目に見える効果があるべきではありません(ゲームプロパティが通常のアクティビティのライフサイクル中に状態を保存していると仮定しています)。


アプリケーションがAPKファイルに数メガバイトを要求する場合、ユーザーが内部ストレージのスペースを確保するために、外部ストレージにインストールすることを有効にするかどうか、注意深く検討すべきでしょう。



文責:技術部 瀬戸 直喜(前・日本Androidの会四国支部長/情報セキュリティスペシャリスト)





Related Posts Plugin for WordPress, Blogger...