使用OkHttp3網路請求的錯誤解析
使用OkHttp3網路請求時出現如下錯誤:
12-26 17:27:24.942: E/AndroidRuntime(12089): FATAL EXCEPTION: OkHttp Dispatcher 12-26 17:27:24.942: E/AndroidRuntime(12089): java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare() 12-26 17:27:24.942: E/AndroidRuntime(12089): at android.os.Handler.<init>(Handler.java:121) 12-26 17:27:24.942: E/AndroidRuntime(12089): at android.widget.Toast$TN.<init>(Toast.java:317) 12-26 17:27:24.942: E/AndroidRuntime(12089): at android.widget.Toast.<init>(Toast.java:91) 12-26 17:27:24.942: E/AndroidRuntime(12089): at android.widget.Toast.makeText(Toast.java:233) 12-26 17:27:24.942: E/AndroidRuntime(12089): at com.example.zza_android_test6.MainActivity$4.onResponse(MainActivity.java:132) 12-26 17:27:24.942: E/AndroidRuntime(12089): at okhttp3.RealCall$AsyncCall.execute(RealCall.java:126) 12-26 17:27:24.942: E/AndroidRuntime(12089): at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32) 12-26 17:27:24.942: E/AndroidRuntime(12089): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076) 12-26 17:27:24.942: E/AndroidRuntime(12089): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569) 12-26 17:27:24.942: E/AndroidRuntime(12089): at java.lang.Thread.run(Thread.java:856)
程式碼部分
/** * 豆瓣請求 String url = "https://api.douban.com/v2/book/1220562"; */ public void getDouban() { String url = "https://api.douban.com/v2/book/1220562"; OkHttpClient okHttpClient = new OkHttpClient(); Request request = new Request.Builder().url(url).build(); okHttpClient.newCall(request).enqueue(new Callback() { @Override public void onResponse(Call call, Response response) throws IOException { // TODO Auto-generated method stub String json = response.body().string(); text.setText("json:"+json); } @Override public void onFailure(Call call, IOException e) { // TODO Auto-generated method stub Log.e(TAG, e.getMessage()); } }); }
解決方法:注意這裡是//注意是string(),不是toString(),之前就在出了錯誤列印了toString()
/** * 豆瓣請求 String url = "https://api.douban.com/v2/book/1220562"; */ public void getDouban() { String url = "https://api.douban.com/v2/book/1220562"; OkHttpClient okHttpClient = new OkHttpClient(); Request request = new Request.Builder().url(url).build(); okHttpClient.newCall(request).enqueue(new Callback() { @Override public void onResponse(Call call, Response response) throws IOException { // TODO Auto-generated method stub final String json = response.body().string(); runOnUiThread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub text.setText("json:"+json); } }); } @Override public void onFailure(Call call, IOException e) { // TODO Auto-generated method stub Log.e(TAG, e.getMessage()); } }); }
這是因為Okhttp3與2.x版本並沒有什麼不同,回撥仍然不在UI執行緒,所以需要通過runOnUiThread進行ui更新。
(ui更新必須在主執行緒,所以ui執行緒又叫主執行緒;耗時操作不能再主執行緒,注意耗時操作與ui更新操作的區別)
耗時操作:
1.下載檔案操作
2.網路連線操作(尤其是網路不好的時候)
3.音訊格式轉換操作
4.檔案操作
5.比較大的資料的初始化操作
6.sleep函式等
UI執行緒
應用的主UI執行緒的概念及其重要性是每個Android開發者都應理解。當一個應用啟動,系統會為應用建立一個名為“main”的主執行緒。這個主執行緒(也就是UI主執行緒)主要負責把事件分發給合適的view或者widget, 因此它非常重要。它也是你的應用和應用的UI互動的執行緒。例如,如果你點選了螢幕上的一個按鈕,UI執行緒會把點選時間交給view處理,view接到事件後會設定它的pressed狀態,然後向事件佇列中傳送一個invalidate請求。 UI執行緒會依次讀取佇列並且告訴view去重繪自己。
除非你的Android應用實現的非常合理,否則這個單執行緒模型會使效能變得極低。在極端情況下,如果UI執行緒負責整個應用中的所有操作,進行耗時的操作比如傳送網路請求,或者資料庫查詢等都會導致使用者介面的阻塞。這些操作在未完成之前,所有的時間包括繪製和觸屏事件都不會被派發。從使用者的角度來看,程式似乎是卡死了。
在這些情況下,即時的反饋相當重要。研究表明0.1s是使用者感覺系統是否流暢的臨界值。任何比臨界值更慢的都被認為延遲(Miller 1968; Card et al. 1991)。雖然1秒看起來沒什麼影響,但在GooglePlay中,即便是十分之一秒也可能是好評和差評的區別。更糟糕的是,如果UI執行緒被阻塞5秒以上,使用者會收到“程式未響應”(ANR)的提示對話方塊,並且會強制退出。