安卓核心技术高级——HttpURLConnection与HttpClient

HTTP协议

Http是Hyper Text Transfer Protocol(超文本传输协议),我们今天普遍使用的一个版本——HTTP 1.1。

(1)在TCP/IO协议栈中的位置
HTTP协议通常承载于TCP协议之上,有时也承载于TLS或SSL协议层之上,这个时候,就成了我们常说的HTTPS。
如下图所示:默认HTTP的端口号为80,HTTPS的端口号为443。
安卓核心技术高级——HttpURLConnection与HttpClient
(二)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格式。
安卓核心技术高级——HttpURLConnection与HttpClient
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();
    }

安卓核心技术高级——HttpURLConnection与HttpClient
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();
    }
}

安卓核心技术高级——HttpURLConnection与HttpClient
安卓核心技术高级——HttpURLConnection与HttpClient

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();
    }
}

安卓核心技术高级——HttpURLConnection与HttpClient
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();
    }

安卓核心技术高级——HttpURLConnection与HttpClient