本文由阿里閑魚技術(shù)團(tuán)隊(duì)逸昂分享,原題“消息鏈路優(yōu)化之弱感知鏈路優(yōu)化”,有修訂和改動(dòng),感謝作者的分享。
為元氏等地區(qū)用戶提供了全套網(wǎng)頁(yè)設(shè)計(jì)制作服務(wù),及元氏網(wǎng)站建設(shè)行業(yè)解決方案。主營(yíng)業(yè)務(wù)為做網(wǎng)站、網(wǎng)站建設(shè)、元氏網(wǎng)站設(shè)計(jì),以傳統(tǒng)方式定制建設(shè)網(wǎng)站,并提供域名空間備案等一條龍服務(wù),秉承以專業(yè)、用心的態(tài)度為用戶提供真誠(chéng)的服務(wù)。我們深信只要達(dá)到每一位用戶的要求,就會(huì)得到認(rèn)可,從而選擇與我們長(zhǎng)期合作。這樣,我們也可以走得更遠(yuǎn)!
閑魚的IM消息系統(tǒng)作為買家與賣家的溝通工具,增進(jìn)理解、促進(jìn)信任,對(duì)閑魚的商品成交有重要的價(jià)值,是提升用戶體驗(yàn)最關(guān)鍵的環(huán)節(jié)。
然而,隨著業(yè)務(wù)體量的快速增長(zhǎng),當(dāng)前這套消息系統(tǒng)正面臨著諸多急待解決的問題。
以下幾個(gè)問題典型最為典型:
1) 在線消息的體驗(yàn)提升;
2) 離線推送的到達(dá)率;
3) 消息玩法與消息底層系統(tǒng)的耦合過強(qiáng)。
經(jīng)過評(píng)估,我們認(rèn)為現(xiàn)階段離線推送的到達(dá)率問題最為關(guān)鍵,對(duì)用戶體驗(yàn)影響較大。
本文將要分享的是閑魚IM消息在解決離線推送的到達(dá)率方面的技術(shù)實(shí)踐,內(nèi)容包括問題分析和技術(shù)優(yōu)化思路等 ,希望能帶給你啟發(fā)。
(本文已同步發(fā)布于: ?)
本文是系列文章的第6篇,總目錄如下:
《 阿里IM技術(shù)分享(一):企業(yè)級(jí)IM王者——釘釘在后端架構(gòu)上的過人之處 》
《 阿里IM技術(shù)分享(二):閑魚IM基于Flutter的移動(dòng)端跨端改造實(shí)踐 》
《 阿里IM技術(shù)分享(三):閑魚億級(jí)IM消息系統(tǒng)的架構(gòu)演進(jìn)之路 》
《 阿里IM技術(shù)分享(四):閑魚億級(jí)IM消息系統(tǒng)的可靠投遞優(yōu)化實(shí)踐 》
《 阿里IM技術(shù)分享(五):閑魚億級(jí)IM消息系統(tǒng)的及時(shí)性優(yōu)化實(shí)踐 》
《 阿里IM技術(shù)分享(六):閑魚億級(jí)IM消息系統(tǒng)的離線推送到達(dá)率優(yōu)化 》(* 本文)
從數(shù)據(jù)通信鏈接的技術(shù)角度,我們根據(jù)閑魚客戶端是否在線,將整體消息鏈路大致分為強(qiáng)感知鏈路和弱感知鏈路。
強(qiáng)感知鏈路由以下子系統(tǒng)或模塊:
1) 發(fā)送方客戶端;
2) idleapi-message(閑魚的消息網(wǎng)關(guān));
3) heracles(閑魚的消息底層服務(wù));
4) accs(阿里自研的長(zhǎng)連接通道);
5) 接收方客戶端組成。
整條鏈路的核心指標(biāo)在于端到端延遲和消息到達(dá)率。
強(qiáng)感知鏈路中的雙方都是在線的,消息到達(dá)客戶端就可以保證接收方感知到。強(qiáng)感知鏈路的主要痛點(diǎn)在消息的端到端延遲。
弱感知鏈路與強(qiáng)感知鏈路的主要不同在于: 弱感知鏈路的接收方是離線的,需要依賴離線推送這樣的方式送達(dá)。
因此弱感知鏈路的用戶感知度不強(qiáng),其核心指標(biāo)在于消息的到達(dá)率,而非延遲。
所以當(dāng)前階段,優(yōu)化弱感知鏈路的重點(diǎn)也就是提升離線消息的到達(dá)率。換句話說, 提升離線消息到達(dá)率問題,也就是優(yōu)化弱感知鏈路本身 。
下圖一張整個(gè)IM消息系統(tǒng)的架構(gòu)圖,感受下整體鏈路:
如上圖所示,各主要組件和子系統(tǒng)分工如下:
1) HSF是一個(gè)遠(yuǎn)程服務(wù)框架,是dubbo的內(nèi)部版本;
2) tair是阿里自研的分布式緩存框架,支持 memcached、Redis、LevelDB 等不同存儲(chǔ)引擎;
3) agoo是阿里的離線推送中臺(tái),負(fù)責(zé)整合不同廠商的離線推送通道,向集團(tuán)用戶提供一個(gè)統(tǒng)一的離線推送服務(wù);
4) accs是阿里自研的長(zhǎng)連接通道,為客戶端、服務(wù)端的實(shí)時(shí)雙向交互提供便利;
5) lindorm是阿里自研的NoSQL產(chǎn)品,與HBase有異曲同工之妙;
6) 域環(huán)是閑魚消息優(yōu)化性能的核心結(jié)構(gòu),用來存儲(chǔ)用戶最新的若干條消息。
強(qiáng)感知鏈路和弱感知鏈路在通道選擇上是不同的:
1) 強(qiáng)感知鏈路使用accs這個(gè)在線通道;
2) 弱感知鏈路使用agoo這個(gè)離線通道。
通俗了說,弱感知鏈路指的就是離線消息推送系統(tǒng)。
相比較于在線消息和端內(nèi)推送(也就是上面說的強(qiáng)感知鏈路),離線推送難以確保被用戶感知到。
典型的情況包括:
1) 未發(fā)送到用戶設(shè)備:即推送未送達(dá)用戶設(shè)備,這種情況可以從通道的返回分析;
2) 發(fā)送到用戶設(shè)備但沒有展示到系統(tǒng)通知欄:閑魚曾遇到通道返回成功,但是用戶未看到推送的案例;
3) 展示到通知欄,并被系統(tǒng)折疊:不同安卓廠商對(duì)推送的折疊策略不同,被折疊后,需用戶主動(dòng)展開才能看到內(nèi)容,觸達(dá)效果明顯變差;
4) 展示到通知欄,并被用戶忽略:離線推送的點(diǎn)擊率相比于在線推送更低。
針對(duì)“1)未發(fā)送到用戶設(shè)備”,原因有:
1) 離線通道的token失效;
2) 參數(shù)錯(cuò)誤;
3) 用戶關(guān)閉應(yīng)用通知;
4) 用戶已卸載等。
針對(duì)“3)展示到通知欄,并被系統(tǒng)折疊”,原因有:
1) 通知的點(diǎn)擊率;
2) 應(yīng)用在廠商處的權(quán)重;
3) 推送的數(shù)量等。
針對(duì)“4)展示到通知欄,并被用戶忽略”,原因有:
1) 用戶不愿意查看推送;
2) 用戶看到了推送,但是對(duì)內(nèi)容不感興趣;
3) 用戶在忙別的事,無暇處理。
總之: 以上這些離線消息推送場(chǎng)景,對(duì)于用戶來說感知度不高,我們也便稱之為弱感知鏈路。
我們的弱感知鏈路分為3部分,即:
1) 系統(tǒng);
2) 通道;
3) 用戶。
共包含了Hermes、agoo、廠商、設(shè)備、用戶、承接頁(yè)這幾個(gè)環(huán)節(jié)。具體如下圖所示。
從推送的產(chǎn)生到用戶最終進(jìn)入APP,共分為如下幾個(gè)步驟:
步驟1 :Hermes是閑魚的用戶觸達(dá)系統(tǒng),負(fù)責(zé)人群管理、內(nèi)容管理、時(shí)機(jī)把控,是整個(gè)弱感知鏈路的起點(diǎn)。;
步驟2 :agoo是阿里內(nèi)部承接離線推送的中臺(tái),是閑魚離線推送能力的基礎(chǔ);
步驟3 :agoo實(shí)現(xiàn)離線推送依靠的是廠商的推送通道(如:蘋果的 apns通道 、Google的fcm通道、及 國(guó)內(nèi)各廠商的自建通道 。;
步驟4 :通過廠商的通道,推送最終出現(xiàn)在用戶的設(shè)備上,這是用戶能感知到推送的前提條件;
步驟5 :如果用戶剛巧看到這條推送,推送的內(nèi)容也很有趣,在用戶的主動(dòng)點(diǎn)擊下會(huì)喚起APP,打開承接頁(yè),進(jìn)而給用戶展示個(gè)性化的商品。
經(jīng)過以上5個(gè)步驟,至此弱感知鏈路就完成了使命。
弱感知鏈路的核心問題在于:
1) 推送的消息是否投遞給了用戶;
2) 已投遞到的消息用戶是否有感知。
這對(duì)應(yīng)推送的兩個(gè)階段:
1) 推送消息是否已到達(dá)設(shè)備;
2) 用戶是否查看推送并點(diǎn)擊。
其中: 到達(dá)設(shè)備這個(gè)階段是最基礎(chǔ)的,也是本次優(yōu)化的核心。
我們可以將每一步的消息處理量依次平鋪,展開為一張漏斗圖,從而直觀的查看鏈路的瓶頸。
漏斗圖斜率最大的地方是優(yōu)化的重點(diǎn),差異小的地方不需要優(yōu)化:
通過分析以上漏斗圖,弱感知鏈路的優(yōu)化重點(diǎn)在三個(gè)方面:
1) agoo受理率:是指我們發(fā)送推送請(qǐng)到的數(shù)量到可以通過agoo(阿里承接離線推送的中臺(tái))轉(zhuǎn)發(fā)到廠商通道的數(shù)量之間的漏斗;
2) 廠商受理率:是指agoo中臺(tái)受理的量到廠商返回成功的量之間的漏斗;
3) Push點(diǎn)擊率:也就通過以上通道最終已送到到用戶終端的消息,是否最終轉(zhuǎn)化為用戶的主動(dòng)“點(diǎn)擊”。
有了優(yōu)化方向,我們來看看優(yōu)化手段吧。
跟隨推送的視角,順著鏈路看一下我們是如何進(jìn)行優(yōu)化的。
用戶的推送,從 Hermes 站點(diǎn)搭乘“班車”,駛向下一站:? agoo 。
這是推送經(jīng)歷的第一站。到站一看,傻眼了,只有不到一半的推送到站下車了。這是咋回事嘞?
這就要先說說 agoo 了,調(diào)用 agoo 有兩種方式:
1) 指定設(shè)備和客戶端,agoo直接將推送投遞到相應(yīng)的設(shè)備;
2) 指定用戶和客戶端,agoo根據(jù)內(nèi)部的轉(zhuǎn)換表,找到用戶對(duì)應(yīng)的設(shè)備,再進(jìn)行投遞。
我們的系統(tǒng)不保存用戶的設(shè)備信息。因此,是按照用戶來調(diào)用agoo的。
同時(shí): 由于沒有用戶的設(shè)備信息,并不知道用戶是 iOS 客戶端還是 Android 客戶端。工程側(cè)不得不向 iOS 和 Android 都發(fā)送一遍推送。雖然保證了到達(dá),但是,一半的調(diào)用都是無效的。
為了解這個(gè)問題: 我們使用了agoo的設(shè)備信息。將用戶轉(zhuǎn)換設(shè)備這一階段提前到了調(diào)用 agoo 之前,先明確用戶對(duì)應(yīng)的設(shè)備,再指定設(shè)備調(diào)用 agoo,從而避免無效調(diào)用。
agoo調(diào)用方式優(yōu)化后,立刻剔除了無效調(diào)用,agoo受理率有了明顯提升。
至此: 我們總算能對(duì) agoo 受理失敗的真正原因做一個(gè)高大上的分析了。
根據(jù)統(tǒng)計(jì): 推送被 agoo 拒絕的主要原因是——用戶關(guān)閉了通知權(quán)限。同時(shí),我們對(duì) agoo 調(diào)用數(shù)據(jù)的進(jìn)一步分析發(fā)現(xiàn)——有部分用戶找不到對(duì)應(yīng)的設(shè)備。 優(yōu)化到此,我們猛然發(fā)現(xiàn)多了兩個(gè)問題。
那就繼續(xù)優(yōu)化唄:
1) 通知體驗(yàn)優(yōu)化,引導(dǎo)打開通知權(quán)限;
2) 與agoo共建設(shè)備庫(kù),解決設(shè)備轉(zhuǎn)換失敗的問題。
這兩個(gè)優(yōu)化方向又是一片新天地,我們擇日再聊。
推送到達(dá) agoo ,分機(jī)型搭乘廠商“專列”,駛向下一站:用戶設(shè)備。
這是推送經(jīng)歷的第二站。出站查票,發(fā)現(xiàn)竟然超員了。
于是乎: 我們每天有大量推送因?yàn)槌^廠商設(shè)定的限額被攔截。
為什么會(huì)這樣呢?
實(shí)際上: 提供推送通道的廠商(沒錯(cuò), 各手機(jī)廠商的自家推送通道良莠不齊 ),為了保證用戶體驗(yàn),會(huì)對(duì)每個(gè)應(yīng)用能夠推送的消息總量進(jìn)行限制。
對(duì)于廠商而言,這個(gè)限制會(huì)根據(jù)推送的類型和應(yīng)用的用戶規(guī)模設(shè)定——推送主要分為產(chǎn)品類的推送和營(yíng)銷類的推送。
廠商推送通道對(duì)于不同類型消息的限制是:
1) 對(duì)于產(chǎn)品類推送,廠商會(huì)保證到達(dá);
2) 對(duì)于營(yíng)銷類推送,廠商會(huì)進(jìn)行額度限制;
3) 未標(biāo)記的推送,默認(rèn)作為營(yíng)銷類推送對(duì)待。
我們剛好沒有對(duì)推送進(jìn)行標(biāo)記,因此觸發(fā)了廠商的推送限制。
這對(duì)我們的用戶來說,會(huì)帶來困擾。閑魚的交易,很依賴買賣家之間的消息互動(dòng)。這部分消息是需要確保到達(dá)的。
同樣: 訂單類的消息、用戶的關(guān)注,也需要保證推送給用戶。
根據(jù)主流廠商的接口協(xié)議,我們將推送的消息分為以下幾類,并進(jìn)行相應(yīng)標(biāo)記:
1) 即時(shí)通訊消息;
2) 訂單狀態(tài)變化;
3) 用戶關(guān)注內(nèi)容;
4) 營(yíng)銷消息這幾類。
同時(shí),在業(yè)務(wù)上,我們也進(jìn)行了推送的治理——將用戶關(guān)注度不高的消息,取消推送,避免打擾。
經(jīng)過這些優(yōu)化,因?yàn)槌^廠商限額而被攔截的推送實(shí)現(xiàn)了清零。
通過優(yōu)化agoo受理率、廠商受理率,我們解決了推送到達(dá)量的瓶頸。但即使消息被最終送達(dá),用戶到底點(diǎn)擊了沒有?這才是消息推送的根本意義所在。
于是,在日常的開發(fā)測(cè)試過程中,我們發(fā)現(xiàn)了推送的兩個(gè)體驗(yàn)問題:
1) 用戶點(diǎn)擊Push有開屏廣告;
2) 營(yíng)銷Push也有權(quán)限校驗(yàn),更換用戶登陸后無法點(diǎn)擊。
對(duì)于開屏廣告功能,我們?cè)黾恿薖ush點(diǎn)擊跳過廣告的能力。
針對(duì)Push的權(quán)限校驗(yàn)功能,閑魚根據(jù)場(chǎng)景做了細(xì)分:
1) 涉及個(gè)人隱私的推送,保持權(quán)限校驗(yàn)不變;
2) 營(yíng)銷類的推送,放開權(quán)限校驗(yàn)。
以上是點(diǎn)擊體驗(yàn)的優(yōu)化,我們還需要考慮用戶的點(diǎn)擊意愿。
用戶點(diǎn)擊量與推送的曝光量、推送素材的有趣程度相關(guān)。推送的曝光量又和推送的到達(dá)量、推送的到達(dá)時(shí)機(jī)有關(guān)。
具體的優(yōu)化手段是:
1) 在推送內(nèi)容上:我們需要優(yōu)化的是推送的時(shí)機(jī)和相應(yīng)的素材;
2) 在推送時(shí)機(jī)上:算法會(huì)根據(jù)用戶的偏好和個(gè)性化行為數(shù)據(jù),計(jì)算每個(gè)用戶的個(gè)性化推送時(shí)間,在用戶空閑的時(shí)間推送(避免在不合適的時(shí)間打擾用戶,同時(shí)也能提升用戶看到推送的可能性)。
3) 在推送素材上:算法會(huì)根據(jù)素材的實(shí)時(shí)點(diǎn)擊反饋,對(duì)素材做實(shí)時(shí)賽馬。只發(fā)用戶感興趣的素材,提高用戶點(diǎn)擊意愿。
通過以上我們的分析和技術(shù)優(yōu)化手段,整體弱推送鏈路鏈路有了不錯(cuò)的提升,離線消息的到達(dá)率相對(duì)提升了兩位數(shù)。
本篇主要和大家聊的是只是IM消息系統(tǒng)鏈路中的一環(huán)——弱感知鏈路的優(yōu)化,落地到到具體的業(yè)務(wù)也就是離線消息送達(dá)率問題。
整體IM消息系統(tǒng),還是一個(gè)比較復(fù)雜的領(lǐng)域。
我們?cè)谙⑾到y(tǒng)的發(fā)展過程中,面臨著如下問題:
1) 如何進(jìn)行消息的鏈路追蹤;
2) 如何保證IM消息的快速到達(dá)(見《 閑魚億級(jí)IM消息系統(tǒng)的及時(shí)性優(yōu)化實(shí)踐 》);
3) 如何將消息的玩法和底層能力分離;
4) 離線推送中如何通過用戶找到對(duì)應(yīng)的設(shè)備。
這些問題,我們?cè)谝郧暗奈恼轮杏兴窒?,以后也?huì)陸續(xù)分享更多,敬請(qǐng)期待。
[1]? Android P正式版即將到來:后臺(tái)應(yīng)用保活、消息推送的真正噩夢(mèng)
[2]? 一套高可用、易伸縮、高并發(fā)的IM群聊、單聊架構(gòu)方案設(shè)計(jì)實(shí)踐
[3]? 一套億級(jí)用戶的IM架構(gòu)技術(shù)干貨(上篇):整體架構(gòu)、服務(wù)拆分等
[4]? 一套億級(jí)用戶的IM架構(gòu)技術(shù)干貨(下篇):可靠性、有序性、弱網(wǎng)優(yōu)化等
[5]? 從新手到專家:如何設(shè)計(jì)一套億級(jí)消息量的分布式IM系統(tǒng)
[6]? 企業(yè)微信的IM架構(gòu)設(shè)計(jì)揭秘:消息模型、萬人群、已讀回執(zhí)、消息撤回等
[7]? 融云技術(shù)分享:全面揭秘億級(jí)IM消息的可靠投遞機(jī)制
[8]? 移動(dòng)端IM中大規(guī)模群消息的推送如何保證效率、實(shí)時(shí)性?
[9]? 現(xiàn)代IM系統(tǒng)中聊天消息的同步和存儲(chǔ)方案探討
[10]? 新手入門一篇就夠:從零開發(fā)移動(dòng)端IM
[11]? 移動(dòng)端IM開發(fā)者必讀(一):通俗易懂,理解移動(dòng)網(wǎng)絡(luò)的“弱”和“慢”
[12]? 移動(dòng)端IM開發(fā)者必讀(二):史上最全移動(dòng)弱網(wǎng)絡(luò)優(yōu)化方法總結(jié)
[13]? IM消息送達(dá)保證機(jī)制實(shí)現(xiàn)(一):保證在線實(shí)時(shí)消息的可靠投遞
[14]? IM消息送達(dá)保證機(jī)制實(shí)現(xiàn)(二):保證離線消息的可靠投遞
[15]? 零基礎(chǔ)IM開發(fā)入門(一):什么是IM系統(tǒng)?
[16]? 零基礎(chǔ)IM開發(fā)入門(二):什么是IM系統(tǒng)的實(shí)時(shí)性?
[17]? 零基礎(chǔ)IM開發(fā)入門(三):什么是IM系統(tǒng)的可靠性?
[18]? 零基礎(chǔ)IM開發(fā)入門(四):什么是IM系統(tǒng)的消息時(shí)序一致性?
(本文已同步發(fā)布于: ?)
1。如果只是遇見,不能停留,不如不遇見。 If we can only encounter each other rather than stay with each other,then I wish we had never encountered. 2。寧愿笑著流淚,也不哭著說后悔。心碎了,還需再補(bǔ)嗎? I would like weeping with the smile rather than repenting with the cry,when my heart is broken ,is it needed to fix? 3。沒有誰(shuí)對(duì)不起誰(shuí),只有誰(shuí)不懂得珍惜誰(shuí)。 No one indebted for others,while many people don't know how to cherish others. 4。命里有時(shí)鐘需有 命里無時(shí)莫強(qiáng)求 You will have it if it belongs to you,whereas you don't kvetch for it if it doesn't appear in your life. 5。當(dāng)香煙愛上火柴時(shí),就注定受到傷害 When a cigarette falls in love with a match,it is destined to be hurt. 6。愛情…在指縫間承諾 指縫…。在愛情下交纏。 Love ,promised between the fingers Finger rift,twisted in the love 7。沒有人值得你流淚,值得讓你這么做的人不會(huì)讓你哭泣。 No man or woman is worth your tears, and the one who is, won’t make you cry. 8。記住該記住的,忘記該忘記的。改變能改變的,接受不能改變的。 Remember what should be remembered, and forget what should be forgotten.Alter what is changeable, and accept what is unchangeable. Love is like a butterfly. It goes where it pleases and it pleases where it goes. 愛情就像一只蝴蝶,它喜歡飛到哪里,就把歡樂帶到哪里。 If I had a single flower for every time I think about you, I could walk forever in my garden. 假如每次想起你我都會(huì)得到一朵鮮花,那么我將永遠(yuǎn)在花叢中徜徉。 Within you I lose myself, without you I find myself wanting to be lost again. 有了你,我迷失了自我。失去你,我多么希望自己再度迷失。 At the touch of love everyone becomes a poet. 每一個(gè)沐浴在愛河中的人都是詩(shī)人。 Look into my eyes - you will see what you mean to me. 看看我的眼睛,你會(huì)發(fā)現(xiàn)你對(duì)我而言意味著什么。 Distance makes the hearts grow fonder. 距離使兩顆心靠得更近。 I need him like I need the air to breathe. 我需要他,正如我需要呼吸空氣。 If equal affection cannot be, let the more loving be me. 如果沒有相等的愛,那就讓我愛多一些吧。 Love is a vine that grows into our hearts. 愛是長(zhǎng)在我們心里的藤蔓。 If I know what love is, it is because of you. 因?yàn)槟?,我懂得了愛?Love is the greatest refreshment in life. 愛情是生活最好的提神劑。 Love never dies. 愛情永不死。 The darkness is no darkness with thee. 有了你,黑暗不再是黑暗。 We cease loving ourselves if no one loves us. 如果沒有人愛我們,我們也就不會(huì)再愛自己了。 There is no remedy for love but to love more. 治療愛的創(chuàng)傷唯有加倍地去愛。 When love is not madness, it is not love. 如果愛不瘋狂就不是愛了。 A heart that loves is always young. 有愛的心永遠(yuǎn)年輕。 Love is blind. 愛情是盲目的。 1.A bad workman always blames his tools. 拙匠總怪工具差。 2.A contented mind is a perpetual feast. 知足長(zhǎng)樂。 3.A good beginning is half the battle. 好的開端等于成功一半。 4.A little pot is soon hot. 壺小易熱,量小易怒。 5.All lay loads on a willing horse. 好馬重負(fù)。 6.A merry heart goes all the way. 心情愉快,萬事順利 。 7.Bad excuses are worse than none. 狡辯比不辯護(hù)還糟 。 8.Character is the first and last word in the success circle. 人的品格是事業(yè)成功的先決條件。 9.Cleanliness is next to godliness. 整潔近于美德 。 10.Courtesy costs nothing. 彬彬有禮,惠而不費(fèi) 。 11.Doing nothing is doing ill. 無所事事,必干壞事。 12.Early to bed, early to rise, make a man healthy, wealthy, and wise. 睡得早,起得早,聰明、富裕、身體好 。 13.Empty vessels make the most noise. 滿瓶子不響,半瓶子晃蕩 。 14.Every man hath his weak side. 人皆有弱點(diǎn) 。 15.Everything ought to be beautiful in a human being: face, dress, soul and idea. 人的一切都應(yīng)當(dāng)是美麗的:容貌、衣著、心靈和思想。 16.Extremes are dangerous. 凡事走向極端是危險(xiǎn)的 。 17.Good advice is harsh to the ear. 忠言逆耳 。 18.Grasp all, lose all. 欲盡得,必盡失 。 19.Great hopes make great men. 偉大的理想造就偉大的人物。 20.Handsome is he who does handsomely. 行為美者才真美。 21.Have but few friends, though many acquaintances. 結(jié)交可廣,知己宜少。 22.Hear all parties.兼聽則明,偏聽則暗 。 23.He is a wise man who speaks little. 智者寡言。 24.He is not laughed at that laughs at himself first. 有自知之明者被人尊敬。 25.He is rich enough that wants nothing. 無欲者最富有,貪欲者最貧窮。 26.He is truly happy who makes others happy. 使他人幸福的人,是真正的幸福。 27.Honesty is the best policy. 誠(chéng)實(shí)乃上策。 28.Hope for the best and prepare for the worst.?? 抱最好的希望,作最壞的準(zhǔn)備 。 29.Idleness is the root of all evil. 懶惰是萬惡之源 。 30.If we dream, everything is possible. 敢于夢(mèng)想,一切都將成為可能。 31.Kind hearts are the gardens, kind thoughts are the roots, kind words are flowers and kind deeds are the fruits. 仁慈的心田是花園,崇高的思想是根莖,友善的言語(yǔ)是花朵,良好的行為是果實(shí)。 32.Laugh, and the world laughs with you; Weep, and you weep lone. 歡笑,整個(gè)世界伴你歡笑。哭泣,只有你獨(dú)自向隅而泣 。 33.Life is measured by thought and action not by time. 衡量生命的尺度是思想和行為,而不是時(shí)間。 34.Life is not all beer and skittles. 人生并非盡是樂事 。 35.Long absent, soon forgotten. 別久情疏 。 36.Look before you leap. 三思而后行 。 37.Lookers-on see most of the game. 旁觀者清,當(dāng)局者迷。 38.Manners make the man.觀其待人而知其人 。 39.Misfortune tests the sincerity of friends. 患難識(shí)知交。 40.No cross, no crown.沒有苦難,就沒有快樂 。 41.Nobody's enemy but his own. 自尋苦惱 。 42.One man's fault is another man's lesson. 前車之覆,后車之鑒 。 43.Pardon all men, but never thyself. 嚴(yán)以律已,寬以待人。 44.Reason is the guide and light of life. 理智是人生的燈塔 。 45.Sadness and gladness succeed one another. 樂極生悲,苦盡甘來 。 46.Still waters run deep.流靜水深,人靜心深 。 47.The fire is the test of gold; adversity of strong men. 烈火煉真金,逆境煉壯士 。 48.The fox may grow grey, but never good. 江山易改,本性難移 。 49.The more a man learns, the more he sees his ignorance. 知識(shí)越廣博,越感已無知 。 50.Virtue is a jewel of great price. 美德是無價(jià)之寶 。 51.Weak things united become strong. 一根筷子易折斷,十根筷子硬如鐵 。 52.We can't judge a person by what he says but by what he does. 判斷一個(gè)人,不聽言語(yǔ)看行動(dòng) 。 53.Where there is a will there is a way. 有志者,事竟成 。 54.Will is power. 意志就是力量 。 55.Wise men are silent; fools talk. 智者沉默寡言,愚者滔滔不絕 。 56.Wise men learn by others' harm, fools by their own. 智者以他人挫折為鑒,愚者必自身碰壁方知覺。 1 夏天的飛鳥,飛到我的窗前唱歌,又飛去了。 秋天的黃葉,它們沒有什么可唱,只嘆息一聲,飛落在那里。 stray birds of summer come to my window to sing and fly away. and yellow leaves of autumn, which have no songs, flutter and fall there with a sign. 2 世界上的一隊(duì)小小的漂泊者呀,請(qǐng)留下你們的足印在我的文字里。 o troupe of little vagrants of the world, leave your footprints in my words. 3 世界對(duì)著它的愛人,把它浩翰的面具揭下了。 它變小了,小如一首歌,小如一回永恒的接吻。 the world puts off its mask of vastness to its lover. it becomes small as one song, as one kiss of the eternal. 4 是大地的淚點(diǎn),使她的微笑保持著青春不謝。 it is the tears of the earth that keep here smiles in bloom. 5 無垠的沙漠熱烈追求一葉綠草的愛,她搖搖頭笑著飛開了。 the mighty desert is burning for the love of a bladeof grass who shakes her head and laughs and flies away. 6 如果你因失去了太陽(yáng)而流淚,那么你也將失去群星了。 if you shed tears when you miss the sun, you also miss the stars. 7 跳舞著的流水呀,在你途中的泥沙,要求你的歌聲,你的流動(dòng)呢。你肯挾 瘸足的泥沙而俱下么? the sands in your way beg for your song and your movement, dancing water. will you carry the burden of their lameness? 8 她的熱切的臉,如夜雨似的,攪擾著我的夢(mèng)魂。 her wishful face haunts my dreams like the rain at night. 9 有一次,我們夢(mèng)見大家都是不相識(shí)的。 我們醒了,卻知道我們?cè)窍嘤H相愛的。 once we dreamt that we were strangers. we wake up to find that we were dear to each other. 10 憂思在我的心里平靜下去,正如暮色降臨在寂靜的山林中。 sorrow is hushed into peace in my heart like the evening among the silent trees A star has 5 ends; A square has 4 ends; A triangle has 3 ends; A line has 2 ends; A life has one end. But I hope your happiness has no end. 1. 記住該記住的,忘記該忘記的。改變能改變的,接受不能改變的。 Remember what should be remembered, and forget what should be forgotten. Alter what is changeable, and accept what is mutable. 2.魚對(duì)水說你看不到我的眼淚,因?yàn)槲以谒?。水說我能感覺到你的眼淚,因?yàn)槟阍谖倚睦铩?“You couldn’t see my tears cause I am in the water.” Fish said to water. “But I could feel your tears cause you are in my heart.” Answered water. 3.人生短短幾十年,不要給自己留下了什么遺憾,想笑就笑,想哭就哭,該愛的時(shí)候就去愛,無謂壓抑自己。 Your life only lasts for a few decades, so be sure that you don't leave any regrets. Laugh or cry as you like, and it‘s meaningless to oppress yourself. 4. 生命中,不斷地有人進(jìn)入或離開。于是,看見的,看不見了;記住的,遺忘了。生命中,不斷地有得到和失落。于是,看不見的,看見了;遺忘的,記住了。然而,看不見的,是不是就等于不存在?記住的,是不是永遠(yuǎn)不會(huì)消失? There is someone that is coming or passing away in your life around the clock, so you may lose sight of those seen, and forget those remembered. There is gain and loss in your life, so you may catch sight of those unseen, and remember those forgotten. Nevertheless, doesn’t the unseen exist for sure? Will the remembered remain forever? 5. 后悔是一種耗費(fèi)精神的情緒。后悔是比損失更大的損失,比錯(cuò)誤更大的錯(cuò)誤。所以不要后悔。 Penitence is something that enervates our spirit, causing a greater loss than the loss itself and making a bigger mistake than the mistake itself. So never regret.
前端最火熱的話題無法就是flutter,不管是刷哪個(gè)論壇,必定有探討flutter的文章。沒用過flutter,但是對(duì)于跨平臺(tái)的技術(shù),我一直都在研究。
為什么是uni-app
之前一直在找解決跨平臺(tái)的方案,嘗試了很多方案,比如滴滴的變色龍,但是最終還是選擇了uni-app,這里附上uni-app的官網(wǎng)。為什么會(huì)選擇它呢,第一,vue語(yǔ)法,學(xué)習(xí)成本低,上手速度快,只要之前你做過vue的項(xiàng)目,那么就能很快上手,其實(shí)是vue和微信小程序的結(jié)合體,一半vue,一半微信小程序。第二,長(zhǎng)期維護(hù),之前做微信小程序的時(shí)候,選擇了美團(tuán)的mpvue,但是后面發(fā)現(xiàn)長(zhǎng)期不維護(hù)了,提了Issues也沒人理,隨之就放棄了,而uni-app長(zhǎng)期在維護(hù),這樣看出了開發(fā)團(tuán)隊(duì)的用心。第三,跨平臺(tái)的能力,uni-app能夠跨多個(gè)終端,H5,安卓,Ios,微信小程序,百度小程序,頭條小程序,支付寶小程序,真正實(shí)現(xiàn)了一套代碼,多端運(yùn)行,而且很好適應(yīng)了我國(guó)的市場(chǎng)。第四,日益豐富的插件市場(chǎng),uni的插件市場(chǎng)也在日益強(qiáng)大,能夠基本上滿足我們平時(shí)的開發(fā)需求。
uni-app的組件有原生調(diào)用能力,第三方的vue庫(kù)在調(diào)原生接口時(shí)跟 5+runtime 不兼容。就像nativescript 有vue版和angular版,類似于react native , 都是起源于phonegap/cordova
實(shí)際開發(fā)效果遵義小紅椒 做了一款app,打包了安卓,Ios,微信小程序3個(gè)平臺(tái),產(chǎn)出的效果都還是不錯(cuò),總體還是比較滿意。而且打包過程也很方便簡(jiǎn)單,配套的HBuilderX自動(dòng)內(nèi)置了打包功能,所以也省去了打包的煩惱。
遵義小紅椒 建議
如果你現(xiàn)在想做一款跨平臺(tái)的產(chǎn)品,而且有vue和微信小程序的經(jīng)驗(yàn),最重要的,你不想學(xué)習(xí)一門新語(yǔ)言,那么uni-app也許是你的一個(gè)選擇。
注:亮度調(diào)節(jié)和音量調(diào)節(jié)gif無法體現(xiàn),功能是ok的,其次默認(rèn)Icon鎖的close和open實(shí)在難以分辨。
環(huán)境:Flutter 2.8.1 channel stable ;Dart 2.15.1
需要音頻播放器的看這里: Flutter音樂播放器
重點(diǎn)說下這個(gè)工具類,因?yàn)橐曨l播放,涉及到狀態(tài)改變有很多,筆者剛開始選擇使用 InheritedWidget 來在眾多的widget之間共享數(shù)據(jù)。但是總感覺這樣有點(diǎn)繁瑣,且不很優(yōu)雅!
這里非廣告,如果是使用 GetX 就很簡(jiǎn)單了,筆者也使用了 GetX 進(jìn)行封裝了,一瀉千里的趕腳!,但是筆者還是那句話:剛開始接觸Flutter的開發(fā)者不是很建議使用 GetX ,可以先熟悉下Flutter狀態(tài)管理的基礎(chǔ)原理再行使用。而且為了盡量簡(jiǎn)潔,還是不引入其他的第三方了。
我們選擇對(duì)第三方插件進(jìn)行封裝的目的不外乎這幾個(gè):
于是筆者就寫了一個(gè)工具類 VideoPlayerUtils ,專門且只用來處理播放器的所有業(yè)務(wù)。包括暫停、播放、跳轉(zhuǎn)、調(diào)節(jié)音量、調(diào)節(jié)亮度、切換視頻等操作。在所有的widget中不會(huì)引用關(guān)于 video_player 或其他第三方插件的任何信息, VideoPlayerUtils 負(fù)責(zé)widget與播放器之間的所有操作交互。后續(xù)優(yōu)化迭代或更換播放器插件時(shí),只需針對(duì)這個(gè)工具類進(jìn)行修改,對(duì)所有widget不會(huì)有任何的影響,大大的解耦合了。
其中 VideoPlayerState :
提供以上的公共屬性,可以通過 VideoPlayerUtils 來獲取對(duì)應(yīng)的值,使用 get 只讀,使外界不會(huì)誤修改這些屬性,以保證數(shù)值的安全性。開發(fā)者可根據(jù)自身需要自行添加屬性。
提供以上方法來處理播放器的所有業(yè)務(wù)。同樣的開發(fā)者可根據(jù)自身需要自行添加或修改。
重點(diǎn)說下這個(gè)方法,是整個(gè)業(yè)務(wù)的核心方法,控制視頻的播放或暫停。開發(fā)者只要遇到播放或暫停是均可調(diào)用此方法,具體是播放或暫停,內(nèi)部根據(jù)傳入的 url 自行判斷,開發(fā)者不需要關(guān)心。
切換新視頻也是使用此方法,傳入的 url 與上次不一致,自動(dòng)切換新視頻。筆者可根據(jù) statusListener 來監(jiān)聽播放狀態(tài)的改變,以此處理自身邏輯。
這個(gè)也需要提下,視頻播放器在播放新視頻時(shí)會(huì)異步初始化,一般我們的操作是在 initState() 初始化,成功后再 setState() 。這里筆者遇到一個(gè)讓人蛋疼的問題:
我們看 video_player 的使用:
VideoPlayer(controller) :widget中已經(jīng)持有了controller。本來筆者封裝的目的就是為了讓widget與controller的之間解耦合。但此時(shí)的筆者。。。。
放棄不是不可能放棄的,這輩子都不會(huì)放棄的!
于是筆者取了巧,寫了一個(gè)初始化監(jiān)聽器 initializedListener ,包換2個(gè)參數(shù): bool,Widget ,初始化是否成功;其中widget為初始化成功返回需要展示的播放器UI,失敗默認(rèn)返回 const SizedBox() 。
到這里就可以簡(jiǎn)單使用了:
沒看錯(cuò),視頻播放就是這么簡(jiǎn)單。
如果有更多的業(yè)務(wù)功能,筆者也按照自己的需求寫了一套,同樣的開發(fā)者可根據(jù)自身需要自行添加或修改。
VideoPlayerGestures 主要是處理手勢(shì)的,比如快進(jìn)、快退等跳轉(zhuǎn)播放;左側(cè)上下滑動(dòng)調(diào)節(jié)亮度;右側(cè)上下滑動(dòng)調(diào)節(jié)音量;單擊是否開啟沉浸式播放,所有widget的隱藏與顯示;雙擊播放、暫停等。
哦,還有 PercentageWidget 也放到這個(gè)文件下了,就是這玩意:
因?yàn)轱@示的百分比與手勢(shì)相關(guān),隨著手勢(shì)移動(dòng)而更新。開發(fā)者可自行處理。
筆者處出于簡(jiǎn)單考慮,就按照整個(gè)UI的位置命名了。瞅一眼就知道是啥玩意。
同樣的開發(fā)者可根據(jù)自身需要自行添加或修改。
就是這玩意:
同樣的開發(fā)者可根據(jù)自身需要自行添加或修改。話說這個(gè)鎖的 Icon 的open和close是真的難分辨!
就是這玩意:
同樣的開發(fā)者可根據(jù)自身需要自行添加或修改。
這玩意是自定義的,別問,問就是跟產(chǎn)品干一架落了下風(fēng)
主要就是自定義這玩意:
同樣的開發(fā)者可根據(jù)自身需要自定義。
注:這里沒有添加緩沖的進(jìn)度,開發(fā)可查看 video_player 中的源碼 VideoProgressIndicator ,按業(yè)務(wù)自行定義。
這玩意就是整合以上的widget,再考慮下全屏的安全區(qū)域,沒啥東西。開發(fā)者可自行處理!
具體的實(shí)現(xiàn)監(jiān)聽器的思路, 看這里 。
自此一個(gè)漂亮的Flutter視頻播放器就已經(jīng)結(jié)束了。如果您覺得對(duì)您有些許幫助的話,歡迎 Star !
近來閑暇時(shí)間一直在做Flutter,閃屏頁(yè)是一個(gè)比較常見的需求,網(wǎng)上的閃屏頁(yè)教程大部分是那種類似于廣告頁(yè),而非iOS中的 LaunchScreen 性質(zhì)的閃屏頁(yè).按照原來的方案我們要配置閃屏頁(yè)的話,我們需要同時(shí)配置兩端的閃屏頁(yè),那么有沒有比較簡(jiǎn)單的方案來配置閃屏頁(yè)呢? 毋庸置疑,當(dāng)然是有了,那就是Flutter的插件 - flutter_native_splash . 接下來我們就來看一下具體應(yīng)該怎么使用這個(gè)插件.
首先把 flutter_native_splash 導(dǎo)入到工程的 pubspec.yaml 中.這里需要注意的是需要放在 dev_dependencies 下,而不是 dependencies .具體如下所示.
接下來我們就來配置 flutter_native_splash ,在配置之前我們看一下 flutter_native_splash 的可配置項(xiàng).
例如,我現(xiàn)在只有一個(gè)logo圖片,那么我想生成iOS和android兩端的閃屏頁(yè),這時(shí)候我只需在 pubspec.yaml 如下設(shè)置即可.
當(dāng)然了,如果你有其他配置可以自行進(jìn)行添加.
配置完成了,我們?cè)撊绾紊赡?這時(shí)候需要我們打開終端 cd 到我們的工程目錄下.如果是Android Studio 或者 VSCode 默認(rèn)就是在當(dāng)前工程目錄下.
然后我們需要執(zhí)行下面的三個(gè)命令來生成閃屏頁(yè)
每一次都敲三個(gè)命令實(shí)屬麻煩,我們把上訴的三個(gè)命令整合成一個(gè)命令,如下所示.
那么,我們不想使用該插件生成的閃屏頁(yè)該怎么辦呢?我們只需要執(zhí)行下面命令即可.
注:每一次更換圖片都是需要重新執(zhí)行命令重新生成.
OK,上面就是關(guān)于 flutter_native_splash 的使用全部?jī)?nèi)容,其實(shí)比較簡(jiǎn)單,如果需要定制化的,建議還是各自平臺(tái)配置各自的閃屏頁(yè).如果有任何問題歡迎在評(píng)論區(qū)批評(píng)指導(dǎo),感謝大家了.
字譯:Aesthetic feeling dispersing in the air
衍生:Let the fragrance flutter in your breath
再來:Scenting all around
想到一句成語(yǔ)千古流香(芳香):Fragrance that last thousands of generations