目录
一、使用HttpUrlConnection访问网络
在Android 6.0以前,Android上发送HTTP请求一般有两种方式,HttpUrlConnection和HttpClient。HttpClient是Apache的开源库,存在API数量过多、扩展困难等缺点,在Android6.0系统中,将HttpClient的功能完全移除了。官方建议使用HttpUrlConnection来进行网络请求。
1、发送GET请求
/*
使用HttpUrlConnection发送GET请求
*/
public static void sendHttpGetRequest(final String address){
new Thread(new Runnable() {
@Override
public void run() {
URL url = null;//请求的url地址
HttpURLConnection conn = null;
InputStream is = null;
ByteArrayOutputStream baos = null;
String requestHeader = null;//请求头
String requestBody = null;//请求体
String responseHeader = null;//响应头
String responseBody = null;//响应体
try {
//创建一个URL对象
url = new URL(address);
//打开一个HttpUrlConnection连接
conn = (HttpURLConnection)url.openConnection();
//设置GET、POST请求方式,默认使用GET发送请求
conn.setRequestMethod("GET");
//设置连接超时时间
conn.setConnectTimeout(5000);
//设置从服务器读取数据超时时间
conn.setReadTimeout(5000);
//设置是否使用缓存,默认true
conn.setUseCaches(true);
//设置从服务端读取结果流,默认true
conn.setDoInput(true);
//用来设置请求头
conn.setRequestProperty("Connection", "Keep-Alive");
conn.setRequestProperty("TestDemo", "GET");
//在对各种参数配置完成后,通过调用connect方法建立TCP连接,但是并未真正获取数据
//connect()方法不必主动调用,当调用getInputStream()方法时内部也会自动调用connect方法
conn.connect();
if(conn.getResponseCode() == 200){
//调用getInputStream方法后,服务端才会收到请求,并阻塞式地接收服务端返回的数据
is = conn.getInputStream();
baos = new ByteArrayOutputStream();
byte[] bytes = new byte[1024];
int len = 0;
while((len = is.read(bytes)) != -1){
baos.write(bytes, 0, len);
}
requestBody = baos.toString();
Log.d(TAG, "run: response " + requestBody);
}else if(conn.getResponseCode() == 302){
//处理重定向,这里只是一个简单的例子,不是标准写法
String location = conn.getHeaderField("Location");
url = new URL(location);
//打开一个HttpUrlConnection连接
conn = (HttpURLConnection)url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
conn.setUseCaches(true);
conn.connect();
}
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
if (baos != null){
baos.close();
}
if(is != null){
is.close();
}
if(conn != null){
conn.disconnect();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}).start();
}
tips: 如何处理302重定向
1、网络请求属于耗时操作,不可以在主线程内进行,需要单独起一个线程进行网络请求操作;
2、HttpUrlConnection有两个方法来设置是否自动处理重定向setFollowRedirects()和setInstanceFollowRedirects(),但我设置后访问http://www.baidu.com,并没有自动重定向,还会返回302的错误码,手动处理后,可以获取response header里的Location,是重定向后的url。而OkHttp可以自动处理重定向,okhttp的重定向默认是开启的,可以手动设置为关闭。
2、发送POST请求
/*
* 使用HttpUrlConnection发送POST请求
* */
public static void sendHttpPostRequest(final String address){
new Thread(new Runnable() {
@Override
public void run() {
URL url = null;
HttpURLConnection conn = null;
DataOutputStream dos = null;
InputStream is = null;
ByteArrayOutputStream baos = null;
String requestHeader = null;//请求头
String requestBody = null;//请求体
String responseHeader = null;//响应头
String responseBody = null;//响应体
try {
//创建一个URL对象
url = new URL(address);
//打开一个HttpUrlConnection连接
conn = (HttpURLConnection)url.openConnection();
//设置请求方式为POST,必须设置,默认GET
conn.setRequestMethod("POST");
//设置允许输出,POST请求必须设置,默认false
conn.setDoOutput(true);
//设置请求默认输入,默认true
conn.setDoInput(true);
//设置请求头
conn.setRequestProperty("TestDemo", "POST");
//设置连接超时时间
conn.setConnectTimeout(5000);
//设置从服务器读取数据超时时间
conn.setReadTimeout(5000);
//设置本次连接是否自动处理重定向
conn.setInstanceFollowRedirects(true);
//开始连接
conn.connect();
//发送请求的POST参数,键值对形式
dos = new DataOutputStream(conn.getOutputStream());
dos.writeBytes("username=prance&password=123456");
dos.flush();
dos.close();
if(conn.getResponseCode() == 200){
is = conn.getInputStream();
baos = new ByteArrayOutputStream();
byte[] bytes = new byte[1024];
int len = 0;
while((len = is.read(bytes))!=-1){
baos.write(bytes, 0 , len);
}
responseBody = baos.toString();
Log.d(TAG, "run: response" + responseBody);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
if (baos != null){
baos.close();
}
if(is != null){
is.close();
}
if(conn != null){
conn.disconnect();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}).start();
}
二、OkHttp的使用方法
1、添加OkHttp库的依赖
在app/build.gradle文件中,dependencies闭包中加入依赖:
dependencies {
......
implementation 'com.squareup.okhttp3:okhttp:3.12.1'
}
其他引入方式,见github上okhttp库的说明:https://github.com/square/okhttp
2、使用方法
(1)Get请求
/*发送HTTP Get请求
* 参数:
* address:请求的url地址
* */
public static void sendOkHttpRequest(final String address){
new Thread(new Runnable() {
@Override
public void run() {
//创建一个OkHttpClient的实例
OkHttpClient okHttpClient = new OkHttpClient();
//创建一个Request对象
Request request = new Request.Builder().url(address).build();
try {
//调用OkHttpClient的newCall()方法来创建一个Call对象,通过它的execute()方法来发送请求并获取服务器返回的数据
Response response = okHttpClient.newCall(request).execute();
Log.d(TAG, "run: " + response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
需要注意的是,发送网络请求属于耗时操作,无法在UI线程(主线程)进行,使用的时候一定单独起一个子线程去发送网络请求,否则会抛出android.os.NetworkOnMainThreadException的异常导致程序崩溃。
因此,OkHttp提供了一个回调接口,可供调用方有时机去处理服务器返回的数据
/*发送HTTP Get请求
* 参数:
* address: 请求的url地址
* callback: OkHttp回调接口
* */
public static void sendOkHttpRequest2(String address, Callback callback){
//创建一个OkHttpClient的实例
OkHttpClient okHttpClient = new OkHttpClient();
//创建一个Request对象
Request request = new Request.Builder().url(address).build();
//调用OkHttpClient的newCall()方法来创建一个Call对象
//enqueue()方法内部会开启一个子线程去执行http请求,并将最终的请求结果回调到Callback中
okHttpClient.newCall(request).enqueue(callback);
}
调用方式:
HttpUtilty.sendOkHttpRequest("http://www.baidu.com", new Callback() {
@Override
public void onFailure(Call call, IOException e) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), "http connected failed", Toast.LENGTH_SHORT).show();
}
});
}
@Override
public void onResponse(Call call, Response response) throws IOException {
Log.d(TAG, "onResponse: " + response.body().string());
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), "success", Toast.LENGTH_SHORT).show();
}
});
}
});
需要注意的是,Callback最终的回调接口还是在okhttp起的子线程中运行的,因此不可以直接在回调接口里执行任何UI操作,可以借助runOnUiThread()方法来进行线程切换。
(2)Post请求
public static void sendOkHttpPostRequest(String address, Callback callback){
//创建一个OkHttpClient的实例
OkHttpClient okHttpClient = new OkHttpClient ();
//构建一个RequestBody对象来存放待提交的参数
RequestBody requestBody = new FormBody.Builder()
.add("username", "admin")
.add("password", "123456")
.build();
//创建一个Request对象
Request request = new Request.Builder()
.url(address)
.post(requestBody)
.build();
//调用OkHttpClient的newCall()方法来创建一个Call对象
//enqueue()方法内部会开启一个子线程去执行http请求,并将最终的请求结果回调到Callback中
okHttpClient.newCall(request).enqueue(callback);
}
三、WebView的使用方法
Android提供了一个WebView控件,借助它可以在自己的应用程序里嵌入一个浏览器,从而非常轻松的展示各种各样的网页。
1、创建一个布局文件,引入WebView控件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="@layout/title_layout"/>
<WebView
android:id="@+id/webview"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</LinearLayout>
2、在Activity中使用WebView控件
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_http_demo);
WebView webView = findViewById(R.id.webview);
webView.getSettings().setJavaScriptEnabled(true);
webView.setWebViewClient(new WebViewClient());
webView.loadUrl("http://www.baidu.com");
}
注意,这里使用了WevView的setWebViewClient()方法,并传入了一个WevViewClient的实例,作用是,希望目标网页仍然在当前WebView中展示,而不是打开系统浏览器。如果没有设置这个,那应用中会弹窗提示使用哪个浏览器打开目标网页,而不会在当前你自己的应用中展现网页。