您好,登錄后才能下訂單哦!
本篇內容介紹了“Android斬首行動接口預請求問題怎么解決”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
開發同學應該都很熟悉我們頁面的渲染過程一般是從Activity#onCreate開始,再發起網絡請求,等請求回調回來后,再基于網絡數據渲染頁面。可以用下面這幅圖來粗略描述這個過程:
可以看到,目標頁面渲染完成前必須得等待網絡請求,導致渲染速度并沒有那么快。尤其是當網絡并不好的時候感受會更加明顯。并且,當目標頁面是H5頁面或者是Flutter頁面的時候,因為涉及到H5容器與Flutter容器的創建,白屏時間會更長。
那么有沒有可能提前發起請求,來縮短網絡請求這一部分的等待時間呢?這就是我們今天要講的部分,接口預請求。
我們要達到的目標很簡單,就是提前異步發起目標頁面的網絡請求,從而加快目標頁面的渲染速度。改善后的過程可以用下圖表示:
并且,我們的預請求能力需要盡量少地侵入業務,與業務解耦,并保證能力的通用性,適用于工程內的任意頁面(Android頁面、H5頁面、Flutter頁面)。
首先給大家看一下整體鏈路,具體的細節可以先不用去摳,下面會一一講到。
預請求時機一般有三種選擇:
由業務層自行選擇時機進行異步預請求
點擊控件時進行異步預請求
路由最終跳轉前進行異步預請求
第1種選擇,由業務層自行選擇時機進行預請求,需要涉及到業務層的改造,以及對時機合理性的把握。一方面是存在改造成本,另一方面是無法保證業務側調用時機的合理性。
第2種選擇,點擊控件時進行預請求。若點擊時進行預請求,點擊事件監聽并不是業務域統一的,無法形成有效封裝。并且,若后續路由攔截器修改了參數,或是終止了跳轉,這次預請求就失去了意義。
因此這里我們選擇第3種,基于統一路由框架,在路由最終跳轉前進行預請求。既保證了良好的封裝性,也實現了對業務的零侵入,同時也做到了懶請求,即用戶必然要發起該請求時才會去預請求。這里需要注意的是必須是在最終跳轉前進行預請求,可以理解為是路由的最后一個前置異步攔截器。
我們通過本地的json文件(當然,有需要也可以上云通過配置后臺下發),對預請求的規則進行配置,并將這份配置在App啟動階段異步讀入到內存。后續在路由過程中,只有命中了預請求規則,才能發起預請求。配置demo如下:
{ "routeConfig":{ "scheme://domain/path?param1=true&itemId=123":["prefetchKey"], "route2":["prefetchKey2"], "route3":["prefetchKey3","prefetchKey4"] }, "prefetcher":{ "prefetchKey":{ "prefetchType":"network", "prefetchInfo":{ "api":"network.api.name", "apiVersion":"1.0", "method":"post", "needLogin":"false", "showLoginUI":"false", "params": { "itemId":"$route.itemId", "firstTime":"true" }, "headers": { }, "prefetchImgInResponse": [ { "imgUrl":"$data.imgData.img", "imgWidth":"$data.imgData.imgWidth", "imgHeight":150 } ] } }, "prefetchKey2":{ "prefetchType":"network", "prefetchInfo":{ "api":"network.api.name2", "apiVersion":"1.0", "method":"post", "needLogin":"false", "showLoginUI":"false", "params": { "itemId":"$route.productId", "firstTime":"false" }, "headers": { } }, "prefetchKey3":{ "prefetchType":"image", "prefetchInfo":{ "imgUrl":"$route.imgUrl", "imgWidth":"$route.imgWidth", "imgHeight": 150 } }, "prefetchKey4":{ "prefetchInfo":{} } } }
參數名 | 描述 | 備注 |
---|---|---|
routeConfig | 路由配置 | 配置路由到預請求的映射 |
prefetcher | 預請求配置 | 記錄所有的預請求 |
prefetchKey | 預請求的key | |
prefetchType | 預請求類型 | 分為network類型與image類型,兩種類型所需要的參數不同 |
prefetchInfo | 預請求所需要的信息 | 其中value若為route.param格式,那么該值從路由中獲取;若為route.param格式,那么該值從路由中獲取;若為route.param格式,那么該值從路由中獲取;若為data.param格式,則從響應數據中獲取。 |
params | network請求所需要的請求params | |
headers | network請求所需要的請求headers | |
prefetchImgFromResponse | 預請求的響應返回后,需要預加載的圖片 | 用于需要預加載圖片時,無法確定圖片url,圖片url只能從預請求響應中獲取的場景。 |
例如跳轉目標頁面,它的路由是scheme://domain/path?param1=true&itemId=123
。
首先我們在跳轉路由時,若跳轉的路由是這個目標頁面,我們就會嘗試去發起預請求。根據上面的demo配置文件,它將匹配到prefetchKey
這個預請求。
那么我們詳細看prefetchKey
這個預請求,預請求類型prefetchType
為network
,是一個網絡預請求,prefetchInfo
中具備了請求的基本參數(如apiName、apiVersion、method、請求params與請求headers,不同工程不一樣,大家可以根據自己的工程項目進行修改)。具體看params
中,有一個參數為itemId:$route.itemId
。以$route.
開頭的意思,就是這個value值要從路由中獲取,即itemId=123
,那么這個值就是123。
在做網絡預請求的過程中,我忽然想到圖片做預請求也是可以大大提升用戶體驗的,尤其是當大圖片首次下載到內存中渲染需要的時間會比較長。圖片預請求分為url已知與url未知兩種場景,下面各舉兩個例子。
什么是圖片url已知呢?比如我們在首頁跳轉首頁的二級頁面時,如果二級頁面需要預加載的圖片跟首頁的某張圖是一樣的(尺寸可能不同),那么首頁跳轉路由時我們是能夠提前知道這個圖片的url的,所以我們看到prefetchKey3
中配置了prefetchType
為image
的預請求。image的信息來自于路由參數,需要在跳轉時將圖片url和寬高作為路由參數之一。
比如scheme://domain/path?imgUrl=${encodeUrl}&imgWidth=200
,那么根據配置項,我們將提前將encodeUrl
這個圖片以寬200,高150的尺寸,加載到內存中去。當目標頁面用到這個圖片時,將能很快渲染出來。
相反,當跳轉目標頁面時,目標頁面所要加載的圖片url沒法取到,就對應了圖片url未知的場景。
例如閃屏頁跳轉首頁時,如果需要預加載首頁頂部的圖片,此時閃屏頁是無法獲取到圖片的url的,因為這個圖片url是首頁接口返回的。這種情況下,我們只能依賴首頁的預請求進行。
在demo配置文件中,我們可以看到prefetchImgFromResponse
字段。這個字段代表著,當這個預請求響應回來之后,我需要去預請求某張圖片。其中,imgUrl
是$data.param
格式,以$data.
開頭,代表著這份數據是來自于響應數據的。響應數據就是一串json串,可以憑此,索引到預請求響應中圖片url的位置,就能實現圖片的提前加載了。
至于圖片怎么提前加載到內存中,以及真實圖片的加載怎么匹配到內存中的圖片,這一部分是通過glide已有的preload機制實現的,感興趣的同學可以去看一下源碼了解一下,這里就不展開了。后面講的預請求的方案細節,都只限于網絡請求。
預請求匹配指的是實際的業務請求怎樣與已經執行的預請求匹配上,從而節省請求的空中時間,直接返回預請求的結果。
首先網絡預請求執行前先在內存中生成一份PrefetchRecord
,代表著已經執行的預請求,其中的字段跟配置文件中差不多,主要就是記錄預請求相關的信息:
class PrefetchRecord { // 請求信息 String api; String apiVersion; String method; String needLogin; String showLoginUI; JSONObject params; JSONObject headers; // 預請求狀態 int status; // 預請求結果 ResponseModel response; // 生成的請求id String requestId; boolean isMatch(RealRequest realRequest) { requestId.equals(realRequest.requestId) } }
每一個PrefetchRecord
生成時,都會生成一個requestId
,用于跟實際業務請求進行匹配。requestId
的生成規則可以自行制定,比如將所有請求信息包一起做一下md5處理之類。
在實際業務請求發起之前,也會根據同樣的規則生成requestId
。若內存中存在相同requestId
對應的PrefetchRecord
,那么就相當于匹配成功了。匹配成功后,再根據預請求的狀態進行進一步的處理。
預請求狀態分為START、FINISH、ABORT
,對應“正在發起預請求”、“已經獲得預請求結果”、“預請求被拋棄”。ABORT
狀態下一節再講。
為什么要記錄這個狀態呢?因為我們無法保證,預請求的響應一定在實際請求之前。用圖來表示:
因為預請求是一個并發行為。當預請求的空中時間特別長,長到目標頁面已經發出實際請求了,預請求的響應還沒回來,即預請求狀態為START
,而非FINISH
。那么此時該怎么辦?我們就需要讓實際請求在一旁等著(記錄到內存中,RealRequestRecord
),等預請求接收到響應了,再根據requestId
去進行匹配,匹配到RealRequestRecord
了,就觸發RealRequestRecord
中的回調,返回數據。
另外,在匹配過程中需要注意一點,因為每次路由跳轉,如果發起預請求了,總會生成一個Record在內存中等待匹配。因此在匹配結束后,不管是匹配成功還是匹配失敗,都要及時釋放將Record從內存中釋放掉。
基于實際請求等待預請求響應的場景,我們再延伸一下。若預請求請求超時,遲遲拿不到響應,該怎么辦?用圖表示:
假設目前的網絡請求,端上默認的超時時間是30s。那么在超時場景下,實際的業務請求在30s內若拿不到預請求的結果,就需要重新發起業務請求,拋棄預請求,并將預請求的狀態置為ABORT,這樣即使后面預請求響應回來了也不做任何處理。
忽然想到一個很貼切的場景來比喻這個預請求方案。
我們把跳轉頁面理解為去柜臺取餐。
預請求代表著我們人還沒到柜臺,就先遠程下單讓柜員去準備食物。
如果柜員準備得比較快,那么我們到柜臺后就能直接把食物拿走了,就能快點吃上了(代表著頁面渲染速度變快)。
如果柜員準備得比較慢,那么我們到柜臺后還是得等一會兒才能取餐,但總體上吃上食物的速度還是要比到柜臺后再點餐來得快。
但如果這個柜員消極怠工準備得太慢了,我們到柜臺等了很久都沒拿到食物,那么我們就只能換個柜員重新點了(超時后發起實際的業務請求),同時還不忘投訴一把(預請求空中時間太慢了)。
“Android斬首行動接口預請求問題怎么解決”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。