1. 網路請求框架-OkHttp原理解析
okhttp是square公司貢獻的一個處理網路請求的開源框架,是目前Android開發使用最廣泛的一個網路框架,從Android4.4開始,httpURLconnection的底層實現採用的就是okhttp。內部實現就是利用java基礎,對socket進行封裝,實現http通信。最重要的兩個關鍵點就是分發器和5個攔截器。
分發器 就是內部維護隊列和線程池,完成請求分配,總結就是用於對非同步任務加入隊列管理,然後判斷條件,控制數量,加入線程池執行非同步請求任務。
五個默認攔截器 就是利用責任鏈模式對網路請求進行層層處理,完成整個請求過程,簡單總結如下。
1.橋接攔截器對用戶發出的請求添加缺少的請求配置欄位,比如keep-alive等
2.緩存攔截器就是查詢有沒有符合判斷條件的已緩存的網路請求,執行復用,直接返回response
3.連接攔截器就是創建請求,加入連接器 或者訪問連接池,根據條件判斷,是否能懟已創建的tcp請求進行復用
4.請求伺服器攔截器就是對scoket進行操作,請求網路訪問伺服器,返回response,
5.重試和重定向攔截器就是對返回的response進行code判斷,決定是否要重試或者重定向操作。
1.支持http2.0版本,並且允許對同一主機的所有請求共享一個套接字
2.即使不是http2.0版本,通過連接池,減少請求延遲
3.默認使用Gzip 壓縮數據
4.響應緩存,避免重復請求網路
最簡單的http請求案例
1.利用建造者模式構建okHttpClient實例對象,構建過程中可以動態配置參數,請求時間,響應時間,緩存信息等。
2.創建Request對象,設置請求方式,鏈接地址,參數等信息。
3.把request對象,傳給client,通過newCall函數,得到RealCall對象。
4.RealCall 分為同步和非同步執行
5.同步執行時,分發器只是做個記錄,把請求任務加到隊列中,然後直接通過攔截器訪問伺服器,返回response。
6.非同步執行
6.1先對非同步任務進一步封裝,把任務放到AsyncCall對象中
2.分發器 把 封裝後的非同步任務 添加到等待運行的隊列中
7. 通過攔截器,獲取response
okhttp 默認提供5個攔截器 重試重定向攔截器,橋接攔截器,緩存攔截器,連接攔截器,訪問伺服器攔截器。還可以自定義攔截器。
自定義攔截器分為應用攔截器(通過addInterceptor 添加)和網路攔截器(通過addNetworkInterceptor攔截)
攔截器採用責任鏈的設計默認,讓請求者和處理者解耦,最終請求從前往後,響應從後往前。
首先先判斷用戶是否取消了請求,如果沒有取消,就把請求交個橋接攔截器。
在獲得響應結果response的時候根據響應碼,判斷是否需要重試或者重定向, 重試不限制次數,重定向最多20次 ,如果需要重試或者重定向,那麼會再一次重新執行所有攔截器。
有如下幾種情況不會重試:IO異常,線路異常,配置client實例時配置不允許重試,協議異常,證書異常等等。
先獲取用戶發送的請求,判斷條件用戶是否已經配置過請求頭欄位,若用戶沒有配置,則將http協議必備的請求頭欄位補齊,比如Content-Type,Content-Length等,然後交給下一個攔截器。
在獲得響應結果response之後,調用保存cookie的介面(也可以在配置client的時候,設置cookjar進行cookie回調數據),並且解析gzip數據
獲取結果之後,對cookie進行保存,對返回的數據進行gzip解壓
就是根據緩存策略從緩存中查找是否有合適的緩存response,如果有合適的緩存,直接返回給請求任務,不在繼續執行後面的攔截器。
獲得響應結果response後,根據條件判斷,決定是否要緩存。
維護一個連接池,負責對連接的服務。在把請求交給下一個攔截器之前。會先在連接池中找到一個合適的連接(滿足適配條件相同,並且沒有正在被使用)或者新建一個連接,並且接入連接池,獲得對應的socket流,把請求交給下一個攔截器。獲得response結果後不會進行額外的處理。
連接池, 也稱之為對象池,主要用來存放request請求連接,內部維護了一個LinkedQueue隊列用來存放請求。在添加新的請求對象時,都會執行一個周期性任務,用以對連接池進行清理操作。
1.隊列長度超過5,清理最近未被使用連接,LRE演算法
2.存儲的連接,5分鍾未被復用,清理
拿到上一個攔截器返回的請求,真正的與伺服器進行通信,向伺服器發送數據,解析讀取響應的數據,返回給上一個攔截器。
1.創建request =>OkHttpClient=>RealCall()
2.同步執行 ,分發器添加同步任務,執行攔截器,訪問伺服器,返回reponse,觸發非同步分發流程。
3.非同步執行 ,封裝任務= >AsyncCall ,實現runnable介面。添加任務到非同步任務等待隊列,執行分發任務,判斷非同步任務是否能加入正在執行的非同步任務隊列,滿足兩個條件
同時執行的非同步任務數量不得大於64個
對同一個主機的訪問任務,最多不得大於5個
4.加入正在執行的非同步任務隊列,通過線程池執行任務,經過5個默認攔截器訪問伺服器,返回response,執行非同步任務分發。
分發器工作 分為同步任務和非同步任務兩種
同步任務 就是把任務加入同步任務隊列,加個標記,執行結束之後,觸發非同步任務的分發操作。
非同步任務 先封裝任務到asyncCall對象,實現了runnable介面。把任務加入等待執行隊列,執行分發操作。
先遍歷等待任務隊列,判斷是否符合加入正在運行的非同步任務隊列,要同時滿足兩個條件。
同時執行的非同步任務數量不得大於64個
對同一個主機的訪問任務,最多不得大於5個
當滿足條件後,從等待隊列中刪除任務,把任務加入正在執行的隊列中,通過自定義的線程池,執行任務,任務執行結束後,再次執行分發操作。
攔截器採用了責任鏈設計默認,讓請求者和執行者解耦,請求者只需要將請求發給責任鏈即可,無需關心請求過程和細節。okHttp 默認有5個攔截器,重試重定向攔截器,橋接攔截器,緩存攔截器,連接攔截器,請求服務攔截器。工作細節參考上面攔截器原理分析部分
1.位置的關系,應用攔截器 放在責任鏈最頂端,網路攔截器放在責任鏈倒數第二的位置。所以應用攔截器 最先攔截,最後響應,網路攔截器 倒數第二攔截,第二響應。如果列印請求日誌的情況,應用攔截器列印的是用戶請求信息,經過重試重定向,橋接,緩存,鏈接 等攔截器的層層包裝,網路攔截器列印的是實際請求的信息。
2.應用攔截器一定會被執行,網路攔截器不一定被執行。
利用連接池,緩存所有的有效連接對象。
清理機制:垃圾連接
1.超過5分鍾沒有用過的鏈接
2.超過5個閑置鏈接後,從最久閑置的鏈接開始執行清理(LRU)
2. 如何解決EnterLib異常處理框架最大的局限——基於異常"類型"的異常處理策略
但是,在我看來,EHAB有一個最大的局限,把就是異常處理策略的粒度過大——只能提供基於異常類型級別。本篇文章通過一個自定義ExceptionHandler很好地解決了這個問題。一、EnterLib基於異常類型的異常處理策略EnterLib的異常處理策略基本上可以通過這樣的的公式來表示:Exception Policy = Exception Type + Exception Handlers + Post Handling Action,它表達的意思是:「對於某種類型的異常,應該採用哪些Exception Handler去處理,而被處理後的異常還需要採用怎樣的後續操作(將異常吃掉、或者是重新拋出)」。也就是說,拋出類型的異常類型決定了最終採取的處理策略,這在大部分情況下是可以接受的。但是在很多場景中,不同情況下也可以拋出相同類型的異常,我們期望的行為是:盡管異常類型一樣,我們也可以根據具體拋出的異常定義不同的異常處理策略。一個最為典型的場景就是基於資料庫的數據存取,如果你採用的SQL Server,拋出的異常永遠只有一種:SqlException。如果完全按照EnterLib EHAB的做法,在任何情況下拋出的SqlException對應的處理方式都是一樣的。但是拋出SqlException的情況非常多,比如Server連接斷開、認證失敗、資料庫對象不存在、違反一致性約束等等,如果異常處理框架能夠根據最終拋出的異常的具體屬性,「智能」地應用相應的策略去處理,這才是我們樂於看到的。二、一個特殊的ExceptionHandler——FilterableHandler為了解決這個問題,我創建了一個特殊的Exception Handler,我將它起名為FilterableHandler。說它特別,是因為FilterableHandler並不從事具體的異常處理操作(比如異常封裝、替換、日誌等),而是為某個具體的異常類型重新定義了異常處理策略。實際上,我在很早之前就定義了一個相似的FilterableHandler,有興趣的話可以參考《創建一個自定義Exception Handler改變ELAB的異常處理機制》。由於在最新的EnterLib中,底層的實現機制發生了根本性的改變,這個ExceptionHandler已經不能在使用。所以我對其進行的了修正,同時根據可擴展性進行重新設計。之所以稱這個ExceptionHandler為FilterableHandler,是在於它具有對拋出的異常具有「篩選」的功能。說得具體點,FilterableHandler將拋出的異常對象,傳入一組具有篩選功能的ExceptionHandler列表,我個人將這個列表命名為。對象包含一個篩選器和一組ExceptionHandler,如果傳入的異常通過了篩選器的篩選,該異常最終被分發到相應的ExceptionHandler列表中進行處理。大概的定義如下: 1:publicclass FilterableHandlerPipeline 2: { 3:public IFilter Filter { get; private set; } 4:public IEnumerable ExceptionHandlers { get; private set; } 5: 6:public FilterableHandlerPipeline(IFilter filter, IEnumerable exceptionHandlers) 7: { 8: Guard.ArgumentNotNull(filter, "filter"); 9: Guard.ArgumentNotNull(exceptionHandlers, "exceptionHandlers"); 10:this.Filter = filter; 11:this.ExceptionHandlers = exceptionHandlers; 12: } 13: }而IFilter介面在更為簡單,僅僅具有如下一個唯一的Match方法。布爾類型的返回值表明是否和指定的異常相匹配,當返回值為True的時候,採用用自己的ExceptionHandler列表去處理拋出的異常,否則就直接忽略掉。 1:publicinterface IFilter 2: { 3:bool Match(Exception ex); 4: }你可以從下面給出的關於FilterableHandler的完整的代碼去分析具體的異常處理實現原理。而實際上,最為復雜的不是FilterableHandler本身的實現,而是與之相關的配置元素的定義。由於這會涉及到很多關於EnterLib底層和Unity相關的知識點,不是三言兩語就能講明白的,所以在這里就不對FilterableHandler的配置體系作介紹了,有興趣的話可以通過這里直接下載源代碼。 1: [ConfigurationElementType(typeof(FilterableHandlerData))] 2:publicclass FilterableHandler:IExceptionHandler 3: { 4:public IEnumerable FilterableHandlerPipelines { get; private set; } 5:public IEnumerable DefaultHandlers { get; private set; } 6: 7:public FilterableHandler(IEnumerable filterableHandlerPipelines, IEnumerable defaultHandlers) 8: { 9: Guard.ArgumentNotNull(defaultHandlers, "defaultHandlers"); 10: filterableHandlerPipelines = filterableHandlerPipelines ? new List(); 11:this.FilterableHandlerPipelines = filterableHandlerPipelines; 12:this.DefaultHandlers = defaultHandlers; 13: } 14:public Exception HandleException(Exception exception, Guid handlingInstanceId) 15: { 16: Guard.ArgumentNotNull(exception,"exception"); 17: var handlerPipeline = (from pipeline inthis.FilterableHandlerPipelines 18:where pipeline.Filter.Match(exception) 19: select pipeline).FirstOrDefault(); 20:if (null != handlerPipeline) 21: { 22:return ExecuteHandlerChain(exception, handlingInstanceId, handlerPipeline.ExceptionHandlers); 23: } 24:else 25: { 26:return ExecuteHandlerChain(exception, handlingInstanceId, DefaultHandlers); 27: } 28: } 29: 30:privatestatic Exception ExecuteHandlerChain(Exception exception, Guid handlingInstanceId, IEnumerable handlers) 31: { 32: var lastHandlerName = String.Empty; 33:try 34: { 35:foreach (var handler in handlers) 36: { 37: lastHandlerName = handler.GetType().Name; 38: exception = handler.HandleException(exception, handlingInstanceId); 39: } 40: } 41:catch (Exception ex) 42: { 43: var errorMsg = string.Format("Unable to handle the exception: {0}", lastHandlerName); 44:thrownew ExceptionHandlingException(errorMsg, ex); 45: } 46:return exception; 47: } 48: }三、通過FilterableHandler對SqlException進行針對性處理我現在通過一個簡單的例子來演示FilterableHandler如何使用(源代碼從這里下載),我們使用的場景就是上面提到過的對SqlException的針對性處理。根據SqlException拋出的場景,本例將起分為三種類型:系統異常:基於SQL Server自身拋出的異常,我們將異常編號,即SqlException的Number小於50000的稱為系統異常; 業務異常:編程人員根在編寫SQL腳本的時候,根據相應的業務邏輯,通過調用RAISERROR語句手工拋出的異常。在默認情況下這種異常的編號為50000; 其他:任何編號高於50000的異常。 註:關於RAIERROR語句以及SQL Server異常處理相關的內容,你可以參閱我下面三篇文章:談談基於SQL Server的Exception Handling - PART I談談基於SQL Server 的Exception Handling - PART II談談基於SQL Server 的Exception Handling - PART III為了對SqlException進行針對處理,我們對拋出的SqlException進行封裝。對應於上述三種類型,我定義如如下三種異常:SqlSystemException、SqlBusinessException和DbException。 1:namespace Artech.ExceptionHandling.Demo 2: { 3: [Serializable] 4:publicclass SqlSystemException : Exception 5: { 6:public SqlSystemException() { } 7:public SqlSystemException(string message) : base(message) { } 8:public SqlSystemException(string message, Exception inner) : base(message, inner) { } 9:protected SqlSystemException( 10: System.Runtime.Serialization.SerializationInfo info, 11: System.Runtime.Serialization.StreamingContext context) 12: : base(info, context) { } 13: } 14: 15: [Serializable] 16:publicclass SqlBusinessException : Exception 17: { 18:public SqlBusinessException() { } 19:public SqlBusinessException(string message) : base(message) { } 20:public SqlBusinessException(string message, Exception inner) : base(message, inner) { } 21:protected SqlBusinessException( 22: System.Runtime.Serialization.SerializationInfo info, 23: System.Runtime.Serialization.StreamingContext context) 24: : base(info, context) { } 25: } 26: 27: [Serializable] 28:publicclass DbException : Exception 29: { 30:public DbException() { } 31:public DbException(string message) : base(message) { } 32:public DbException(string message, Exception inner) : base(message, inner) { } 33:protected DbException( 34: System.Runtime.Serialization.SerializationInfo info, 35: System.Runtime.Serialization.StreamingContext context) 36: : base(info, context) { } 37: } 38: }我們需要作的進行通過配置定義處理SqlException的處理策略,整個配置定義在如下的代碼片斷中。 1:
3. 做什麼transactiontoolargeexception問題,怎麼解決
AndroidN除了提供諸多新特性和功能外,還對系統和API行為做出了各種變更。本文重點介紹您應該了解並在開發應用時加以考慮的一些重要變更。如果您之前發布過Android應用,請注意您的應用可能受到這些平台變更的影響。電池和內存AndroidN包括旨在延長設備電池壽命和減少RAM使用的系統行為變更。這些變更可能會影響您的應用訪問系統資源,以及您的系統通過特定隱式Intent與其他應用互動的方式。低電耗模式Android6.0(API級別23)引入了低電耗模式,當用戶設備未插接電源、處於靜止狀態且屏幕關閉時,該模式會推遲CPU和網路活動,從而延長電池壽命。而AndroidN則通過在設備未插接電源且屏幕關閉狀態下、但不一定要處於靜止狀態(例如用戶外出時把手持式設備裝在口袋裡)時應用部分CPU和網路限制,進一步增強了低電耗模式。圖1.低電耗模式如何應用第一級系統活動限制以延長電池壽命的圖示。當設備處於充電狀態且屏幕已關閉一定時間後,設備會進入低電耗模式並應用第一部分限制:關閉應用網路訪問、推遲作業和同步。如果進入低電耗模式後設備處於靜止狀態達到一定時間,系統則會對PowerManager.WakeLock、AlarmManager鬧鈴、GPS和Wi-Fi掃描應用餘下的低電耗模式限制。無論是應用部分還是全部低電耗模式限制,系統都會喚醒設備以提供簡短的維護時間窗口,在此窗口期間,應用程序可以訪問網路並執行任何被推遲的作業/同步。圖2.低電耗模式如何在設備處於靜止狀態達到一定時間後應用第二級系統活動限制的圖示。請注意,激活屏幕或插接設備電源時,系統將退出低電耗模式並取消這些處理限制。此項新增的行為不會影響有關使您的應用適應Android6.0(API級別23)中所推出的舊版本低電耗模式的建議和最佳實踐,如低電耗模式和應用待機模式優化中所討論。您仍應遵循這些建議(例如使用GoogleCloudMessaging(GCM)發送和接收消息)並開始安排更新計劃以適應新增的低電耗模式行為。ProjectSvelte:後台優化AndroidN刪除了三項隱式廣播,以幫助優化內存使用和電量消耗。此項變更很有必要,因為隱式廣播會在後台頻繁啟動已注冊偵聽這些廣播的應用。刪除這些廣播可以顯著提升設備性能和用戶體驗。移動設備會經歷頻繁的連接變更,例如在Wi-Fi和移動數據之間切換時。目前,可以通過在應用清單中注冊一個接收器來偵聽隱式CONNECTIVITY_ACTION廣播,讓應用能夠監控這些變更。由於很多應用會注冊接收此廣播,因此單次網路切換即會導致所有應用被喚醒並同時處理此廣播。同理,應用可以注冊接收來自其他應用(例如相機)的隱式ACTION_NEW_PICTURE和ACTION_NEW_VIDEO廣播。當用戶使用相機應用拍攝照片時,這些應用即會被喚醒以處理廣播。為緩解這些問題,AndroidN應用了以下優化措施:面向AndroidN開發的應用不會收到CONNECTIVITY_ACTION廣播,即使它們已有清單條目來請求接受這些事件的通知。在前台運行的應用如果使用BroadcastReceiver請求接收通知,則仍可以在主線程中偵聽CONNECTIVITY_CHANGE。應用無法發送或接收ACTION_NEW_PICTURE或ACTION_NEW_VIDEO廣播。此項優化會影響所有應用,而不僅僅是面向AndroidN的應用。如果您的應用使用任何Intent,您仍需要盡快移除它們的依賴關系,以正確適配AndroidN設備。Android框架提供多個解決方案來緩解對這些隱式廣播的需求。例如,JobSchelerAPI提供了一個穩健可靠的機制來安排滿足指定條件(例如連入無限流量網路)時所執行的網路操作。您甚至可以使用JobScheler來適應內容提供程序變化。如需了解有關AndroidN中後台優化以及如何改寫應用的詳細信息,請參閱後台優化。許可權更改AndroidN做了一些許可權更改,這些更改可能會影響您的應用。系統許可權更改為了提高私有文件的安全性,面向AndroidN或更高版本的應用私有目錄被限制訪問(0700)。此設置可防止私有文件的元數據泄漏,如它們的大小或存在。此許可權更改有多重副作用:私有文件的文件許可權不應再由所有者放寬,為使用MODE_WORLD_READABLE和/或MODE_WORLD_WRITEABLE而進行的此類嘗試將觸發SecurityException。註:迄今為止,這種限制尚不能完全執行。應用仍可能使用原生API或FileAPI來修改它們的私有目錄許可權。但是,我們強烈反對放寬私有目錄的許可權。傳遞軟體包網域外的file://URI可能給接收器留下無法訪問的路徑。因此,嘗試傳遞file://URI會觸發FileUriExposedException。分享私有文件內容的推薦方法是使用FileProvider。DownloadManager不再按文件名分享私人存儲的文件。舊版應用在訪問COLUMN_LOCAL_FILENAME時可能出現無法訪問的路徑。面向AndroidN或更高版本的應用在嘗試訪問COLUMN_LOCAL_FILENAME時會觸發SecurityException。通過使用DownloadManager.Request.()或DownloadManager.Request.()將下載位置設置為公共位置的舊版應用仍可以訪問COLUMN_LOCAL_FILENAME中的路徑,但是我們強烈反對使用這種方法。訪問由DownloadManager公開的文件的首選方式是使用ContentResolver.openFileDescriptor()。應用間共享文件對於面向AndroidN的應用,Android框架執行的StrictModeAPI政策禁止向您的應用外公開file://URI。如果一項包含文件URI的Intent離開您的應用,應用失敗,並出現FileUriExposedException異常。若要在應用間共享文件,您應發送一項content://URI,並授予URI臨時訪問許可權。進行此授權的最簡單方式是使用FileProvider類。如需有關許可權和共享文件的信息,請參閱共享文件。無障礙改進為提高平台對於視力不佳或視力受損用戶的可用性,AndroidN做出了一些更改。這些更改一般並不要求更改您的應用代碼,不過您應仔細檢查並使用您的應用測試這些功能,以評估它們對用戶體驗的潛在影響。屏幕縮放AndroidN支持用戶設置顯示尺寸,以放大或縮小屏幕上的所有元素,從而提升設備對視力不佳用戶的可訪問性。用戶無法將屏幕縮放至低於最小屏幕寬度sw320dp,該寬度是Nexus4的寬度,也是常規中等大小手機的寬度。圖3.右側屏幕顯示的是一台運行AndroidN系統映像的設備增大顯示尺寸後的效果。當設備密度發生更改時,系統會以如下方式通知正在運行的應用:如果是面向API級別23或更低版本系統的應用,系統會自動終止其所有後台進程。這意味著如果用戶切換離開此類應用,轉而打開「Settings」屏幕並更改Displaysize設置,則系統會像處理內存不足的情況一樣終止該應用。如果應用具有任何前台進程,則系統會如處理運行時變更中所述將配置變更通知給這些進程,就像對待設備屏幕方向變更一樣。如果是面向AndroidN的應用,則其所有進程(前台和後台)都會收到有關配置變更的通知,如處理運行時變更中所述。大多數應用並不需要進行任何更改即可支持此功能,不過前提是這些應用遵循Android最佳實踐。具體要檢查的事項:在屏幕寬度為sw320dp的設備上測試您的應用,並確保其充分運行。當設備配置發生變更時,更新任何與密度相關的緩存信息,例如緩存點陣圖或從網路載入的資源。當應用從暫停狀態恢復運行時,檢查配置變更。註:如果您要緩存與配置相關的數據,則最好也包括相關元數據,例如該數據對應的屏幕尺寸或像素密度。保存這些元數據便於您在配置變更後決定是否需要刷新緩存數據。避免用像素單位指定尺寸,因為像素不會隨屏幕密度縮放。應改為使用與密度無關像素(dp)單位指定尺寸。設置向導中的視覺設置AndroidN在「Welcome」屏幕中加入了「VisionSettings」,用戶可以在新設備上設置以下無障礙功能設置:Magnificationgesture、Fontsize、Displaysize和TalkBack。此項變更增強了與不同屏幕設置相關的錯誤的可見性。要評估此功能的影響,您應在啟用這些設置的狀態下測試應用。您可以在Settings>Accessibility中找到這些設置。NDK應用鏈接至平台庫AndroidN做了一些命名空間更改,以阻止載入非公開API。如果您使用NDK,則只能使用Android平台提供的公開API。在下一個官方發布的Android版本上使用非公開API會導致應用崩潰。為提醒您使用了非公開API,在AndroidN設備上運行的應用會在有應用調用非公開API時在日誌消息輸出中生成一個錯誤。此錯誤還會作為消息顯示在設備屏幕上,以幫助增強您對此情況的認識。您應檢查應用代碼以刪除使用非公開平台API,並使用預覽版設備或模擬器全面測試應用。如果您的應用依賴平台庫,則請參見NDK文檔,了解使用公開API等效項替換普通私有API的典型修復。您還可以鏈接至平台庫,而無需實現此應用,如果應用使用的庫是平台的一部分(例如libpng),但不屬於NDK,則更可如此。此情況下,請確保您的APK包含您打算鏈接到的所有.so文件。注意:有些第三方庫可能會鏈接至非公開API。如果您的應用使用這些庫,那麼當您的應用在下一個官方發布的Android版本上運行時可能會出現崩潰現象。應用不應依賴或使用不屬於NDK的原生庫,因為這些庫可能會發生更改或從一個Android版本遷移至另一版本。例如,從OpenSSL切換至BoringSSL即屬於此類更改。此外,不同的設備可能提供不同級別的兼容性,因為不屬於NDK中的平台庫沒有兼容性要求。如果您必須在較舊設備上訪問非NDK庫,則請依據AndroidAPI級別進行載入。為幫助您診斷此類問題,下面列舉了一些在您試圖使用AndroidN開發應用時可能遇到的Java和NDK錯誤:Java錯誤示例:java.lang.UnsatisfiedLinkError:dlopenfailed:library"/system/lib/libcutils.so""classloader-namespace"NDK錯誤示例:dlopenfailed:cannotlocatesymbol"__system_property_get"referencedby以下是遇到這類錯誤的應用的一些典型修復:可以使用標准JNI函數來替代使用libandroid_runtime.so中的getJavaVM和getJNIEnv:AndroidRuntime::getJavaVM->GetJavaVMfromAndroidRuntime::getJNIEnv->JavaVM::GetEnvorJavaVM::AttachCurrentThreadfrom.可以使用公開alternative__system_property_get來替代使用libcutils.so中的property_get符號。如需這樣做,請使用__system_property_get及以下include函數:#include應使用應用本地版本來替代使用libcrypto.so中的SSL_ctrl符號。例如,您應在.so文件中靜態鏈接libcyrpto.a,或者在應用中包含您自己的來自BoringSSL或OpenSSL的動態libcrypto.so。AndroidforWorkAndroidN包含一些針對面向AndroidforWork的應用的變更,包括對證書安裝、密碼重置、二級用戶管理、設備標識符訪問許可權的變更。如果您是要針對AndroidforWork環境開發應用,則應仔細檢查這些變更並相應地修改您的應用。您必須先安裝授權證書安裝程序,然後DPC才能對其進行設置。對於面向NSDK的個人資料和設備所有者應用,您應在設備策略控制器(DPC)調用DevicePolicyManager.setCertInstallerPackage()之前安裝授權證書安裝程序。如果尚未安裝此安裝程序,則系統會引發IllegalArgumentException。針對設備管理員的重置密碼限制現在也適用於個人資料所有者。設備管理員無法再使用DevicePolicyManager.resetPassword()來清除或更改已經設置的密碼。設備管理員仍可以設置密碼,但只能在設備沒有密碼、PIN或圖案時這樣做。即使設置了限制,設備所有者和個人資料所有者仍可以管理帳戶。而且,即使具有DISALLOW_MODIFY_ACCOUNTS用戶限制,設備所有者和個人資料所有者仍可調用AccountManagementAPI。設備所有者可以更輕松地管理二級用戶。當設備在設備所有者模式下運行時,系統將自動設置DISALLOW_ADD_USER限制。這樣可以防止用戶創建非託管二級用戶。此外,CreateUser()和createAndInitializeUser()方法已棄用,取而代之的是DevicePolicyManager.createAndManageUser()方法。設備所有者可以訪問設備標識符。設備所有者可以使用DevicePolicyManagewr.getWifiMacAddress()訪問設備的Wi-FiMAC地址。如果設備上從未啟用Wi-Fi,則此方法將返回一個null值。工作模式設置控制工作應用訪問。當工作模式關閉時,系統啟動器通過使工作應用顯示為灰色來指示它們不可用。啟用工作模式會再次恢復正常行為。如需了解有關AndroidN中針對AndroidforWork所做變更的詳細信息,請參閱AndroidforWork更新。註解保留AndroidN在註解可見性被忽略時修復錯誤。這種問題將啟用本不應被允許的運行時訪問註解。這些註解包括:VISIBILITY_BUILD:僅應編譯時可見。VISIBILITY_SYSTEM:運行時應可見,但僅限基本系統。如果您的應用依賴這種行為,請在註解中添加一項運行時必須可用的保留政策。您可通過使用@Retention(RetentionPolicy.RUNTIME)來如此做。其他重要說明如果一個應用在AndroidN上運行,但卻是針對更低API級別開發的,那麼在用戶更改顯示尺寸時,系統將終止此應用進程。應用必須能夠正常處理此情景。否則,當用戶從最近使用記錄中恢復運行應用時,應用將會出現崩潰現象。您應測試應用以確保不會發生此行為。要進行此測試,您可以通過DDMS手動終止應用,以造成相同的崩潰現象。在密度發生更改時,系統不會自動終止面向N及更高版本的應用;不過,這些應用仍可能對配置變更做出不良響應。AndroidN上的應用應能夠正常處理配置變更,並且在後續啟動時不會出現崩潰現象。您可以通過更改字體大小(Setting>Display>Fontsize)並隨後從最近使用記錄中恢復運行應用,來驗證應用行為。由於之前的Android版本中的一項錯誤,系統未能將對主線程上的一個TCP套接字的寫入操作舉報為嚴格模式違反。AndroidN修復了此錯誤。呈現出這種行為的應用引發android.os.NetworkOnMainThreadException。一般情況下,我們不建議在主線程上執行網路操作,因為這些操作通常都有可能導致ANR和卡頓的高尾延遲。Debug.startMethodTracing()方法族現在默認在您的共享的存儲空間上的軟體包特定目錄中存儲輸出,而非SD卡頂級。這意味著應用不再需要請求WRITE_EXTERNAL_STORAGE使用這些API的許可權。許多平台API現在開始檢查在Binder事務間發送的大負載,系統現在會將TransactionTooLargeExceptions再次作為RuntimeExceptions引發,而不再只是默默記錄或抑制它們。一個常見例子是在Activity.onSaveInstanceState()上存儲過多數據,導致ActivityThread.StopInfo在您的應用面向AndroidN時引發RuntimeException。如果應用向View發布Runnable任務,並且View未附加到窗口,系統會用View為Runnable任務排隊;在View附加到窗口之前,Runnable任務不會執行。此行為會修復以下錯誤:如果一項應用是從並非預期窗口UI線程的其他線程發布到View,則Runnable可能會因此運行錯誤的線程。如果Runnable任務是從並非環路線程的其他線程發布,則應用可能會曝光Runnable任務。如果AndroidN上一項有DELETE_PACKAGES許可權的應用嘗試刪除一個軟體包,但另一項應用已經安裝了這個軟體包,則系統可能要求用戶確認。在這種情況下,應用在調用PackageInstaller.uninstall()時的返回狀態應為STATUS_PENDING_USER_ACTION。
4. 安卓anr和crash的區別
CRASH是程序崩潰的意思,ANR是經常遇到的CRASH報錯,ANR就是內存溢出,比如圖片載入過大,線程開太多就會出現這個錯誤,可以這么理解,ANR是CRASH的一種而已。
5. android客戶端運行報錯,怎麼解決
1、可以通過一些第三方框架來採集錯誤日誌
2、可以自己寫 UncaughtException處理類,當程序發生Uncaught異常的時候,有該類來接管程序,並記錄發送錯誤報告. 然後將日誌上傳伺服器或者發送到你的郵箱什麼的
3、寫代碼的時候可以使用try catch的方式,這樣保證不會隨意崩潰