前回は、
第14回となる今回は、
今回使用したソースコードについては、
以降ではチャットアプリケーションの実装をある程度知っている前提で解説しています。そのため、
A/Bテストのシナリオと手順
前回例として示した、
前回立てた
- (パターンA):スタンプ一覧ボタンをデフォルトの色のままにする
(現実装) - (パターンB):スタンプ一覧ボタンを赤色にする
(改善実装)


パターンの良し悪しは、
上記のA/
- A/
Bテストの設定:作成したシナリオに基づいたA/ Bテストを開発者ポータルから設定する。 - クライアントの実装: 開発者ポータルで設定したA/
Bテストの情報をKii Cloudから取得して、 ユーザインターフェースに反映する。その後、 「スタンプ一覧ボタンの表示」 「スタンプの投稿」 が行われるたびに、 Kii Cloudにイベントを送信する。
A/Bテストの設定
開発者ポータルからA/

次に
Title | A/ | |
---|---|---|
Description | A/ | |
Events | A/ | |
Viewイベント | Conversionイベント: | |
「A/ | 「A/ | |
Distribution parameters | A/ | |
Ratio of distribution | Variables | |
各パターンを適用する割合です。今回はパターンA:50%、 | 各パターンで使用する変数です。具体的には |

設定後、
新規作成したA/

ステータスを変更するため、

その後に一覧画面を見ると、

クライアント側の実装
A/
- 「A/
Bテストの設定」 で作成したA/ Bテストの情報を取得する。 - 取得したA/
Bテストの情報を元に、 A/ Bテストのパターンをユーザインターフェースに適用する。 - A/
Bテストの各パターンを評価するために、 特定のタイミングでイベントを送信する。
A/Bテストの情報取得
A/
public class SigninActivity extends FragmentActivity implements OnInitializeListener{
…
private class PostSigninTask extends ChatUserInitializeTask {
…
@Override
protected void onPostExecute(Boolean result) {
SimpleProgressDialogFragment.hide(getSupportFragmentManager());
if (result) {
// サインアップ処理が正常に行われた場合はメイン画面に遷移する
new PreMoveToChatMainTask().execute();
} else {
ToastUtils.showShort(SigninActivity.this, "Unable to sign in");
}
}
}
…
/**
* Chatのメイン画面に遷移する前にA/Bテストの情報取得を行います。
*/
private class PreMoveToChatMainTask extends ABTestInfoFetchTask {
@Override
protected void onPostExecute(KiiExperiment result) {
moveToChatMain(result);
}
}
…
}
取得処理の実態はABTestInfoFetchTaskクラスで定義されています。
public abstract class ABTestInfoFetchTask extends AsyncTask<Void, Void, KiiExperiment> {
@Override
protected KiiExperiment doInBackground(Void... params) {
KiiExperiment experiment = null;
try {
experiment = KiiExperiment.getByID(ApplicationConst.ABTEST_ID);
} catch (Exception e) {
Logger.e("Fetching A/B test info is failed.", e);
}
return experiment;
}
}
KiiExperiment.

パターンの適用
次に取得した情報を元に、
public class ChatActivity extends FragmentActivity implements OnSelectStampListener {
…
private KiiExperiment experiment;
@Override
protected void onCreate(Bundle savedInstanceState) {
…
// A/Bテストのパターン適用
this.experiment = getIntent().getParcelableExtra(ChatActivity.INTENT_EXPERIMENT);
if (experiment != null) {
/*
* 以下の場合、現実装(パターンA)をそのまま使用する。
* - 何らかのエラーが発生した場合
* - stamp_button_color="default"の場合
*/
try {
Variation variation = this.experiment.getAppliedVariation();
Logger.d(String.format("Applied Pattern: %s", variation.getName()));
// ABTEST_STAMP_BUTTON_COLOR=開発者ポータルで指定した変数名(”stamp_button_color”)
String color = variation.getVariableSet().getString(
ApplicationConst.ABTEST_STAMP_BUTTON_COLOR);
if (!ApplicationConst.ABTEST_DEFAULT.equals(color)) {
this.btnSelectEmoticon.setBackgroundColor(Color.parseColor(color));
}
} catch (Exception ignore) {
Logger.d("A/B test pattern can't be applied. Thus, current implementation is used.");
}
} else {
Logger.d("A/B test pattern can't be applied. Thus, current implementation is used.");
}
…
}
}
KiiExperiment#getAppliedVariation()メソッドは、
パターンを取得した後はVariation#getVariableSet()メソッドで、
A/Bテストのイベント送信
開発者ポータルで設定したイベントを、
“stamp_
public class ChatActivity extends FragmentActivity implements OnSelectStampListener,
OnCancelStampDialogListner {
…
@Override
protected void onResume() {
super.onResume();
…
this.experiment = getIntent().getParcelableExtra(INTENT_EXPERIMENT);
// スタンプ一覧ボタンが表示されて且つクリック可能である時、viewedEventを送信する
onViewStampListButton();
…
}
…
@Override
public void onViewStampListButton() {
new SendABTestEventTask(ApplicationConst.ABTEST_STAMP_BUTTON_VIEWED_EVENT).execute();
}
…
}
“stamp_
public class ChatActivity extends FragmentActivity implements OnSelectStampListener,
OnCancelStampDialogListner {
…
@Override
public void onSelectStamp(ChatStamp stamp) {
// スタンプ機能を利用した(スタンプ投稿/新規スタンプ追加)時、postedEventを送信する
new SendABTestEventTask(ApplicationConst.ABTEST_STAMP_POSTED_EVENT).execute();
// 選択されたスタンプをメッセージとしてバックグラウンドでKiiCloudに保存する
new SendMessageTask(ChatMessage.createStampChatMessage(this.kiiGroup, stamp)).execute();
}
…
}
public class SelectStampDialogFragment extends DialogFragment implements
LoaderCallbacks<List<ChatStamp>>, OnItemClickListener {
…
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// 選択されたスタンプをリスナー経由で通知する
ChatStamp stamp = (ChatStamp) parent.getItemAtPosition(position);
OnSelectStampListener selectListener = onSelectStampListener.get();
if (selectListener != null && stamp != null) {
selectListener.onSelectStamp(stamp);
}
…
dismiss();
}
…
}
イベントの送信処理の実体はChatActivity#SendABTestEventTaskクラスで定義しています。
public class ChatActivity extends FragmentActivity implements OnSelectStampListener,
OnCancelStampDialogListner {
…
/**
* A/Bテスト関係のイベントをバックグラウンドで送信します。
*/
private class SendABTestEventTask extends AsyncTask<Void, Void, Boolean> {
private String eventName;
private KiiEvent event;
private SendABTestEventTask(String eventName) {
this.eventName = eventName;
try {
if (experiment != null) {
Variation variation = experiment.getAppliedVariation();
event = variation
.eventForConversion(KiiChatApplication.getContext(), eventName);
}
} catch (Exception ignore) {
// eventがセットされない(null)であることを失敗とみなす。
}
}
@Override
protected Boolean doInBackground(Void... params) {
if (event == null) {
return false;
}
try {
event.push();
return true;
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
…
}
Variation#eventForConversion(Context, String)メソッドにより、
A/Bテストの終了
以上の設定が完了した後は、
A/

この結果を元に、
ただし、
以降は、
まず、

すると、

A/
// 「パターンの適用」で示したコード
public class ChatActivity extends FragmentActivity implements OnSelectStampListener {
…
@Override
protected void onCreate(Bundle savedInstanceState) {
…
this.experiment = getIntent().getParcelableExtra(ChatActivity.INTENT_EXPERIMENT);
if (experiment != null) {
try {
// 常にパターンBに対応するVariationインスタンスを返す
Variation variation = this.experiment.getAppliedVariation();
...
// 常にパターンBのため、スタンプ一覧ボタンの色は必ず変更される
String color = variation.getVariableSet().getString(
ApplicationConst.ABTEST_STAMP_BUTTON_COLOR);
if (!ApplicationConst.ABTEST_DEFAULT.equals(color)) {
this.btnSelectEmoticon.setBackgroundColor(Color.parseColor(color));
}
} catch (Exception ignore) {
Logger.d("A/B test pattern can't be applied. Thus, current implementation is used.");
}
} else {
Logger.d("A/B test pattern can't be applied. Thus, current implementation is used.");
}
…
}
}
まとめ
今回は、