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

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


4.2.后臺代碼
4.2.1 NetRequestSlice.java 網絡請求&流量統計 功能
a.初始化網絡管理對象NetManager
- //初始化網絡管理對象
- netManager = NetManager.getInstance(null);
b.通過線程池獲取一個新線程處理進行連接請求
獲取默認數據網絡的時候需要ohos.permission.GET_NETWORK_INFO權限。
- //用線程池的取一個新線程處理
- ThreadPoolUtil.submit(() -> {
- HiLog.debug(LABEL_LOG, "%{public}s", "ThreadPoolUtil submit");
- //獲取默認的數據網絡,wifi和流量都關閉時,netId==0,其它時 netId=390/391, must have the ohos.permission.GET_NETWORK_INFO permission
- NetHandle netHandle = netManager.getDefaultNet();
- //接收默認數據網絡的狀態更改的回調
- netManager.addDefaultNetStatusCallback(callback);
- //netManager.setAppNet(netHandle);
- //支持 HTTP 特定功能的 URLConnection。
- HttpURLConnection connection = null;
- //輸出流
- try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
- //請求的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");
- //強轉換類型
- if (urlConnection instanceof HttpURLConnection) {
- connection = (HttpURLConnection) urlConnection;
- }
- //請求類型
- connection.setRequestMethod("GET");
- //連接
- connection.connect();
- //流量統計
- 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");
- }
- //返回結果
- String result = new String(outputStream.toByteArray());
- //UI顯示
- getUITaskDispatcher().asyncDispatch(() -> outText.setText(result));
- //統計完畢
- trafficDataStatistics(true);
- } catch (IOException e) {
- HiLog.error(LABEL_LOG, "%{public}s", "netRequest IOException");
- }
- });
c.按照應用ID,進行數據流量統計
在發送請求前獲取一次,在請求完成后獲取一次,
gitee代碼中這個地方有一處筆誤,tx代表上行流量,rx代表下行流量才對。詳情見官方文檔說明 流量統計。
- /**
- * 按照應用ID,進行數據流量統計
- *
- * @param isStart
- */
- private void trafficDataStatistics(boolean isStart) {
- int uid = 0;
- try {
- //根據給定的包名稱和用戶 ID 獲取應用程序 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;
- //設置UI顯示
- getUITaskDispatcher().asyncDispatch(() -> statisticsText.setText(
- "TrafficDataStatistics:" + System.lineSeparator()
- + "Receive traffic:" + rx + System.lineSeparator()
- + "Sent traffic:" + tx));
- }
- }
- }
4.2.2 SocketClientSlice.java/SocketServerSlice.java Socket客戶端/服務端
實現Socket通信,是要客戶端和服務端的,服務端在指定網卡上監聽指定端口,客戶端向指定IP指定端口發送數據,實現通信。
a.Socket服務端開啟監聽,等待接收數據
- //監聽端口
- private static final int PORT = 8888;
- /**
- * 啟動socket服務監聽
- *
- * @param component
- */
- private void startServer(Component component) {
- HiLog.debug(LABEL_LOG, "startServer");
- //線程池獲取新線程處理
- ThreadPoolUtil.submit(() -> {
- //在指定端口開啟監聽
- try (DatagramSocket socket = new DatagramSocket(PORT)) {
- //數據報包
- DatagramPacket packet = new DatagramPacket(new byte[255], 255);
- //死循環接收數據
- while (true) {
- //接收數據
- socket.receive(packet);
- //通過專有線程同步設置要顯示接收到的數據
- 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);
- //延遲一下,留出處理數據的時間
- Thread.sleep(1000);
- }
- } catch (IOException | InterruptedException e) {
- e.printStackTrace();
- HiLog.error(LABEL_LOG, "%{public}s", "StartServer IOException | InterruptedException" + e);
- }
- });
- }
- /**
- * 獲取服務器端IP地址,顯示給客戶端發送消息使用
- *
- * @return
- */
- private String getLocationIpAddress() {
- HiLog.debug(LABEL_LOG, "getLocationIpAddress");
- //提供接口來管理 Wi-Fi。
- WifiDevice wifiDevice = WifiDevice.getInstance(this);
- HiLog.debug(LABEL_LOG, "wifiDevice:" + wifiDevice);
- //WifiLinkedInfo提供有關 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客戶端發送數據,等待接收數據
初始化NetManager對象->new 一個DatagramSocket->獲取當前數據網絡NetHandle->獲取服務端的IP地址對象InetAddress
->將DatagramSocket綁定到NetHandle -> new 一個數據報包DatagramPacket -> 發送數據
- //通信端口
- private static final int PORT = 8888;
- /**
- * 發送網絡請求
- * @param component
- */
- private void netRequest(Component component) {
- HiLog.debug(LABEL_LOG, "netRequest");
- //啟動新線程
- ThreadPoolUtil.submit(() -> {
- //初始化網絡管理對象
- NetManager netManager = NetManager.getInstance(null);
- //檢查默認數據網絡是否已激活。
- if (!netManager.hasDefaultNet()) {
- return;
- }
- //new套接字
- try (DatagramSocket socket = new DatagramSocket()) {
- //獲取當前數據網絡
- NetHandle netHandle = netManager.getDefaultNet();
- //獲取服務端的IP地址
- InetAddress address = netHandle.getByName(inputText.getText());
- //將套接字綁定到當前網絡
- netHandle.bindSocket(socket);
- byte[] buffer = "I'm from Client".getBytes();
- //數據包
- DatagramPacket request = new DatagramPacket(buffer, buffer.length, address, PORT);
- //發送數據
- 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緩存功能
應用重復打開一個相同網頁時,可以優先從緩存文件里讀取內容,從而減少數據流量,降低設備功耗,提升應用性能。
問:如何設置優先從緩存文件里讀取內容,
答:不用額外設置,自動的 ~~
設置緩存,需要考慮 緩存位置和緩存大小。
a.初始化緩存 install
- /**
- * 開啟緩存
- * 開啟后自動緩存數據,不需要手動處理
- */
- private void initCache(Component component) {
- //默認的緩存目錄
- 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.保存緩存,將緩存寫入文件系統 flush
- /**
- * 將緩存寫入文件系統,防止緩存丟失
- */
- private void flushCache(Component component) {
- HiLog.debug(LABEL_LOG, "%{public}s", "flushCache");
- try {
- //獲取緩存對象
- HttpResponseCache cache = HttpResponseCache.getInstalled();
- HiLog.debug(LABEL_LOG, "%{public}s", "cache:"+cache);
- if (cache != null) {
- try {
- //將緩存寫入文件系統,如果cache is closed 會報錯 //java.lang.IllegalStateException: cache is closed
- cache.flush();
- getUITaskDispatcher().syncDispatch(() -> {
- //圖片加載時間,測試緩存和不緩存的差別
- 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.禁用緩存并刪除其中的數據delete
- /**
- * 禁用緩存并刪除其中的數據
- */
- private void deleteCache(Component component) {
- HiLog.debug(LABEL_LOG, "%{public}s", "deleteCache");
- //獲取緩存對象
- HttpResponseCache cache = HttpResponseCache.getInstalled();
- HiLog.debug(LABEL_LOG, "%{public}s", "cache:"+cache);
- if (cache != null) {
- try {
- //禁用緩存并刪除其中的數據。
- cache.delete();
- image.setPixelMap(null);
- HiLog.debug(LABEL_LOG, "%{public}s", "cache delete");
- } catch (IOException e) {
- HiLog.error(LABEL_LOG, "%{public}s", "onStop IOException");
- }
- }
- }
- /**
- * 測試請求
- *
- * @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);
- //請求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");
- //連接結果
- if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
- HiLog.debug(LABEL_LOG, "%{public}s", "HTTP_OK");
- //描述圖像數據源選項,例如包括表示為 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);
- //專有線程同步設置圖片顯示
- getUITaskDispatcher().syncDispatch(() -> {
- image.setPixelMap(pixelMap);
- //圖片加載時間,測試緩存和不緩存的差別
- 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");
- //關閉連接
- connection.disconnect();
- }
- } catch (Exception e) {
- HiLog.error(LABEL_LOG, "%{public}s", "initCache Exception"+e);
- getUITaskDispatcher().syncDispatch(() -> {
- //圖片加載時間,測試緩存和不緩存的差別
- duration.setText("cache is closed, please open cache");
- });
- }
- });
- }
5.總結說明
(1)兩種打開網絡連接的方式
- URLConnection urlConnection = url.openConnection();
- //使用 netHandle打開URL連接,不使用代理
- URLConnection urlConnection = netHandle.openConnection(url, java.net.Proxy.NO_PROXY);
(2)未開啟緩存情況下,發送請求的時長在400-2000ms之間,開啟后,需要點擊兩次發送請求,時長維持在90-200ms左右。
(3)禁用并清除緩存delete/close后,在沒有再次開啟緩存前,無法發送請求。這個操作官方文檔注釋寫的是 “結束時關閉緩存”。
6.完整代碼