鸿蒙的网络管理功能,十分强悍!

前方就是光明 2021-09-27 14:44:00 7518

本示例演示了如何使用网络管理模块相关接口,演示了以下功能:

功能 1:使用默认网络,打开连接,发送 HTTP 请求。

功能 2:统计指定 UID 的上行/下行流量。

功能 3:使用 Socket 方式实现不同设备间通信。此功能需要打开 WIFI,并且通信的设备连接相同的 WIFI 组成局域网。操作上,先启动服务端,再启动客户端,然后从客户端发送消息,查看服务端是否收到消息。

功能 4:HTTP 缓存的使用,创建缓存,供下一次请求使用,减少数据流量和加载时间。

注意,需要以下权限:

ohos.permission.GET_NETWORK_INFO:获取网络连接信息。

ohos.permission.SET_NETWORK_INFO:修改网络连接状态。

ohos.permission.INTERNET:允许程序打开网络套接字,进行网络连接。

详情见官方文档,网络管理开发概述:
https://developer.harmonyos.com/cn/docs/documentation/doc-guides/connectivity-net-overview-0000000000029978

搭建环境

安装 DevEco Studio,详情请参考 DevEco Studio 下载:
https://developer.harmonyos.com/cn/develop/deveco-studio

设置 DevEco Studio 开发环境,DevEco Studio 开发环境需要依赖于网络环境,需要连接上网络才能确保工具的正常使用。

可以根据如下两种情况来配置开发环境:

如果可以直接访问 Internet,只需进行下载 HarmonyOS SDK 操作。

如果网络不能直接访问 Internet,需要通过代理服务器才可以访问,请参考配置开发环境。
https://developer.harmonyos.com/cn/docs/documentation/doc-guides/environment_config-0000001052902427

下载源码后,使用 DevEco Studio 打开项目,模拟器运行即可。真机运行需要将 config.json 中的 buddleName 修改为自己的,如果没有请到 AGC 上进行配置。

参见《使用模拟器进行调试》:

https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ide_debug_device-0000001053822404

代码结构

①代码结构

如下图:

②相关文件介绍

核心类:

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 //主页面

实例讲解

①界面布局

如下图:


②后台代码

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 代表下行流量才对。

详情见官方文档说明《流量统计》:
https://developer.harmonyos.com/cn/docs/documentation/doc-guides/connectivity-net-traffic-0000000000045998

/**

  • 按照应用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));

    }
    }
    }

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

    });
    }

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

总结说明

①两种打开网络连接的方式:

URLConnection urlConnection = url.openConnection();

//使用 netHandle打开URL连接,不使用代理
URLConnection urlConnection = netHandle.openConnection(url, java.net.Proxy.NO_PROXY);

②未开启缓存情况下,发送请求的时长在 400-2000ms 之间,开启后,需要点击两次发送请求,时长维持在 90-200ms 左右。

③禁用并清除缓存 delete/close 后,在没有再次开启缓存前,无法发送请求。这个操作官方文档注释写的是 “结束时关闭缓存”。

完整代码

附件直接下载:

https://harmonyos.51cto.com/resource/1270

来源:鸿蒙技术社区

声明:本文内容由易百纳平台入驻作者撰写,文章观点仅代表作者本人,不代表易百纳立场。如有内容侵权或者其他问题,请联系本站进行删除。
红包 94 收藏 评论 打赏
评论
1个
内容存在敏感词
手气红包
  • nfzanken 2021-10-26 15:06:07
    回复
    //获取服务端的IP地址 InetAddress address = netHandle.getByName(inputText.getText()); 这里就没高明白到底拿的是个啥?我想用比如192.168.1.2:8080这样一个服务器地址桌面搞?
相关专栏
置顶时间设置
结束时间
删除原因
  • 广告/SPAM
  • 恶意灌水
  • 违规内容
  • 文不对题
  • 重复发帖
打赏作者
易百纳技术社区
前方就是光明
您的支持将鼓励我继续创作!
打赏金额:
¥1易百纳技术社区
¥5易百纳技术社区
¥10易百纳技术社区
¥50易百纳技术社区
¥100易百纳技术社区
支付方式:
微信支付
支付宝支付
易百纳技术社区微信支付
易百纳技术社区
打赏成功!

感谢您的打赏,如若您也想被打赏,可前往 发表专栏 哦~

举报反馈

举报类型

  • 内容涉黄/赌/毒
  • 内容侵权/抄袭
  • 政治相关
  • 涉嫌广告
  • 侮辱谩骂
  • 其他

详细说明

审核成功

发布时间设置
发布时间:
是否关联周任务-专栏模块

审核失败

失败原因
备注
拼手气红包 红包规则
祝福语
恭喜发财,大吉大利!
红包金额
红包最小金额不能低于5元
红包数量
红包数量范围10~50个
余额支付
当前余额:
可前往问答、专栏板块获取收益 去获取
取 消 确 定

小包子的红包

恭喜发财,大吉大利

已领取20/40,共1.6元 红包规则

    易百纳技术社区