當前位置:
首頁 > 最新 > 什麼?你使用Android原生下載也會遇到異常?

什麼?你使用Android原生下載也會遇到異常?

今日科技快訊

近日,中國互聯網路信息中心的報告顯示,截至2017年12月,中國網民規模達到7.72億,普及率達55.8%,全年共計新增網民4074萬人,其中,手機網民達7.53億。報告顯示,中國網民結構規模向高收入群體擴散,月收入在5000元以上的群體佔比較2016年增長3.7個百分點,其中月收入在5001-8000元、8000元以上的群體佔比分別佔比11.7%和8.5%。

作者簡介

本篇文章來自投稿老司機冷漠的學徒的投稿。主要介紹了在使用Android原生下載遇到的問題,希望對大家有所幫助!

冷漠的學徒的博客地址:

前言

最近在進行一次測試用例中,發現測試手機在利用本地下載功能下載0K大小的文件時,進度條一直處於進度模糊狀態中,雖然查看本地存儲路徑,發現文件已經存在,但是頁面上並沒有提示下載成功,此時只能對下載執行暫停或刪除操作。最初只是懷疑是自身應用的問題,但是在試了自己的華為暢享5s(Android5.1)、聯想S560(Android.4.2)(暴露貧窮了)及朋友的ZTE小鮮(Android6.0)、華為P9(Android7.0)等三款不同廠商的設備後,發現都有相同的現象,所以懷疑這是android自身的一個待優化點(說bug有點嚴重了,畢竟0K大小的文件誰會經常遇到呢)。在此基礎上,對AOSP的DownloadProvider進行了一番研究,源碼資源大家可以訪問http://androidxref.com/,最新的Android Oreo源碼也可以在上面查閱。

正文

下面的調查以Android7.0和Android8.0的DownloadProvider源碼為基礎展開。將探討兩個問題:

進度條樣式為什麼是進度模糊樣式?

0K文件是否真正意義上下載成功了?

Android的本地下載分為三部分:

frameworks/base/core/java/android/app/下的DownloadManager.java和frameworks/base/core/java/android/provider/下的Downloads.java

packages/apps/providers/下的DownloadProvider

frameworks/base/packages/下的DocumentsUI

其中,DocumentsUI就是我們常見的下載列表。而DownloadManager.java就是開放給開發者調用的下載器,也是系統自身使用的下載器,Downloads.java負責表路徑和下載狀態標記的管理,DownloadProvider負責下載過程中數據的處理和展示。鑒於ANDROID下載機制中,默認下載進度是以通知的形式展示的,所以我通過檢索Notification,發現只在DownloadProvider中出現了調用,那麼問題的根源可能就存在在DownloadProvider中。

DownloadProvider類目錄結構如下:

可以發現一個很明顯的DownloadNotifier.java,分析其代碼,DownloadNotifier在構造函數中創建了一個NotificationManager對象,代碼詳情如下:

更新下載通知的操作由update()(低版本中叫updateWith())來實現,該方法將被具體的下載線程進行調用,而通知樣式圍繞下載狀態標記的具體實現則是updateWithLocked()內部的一個私有方法updateWithLocked(Cursor cursor)(這個方法名一直沒有變),代碼詳情如下:

在解析updateWithLocked()方法前,先介紹下DownloadProvider是如何調用DownloadNotifier.update()方法的。DownloadProvider下載運行圖如下:

下載記錄是統一保存在Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI指向的表中,當用戶執行下載操作時,系統通過URI調起DownloadProvider.java,該類負責統一管理DownloadProvider下各類的使用,首先初始化待訪問的數據集合DownloadInfo,啟動DownloadJobService管理本次下載,DownloadJobService會把DownloadProvider提供的DownloadInfo交給DownloadThread完成更新和操作,同時具體的數據讀寫也在DownloadThread中完成。DownloadJobService會先註冊一個數據監聽器:

監聽下載過程中資料庫的變化,及時調用DownloadNotifier.update()更新進度條。等到下載結束時,DownloadThread會再一次調用DownloadNotifier.update()。這便是下載中通知變化的流程。

下面來分析下updateWithLocked()中對通知更新的實現流程,總體上可以分成三步走:

不管入參是Cursor還是ArrayList,都是通過訪問Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI(content://downloads/all_downloads)獲得當前下載記錄。針對每條數據,根據status和visibility欄位生成一個標記,作為key,一併保存到ArrayMap clustered中。

標記整體分為兩部分:下載狀態;當前調用下載的應用包名/下載記錄的ID

標記生成方法如下:buildNotificationTag(Cursor cursor)

對clustered中每一條下載記錄創建一條對應的通知對象。此時用到了一個全局變數mActiveNotifs,存放當前活躍的通知。

根據clustered中提供的標記通過getNotificationTagType方法提取中下載類型type,實際上通過getNotificationTagType的實現代碼,type就是步驟1中提到的下載狀態,getNotificationTagType實現代碼如下:

根據type的不同,通知將配置不同的文字、圖片等樣式,

刪除未更新的過期通知。即只展示當前正在下載的通知。

關鍵看步驟2中當前進度的計算這段邏輯,代碼如下:

進度值的計算是在狀態標記為TYPE_ACTIVE時才會觸發。那麼什麼時候進入TYPE_ACTIVE狀態呢,Downloads提供了詳細的下載狀態,但是DownloadManager可以更清晰的告訴我們。DownloadManager在Downloads基礎上轉化成五種下載狀態:

DownloadManager.STATUS_PENDING

DownloadManager.STATUS_RUNNING

DownloadManager.STATUS_PAUSED

DownloadManager.STATUS_SUCCESSFUL

DownloadManager.STATUS_FAILED

任何一次下載都要經過 STATUS_PENDING->STATUS_RUNNING->STATUS_SUCCESSFUL/STATUS_FAILED 這樣一個過程。重看buildNotificationTag(),你會發現TYPE_ACTIVE是通過isActiveAndVisible()生成的,代碼如下:

進度條樣式的問題到這裡就可以告一段落了,關於0K文件是否下載成功,我們可以回看DownloadThread。DownloadThread中讀寫操作是有transferData(InputStream in, OutputStream out, FileDescriptor outFd)來實現。這裡有一個入參FileDescriptor。顧名思義它會先創建一個待寫入的目標文件。transferData()中的讀寫操作如下:

由於是一個0K文件,in.read(buffer)實際上是讀不到數據的,所以len依然是-1,這就導致while循環不能完整執行,後續的寫入操作、標誌更新操作等都沒有被執行,由於又沒有異常產生,通知頁面便始終處於下載中狀態,直到超時。所以我在本地存儲路徑下看到的文件,實際上是沒有執行寫入操作的,嚴格意義上並沒有下載成功。

總結

這就是關於0K大小文件的下載異常分析,雖然在平時的文件下載需求中不大會出現0K這種現象,但不代表著永遠不會遇到,也許你下載的文件的是破損壓縮文件、上傳失敗的壓縮文件或者是標記文件,這種場景下還是要考慮進來的,畢竟存在即為合理。對於0K文件直接本地創建即可,無需執行讀寫操作。

或者掃一掃關注我的公眾號

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 郭霖 的精彩文章:

Android組件自動註冊方案

TAG:郭霖 |