HarmonyOS Sample之NetworkManagement網(wǎng)絡(luò)管理功能
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
1.介紹
本示例演示了如何使用網(wǎng)絡(luò)管理模塊相關(guān)接口,演示了以下功能:
功能1:使用默認(rèn)網(wǎng)絡(luò),打開連接,發(fā)送HTTP請(qǐng)求。
功能2:統(tǒng)計(jì)指定UID的上行/下行流量。
功能3:使用Socket方式實(shí)現(xiàn)不同設(shè)備間通信。
此功能需要打開WIFI,并且通信的設(shè)備連接相同的WIFI 組成局域網(wǎng)。
操作上,先啟動(dòng)服務(wù)端,再啟動(dòng)客戶端,然后從客戶端發(fā)送消息,查看服務(wù)端是否收到消息。
功能4:HTTP緩存的使用,創(chuàng)建緩存,供下一次請(qǐng)求使用,減少數(shù)據(jù)流量和加載時(shí)間。
注意,需要以下權(quán)限:
ohos.permission.GET_NETWORK_INFO 獲取網(wǎng)絡(luò)連接信息。
ohos.permission.SET_NETWORK_INFO 修改網(wǎng)絡(luò)連接狀態(tài)。
ohos.permission.INTERNET 允許程序打開網(wǎng)絡(luò)套接字,進(jìn)行網(wǎng)絡(luò)連接。
詳情見官方文檔 網(wǎng)絡(luò)管理開發(fā)概述
2.搭建環(huán)境
安裝DevEco Studio,詳情請(qǐng)參考DevEco Studio下載。
設(shè)置DevEco Studio開發(fā)環(huán)境,DevEco Studio開發(fā)環(huán)境需要依賴于網(wǎng)絡(luò)環(huán)境,需要連接上網(wǎng)絡(luò)才能確保工具的正常使用,可以根據(jù)如下兩種情況來配置開發(fā)環(huán)境:
如果可以直接訪問Internet,只需進(jìn)行下載HarmonyOS SDK操作。
如果網(wǎng)絡(luò)不能直接訪問Internet,需要通過代理服務(wù)器才可以訪問,請(qǐng)參考配置開發(fā)環(huán)境。
下載源碼后,使用DevEco Studio 打開項(xiàng)目,模擬器運(yùn)行即可。
真機(jī)運(yùn)行需要將config.json中的buddleName修改為自己的,如果沒有請(qǐng)到AGC上進(jìn)行配置,參見 使用模擬器進(jìn)行調(diào)試
3.代碼結(jié)構(gòu)
3.1 代碼結(jié)構(gòu)

3.2 相關(guān)文件介紹
核心類:
HttpURLConnection.java //支持 HTTP 特定功能的 URLConnection
URLConnection.java //URL連接
URL.java //指向萬維網(wǎng)上“資源”的指針
NetStatusCallback.java //網(wǎng)絡(luò)狀態(tài)的回調(diào)類,出現(xiàn)可用網(wǎng)絡(luò)觸發(fā)onAvailable函數(shù)
DataFlowStatistics.java //該類提供查詢指定蜂窩網(wǎng)絡(luò)、應(yīng)用和網(wǎng)卡的整體流量統(tǒng)計(jì)和流量統(tǒng)計(jì)的接口。
DatagramSocket.java //此類表示用于發(fā)送和接收數(shù)據(jù)報(bào)包的套接字。
DatagramPacket.java //數(shù)據(jù)報(bào)包
WifiDevice.java //該類提供Wi-Fi管理接口
NetManager.java //提供接口來管理和使用數(shù)據(jù)網(wǎng)絡(luò)。
NetHandle.java // 數(shù)據(jù)網(wǎng)絡(luò)
InetAddress.java //網(wǎng)絡(luò)IP地址
HttpResponseCache.java //該類緩存 HTTP 和 HTTPS 響應(yīng)以供重用
自定義的類:
ThreadPoolUtil.java //線程池工具類
MainAbilitySlice.java //主頁面
NetRequestSlice.java //網(wǎng)絡(luò)請(qǐng)求&流量統(tǒng)計(jì) 功能頁
SocketClientSlice.java //Socket客戶端
SocketServerSlice.java //Socket服務(wù)端
HttpCacheSlice.java //HTTP緩存功能頁
頁面布局:
http_cache_slice.xml //HTTP緩存示例頁
net_request.slice.xml //HTTP請(qǐng)求頁面
socket_client_slice.xml //Socket通信客戶端頁
socket_server_slice.xml //Socket通信服務(wù)端頁
main_ability_slice.xml //主頁面
4.實(shí)例講解
4.1.界面布局


4.2.后臺(tái)代碼
4.2.1 NetRequestSlice.java 網(wǎng)絡(luò)請(qǐng)求&流量統(tǒng)計(jì) 功能
a.初始化網(wǎng)絡(luò)管理對(duì)象NetManager
- //初始化網(wǎng)絡(luò)管理對(duì)象
- netManager = NetManager.getInstance(null);
b.通過線程池獲取一個(gè)新線程處理進(jìn)行連接請(qǐng)求
獲取默認(rèn)數(shù)據(jù)網(wǎng)絡(luò)的時(shí)候需要ohos.permission.GET_NETWORK_INFO權(quán)限。
- //用線程池的取一個(gè)新線程處理
- ThreadPoolUtil.submit(() -> {
- HiLog.debug(LABEL_LOG, "%{public}s", "ThreadPoolUtil submit");
- //獲取默認(rèn)的數(shù)據(jù)網(wǎng)絡(luò),wifi和流量都關(guān)閉時(shí),netId==0,其它時(shí) netId=390/391, must have the ohos.permission.GET_NETWORK_INFO permission
- NetHandle netHandle = netManager.getDefaultNet();
- //接收默認(rèn)數(shù)據(jù)網(wǎng)絡(luò)的狀態(tài)更改的回調(diào)
- netManager.addDefaultNetStatusCallback(callback);
- //netManager.setAppNet(netHandle);
- //支持 HTTP 特定功能的 URLConnection。
- HttpURLConnection connection = null;
- //輸出流
- try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
- //請(qǐng)求的URL
- String urlString = inputText.getText();
- URL url = new URL(urlString);
- //使用 netHandle打開URL連接,不使用代理
- URLConnection urlConnection = netHandle.openConnection(url, java.net.Proxy.NO_PROXY);
- HiLog.debug(LABEL_LOG, "%{public}s", "netHandle openConnection");
- //強(qiáng)轉(zhuǎn)換類型
- if (urlConnection instanceof HttpURLConnection) {
- connection = (HttpURLConnection) urlConnection;
- }
- //請(qǐng)求類型
- connection.setRequestMethod("GET");
- //連接
- connection.connect();
- //流量統(tǒng)計(jì)
- trafficDataStatistics(false);
- try (InputStream inputStream = urlConnection.getInputStream()) {
- byte[] cache = new byte[2 * 1024];
- int len = inputStream.read(cache);
- while (len != -1) {
- //
- outputStream.write(cache, 0, len);
- len = inputStream.read(cache);
- }
- } catch (IOException e) {
- HiLog.error(LABEL_LOG, "%{public}s", "netRequest inner IOException");
- }
- //返回結(jié)果
- String result = new String(outputStream.toByteArray());
- //UI顯示
- getUITaskDispatcher().asyncDispatch(() -> outText.setText(result));
- //統(tǒng)計(jì)完畢
- trafficDataStatistics(true);
- } catch (IOException e) {
- HiLog.error(LABEL_LOG, "%{public}s", "netRequest IOException");
- }
- });
c.按照應(yīng)用ID,進(jìn)行數(shù)據(jù)流量統(tǒng)計(jì)
在發(fā)送請(qǐng)求前獲取一次,在請(qǐng)求完成后獲取一次,
gitee代碼中這個(gè)地方有一處筆誤,tx代表上行流量,rx代表下行流量才對(duì)。詳情見官方文檔說明 流量統(tǒng)計(jì)。
- /**
- * 按照應(yīng)用ID,進(jìn)行數(shù)據(jù)流量統(tǒng)計(jì)
- *
- * @param isStart
- */
- private void trafficDataStatistics(boolean isStart) {
- int uid = 0;
- try {
- //根據(jù)給定的包名稱和用戶 ID 獲取應(yīng)用程序 UID。
- uid = getBundleManager().getUidByBundleName(getBundleName(), 0);
- } catch (RemoteException e) {
- HiLog.error(LABEL_LOG, "%{public}s", "trafficDataStatistics RemoteException");
- }
- if (isStart) {
- //獲取指定UID的下行流量。
- rx = DataFlowStatistics.getUidRxBytes(uid);
- //獲取指定UID的上行流量。
- tx = DataFlowStatistics.getUidTxBytes(uid);
- } else {
- rx = DataFlowStatistics.getUidRxBytes(uid) - rx;
- tx = DataFlowStatistics.getUidTxBytes(uid) - tx;
- //設(shè)置UI顯示
- getUITaskDispatcher().asyncDispatch(() -> statisticsText.setText(
- "TrafficDataStatistics:" + System.lineSeparator()
- + "Receive traffic:" + rx + System.lineSeparator()
- + "Sent traffic:" + tx));
- }
- }
- }
4.2.2 SocketClientSlice.java/SocketServerSlice.java Socket客戶端/服務(wù)端
實(shí)現(xiàn)Socket通信,是要客戶端和服務(wù)端的,服務(wù)端在指定網(wǎng)卡上監(jiān)聽指定端口,客戶端向指定IP指定端口發(fā)送數(shù)據(jù),實(shí)現(xiàn)通信。
a.Socket服務(wù)端開啟監(jiān)聽,等待接收數(shù)據(jù)
- //監(jiān)聽端口
- private static final int PORT = 8888;
- /**
- * 啟動(dòng)socket服務(wù)監(jiān)聽
- *
- * @param component
- */
- private void startServer(Component component) {
- HiLog.debug(LABEL_LOG, "startServer");
- //線程池獲取新線程處理
- ThreadPoolUtil.submit(() -> {
- //在指定端口開啟監(jiān)聽
- try (DatagramSocket socket = new DatagramSocket(PORT)) {
- //數(shù)據(jù)報(bào)包
- DatagramPacket packet = new DatagramPacket(new byte[255], 255);
- //死循環(huán)接收數(shù)據(jù)
- while (true) {
- //接收數(shù)據(jù)
- socket.receive(packet);
- //通過專有線程同步設(shè)置要顯示接收到的數(shù)據(jù)
- getUITaskDispatcher().syncDispatch(() -> outText.setText(
- "Receive a message from :" + packet.getAddress().getHostAddress()
- + System.lineSeparator() + " on port " + packet.getPort()
- + System.lineSeparator() + "message :" + new String(
- packet.getData()).substring(0, packet.getLength())
- ));
- packet.setLength(255);
- //延遲一下,留出處理數(shù)據(jù)的時(shí)間
- Thread.sleep(1000);
- }
- } catch (IOException | InterruptedException e) {
- e.printStackTrace();
- HiLog.error(LABEL_LOG, "%{public}s", "StartServer IOException | InterruptedException" + e);
- }
- });
- }
- /**
- * 獲取服務(wù)器端IP地址,顯示給客戶端發(fā)送消息使用
- *
- * @return
- */
- private String getLocationIpAddress() {
- HiLog.debug(LABEL_LOG, "getLocationIpAddress");
- //提供接口來管理 Wi-Fi。
- WifiDevice wifiDevice = WifiDevice.getInstance(this);
- HiLog.debug(LABEL_LOG, "wifiDevice:" + wifiDevice);
- //WifiLinkedInfo提供有關(guān) Wi-Fi 連接的信息。
- Optional<WifiLinkedInfo> linkedInfo = wifiDevice.getLinkedInfo();
- HiLog.debug(LABEL_LOG, "linkedInfo:" + linkedInfo);
- //獲取IP地址
- int ip = linkedInfo.get().getIpAddress();
- HiLog.debug(LABEL_LOG, "ip:" + ip);
- return (ip & 0xFF) + "." + ((ip >> 8) & 0xFF) + "." + ((ip >> 16) & 0xFF) + "." + (ip >> 24 & 0xFF);
- }
b.Socket客戶端發(fā)送數(shù)據(jù),等待接收數(shù)據(jù)
初始化NetManager對(duì)象->new 一個(gè)DatagramSocket->獲取當(dāng)前數(shù)據(jù)網(wǎng)絡(luò)NetHandle->獲取服務(wù)端的IP地址對(duì)象InetAddress
->將DatagramSocket綁定到NetHandle -> new 一個(gè)數(shù)據(jù)報(bào)包DatagramPacket -> 發(fā)送數(shù)據(jù)
- //通信端口
- private static final int PORT = 8888;
- /**
- * 發(fā)送網(wǎng)絡(luò)請(qǐng)求
- * @param component
- */
- private void netRequest(Component component) {
- HiLog.debug(LABEL_LOG, "netRequest");
- //啟動(dòng)新線程
- ThreadPoolUtil.submit(() -> {
- //初始化網(wǎng)絡(luò)管理對(duì)象
- NetManager netManager = NetManager.getInstance(null);
- //檢查默認(rèn)數(shù)據(jù)網(wǎng)絡(luò)是否已激活。
- if (!netManager.hasDefaultNet()) {
- return;
- }
- //new套接字
- try (DatagramSocket socket = new DatagramSocket()) {
- //獲取當(dāng)前數(shù)據(jù)網(wǎng)絡(luò)
- NetHandle netHandle = netManager.getDefaultNet();
- //獲取服務(wù)端的IP地址
- InetAddress address = netHandle.getByName(inputText.getText());
- //將套接字綁定到當(dāng)前網(wǎng)絡(luò)
- netHandle.bindSocket(socket);
- byte[] buffer = "I'm from Client".getBytes();
- //數(shù)據(jù)包
- DatagramPacket request = new DatagramPacket(buffer, buffer.length, address, PORT);
- //發(fā)送數(shù)據(jù)
- socket.send(request);
- HiLog.debug(LABEL_LOG, "send socket");
- } catch (IOException e) {
- e.printStackTrace();
- HiLog.error(LABEL_LOG, "%{public}s", "netRequest IOException"+e);
- }
- });
- }
4.2.3 HttpCacheSlice.java HTTP緩存功能
應(yīng)用重復(fù)打開一個(gè)相同網(wǎng)頁時(shí),可以優(yōu)先從緩存文件里讀取內(nèi)容,從而減少數(shù)據(jù)流量,降低設(shè)備功耗,提升應(yīng)用性能。
問:如何設(shè)置優(yōu)先從緩存文件里讀取內(nèi)容,
答:不用額外設(shè)置,自動(dòng)的 ~~
設(shè)置緩存,需要考慮 緩存位置和緩存大小。
a.初始化緩存 install
- /**
- * 開啟緩存
- * 開啟后自動(dòng)緩存數(shù)據(jù),不需要手動(dòng)處理
- */
- private void initCache(Component component) {
- //默認(rèn)的緩存目錄
- File httpCacheDir = new File(this.getCacheDir(), "http");
- //緩存大小
- long httpCacheSize = 10 * 1024 * 1024;
- try {
- //配置緩存目錄及最大緩存空間
- HttpResponseCache.install(httpCacheDir, httpCacheSize);
- HiLog.debug(LABEL_LOG, "%{public}s", "initCache,cache installed[" + httpCacheDir + "]");
- } catch (IOException e) {
- HiLog.error(LABEL_LOG, "%{public}s", "initCache IOException");
- }
- }
b.保存緩存,將緩存寫入文件系統(tǒng) flush
- /**
- * 將緩存寫入文件系統(tǒng),防止緩存丟失
- */
- private void flushCache(Component component) {
- HiLog.debug(LABEL_LOG, "%{public}s", "flushCache");
- try {
- //獲取緩存對(duì)象
- HttpResponseCache cache = HttpResponseCache.getInstalled();
- HiLog.debug(LABEL_LOG, "%{public}s", "cache:"+cache);
- if (cache != null) {
- try {
- //將緩存寫入文件系統(tǒng),如果cache is closed 會(huì)報(bào)錯(cuò) //java.lang.IllegalStateException: cache is closed
- cache.flush();
- getUITaskDispatcher().syncDispatch(() -> {
- //圖片加載時(shí)間,測(cè)試緩存和不緩存的差別
- duration.setText("cache flush success");
- });
- HiLog.debug(LABEL_LOG, "%{public}s", "cache flush");
- } catch (IOException e) {
- HiLog.error(LABEL_LOG, "%{public}s", "onStop IOException");
- }
- }
- } catch (IllegalStateException e) {
- e.printStackTrace();
- }
- }
c.禁用緩存并刪除其中的數(shù)據(jù)delete
- /**
- * 禁用緩存并刪除其中的數(shù)據(jù)
- */
- private void deleteCache(Component component) {
- HiLog.debug(LABEL_LOG, "%{public}s", "deleteCache");
- //獲取緩存對(duì)象
- HttpResponseCache cache = HttpResponseCache.getInstalled();
- HiLog.debug(LABEL_LOG, "%{public}s", "cache:"+cache);
- if (cache != null) {
- try {
- //禁用緩存并刪除其中的數(shù)據(jù)。
- cache.delete();
- image.setPixelMap(null);
- HiLog.debug(LABEL_LOG, "%{public}s", "cache delete");
- } catch (IOException e) {
- HiLog.error(LABEL_LOG, "%{public}s", "onStop IOException");
- }
- }
- }
- /**
- * 測(cè)試請(qǐng)求
- *
- * @param component
- */
- private void startRequest(Component component) {
- HiLog.debug(LABEL_LOG, "%{public}s", "startRequest");
- ThreadPoolUtil.submit(() -> {
- try {
- long startTime=System.currentTimeMillis();
- HiLog.debug(LABEL_LOG, "%{public}s", "startTime:"+startTime);
- //請(qǐng)求URL
- URL url = new URL(inputText.getText());
- URLConnection urlConnection = url.openConnection();
- HiLog.debug(LABEL_LOG, "%{public}s", "openConnection");
- //判斷連接類型
- if (urlConnection instanceof HttpURLConnection) {
- HiLog.debug(LABEL_LOG, "%{public}s", "urlConnection");
- HttpURLConnection connection = (HttpURLConnection) urlConnection;
- HiLog.debug(LABEL_LOG, "%{public}s", "connect:"+connection);
- //連接
- connection.connect();
- HiLog.debug(LABEL_LOG, "%{public}s", "connected");
- //連接結(jié)果
- if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
- HiLog.debug(LABEL_LOG, "%{public}s", "HTTP_OK");
- //描述圖像數(shù)據(jù)源選項(xiàng),例如包括表示為 image/png 的圖像格式。
- ImageSource.SourceOptions srcOpts = new ImageSource.SourceOptions();
- ImageSource imageSource = ImageSource.create(connection.getInputStream(), srcOpts);
- //以像素矩陣的形式提供圖像。
- PixelMap pixelMap = imageSource.createPixelmap(null);
- HiLog.debug(LABEL_LOG, "%{public}s", "pixelMap:"+pixelMap);
- //專有線程同步設(shè)置圖片顯示
- getUITaskDispatcher().syncDispatch(() -> {
- image.setPixelMap(pixelMap);
- //圖片加載時(shí)間,測(cè)試緩存和不緩存的差別
- duration.setText(String.valueOf(System.currentTimeMillis()-startTime)+" ms");
- });
- HiLog.debug(LABEL_LOG, "%{public}s", "setPixelMap");
- HiLog.debug(LABEL_LOG, "%{public}s", "endTime:"+ (System.currentTimeMillis()-startTime));
- }
- HiLog.debug(LABEL_LOG, "%{public}s", "finish");
- //關(guān)閉連接
- connection.disconnect();
- }
- } catch (Exception e) {
- HiLog.error(LABEL_LOG, "%{public}s", "initCache Exception"+e);
- getUITaskDispatcher().syncDispatch(() -> {
- //圖片加載時(shí)間,測(cè)試緩存和不緩存的差別
- duration.setText("cache is closed, please open cache");
- });
- }
- });
- }
5.總結(jié)說明
(1)兩種打開網(wǎng)絡(luò)連接的方式
- URLConnection urlConnection = url.openConnection();
- //使用 netHandle打開URL連接,不使用代理
- URLConnection urlConnection = netHandle.openConnection(url, java.net.Proxy.NO_PROXY);
(2)未開啟緩存情況下,發(fā)送請(qǐng)求的時(shí)長(zhǎng)在400-2000ms之間,開啟后,需要點(diǎn)擊兩次發(fā)送請(qǐng)求,時(shí)長(zhǎng)維持在90-200ms左右。
(3)禁用并清除緩存delete/close后,在沒有再次開啟緩存前,無法發(fā)送請(qǐng)求。這個(gè)操作官方文檔注釋寫的是 “結(jié)束時(shí)關(guān)閉緩存”。
6.完整代碼
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)