在学习Android的网络连接时遇到崩溃或异常(出现的问题就这两个,但是不稳定)的问题,先上代码,看看哪里错了(答案在文末)
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"> <LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"> <EditText
android:id="@+id/et_path"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入网址"
android:text="https://www.baidu.com/" /> <Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="click"
android:text="查看" /> <ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"> <TextView
android:id="@+id/tv_result"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</ScrollView> </LinearLayout> </android.support.constraint.ConstraintLayout>
MainActivity.xml:
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast; import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL; public class MainActivity extends AppCompatActivity { private EditText et_path;
private TextView tv_result; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); et_path = findViewById(R.id.et_path);
tv_result = findViewById(R.id.tv_result); } //点检就进行查看指定路径的源码
public void click(View view) { try {
//获取源码路径
String path = et_path.getText().toString().trim();
//创建url对象制定我们要访问的路径
URL url = new URL(path);
//拿到httpUrlConnection对象
HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
//设置请求方法
httpURLConnection.setRequestMethod("GET");//GET要求大写,默认get请求
//设置请求时间
httpURLConnection.setConnectTimeout(5000);
//获取服务器返回的状态码
int code = httpURLConnection.getResponseCode();
//如果code = 200,说明请求成功
if (code == 200) {
//获取服务器返回的数据,是以流的形式返回的
InputStream in = httpURLConnection.getInputStream();
System.out.println(); //将流转换成字符串
String content = StreamTools.readStreamToString(in); //展示到指定控件中
tv_result.setText(content); }
} catch (ProtocolException e) {
e.printStackTrace();
System.out.println("异常1");
} catch (MalformedURLException e) {
e.printStackTrace();
System.out.println("异常2");
} catch (IOException e) {
e.printStackTrace();
System.out.println("异常3");
} }
}
新建一个工具类来将Stream流转换成String
SrreamTools.java:
import android.content.Context; import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream; public class StreamTools {
//把inStream转换成String
public static String readStreamToString(InputStream in) throws IOException {
//定义一个内存输出流
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
int len = -1;
byte[] buffer = new byte[1024];//1kb
while ((len = in.read(buffer)) != -1) {
byteArrayOutputStream.write(buffer, 0, len);
}
in.close();
String content = new String(byteArrayOutputStream.toByteArray());
return content;
}
}
加权限
<?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:
package com.lgqchinese.httphaha; import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast; import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL; public class MainActivity extends AppCompatActivity { private EditText et_path;
private TextView tv_result;
private static final int REQUESTSUEECSS = 0;
private static final int REQUESTNOTFOUNT = 1; //在主线程中定义Handler
private Handler handler = new Handler() {
//重写方法 @Override
public void handleMessage(Message msg) { //区分发来的消息时哪一条
switch (msg.what){
case REQUESTSUEECSS://代表请求成功
String content = (String) msg.obj;
tv_result.setText(content);
break;
case REQUESTNOTFOUNT://请求失败
Toast.makeText(getApplicationContext(), "请求资源不存在", Toast.LENGTH_SHORT).show();
break; } }
}; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); et_path = findViewById(R.id.et_path);
tv_result = findViewById(R.id.tv_result); //打印当前线程的名字
System.out.println("当前线程的名字" + Thread.currentThread().getName()); } //点检就进行查看指定路径的源码
public void click(View view) { //创建一个子线程
new Thread() {
@Override
public void run() {
try {
//获取源码路径
String path = et_path.getText().toString().trim();
//创建url对象制定我们要访问的路径
URL url = new URL(path);
//拿到httpUrlConnection对象
HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
//设置请求方法
httpURLConnection.setRequestMethod("GET");//GET要求大写,默认get请求
//设置请求时间
httpURLConnection.setConnectTimeout(5000);
//获取服务器返回的状态码
int code = httpURLConnection.getResponseCode();
//如果code = 200,说明请求成功
if (code == 200) {
//获取服务器返回的数据,是以流的形式返回的
InputStream in = httpURLConnection.getInputStream();
System.out.println(); //将流转换成字符串
String content = StreamTools.readStreamToString(in); //创建Message对象
Message msg = new Message();
msg.what = REQUESTSUEECSS;
msg.obj = content;
//用创建的Handler(助手)告诉系统要更新UI
handler.sendMessage(msg); //展示到指定控件中
// tv_result.setText(content); } else {
//请求资源不存在,Toast是一个view,也不能再子线程更新UI
Message msg = new Message();
msg.what = REQUESTNOTFOUNT;
handler.sendMessage(msg);
// Toast.makeText(getApplicationContext(), "请求资源不存在", Toast.LENGTH_SHORT).show();
} } catch (ProtocolException e) {
e.printStackTrace();
System.out.println("异常1");
} catch (MalformedURLException e) {
e.printStackTrace();
System.out.println("异常2");
} catch (IOException e) {
e.printStackTrace();
System.out.println("异常3");
}
}
}.start(); }
}
可以在异常处再向主线程发信息,来提示用户加载失败,这里不再赘述,自练即可。
以上就是今天遇到的问题,个人感觉就是
因为在4.0之后谷歌强制要求连接网络不能在主线程进行访问。