學習Android過程中遇到的問題及解決方法——網路請求
在學習Android的網路連線時遇到崩潰或IOException異常(出現的問題就這兩個,但是不穩定)的問題,先上程式碼,看看哪裡錯了(答案在文末)
activity_main.xml:
1 <?xml version="1.0" encoding="utf-8"?> 2 <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 xmlns:app="http://schemas.android.com/apk/res-auto"4 xmlns:tools="http://schemas.android.com/tools" 5 android:layout_width="match_parent" 6 android:layout_height="match_parent" 7 tools:context=".MainActivity"> 8 9 <LinearLayout 10 android:layout_width="match_parent" 11 android:layout_height="match_parent"12 android:orientation="vertical"> 13 14 15 <EditText 16 android:id="@+id/et_path" 17 android:layout_width="match_parent" 18 android:layout_height="wrap_content" 19 android:hint="請輸入網址" 20 android:text="https://www.baidu.com/"/> 21 22 <Button 23 android:layout_width="wrap_content" 24 android:layout_height="wrap_content" 25 android:onClick="click" 26 android:text="檢視" /> 27 28 <ScrollView 29 android:layout_width="match_parent" 30 android:layout_height="match_parent"> 31 32 <TextView 33 android:id="@+id/tv_result" 34 android:layout_width="match_parent" 35 android:layout_height="match_parent" /> 36 </ScrollView> 37 38 </LinearLayout> 39 40 </android.support.constraint.ConstraintLayout>
MainActivity.xml:
1 import android.content.Context; 2 import android.os.Handler; 3 import android.os.Message; 4 import android.support.v7.app.AppCompatActivity; 5 import android.os.Bundle; 6 import android.view.View; 7 import android.widget.EditText; 8 import android.widget.TextView; 9 import android.widget.Toast; 10 11 import java.io.IOException; 12 import java.io.InputStream; 13 import java.net.HttpURLConnection; 14 import java.net.MalformedURLException; 15 import java.net.ProtocolException; 16 import java.net.URL; 17 18 public class MainActivity extends AppCompatActivity { 19 20 private EditText et_path; 21 private TextView tv_result; 22 23 @Override 24 protected void onCreate(Bundle savedInstanceState) { 25 super.onCreate(savedInstanceState); 26 setContentView(R.layout.activity_main); 27 28 et_path = findViewById(R.id.et_path); 29 tv_result = findViewById(R.id.tv_result); 30 31 } 32 33 //點檢就進行檢視指定路徑的原始碼 34 public void click(View view) { 35 36 try { 37 //獲取原始碼路徑 38 String path = et_path.getText().toString().trim(); 39 //建立url物件制定我們要訪問的路徑 40 URL url = new URL(path); 41 //拿到httpUrlConnection物件 42 HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection(); 43 //設定請求方法 44 httpURLConnection.setRequestMethod("GET");//GET要求大寫,預設get請求 45 //設定請求時間 46 httpURLConnection.setConnectTimeout(5000); 47 //獲取伺服器返回的狀態碼 48 int code = httpURLConnection.getResponseCode(); 49 //如果code = 200,說明請求成功 50 if (code == 200) { 51 //獲取伺服器返回的資料,是以流的形式返回的 52 InputStream in = httpURLConnection.getInputStream(); 53 System.out.println(); 54 55 //將流轉換成字串 56 String content = StreamTools.readStreamToString(in); 57 58 //展示到指定控制元件中 59 tv_result.setText(content); 60 61 } 62 } catch (ProtocolException e) { 63 e.printStackTrace(); 64 System.out.println("異常1"); 65 } catch (MalformedURLException e) { 66 e.printStackTrace(); 67 System.out.println("異常2"); 68 } catch (IOException e) { 69 e.printStackTrace(); 70 System.out.println("異常3"); 71 } 72 73 74 } 75 }
新建一個工具類來將Stream流轉換成String
SrreamTools.java:
1 import android.content.Context; 2 3 import java.io.ByteArrayOutputStream; 4 import java.io.IOException; 5 import java.io.InputStream; 6 7 public class StreamTools { 8 //把inStream轉換成String 9 public static String readStreamToString(InputStream in) throws IOException { 10 //定義一個記憶體輸出流 11 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); 12 int len = -1; 13 byte[] buffer = new byte[1024];//1kb 14 while ((len = in.read(buffer)) != -1) { 15 byteArrayOutputStream.write(buffer, 0, len); 16 } 17 in.close(); 18 String content = new String(byteArrayOutputStream.toByteArray()); 19 return content; 20 } 21 }
加許可權
<?xml version="1.0" encoding="utf-8"?> <manifest ……> <uses-permission android:name="android.permission.INTERNET"/> <application> …… </application> </manifest>
以上就是出現錯誤的全部程式碼。雖然在有一些情況下會執行成功,但不久你就會發現,會崩的或者是出現異常3(不要學我寫System.out.println("異常3"),要用Log,這裡邊與小白參考)。
上網查詢問題,通過課本查詢,都沒有發現解決方法。網上有很多人問,有人說要在新的執行緒中寫,有人說許可權(其實一般不會發生這種低階問題),感覺回答的也是一些小白。很多並沒有參考性。其中回答執行緒的應該是有些功底的了,下面我就發一下解決方法。
因為在4.0之後谷歌強制要求連線網路不能在主執行緒進行訪問,所以以上的程式碼時不能上網的,即使加了許可權也不行。此時我們為您將聯網的邏輯寫在新的執行緒中。
獲取了網路資訊後需要將資訊展示在控制元件上(比如TextView),此時遇到了新的問題:更新UI異常。
這是因為更新UI必須在主執行緒中進行,所以引入新的API——Handler,重寫Handler.handlerMessage()方法來進行更新UI;來要注意一個細節,Toast也是一個View,所以也不能在子執行緒中執行,也需要放在handlerMessage()中。
又有問題了:如何將在子執行緒要進行的邏輯在handlerMessage()中進行呢?也就是說主執行緒和子執行緒需要一個聯絡,不然怎麼更新UI。所以新建Message物件,用Handler的sendMessage()向主執行緒傳送Message物件,在主執行緒中接收,這樣zhuxiancheng 就可以根據子執行緒發來的資訊來進行應做的邏輯了。
說了這麼多,不知同沒聽懂,還是程式碼實在
只需要修改MainActivity.java:
1 package com.lgqchinese.httphaha; 2 3 import android.content.Context; 4 import android.os.Handler; 5 import android.os.Message; 6 import android.support.v7.app.AppCompatActivity; 7 import android.os.Bundle; 8 import android.view.View; 9 import android.widget.EditText; 10 import android.widget.TextView; 11 import android.widget.Toast; 12 13 import java.io.IOException; 14 import java.io.InputStream; 15 import java.net.HttpURLConnection; 16 import java.net.MalformedURLException; 17 import java.net.ProtocolException; 18 import java.net.URL; 19 20 public class MainActivity extends AppCompatActivity { 21 22 private EditText et_path; 23 private TextView tv_result; 24 private static final int REQUESTSUEECSS = 0; 25 private static final int REQUESTNOTFOUNT = 1; 26 27 //在主執行緒中定義Handler 28 private Handler handler = new Handler() { 29 //重寫方法 30 31 @Override 32 public void handleMessage(Message msg) { 33 34 //區分發來的訊息時哪一條 35 switch (msg.what){ 36 case REQUESTSUEECSS://代表請求成功 37 String content = (String) msg.obj; 38 tv_result.setText(content); 39 break; 40 case REQUESTNOTFOUNT://請求失敗 41 Toast.makeText(getApplicationContext(), "請求資源不存在", Toast.LENGTH_SHORT).show(); 42 break; 43 44 45 } 46 47 } 48 }; 49 50 51 @Override 52 protected void onCreate(Bundle savedInstanceState) { 53 super.onCreate(savedInstanceState); 54 setContentView(R.layout.activity_main); 55 56 et_path = findViewById(R.id.et_path); 57 tv_result = findViewById(R.id.tv_result); 58 59 //列印當前執行緒的名字 60 System.out.println("當前執行緒的名字" + Thread.currentThread().getName()); 61 62 63 } 64 65 //點檢就進行檢視指定路徑的原始碼 66 public void click(View view) { 67 68 //建立一個子執行緒 69 new Thread() { 70 @Override 71 public void run() { 72 try { 73 //獲取原始碼路徑 74 String path = et_path.getText().toString().trim(); 75 //建立url物件制定我們要訪問的路徑 76 URL url = new URL(path); 77 //拿到httpUrlConnection物件 78 HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection(); 79 //設定請求方法 80 httpURLConnection.setRequestMethod("GET");//GET要求大寫,預設get請求 81 //設定請求時間 82 httpURLConnection.setConnectTimeout(5000); 83 //獲取伺服器返回的狀態碼 84 int code = httpURLConnection.getResponseCode(); 85 //如果code = 200,說明請求成功 86 if (code == 200) { 87 //獲取伺服器返回的資料,是以流的形式返回的 88 InputStream in = httpURLConnection.getInputStream(); 89 System.out.println(); 90 91 //將流轉換成字串 92 String content = StreamTools.readStreamToString(in); 93 94 //建立Message物件 95 Message msg = new Message(); 96 msg.what = REQUESTSUEECSS; 97 msg.obj = content; 98 //用建立的Handler(助手)告訴系統要更新UI 99 handler.sendMessage(msg); 100 101 //展示到指定控制元件中 102 // tv_result.setText(content); 103 104 } else { 105 //請求資源不存在,Toast是一個view,也不能再子執行緒更新UI 106 Message msg = new Message(); 107 msg.what = REQUESTNOTFOUNT; 108 handler.sendMessage(msg); 109 // Toast.makeText(getApplicationContext(), "請求資源不存在", Toast.LENGTH_SHORT).show(); 110 } 111 112 } catch (ProtocolException e) { 113 e.printStackTrace(); 114 System.out.println("異常1"); 115 } catch (MalformedURLException e) { 116 e.printStackTrace(); 117 System.out.println("異常2"); 118 } catch (IOException e) { 119 e.printStackTrace(); 120 System.out.println("異常3"); 121 } 122 } 123 }.start(); 124 125 126 } 127 }
可以在異常處再向主執行緒發信息,來提示使用者載入失敗,這裡不再贅述,自練即可。
以上就是今天遇到的問題,個人感覺就是
因為在4.0之後谷歌強制要求連線網路不能在主執行緒進行訪問。