「android」 网络

Posted by Dawn-K's Blog on January 24, 2021

网络

原生http

界面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<Button
    android:id="@+id/send_request"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="Send Request" />

<ScrollView
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/response_text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</ScrollView>

在界面上显示获得的报文

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
TextView responseText;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Button sendRequest = (Button) findViewById(R.id.send_request);
    responseText = (TextView) findViewById(R.id.response_text);
    sendRequest.setOnClickListener(this);
}
private void showResponse(final String response_text) {
    // 必须在主进程中才能修改UI
    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            responseText.setText(response_text);
        }
    });
}

连接http

测试原生的Http连接方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
void sendRequestWithHttpURLConnection() {
    new Thread(new Runnable() {
        @Override
        public void run() {
            HttpURLConnection connection = null;
            BufferedReader reader = null;
            try {
                // 建立连接
                URL url = new URL("https://dawn-k.github.io");
                connection = (HttpURLConnection) url.openConnection();
                connection.setRequestMethod("GET");
                connection.setConnectTimeout(8000);
                connection.setReadTimeout(8000);

                // 获取服务返回的流
                InputStream in = connection.getInputStream();
                reader = new BufferedReader(new InputStreamReader(in));

                // 拼接成 response
                StringBuilder response = new StringBuilder();
                String line;
                while ((line = reader.readLine()) != null) {
                    response.append(line);
                }
                // 在界面上显示出来
                showResponse(response.toString());
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (reader != null) {
                    try {
                        reader.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (connection != null) {
                    connection.disconnect();
                }
            }
        }
    }).start();
    // 上面一行很容易漏下,一定要注意
}
// 注册监听器
@Override
public void onClick(View v) {
    switch (v.getId()) {
        case R.id.send_request:
            sendRequestWithHttpURLConnection();
            break;
    }
}

权限

需要网络权限

1
<uses-permission android:name="android.permission.INTERNET" />

OkHttp

OkHttp 是一个通用的流行的开源库. 界面和 showResponse 我们采用和上文一样的方式.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void testOKHttp() {
    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                // 构建GET方法的请求
                Request request = new Request.Builder()
                        .url("http://www.baidu.com")
                        .build();
                // 发起请求
                OkHttpClient client = new OkHttpClient();
                Response response = client.newCall(request).execute();
                String responseData = response.body().string();
                // 显示数据
                showResponse(responseData);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }).start();
}

最后修改 onClick 方法中点击按钮对应的函数为 testOKHttp

post

post请求需要修改如下.

1
2
3
4
5
6
7
8
9
10
// 构建一个请求体(也就是想传的参数)
RequestBody requestBody = new FormBody.Builder()
        .add("username", "admin")
        .add("password", "123456")
        .build();
// 发起post请求
Request request = new Request.Builder()
        .url("http://www.baidu.com")
        .post(requestBody)
        .build();

剩下的部分和之前的一样

配置服务器

下载 Apache httpd

假设安装位置为 D:/myDevEnvironment/Apache24 , 下述 APACHE_HOME . 先在 %APACHE_HOME/conf/httpd.conf 里面, 把安装位置修改为

Define SRVROOT "D:/myDevEnvironment/Apache24"
ServerRoot "${SRVROOT}"

然后在命令行中进入 %APACHE_HOME/bin , 然后输入 httpd -k install

最后打开bin下的 ApacheMonitor.exe , 然后启动服务, 在浏览器里输入 127.0.0.1 即可访问到 Apache 默认页面了. 可以在刚才的配置文件中改端口, 默认是80

下文的文件都放在 %APACHE_HOME/htdocs 下即可. 可通过 127.0.0.1/file_name 访问

解析 xml

准备好get_data.xml文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<apps>
    <app>
        <id>1</id>
        <name>Google Maps</name>
        <version>1.0</version>
    </app>
    <app>
        <id>2</id>
        <name>Chrome</name>
        <version>2.1</version>
    </app>
    <app>
        <id>3</id>
        <name>Google Play</name>
        <version>2.3</version>
    </app>
</apps>

XmlPullParser

原生的解决方式, 直接用 XmlPullParser

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
void testOKHttp() {
    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                // 宿主机测试时用 127.0.0.1
                // 对于手机来说,宿主机就是 10.0.2.2
                Request request = new Request.Builder()
                        .url("http://10.0.2.2/get_data.xml")
                        .build();
                OkHttpClient client = new OkHttpClient();
                Response response = client.newCall(request).execute();
                String responseData = response.body().string();
                parseXMLWithPull(responseData);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }).start();
}
private void parseXMLWithPull(String xmlData) {
    try {
        XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
        XmlPullParser xmlPullParser = factory.newPullParser();
        xmlPullParser.setInput(new StringReader(xmlData));
        int eventType = xmlPullParser.getEventType();
        String id = "";
        String name = "";
        String version = "";
        // 一直循环到文件结尾
        while (eventType != XmlPullParser.END_DOCUMENT) {
            // 当前元素的名字(比如 <app> 1 </app>这种)
            String nodeName = xmlPullParser.getName();
            // 判断类型
            switch (eventType) {
                case XmlPullParser.START_TAG:
                    // 对于相应的类型,将下一个元素存入字符串(因为当前的还是标签)
                    if ("id".equals(nodeName)) {
                        id = xmlPullParser.nextText();
                    } else if ("name".equals(nodeName)) {
                        name = xmlPullParser.nextText();
                    } else if ("version".equals(nodeName)) {
                        version = xmlPullParser.nextText();
                    }
                    break;
                case XmlPullParser.END_TAG:
                    if ("app".equals(nodeName)) {
                        Log.d("MainActivity", "id is " + id);
                        Log.d("MainActivity", "name is " + name);
                        Log.d("MainActivity", "version is " + version);
                    }
                    break;
                default:
                    break;
            }
            eventType = xmlPullParser.next();
        }

    } catch (Exception e) {
        e.printStackTrace();
    }
}

书上还提到了一种 SAX方法, 此处不再赘述.

解析 json

准备好get_data.json文件

[{
        "id": "5",
        "version": "5.5",
        "name": "Clash of Clans"
    },
    {
        "id": "6",
        "version": "7.0",
        "name": "Boom Beach"
    },
    {
        "id": "7",
        "version": "5.5",
        "name": "Clash Royale"
    }
]

JSONObject

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private void parseJSONWithJSONObject(String jsonData) {
    try {
        JSONArray jsonArray = new JSONArray(jsonData);
        for (int i = 0; i < jsonArray.length(); i++) {
            JSONObject jsonObject = jsonArray.getJSONObject(i);
            String id = jsonObject.getString("id");
            Log.d("MainActivity", "id is " + id);
            String name = jsonObject.getString("name");
            Log.d("MainActivity", "name is " + name);
            String version = jsonObject.getString("version");
            Log.d("MainActivity", "version is " + version);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

GSON

新建 app类, 此处略去 getter 和 setter

1
2
3
class App {
    private String id, name, version;
}

然后添加依赖

1
implementation 'com.google.code.gson:gson:2.8.5'

最后编写解析函数

1
2
3
4
5
6
7
8
9
10
private void parseJSONWithGSON(String jsonData) {
    Gson gson = new Gson();
    List<App> appList = gson.fromJson(jsonData, new TypeToken<List<App>>() {
    }.getType());
    for (App app : appList) {
        Log.d("MainActivity", "id is " + app.getId());
        Log.d("MainActivity", "name is " + app.getName());
        Log.d("MainActivity", "version is " + app.getVersion());
    }
}

HttpUtil

创建一个通用的工具类, 进行封装 先创建一个接口类

1
2
3
4
5
public interface HttpCallbackListener {
    void onFinish(String response);

    void onError(Exception e);
}

再创建 HttpUtil (其实若是只使用里面的 OkHttp, 然后把原生的删掉的话就不需要上面的接口类了)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
import android.content.pm.PackageManager;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;

import okhttp3.Call;
import okhttp3.FormBody;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okio.BufferedSink;

public class HttpUtil {
    /*
使用方法:
HttpUtil.sendHttpRequest("http://www.baidu.com", new okhttp3.Callback() {
    @Override
    public void onError(Exception e) {
        // 处理异常情况
    }

    @Override
    public void onFinish(String response){

        // 得到服务器返回的具体内容
        String responseData = response.body().string();
    }
});
 */
    public static void sendHttpRequest(final String address, final HttpCallbackListener listener) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                HttpURLConnection connection = null;
                try {
                    URL url = new URL(address);
                    connection = (HttpURLConnection) url.openConnection();
                    connection.setRequestMethod("GET");
                    connection.setConnectTimeout(8000);
                    connection.setReadTimeout(8000);
                    connection.setDoInput(true);
                    connection.setDoOutput(true);
                    InputStream in = connection.getInputStream();
                    BufferedReader reader = new BufferedReader(new InputStreamReader(in));
                    StringBuilder response = new StringBuilder();
                    String line;
                    while ((line = reader.readLine()) != null) {
                        response.append(line);
                    }
                } catch (Exception e) {
                    if (listener != null) {
                        listener.onError(e);
                    }
                } finally {
                    if (connection != null) {
                        connection.disconnect();
                    }
                }
            }
        }).start();
    }

        /*
        使用方法:
        HttpUtil.sendOkHttpRequest("http://www.baidu.com", new okhttp3.Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                // 处理异常情况
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {

                // 得到服务器返回的具体内容
                String responseData = response.body().string();
            }
        });
         */

    public static void sendOkHttpRequest(String address, okhttp3.Callback callback) {
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder()
                .url(address)
                .build();
        client.newCall(request).enqueue(callback);
    }
}

原生下载器

原生下载器在下载单个文件的时候非常好用, 在多文件下载的时候会受到系统对于并发下载量的限制, 比如小米只能同时下载两个文件.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
 private void downloadFiles(String dirName, String[] fileNames) {
    DownloadManager.Request request = new DownloadManager.Request(Uri.parse(Config.getBaseUrl() + "/download/" + dirName + "/" + fileName));
    Log.d(TAG, "begin to download " + Config.getBaseUrl() + "/download/" + dirName + "/" + fileName);
    //                            request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);//下载中与下载完成后都会在通知中显示| 另外可以选 DownloadManager.Request.VISIBILITY_VISIBLE 仅在下载中时显示在通知中,完成后会自动隐藏
    request.setTitle(fileName);
    request.setDescription("正在下载图像序列");
    request.setAllowedOverRoaming(false);
    request.setDestinationInExternalFilesDir(DicomViewer.this, Environment.DIRECTORY_DOCUMENTS, File.separator + dirName + File.separator + fileName);
    //                            request.setDestinationUri(new Uri(getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS) + "/" + dirName + "/" + fileName));
    // subpath 实际上是文件名
    //                            request.setDestinationInExternalFilesDir(DicomViewer.this, Environment.DIRECTORY_DOWNLOADS, fileName);
    DownloadManager downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
    long downloadId = downloadManager.enqueue(request);
    Log.d(TAG, "download id : " + downloadId);
    listen(fileName, downloadId);
}
private void listen(final String fileName, final long Id) {
    // 注册广播监听系统的下载完成事件。
    IntentFilter intentFilter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
    broadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            long ID = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
            if (ID == Id) {
//                    Toast.makeText(getApplicationContext(), " 文件:" + fileName + " 任务:" + Id + " 下载完成!", Toast.LENGTH_SHORT).show();
//                    Log.d(TAG, "try to compress " + zipFilePath + " to " + getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS));
                Log.d(TAG, "文件 " + fileName + " 下载完成  " + System.currentTimeMillis() / 1000 + " s");
                // 记得在代码中自动增加这个文件夹
//                    FileUtil.decompressFile(getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS).getAbsolutePath(), zipFilePath);
//                    Toast.makeText(getApplicationContext(), "解压完成", Toast.LENGTH_SHORT).show();
            }
        }
    };
    registerReceiver(broadcastReceiver, intentFilter);
}