Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

RecommendWidgetを作った話

224 views

Published on

「potatotips #61 (iOS/Android開発Tips共有会)」で発表された資料です。

https://potatotips.connpass.com/event/127296/

Published in: Engineering
  • Be the first to comment

  • Be the first to like this

RecommendWidgetを作った話

  1. 1. Recommend Widgetを作った 話
  2. 2. Masahiro Higuchi / 樋口雅拓 ● グリーグループのリミア株式会社で、LIMIA という住まい領域のメ ディアを作っています。ゲーム会社ですが、最近はメディアに力を 入れています。 ● 機械学習のエンジニアですが、iOS, Android,JSなどもやっている何で も屋です。4歳の娘のパパ。twitter: @mahiguch1 ● https://limia.jp/ ● https://arine.jp/ ● https://aumo.jp/ ● https://www.mine-3m.com/mine/
  3. 3. LIMIAとは? ● メディアサービス ● 記事一覧を表示し、タップす ると記事詳細を閲覧できる。 ● 記事詳細の最下部に別の記事 への回遊導線が付いている
  4. 4. RecommendWidgetとは? ● メディアアプリの記事下に付いている。オ ススメ記事と広告をセットにしたもの。 ● オススメ記事はRecommendEngine、広告 は広告システムから取得している。 ● 3, 6, 9枠目が広告のようにして、指定位置 に差し込む。
  5. 5. 背景と目的 ● 既存のRecommendWidgetを使っていたが、思ったほど成果が上がら なかった。 ● じゃあ、内製化するか! ● 軽い気持ちで始めたら、想定外の要件が発生。 --> 同じ罠にハマる人が減るように、経験を共有します。
  6. 6. RecommendWidget実装1 想像通り、サーバから取得しているコンテンツを表示している。 serverIdeaClient.callGetIdeaRecommendation(object : RequestListener<List<IdeaCompactDto>> { override fun onSuccess(data: List<IdeaCompactDto>?) { iIdeaDetailView?.showRecommendationIdeas(data?.let { convertFromDtoList(data, ContentViewModel.ListType.RECOMMENDATION) }) } } override fun showRecommendationIdeas(list: List<IContentView>?) { ideaDetailRecyclerAdapter?.addItems(IdeaDetailRecyclerAdapter.LayoutType.IDEA_RECOMMENDATION, list) } fun addItems(type: LayoutType, list: List<IContentView>?) { contentLst.addAll(list) }
  7. 7. RecommendWidget実装2 指定枠(3, 6, 9枠目)には、コンテンツとは別に広告を広告システムか ら取得して表示している。 private fun loadAdvertisement(order: SspViewModel, h: RecyclerView.ViewHolder, position: Int) { adsViewManager.houseAdManager.callAdvertisement(object : LimiaHouseAdViewManager.RequestListener{ override fun onSuccess(houseAdDto: HouseAdDto) { model.houseAdDto = houseAdDto holder.intoView(context, model, object: HouseAdClickListener { 広告取得部分の詳細については、potatotips#60のLT資料で解説していますhttps://speakerdeck.com/mahiguch/firestorewoshi-
  8. 8. オススメ記事の生成方法 オススメ記事の一覧は、サーバ側で生成して いる。 ● ユーザの行動履歴を元に、似たような行 動をしているユーザが見ていて自分が見 ていないもの。 ● 同じようなトピックについて書いてある 記事。 これらを混ぜて応答している。
  9. 9. リリースしてほっとしていたら。。。 ● 二つのアルゴリズムを混ぜて表示したら、どちらがどれだけ効果が あるのかよくわからない。 → CTRを計測することで、この課題を解決しよう!
  10. 10. CTRとは? CTR(Click Through Rate) = タップ数 / 表示回数 【表示回数の定義】 ・広告が視聴可能なスクリーンに表示されていること ・広告の一定面積以上が見える状態にあること ・広告が一定の時間以上見える状態であること ・広告が人間によって視聴されていること つまり、一覧表示のChild/Cellが画面上に表示したうち、タップされた割合。 タップ数は簡単に取れるが、スクリーンに表示された回数はどう取れば良いのか?
  11. 11. スクリーンに表示されたログ送信(iOS) スクリーンに表示された回数は、表示される度にログを送信すれば実現 できる。iOSの場合、cellのwillDisplayでログ送信すれば実現可能。 override func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { ログ送信 } https://developer.apple.com/documentation/uikit/uicollectionviewdelegate/1618087-collectionview?language=objc
  12. 12. スクリーンに表示されたログ送信(Android)1 AndroidではRecyclerView.layoutManagerのpositionを取得する ことで実現した。 (obtainRecyclerView()?.layoutManager as? LinearLayoutManager)?.let { val first = it.findFirstVisibleItemPosition() val last = it.findLastVisibleItemPosition() if (first >= 0 && last >= 0) { for (position in first..last) { ログ送信 https://developer.android.com/reference/android/support/v7/widget/LinearLayoutManager.html#findFirstVisibleItemPositio n()
  13. 13. スクリーンに表示されたログ送信(Android)2 いきなり走らせると更新される度にログが再送されてしまうので、viewTreeObserverの Listnerに仕込むことで描画してからログ送信されるようにした。 obtainRecyclerView()?.viewTreeObserver?.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener { override fun onGlobalLayout() { logImpressionForCurrentVisibleItems { obtainRecyclerView()?.viewTreeObserver?.removeOnGlobalLayoutListener(this) https://developer.android.com/reference/android/view/ViewTreeObserver
  14. 14. まとめ ● RecommendWidgetは簡単に作れる。 ● 計測は面倒なので、良い方法があれば教えて欲しい。 ● RecommendEngineの中身は、どこかで話したい。 ご静聴、ありがとうございました!

×