学习Android过程中遇到的问题及解决方法——网络请求
在学习Android的网络连接时遇到崩溃或异常(出现的问题就这两个,但是不稳定)的问题,先上代码,看看哪里错了(答案在文末)
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之后谷歌强制要求连接网络不能在主线程进行访问。