1. 程式人生 > >使用OkHttp3網路請求的錯誤解析

使用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)的提示對話方塊,並且會強制退出。