1. 程式人生 > >學習Android過程中遇到的問題及解決方法——網路請求

學習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之後谷歌強制要求連線網路不能在主執行緒進行訪問