安卓核心技术高级——HttpURLConnection与HttpClient
HTTP协议
Http是Hyper Text Transfer Protocol(超文本传输协议),我们今天普遍使用的一个版本——HTTP 1.1。
(1)在TCP/IO协议栈中的位置
HTTP协议通常承载于TCP协议之上,有时也承载于TLS或SSL协议层之上,这个时候,就成了我们常说的HTTPS。
如下图所示:默认HTTP的端口号为80,HTTPS的端口号为443。
(二)HTTP的请求响应
这就限制了使用HTTP协议,无法实现在客户端没有发起请求的时候,服务器将消息推送给客户端。
HTTP协议是一个无状态的协议,同一个客户端的这次请求和上次请求是没有对应关系的。
(三)工作流程
一次HTTP操作称为一个事务,其工作过程可分为四步:
1)首先客户机与服务器需要建立连接。只要单击某个超级链接,HTTP的工作开始。
2)建立连接后,客户机发送一个请求给服务器,请求方式的格式为:统一资源标识符(URL)、协议版本号,后边是MIME信息包括请求修饰符、客户机信息和可能的内容。
3)服务器接到请求后,给予相应的响应信息,其格式为一个状态行,包括信息的协议版本号、一个成功或错误的代码,后边是MIME信息包括服务器信息、实体信息和可能的内容。
4)客户端接收服务器所返回的信息通过浏览器显示在用户的显示屏上,然后客户机与服务器断开连接。 如果在以上过程中的某一步出现错误,那么产生错误的信息将返回到客户端,有显示屏输出。对于用户来说,这些过程是由HTTP自己完成的,用户只要用鼠标点击,等待信息显示就可以了。
(四)请求包示例
HTTP请求包(GET?POST等请求方法)由三个部分组成,分别是:方法-URI-协议/版本,请求头,请求正文。
下面是一个HTTP请求包(GET)的例子:
1、GET /index.jsp HTTP/1.1
2、Accept-Language: zh-cn
3、Connection: Keep-Alive
4、Host: 1Array2.168.0.106
5、Content-Length: 37
6、userName=new_andy&password=new_andy
(五)应答包示例
和HTTP请求包相似,由三个部分构成,分别是:协议-状态代码-描述,应答头,应答正文。
下面是一个HTTP应答的例子:
1、HTTP/1.1 200 OK
2、Server: Microsoft-IIS/4.0
3、Date: Mon, 3 Jan 2005 13:13:33 GMT
4、Content-Type: text/html
5、Last-Modified: Mon, 11 Jan 2004 13:23:42 GMT
6、Content-Length: Array0
7、< html>
< head>< title>解读HTTP包示例</ title></ head>
< body>Hello WORLD!</ body>
</ html>
检查网络连接状态
//检查网络是否可用
public boolean isNetWorkConnected(Context context) {
if (context != null) {
ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = manager.getActiveNetworkInfo();
if (networkInfo != null) {
return networkInfo.isAvailable();
}
}
return false;
}
//检查WIFI网络是否可用
public boolean isWifiConnected(Context context) {
if (context != null) {
ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
if (networkInfo != null) {
return networkInfo.isAvailable();
}
}
return false;
}
//检查MOBILE网络是否可用
public boolean isMobileConnected(Context context) {
if (context != null) {
ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = manager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
if (networkInfo != null) {
return networkInfo.isAvailable();
}
}
return false;
}
//检查当前网络连接的类型信息
public static int getConnectedType(Context context) {
if (context != null) {
ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = manager.getActiveNetworkInfo();
if (networkInfo != null && networkInfo.isAvailable()) {
return networkInfo.getType();
}
}
return -1;
}
HttpURLConnection
1、URL
URL(Uniform Resource Locators):统一资源定位符。Web上的每个资源都有唯一的地址,采用的就是URL格式。
2、显示一张网络图片到手机上
private static final int LOAD_SUCCESS = 0x1;
private ImageView iv;
private final MyHandler myHandler = new MyHandler(this);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
iv = (ImageView) findViewById(R.id.imageView);
}
private static class MyHandler extends Handler{
private final WeakReference<MainActivity> weakReference;
public MyHandler(MainActivity mainActivity){
weakReference = new WeakReference<MainActivity>(mainActivity);
}
@Override
public void handleMessage(Message msg) {
MainActivity mainActivity = weakReference.get();
if (mainActivity!=null){
switch (msg.what){
case LOAD_SUCCESS:
mainActivity.iv.setImageBitmap((Bitmap) msg.obj);
break;
}
}
}
}
public void showNetImageClick(View view){
new Thread(new Runnable() {
@Override
public void run() {
try {
URL url = new URL("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1555324230986&di=a09966b6cf24cf150218ee6c5c23e5fa&imgtype=0&src=http%3A%2F%2Fpic1.16pic.com%2F00%2F07%2F66%2F16pic_766152_b.jpg");
InputStream in = url.openStream();
Bitmap bitmap = BitmapFactory.decodeStream(in);
Message msg = myHandler.obtainMessage(LOAD_SUCCESS,bitmap);
myHandler.sendMessage(msg);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
3、HttpURLConnection接口
Http通信中的POST和GET请求方式的不同。GET把参数放在URL字符串后面,传递给服务器。而POST方法的参数是放在Http请求中。因此,在编程之前,应当首先明确使用的请求方法,然后再根据所使用的的方式选择相应的编程方式。
首先要获取权限:
< uses-permission android:name=“android.permission.INTERNET” />
但是在安卓9.0中要求默认使用加密连接,
解决办法:https://www.jianshu.com/p/fd0b0fd0e34c
新建一个security_config.xml
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true"/>
</network-security-config>
再在mainfest文件中application内加入android:networkSecurityConfig="@xml/security_config"
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.lang.ref.WeakReference;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
public class HttpURLConnectionActivity extends AppCompatActivity {
private TextView tv_info;
private EditText et_username,et_password;
private static final int LOAD_SUCCESS = 200;
private static final int LOAD_ERROR = -1;
private final MyHandler myHandler = new MyHandler(this);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_http_urlconnection);
tv_info = (TextView)findViewById(R.id.textView);
et_username = (EditText)findViewById(R.id.editText_username);
et_password = (EditText)findViewById(R.id.editText_password);
}
private static class MyHandler extends Handler {
private final WeakReference<HttpURLConnectionActivity> weakReference;
public MyHandler(HttpURLConnectionActivity activity){
weakReference = new WeakReference<HttpURLConnectionActivity>(activity);
}
@Override
public void handleMessage(Message msg) {
HttpURLConnectionActivity activity = weakReference.get();
if (activity!=null){
switch (msg.what){
case LOAD_SUCCESS:
String json = (String)msg.obj;
activity.jsonToObject(json);
break;
case LOAD_ERROR:
activity.tv_info.setText("登录失败,请检查用户名或密码是否正确");
break;
}
}
}
}
//解析json对象
public void jsonToObject(String json){
Gson gson = new Gson();
JsonObject object = gson.fromJson(json, JsonObject.class);
tv_info.setText(object.toString());
}
//登录
public void loginClick(View view){
final String username = et_username.getText().toString();
if (TextUtils.isEmpty(username)){
Toast.makeText(this,"用户名不能为空",Toast.LENGTH_SHORT).show();
return;
}
final String pwd = et_password.getText().toString();
if (TextUtils.isEmpty(pwd)){
Toast.makeText(this,"密码不能为空",Toast.LENGTH_SHORT).show();
return;
}
System.out.println("name and password not null");
//启动登录工作线程
new Thread(new Runnable() {
@Override
public void run() {
String path = "http://10.0.2.2:8080/HttpURL/LoginServlet";
try {
System.out.println(path);
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setRequestMethod("POST");
conn.setDoInput(true);
conn.setDoOutput(true);
conn.setConnectTimeout(1000 * 30);
conn.setReadTimeout(1000 * 30);
conn.setUseCaches(false);
conn.setRequestProperty("content-type","application/x-www-form-urlencoded");
//对服务器读取或写入数据(使用输入输出流)
//获取连接输出流
DataOutputStream out = new DataOutputStream(conn.getOutputStream());
out.writeBytes("username="+ URLEncoder.encode("vince","GBK"));
out.writeBytes("&password="+URLEncoder.encode("123","GBK"));
out.flush();
out.close();
//从服务端获取内存数据
BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String result = br.readLine();
System.out.println("result="+result);
br.close();
conn.disconnect();//关闭连接
Message msg = myHandler.obtainMessage(LOAD_SUCCESS,result);
myHandler.sendMessage(msg);
}catch (IOException e) {
e.printStackTrace();
myHandler.sendEmptyMessage(LOAD_ERROR);
}
}
}).start();
}
}
Get与Post
两种方式的区别:
- get:大小不能超过4KB,速度快,会在URL上显示,不安全
- post:大小能超过4KB,速度比get慢,不会在URL上显示,安全
Get请求
新版本中apache无法使用,需要在< application>中加入子类
< uses-library android:name=“org.apache.http.legacy” android:required=“false”/>
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import java.io.IOException;
public class ApacheHttpClientActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_apache_http_client);
}
public void getClick(View view){
getRequest();
}
//使用 Apache HTTPClientGET请求
private void getRequest() {
new Thread(new Runnable() {
@Override
public void run() {
String path = "http://10.0.2.2:8080/HttpURL/LoginServlet?username=vince&password=123";
//创建请求对象
HttpGet get = new HttpGet(path);
//创建HTTP客户端对象,用于发送请求
HttpClient httpClient = new DefaultHttpClient();
try {
//向服务器发送请求,并返回响应对象
HttpResponse response = httpClient.execute(get);
//获取响应的状态码
int status = response.getStatusLine().getStatusCode();
System.out.println(status);
switch (status){
case HttpStatus.SC_OK:
//200
HttpEntity entity = response.getEntity();
String result = EntityUtils.toString(entity,"utf-8");
System.out.println(result);
break;
case HttpStatus.SC_NOT_FOUND:
//404
break;
case HttpStatus.SC_INTERNAL_SERVER_ERROR:
//500
break;
}
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
}
Post请求
public void postClick(View view){
postRequest();
}
public void postRequest(){
new Thread(new Runnable() {
@Override
public void run() {
String path = "http://10.0.2.2:8080/HttpURL/LoginServlet";
//创建请求
HttpPost post = new HttpPost(path);
ArrayList<NameValuePair> params = new ArrayList<>();
params.add(new BasicNameValuePair("username","vince"));
params.add(new BasicNameValuePair("password","123"));
try {
HttpEntity entity = new UrlEncodedFormEntity(params);
post.setEntity(entity);
HttpClient httpClient = new DefaultHttpClient();
HttpResponse response = httpClient.execute(post);
int status = response.getStatusLine().getStatusCode();
switch (status){
case HttpStatus.SC_OK:
//200
String result = EntityUtils.toString(response.getEntity());
System.out.println(result);
break;
case HttpStatus.SC_NOT_FOUND:
Log.i("HttpClient", "404");
break;
case HttpStatus.SC_INTERNAL_SERVER_ERROR:
Log.i("HttpClient", "500");
break;
}
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}