2014.12.15
「Mixpanel for Android」を運用したことない僕が紹介する(Advent Calendar 15日目)
ぷよらーの高畑です。10日ぶりの記事になります。
もう少しで正月休みですね。特に予定はないのですがお餅をいっぱい食べられるので楽しみです。
ちなみに僕はきな粉餅が一番好きですね。二番目にぜんざいが好きです。
今回はAnalytics関連で最近話題のMixpanelについて、実運用したことがないくせに紹介したいと思います。
最初にお伝えしたいのはこの記事はMixpanelを使ったAndroidアプリのアナリティクスのベストプラクティスを示すものではなく
本当に機能だけを簡単にお伝えするものですのでご了承ください。
導入手順
gradleの設定
gradleのdependenciesに以下の2つを追加します
1 2 3 4 |
dependencies { compile "com.mixpanel.android:mixpanel-android:4.4.1@aar" compile "com.google.android.gms:play-services:6.5.+" } |
manifestファイルの設定
manifestファイルのpermissionに以下を追加します。
1 2 3 |
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.BLUETOOTH" /> |
必ず必要なのは「android.permission.INTERNET」で他2つはオプションです。
「android.permission.ACCESS_NETWORK_STATE」はデータをスマートに送るためにあった方がよいとのことです。
「android.permission.BLUETOOTH」についてはトラックした端末がbluetoothを使用可能かどうかの情報を取得するために必要みたいです。個人的にこれはなくても良いかなぁと思います。permissionは少ない方がユーザーにとって良いです。
Google PlayのSEO的にもpermissionが少ない方が良いという噂を聞いたことがあるのですが本当でしょうか?
これでMixpanelのSDKが使えます。非常に簡単で良いですね。
次に実際にトラッキングしてみます。
データトラック
とりあえずトラックしてみる
とりあえず以下のコードだけでトラックできます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public class FirstActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_first); MixpanelAPI.getInstance(this, "TOKEN").track("最初の画面",new JSONObject()); } @Override protected void onDestroy() { super.onDestroy(); MixpanelAPI.getInstance(this, "TOKEN").flush(); } } |
MixpanelAPIのtrackを実行しただけではすぐにサーバー側に反映されません。SDKが定期的にサーバーにデータを送信しているようですが、アプリが終了する際にトラッキング情報が送信されないままになってしまう可能性があります。それを避けるために、主要なActivityが終了するときにflushを呼び出して、明示的にトラッキング情報を送信します。
管理画面にトラッキングデータが入っているのがわかります。
複数Activityをトラッキング
先ほどのデータトラックから一歩進んで複数のActivityにまたがってデータをトラックしてみます。
全部のActivityのonCreateに
1 |
MixpanelAPI.getInstance(this, "TOKEN") |
を書くのはさすがにめんどくさいのでActivityのBaseクラスを用意しておきます。
BaseActivityを継承したActivityのonCreateでトラッキングされます。
1 2 3 4 5 6 7 8 9 10 11 |
public class BaseActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getMixPanelAPI().track(MixPanelMapping.activityNameMap.get(getClass().getName()),new JSONObject()); } public MixpanelAPI getMixPanelAPI() { return MixpanelAPI.getInstance(this, "TOKEN"); } } |
BaseActivityを継承した3つのActivityを用意します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
public class FirstActivity extends BaseActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_my); findViewById(R.id.button).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(FirstActivity.this,SecondActivity.class); startActivity(intent); } }); } @Override protected void onDestroy() { super.onDestroy(); getMixPanelAPI().flush(); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public class SecondActivity extends BaseActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_second); findViewById(R.id.button).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(SecondActivity.this,LastActivity.class); startActivity(intent); } }); } } |
1 2 3 4 5 6 7 |
public class LastActivity extends BaseActivity{ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_last); } } |
Activityのクラス名とトラックするイベント名をマッピングするだけのクラスです。
1 2 3 4 5 6 7 8 9 |
public class MixPanelMapping { public static HashMap<String,String> activityNameMap = new HashMap<String, String>(){ { put(FirstActivity.class.getName(),"最初の画面"); put(SecondActivity.class.getName(),"次の画面"); put(LastActivity.class.getName(),"最後の画面"); } }; } |
これで各ActivityのonCreateが呼ばれたと同時にデータがトラッキングされます。
アプリを起動してFirstActivity->SecondAcitivy->LastActivityとただボタンを押して画面遷移していきます。
これだけで各画面でトラックされて管理画面に反映されているのがわかります。
今回はonCreateで画面表示のtrackをしましたが、clickListener内でtrackを呼べばクリックトラックが出来ます。
またtrackの引数にJSONObjectを渡せば自由にプロパティを追加することも可能です。
後々データの絞り込みなどで必要なら設定しましょう。
1 2 3 4 |
JSONObject props = new JSONObject(); props.put("Gender", "Female"); props.put("Plan", "Premium"); getMixPanelAPI().track("イベント名", props); |
Funnelでコンバージョンを見る
恐らくMixpanelの目玉機能でしょう。多くの人はこのFunnelが目当てでMixpanelを導入するのではないでしょうか?
Google Analyticsで言う所の「行動フロー」に近いですが、それよりも高機能で見やすいです。
あるイベントからあるイベントまでのコンバージョンを見やすく表示してくれます。
先ほどのデータを元に検証してみたいと思います。
Funnelを作成する
Funnelの設定画面で
Step1に「最初の画面」Step2に「次の画面」Step3に「最後の画面」を設定します。
これだけで作成完了です。
Funnelの結果を見る
結果以下のようなコンバージョンになります。
Step3のコンバージョンが50%になっていますが
これは別の端末でStep2の画面まで進め、あえてStep3の画面は表示せずにアプリを終了させたので、このような結果になっています。
データはユニークユーザーで管理されているので、同じユーザーが何度もstep1とstep2に行き来しても
step2にコンバージョンが高くなり、step3のコンバージョンが低くなるということはありません。
これを見ればどこでユーザーが脱落しているか一目瞭然ですね。
さらに設定されているプロパティで絞り込むこともできるので
iPhoneとAndoridでコンバージョンを比較できますし
特定の端末やOSのバージョンでも比較することができます。
これを見ると実際に運用に乗せてみたい欲求が湧いて来ます。
ここまでがMixpanelの主要な機能ですね。
ここから先は普通のトラッキングとは別の機能についていくつか紹介したいと思います。
Surveys機能
ユーザーに単一選択式かフリー入力のアンケートを送ることが出来る機能です。
Mixpanelの管理画面から簡単に登録でき、どのような回答結果になったのかの統計も取れるため面白いです。
ユーザーのprofileのセットとsurveyを受け取り可能状態にする
surveyを送る対象を絞り込むためのprofileをsetしなければなりません。
何も設定しなくても管理画面上はデフォルトで色々なパラメーターが登録されるのですが
なぜか最低1userは独自のprofileをsetしておかないとsurveyを作ることが出来ないみたいです。
1 2 3 4 5 6 7 8 9 10 11 |
public class FirstActivity extends BaseActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_first); //user_id的なものがあればそれを設定すると良い getMixPanelAPI().getPeople().identify("123456"); getMixPanelAPI().getPeople().set("test_profile","test"); getMixPanelAPI().getPeople().showSurveyIfAvailable(this); } } |
これを実行するとmixpanelにユーザーのprofileが追加されます。
surveyを管理画面から登録する
左のメニュー「Surveys」を選択すると
プレビューを見ながらアンケートを編集出来ます。
また「Add another question」から1つのsurveyに対して複数の質問を設定することが出来ます。
surveyを作り終えたら、送信対象の範囲を選択します。
先ほどアプリ側から登録した「”test_profile”,”test”」に対してこのsurveyを送ります。
このsurveyを送るタイミングや止めるタイミングを設定できます。
とりあえず今回は即配信します。
配信したらアプリを起動してみましょう。
若干のタイムラグがある気がします。稀にすぐには表示されない場合もありますが、基本的にはすぐに表示されるようです。
アプリを起動すると以下のような感じになります。
回答が集まると下図のようにanalyticsが見れます
アプリ起動時のダイアログのメッセージが英語なので
実際に運用していく上で使うことがあるのか微妙な機能ではありますが
ちょっと使ってみたい気になりますね。
Notifications(In-app message)機能
Mixpanelが持っているnotification機能の1つです。
ちょっと面白いと思ったので紹介します。
ユーザーのprofileのセットとnotificationを受け取り可能状態にする
1 2 3 4 5 6 7 8 9 10 |
public class FirstActivity extends BaseActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_first); getMixPanelAPI().getPeople().identify("123456"); getMixPanelAPI().getPeople().set("test_profile","test"); getMixPanelAPI().getPeople().showNotificationIfAvailable(this); } } |
surveyの時とほぼ同じです。
ユーザーのprofileをsetするのは共通で
showNotificationIfAvailableを追加で必要になるだけです。
Notificationsを管理画面から登録する
「Notification」のメニューから「In-app message」を選択します
プレビューを操作して、notificationを作ります。
設定できる項目は
- 画像
- タイトル
- サブタイトル
- ボタンのタイトル
です
notificationを飛ばすだけでなく複数のnotificationを作ってA/Bテストを行うことができます。
さらにボタンを押した時にブラウザで任意のURLへ飛ばすことができます。
最終的に送信総数やボタンを押された回数などが管理画面上からわかるので
どのクリエイティブのコンバージョンが良かったなどの比較が可能です。
notificationを作成したら次はsurveyと同様に送信対象を選定します。
先ほどと同じように「”test_profile”,”test”」のユーザーに対して送信します。
アプリを起動します
こちらも多少のタイムラグがあるかもしれません。
するとこんな感じでnotificationが表示されます。面白いですね。
以上がIn-app notification機能でした。
実際に動作確認をしてはいませんが
普通のpush通知を送ることも設定上可能になっています。
ユーザーのEmailが登録されていれば
Emailでの通知やSMSの通知にも対応しているみたいです。
最後に
デバッグログを見ると
1 |
D/MixpanelAPI - App Links (OPTIONAL)﹕ Please install the Bolts library >= 1.1.2 to track App Links: bolts.AppLinks |
こんな情報が出ますが、これはデバッグで出しているだけで、エラーではないので無視しても構いません。
と公式のリファレンスにも記述があります。
どうしても気になる場合は別途Bolts Frameworkを入れて上げましょう。
以上ここまでがMixpanelの基本機能でした。
Google Analyticsよりも良い点は多くありそうです。軽く使ってみた感想としては
- 導入が簡単
- トラッキングデータが見やすい(Funnelが良い感じ)
- ユーザー単位で動きを追いやすい
という感じでしょうか。
欠点はFreeプランだと25,000リクエストまでなので、あっというまに上限に達してしまいます。
開発中に1ヶ月近く入れていたのですが、それだけで上限に達してしまいそうになりました……
結果としてGoogle Analyticsを使ってしまいます。
Google AnalyticsにしろMixpanelにしろトラッキングツール全般に言えることですが、ツールを導入するだけは改善に繋がりません。闇雲にデータをトラッキングするのではなく
どういうデータをトラッキングすれば後の改善に役立つかを設計しながらトラッキングすることが重要だと思います。
iOSに浮気を始めたAndroidエンジニア? Androidはほとんど書いてない…