• 学习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之后谷歌强制要求连接网络不能在主线程进行访问

     
    昔日我曾苍老,如今风华正茂(ง •̀_•́)ง
  • 相关阅读:
    Django-ORM
    深入理解vue 修饰符sync
    PS切图
    用Chrome 浏览器调试移动端网页 chrome://inspect/#devices
    float浮动导致父元素高度坍塌的原因及清除浮动方法
    vue keep-alive
    ES6 箭头函数
    ES6 Module(模块)
    MVC模式 和 MVVM模式
    移动端适配代码
  • 原文地址:https://www.cnblogs.com/lgqrlchinese/p/9937160.html
Copyright © 2020-2023  润新知