HarmonyOS Sample 之 NetworkManagement 网络管理功能

知识的领域是无限的,我们的学习也是无限期的。这篇文章主要讲述HarmonyOS Sample 之 NetworkManagement 网络管理功能相关的知识,希望能为你提供帮助。
@toc
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 代码结构

HarmonyOS Sample 之 NetworkManagement 网络管理功能

文章图片

3.2 相关文件介绍核心类:
HttpURLConnection.java //支持 HTTP 特定功能的 URLConnection
URLConnection.java //URL连接
URL.java //指向万维网上“资源”的指针
NetStatusCallback.java //网络状态的回调类,出现可用网络触发onAvailable函数
DataFlowStatistics.java //该类提供查询指定蜂窝网络、应用和网卡的整体流量统计和流量统计的接口。
【HarmonyOS Sample 之 NetworkManagement 网络管理功能】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.界面布局
主页面 HTTP请求页面 缓存示例页
HarmonyOS Sample 之 NetworkManagement 网络管理功能

文章图片
HarmonyOS Sample 之 NetworkManagement 网络管理功能

文章图片
HarmonyOS Sample 之 NetworkManagement 网络管理功能

文章图片
HarmonyOS Sample 之 NetworkManagement 网络管理功能

文章图片

HarmonyOS Sample 之 NetworkManagement 网络管理功能

文章图片

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", "StartServerIOException | 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.完整代码https://harmonyos.51cto.com/resource/1270
想了解更多关于鸿蒙的内容,请访问:
51CTO和华为官方战略合作共建的鸿蒙技术社区
https://harmonyos.51cto.com/#bkwz

    推荐阅读