主頁 > 移動端開發 > 工程實踐 | 在 Flutter 中實作一個精準的滑動埋點

工程實踐 | 在 Flutter 中實作一個精準的滑動埋點

2021-10-07 08:40:25 移動端開發

文/ Vadaski,CFUG 社區成員,滴滴國際化研發工程師

前言

今天的這篇文章要介紹的內容,是我們經常會用到的一個場景:埋點,為了對行為特征的資料進行量化分析、優化產品, 我們常常需要在特定的時機上報資料埋點, 而曝光埋點則是其中的一個高頻使用場景,

🥲 滑動埋點的痛

在 Flutter 中,我們通常會在 initState 這個生命周期上報曝光埋點,這在一般的使用場景下當然是沒有問題的,然而在滑動場景下這個解決方案就不 work 了,我們來看看,

aba5a82d53d9e75101eebfad5470d0ee.gif

很明顯,我們把本來沒有展示的 widget 也給列印出來了,如果這樣做,埋點上報不準確,將會給業務帶來不可恢復的損失,

🤯 ScrollView 加載機制

為什么會出現這種情況呢?在查閱了原始碼之后,我們發現所有的 ScrollView 都是在一個可視區域 Viewport 當中進行繪制,為了讓滑動更加流暢,通常 ScrollView 都會在可視區域之外加載一部分,也就是 cacheExtent,落入該快取區域的專案即使在螢屏上尚不可見,也會進行布局,這時候 initState 就被執行了,ListView 作為 ScrollView 的子類同樣也使用了這個機制,

那么很自然我們能夠想到一個最簡單的解決方案:把預加載機制給禁用掉不就可以了嘛:

ListView.builder(
  cacheExtent: 0,
  itemCount: 40,
  itemBuilder: (context, index) {
    return Item(index: index);
  },
),
25b81a636068cb10baedf43434df1128.gif

好了,本文到此結束,你學會了嗎,😏

🤔 新的問題

開個玩笑,相信大家很容易就能夠聯想到,這樣做大概率會產生性能問題,在我們真實業務中,會考慮到支持的最差的設備性能,以及業務的復雜性,肯定不是這樣簡單的取消掉預加載就能夠解決的,

在做測驗的時候,會發現如果去掉快取機制,平均幀率會下降 5-10 幀左右, 還是在比較好的一加手機上的測驗結果,這當然是不能接受的,(更何況本身在 1.x 版本 的 Flutter 下 ListView 性能就有一些問題)

所以我們想要的是一套 Flutter 上的高準確率的用戶行為埋點方案,而且不要影響到 ScrollView 的性能,

🤨 破局

想清楚了需求,就有了一半的思路,在我們查閱了業界現有的資料后,發現閑魚技術已經分享了一個比較好的解題思路:揭秘!一個高準確率的 Flutter 埋點框架如何設計[1],奈何這個方案也沒有開源的計劃,那就只有自己來寫一個吧,這個問題應該如何解呢?

在前面我們提到過,每一個 ScrollView 都會有一個自己的 Viewport 來決定自己的繪制范圍,這個 Viewport 最后會生成一個 RenderObjectElement,這樣就可以單獨渲染這個區域,把影響回傳控制到最小,那么問題現在就變成了我們想要計算一個 Item 什么時候進入到 Viewport 中,

一個復雜的問題需要把它抽象成更簡單的問題然后逐步求解,我們不妨先把 Item 看成一個點,看看要計算一個 Item 是否在 Viewport 內需要哪些資訊,

很容易能夠想到和滑動的偏移量 (Scroll Offset),以及 Viewport 在滑動方向上的長度 (Viewport Length), 還有 item 自身的資訊,也就是當前 item 距離滑動起始點的距離 (Exposure Offset) 相關,

9c93eeda9ceb62077d0c9c906b18ddc7.png

想象一下滑動的樣子,一個 Item 從 ViewPort 的右邊滑入,進入 ViewPort,被用戶看到,然后再從 ViewPort 的左邊劃出,這一系列程序,我們可以把這個程序抽象為下面的四個狀態:

  • Item 在 ViewPort 右側不可視范圍內:(Scroll Offset + ViewPort Length < Exposure Offset)

  • Item 進入 ViewPort 右側:(Scroll Offset + ViewPort Length > Exposure Offset)

  • Item 在 ViewPort 中

  • Item 在 ViewPort 左側不可視范圍內:(Exposure Offset < Scroll Offset)

對于從左邊劃入右邊則是這幾個狀態:

  • Item 在 ViewPort 左側不可視范圍內:(Exposure Offset < Scroll Offset)

  • Item 進入 ViewPort 左側:(Exposure Offset > Scroll Offset)

  • Item 在 ViewPort 中

  • Item 在 ViewPort 右側不可視范圍內:(Scroll Offset + ViewPort Length < Exposure Offset)

通過觀察可以發現,Item 從左邊劃入和從右邊劃入它的判斷時機是不一樣的,所以我們需要區分兩種滑動情況,

下面我們把 Item 自身的寬度 (Item Width)也帶上,再使用上面得出的結論來進行計算,

我們這里暫時認為 Item 完全劃入 ViewPort 才算一次曝光,

deaf9f46f931668a9c3111e50a528c83.png
  • Item 在 ViewPort 右側不可視范圍內:(Scroll Offset + ViewPort Length < Exposure Offset)

  • Item 進入 ViewPort 右側:(Scroll Offset + ViewPort Length > Exposure Offset)

  • Item 在 ViewPort 中

  • Item 在 ViewPort 左側不可視范圍內:(Exposure Offset + Item Width < Scroll Offset)

對于從左邊劃入右邊則是這幾個狀態:

  • Item 在 ViewPort 左側不可視范圍內:(Exposure Offset + Item Width < Scroll Offset)

  • Item 進入 ViewPort 左側:(Exposure Offset + Item Width > Scroll Offset)

  • Item 在 ViewPort 中

  • Item 在 ViewPort 右側不可視范圍內:(Scroll Offset + ViewPort Length < Exposure Offset)

🧩 如何獲取這些資訊

知道了解法之后,接下來就只需要尋找這些拼圖的碎片就行了,

Item 大小資訊

這塊比較簡單,我們都知道可以通過 Widget 的 BuildContext 拿到它所對應的 RenderObject,通過它去拿當前 Item 的長度和寬度,

// 這里命名為曝光坑位的大小,對于不同滑動方向,我們需要用不同方向的長度,
final exposurePitSize = (context.findRenderObject() as RenderBox).size;

這里的 context 是我們想要判斷是否曝光的 Item 的 context,如果你對這個概念還不太清楚,可以去看看這篇 深入理解 BuildContext[2]

注意:不是每個 Widget 都會創建一個 RenderObject,只有 RenderObjectWidget 才會創建 RenderObjectListView 會默認幫每一個 Item 添加一個 RepaintBoundary,這個 Widget 是一個 SingleChildRenderObjectWidget,所以每一個 Item 其實都會有一個它所對應的 RenderObject

// SliverChildListDelegate 的 build 方法
if (addRepaintBoundaries) child = RepaintBoundary(child: child);

ViewPort 大小資訊

我們在進行曝光判斷的時候,肯定是在每一個 Item 中進行的,而 ViewPort 則是存在于 ListView 這一層級,所以我們需要從祖先的節點中找到它,幸運的是,Flutter 已經為我們提供了這個方法,

static RenderAbstractViewport? of(RenderObject? object) {
  while (object != null) {
    if (object is RenderAbstractViewport)
      return object;
    object = object.parent as RenderObject?;
  }
  return null;
}

我們剛剛已經拿到了 Item 對應的渲染物件,RenderAbstractViewport.of 可以通過這個 RenderObject 向上尋找祖先節點,直到發現離它最近一個節點的 RenderAbstractViewport 就能拿到我們想要的 ViewPort 資訊了,

Size? getViewPortSize(BuildContext context) {
  final RenderObject? box = context.findRenderObject();
  final RenderAbstractViewport? viewport = RenderAbstractViewport.of(box);
  assert(() {
    if (viewport != null) {
      debugPrint('Please make sure you have a `ScrollView` in ancestor');
      return false;
    }
    return true;
  });
  final Size? size = viewport?.paintBounds.size;
  return size;
}

Item 相對 ViewPort 的滑動起始點的距離

RenderAbstractViewport 的另一個方法 getOffsetToReveal,中,我們可以獲得當前的 RenderObject 相對于這個 ViewPort 滑動的起始位置,

double getExposureOffset(BuildContext context) {
  final RenderObject? box = context.findRenderObject();
  final RenderAbstractViewport? viewport = RenderAbstractViewport.of(box);

  if (viewport == null || box == null || !box.attached) {
    return 0.0;
  }

  // box 為當前 Item 的 RenderObject
  // alignment 為 0 的時候獲得距離起點的相對偏移量
  // 為 1 的時候獲得距離終點的相對偏移量,
  final RevealedOffset offsetRevealToTop =
      viewport.getOffsetToReveal(box, 0.0, rect: Rect.zero);
  return offsetRevealToTop.offset;
}

滑動距離

要獲得滑動距離通常有兩種方式:

  • 通過 ScrollController 獲得,

  • 利用 Scrollable Widget 的 Notification 機制,

每次撰寫代碼的時候都必須得寫 ScrollController 看上去有些麻煩,所以我們選擇了Notification 這種方式,(它也更加通用)

Scroll Notification

Scrollable Widget 將會向其其祖先通知有關滾動變化資訊,而這些資訊能夠使用 NotificationListener 來捕獲到,目前有下面幾種 Notification:

  • ScrollStartNotification:滾動開始時發起 Notification

  • ScrollUpdateNotification:滾動進行時不斷發起 Notification,(頻率很高)

  • ScrollEndNotification:滾動結束時發起 Notification

  • UserScrollNotification:當用戶改變滾動方向時,發起通知,(通常在不同方向的 ScrollView 互相嵌套時會出現)

我們這里使用 NotificationListener 來獲取 滑動的資訊,

Widget buildNotificationWidget(BuildContext context, Widget child) {
  return NotificationListener<ScrollNotification>(
    onNotification: (scrollNotification) {
      // 這里就能獲取到滾動資訊
    },
    child: ScrollView,
  );
}

解決資訊共享問題

看到這里,似乎我們要的拼圖都湊齊了,但是總感覺哪里不對勁?🧐

如果你敏銳的話,想必已經發現我們現在這樣的設計根本沒法在一個地方拿到全部資訊,

c6f260c0eacb7f91d42e43360a9ee0b7.png
資料獲取位置不一致

Scroll Notification 僅會向祖先節點發起 Notification 通知,也就是說,我們在 Item 層級是拿不到的!

如果我們想要在 Item 中進行埋點曝光判定,就必須要獲取到更高的祖先節點中的 scrollNotification

當然解法肯定有很多,共享狀態的方法在狀態管理中是一個常見的 Case,但是為了滑動埋點曝光就引入一個狀態管理庫似乎有些得不償失,所以還不如使用 Flutter 最原始的 Inherit 機制來實作資料的共享,

什么是 Inherit 機制

要理解 Inherit 機制,首先你需要了解 Flutter 的三棵樹, 這個網上的解釋文章已經有很多了,我就不再贅述, 感興趣的可以看看 迷鹿[3]的這篇 Widget、Element、Render 是如何形成樹結構?[4]

簡單來說,Inherit 機制是一種能夠在 Flutter 中自頂向下共享資料的方式,我們知道 Flutter 是通過樹形結構來構建視圖的,而其中的 InheritedWidget 則是能夠讓它的資料能夠被所有子節點中的 Widget 訪問到,

它的原理也是很簡單,每個 Element 都持有了一個叫做 Map<Type, InheritedElement>? _inheritedWidgetsMap 的參考,當我們的 Element 在掛載到 Element Tree 的時候 (執行 mount 操作的時候會呼叫 _updateInheritance),將會把 parent 中保存的 _InheritedWidget 參考自己也給留一份,

void _updateInheritance() {
  assert(_lifecycleState == _ElementLifecycle.active);
  _inheritedWidgets = _parent?._inheritedWidgets;
}

InheritedWidget 創建的 Element 則會在 mount 的時候把自己給塞到這個 map 當中,這樣就完成了自頂向下的資料共享了,

@override
void _updateInheritance() {
  assert(_lifecycleState == _ElementLifecycle.active);
  final Map<Type, InheritedElement>? incomingWidgets = _parent?._inheritedWidgets;
  if (incomingWidgets != null)
    _inheritedWidgets = HashMap<Type, InheritedElement>.from(incomingWidgets);
  else
    _inheritedWidgets = HashMap<Type, InheritedElement>();
  _inheritedWidgets![widget.runtimeType] = this;
}

基于此,我們就可以完成對于滑動埋點曝光的計算了,可喜可賀,

拿來吧你

像我們這樣有經驗的開發者,看到這樣好的文章,第一時間那一定是想要 自己實踐一下,

直接拿來吧你

所以為了各位寶貴的 (滑水/嘮嗑/帶娃/...) 時間,這款滑動埋點方案已經登陸了 Pub 倉庫[5],各位可以放心食用了,

目前已經支持的有:

  • 懶曝光模式:僅當滾動結束時再曝光;

  • 曝光比例:可以控制 Item 展現多大的范圍算是一次曝光;

  • 追蹤 Item 何時離開可視范圍:可以獲取到曝光時長;

  • 支持所有 ScrollView:包括 ListViewGridViewCustomScrollView 等等,

這個專案我會一直維護下去 (畢竟自己也要用), 如果你想了解該專案的最新進展, 可以關注該專案的 GitHub[6], 或者有需要增加的功能需求,也歡迎通過郵箱與我聯系:

  • Pub 地址
    https://pub.flutter-io.cn/packages/flutter_exposure

  • Github 地址
    https://github.com/Vadaski/flutter_exposure

  • 郵箱
    xinlei966@gmail.com

寫在最后

這個解決方案其實是在去年公司里就用到了,一直沒有來得及開源,在這里也感謝 閑魚技術[7] 提供的寶貴思路, 最近湊了一些零零碎碎的時間把它給完成了,把趁著國慶第一天寫完了這篇文章, 希望大家能通過我的分享有一點點識訓~

我是鑫磊,和你一起快樂學習 Flutter 的工程師,大家國慶快樂,我們之后再見 👋

文內鏈接

[1]

揭秘!一個高準確率的 Flutter 埋點框架如何設計: https://juejin.cn/post/6844903864479514631#comment

[2]

深入理解 BuildContext: https://juejin.cn/post/6844903777565147150

[3]

迷鹿: https://juejin.cn/user/4309694831660711

[4]

Widget、Element、Render 是如何形成樹結構?: https://juejin.cn/post/6921493845330886670

[5]

Pub 倉庫: https://pub.flutter-io.cn/packages/flutter_exposure

[6]

GitHub: https://github.com/Vadaski/flutter_exposure

[7]

閑魚技術: https://juejin.cn/post/6955304605190357005

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/306003.html

標籤:其他

上一篇:iOS之深度剖析UIScrollView的實作原理與阻尼影片

下一篇:OKhttp3工具類

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【從零開始擼一個App】Dagger2

    Dagger2是一個IOC框架,一般用于Android平臺,第一次接觸的朋友,一定會被搞得暈頭轉向。它延續了Java平臺Spring框架代碼碎片化,注解滿天飛的傳統。嘗試將各處代碼片段串聯起來,理清思緒,真不是件容易的事。更不用說還有各版本細微的差別。 與Spring不同的是,Spring是通過反射 ......

    uj5u.com 2020-09-10 06:57:59 more
  • Flutter Weekly Issue 66

    新聞 Flutter 季度調研結果分享 教程 Flutter+FaaS一體化任務編排的思考與設計 詳解Dart中如何通過注解生成代碼 GitHub 用對了嗎?Flutter 團隊分享如何管理大型開源專案 插件 flutter-bubble-tab-indicator A Flutter librar ......

    uj5u.com 2020-09-10 06:58:52 more
  • Proguard 常用規則

    介紹 Proguard 入口,如何查看輸出,如何使用 keep 設定入口以及使用實體,如何配置壓縮,混淆,校驗等規則。

    ......

    uj5u.com 2020-09-10 06:59:00 more
  • Android 開發技術周報 Issue#292

    新聞 Android即將獲得類AirDrop功能:可向附近設備快速分享檔案 谷歌為安卓檔案管理應用引入可安全隱藏資料的Safe Folder功能 Android TV新主界面將顯示電影、電視節目和應用推薦內容 泄露的Android檔案暗示了傳說中的谷歌Pixel 5a與折疊屏新機 谷歌發布Andro ......

    uj5u.com 2020-09-10 07:00:37 more
  • AutoFitTextureView Error inflating class

    報錯: Binary XML file line #0: Binary XML file line #0: Error inflating class xxx.AutoFitTextureView 解決: <com.example.testy2.AutoFitTextureView android: ......

    uj5u.com 2020-09-10 07:00:41 more
  • 根據Uri,Cursor沒有獲取到對應的屬性

    Android: 背景:呼叫攝像頭,拍攝視頻,指定保存的地址,但是回傳的Cursor檔案,只有名稱和大小的屬性,沒有其他諸如時長,連ID屬性都沒有 使用 cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATIO ......

    uj5u.com 2020-09-10 07:00:44 more
  • Android連載29-持久化技術

    一、持久化技術 我們平時所使用的APP產生的資料,在記憶體中都是瞬時的,會隨著斷電、關機等丟失資料,因此android系統采用了持久化技術,用于存盤這些“瞬時”資料 持久化技術包括:檔案存盤、SharedPreference存盤以及資料庫存盤,還有更復雜的SD卡記憶體儲。 二、檔案存盤 最基本存盤方式, ......

    uj5u.com 2020-09-10 07:00:47 more
  • Android Camera2Video整合到自己專案里

    背景: Android專案里呼叫攝像頭拍攝視頻,原本使用的 MediaStore.ACTION_VIDEO_CAPTURE, 后來因專案需要,改成了camera2 1.Camera2Video 官方demo有點問題,下載后,不能直接整合到專案 問題1.多次拍攝視頻崩潰 問題2.雙擊record按鈕, ......

    uj5u.com 2020-09-10 07:00:50 more
  • Android 開發技術周報 Issue#293

    新聞 谷歌為Android TV開發者提供多種新功能 Android 11將自動填表功能整合到鍵盤輸入建議中 谷歌宣布Android Auto即將支持更多的導航和數字停車應用 谷歌Pixel 5只有XL版本 搭載驍龍765G且將比Pixel 4更便宜 [圖]Wear OS將迎來重磅更新:應用啟動時間 ......

    uj5u.com 2020-09-10 07:01:38 more
  • 海豚星空掃碼投屏 Android 接收端 SDK 集成 六步驟

    掃碼投屏,開放網路,獨占設備,不需要額外下載軟體,微信掃碼,發現設備。支持標準DLNA協議,支持倍速播放。視頻,音頻,圖片投屏。好點意思。還支持自定義基于 DLNA 擴展的操作動作。好像要收費,沒體驗。 這里簡單記錄一下集成程序。 一 跟目錄的build.gradle添加私有mevan倉庫 mave ......

    uj5u.com 2020-09-10 07:01:43 more
最新发布
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:40:31 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:40:11 more
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:39:36 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:39:13 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:16:23 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:16:15 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:15:46 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:14:53 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:14:08 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:08:34 more