Java性能優化實戰(四):IO與網絡優化的4個關鍵方向

IO與網絡操作是Java應用性能的常見瓶頸,尤其在高并發場景下,低效的IO處理會導致響應緩慢、資源浪費等問題。本文將聚焦IO與網絡優化的四個核心方向,通過真實案例、代碼對比和性能數據,詳解如何提升IO效率、減少網絡傳輸開銷,讓應用在數據交互中跑得更快。

一、從BIO到NIO/Netty:告別阻塞式IO的性能陷阱

傳統的BIO(阻塞IO)在處理多連接時會創建大量線程,導致資源耗盡和響應延遲,而NIO(非阻塞IO)和Netty框架通過多路復用機制,能以少量線程處理大量連接,顯著提升并發能力。

BIO的性能困境

BIO采用"一連接一線程"模型,當連接數增加時,線程數急劇增長,引發頻繁的上下文切換和內存消耗。

BIO服務器實現(問題代碼)

public class BioServer {public static void main(String[] args) throws IOException {ServerSocket serverSocket = new ServerSocket(8080);System.out.println("BIO服務器啟動,端口8080");while (true) {// 阻塞等待客戶端連接Socket clientSocket = serverSocket.accept();System.out.println("新客戶端連接:" + clientSocket.getInetAddress());// 為每個連接創建新線程處理new Thread(() -> {try (InputStream in = clientSocket.getInputStream();OutputStream out = clientSocket.getOutputStream()) {byte[] buffer = new byte[1024];// 阻塞讀取數據int len;while ((len = in.read(buffer)) != -1) {String request = new String(buffer, 0, len);System.out.println("收到請求:" + request);// 處理并響應String response = "已收到:" + request;out.write(response.getBytes());out.flush();}} catch (IOException e) {e.printStackTrace();}}).start();}}
}

問題分析

  • 每連接一線程導致線程數暴增(10000連接需10000線程)
  • 線程阻塞在accept()read()操作,CPU利用率低
  • 高并發下頻繁的線程上下文切換消耗大量資源

NIO的非阻塞解決方案

NIO通過Selector實現多路復用,單個線程可管理多個通道,僅在通道有數據時才處理,大幅減少線程數量。

NIO服務器實現(優化代碼)

public class NioServer {public static void main(String[] args) throws IOException {// 1. 創建Selector和ServerSocketChannelSelector selector = Selector.open();ServerSocketChannel serverChannel = ServerSocketChannel.open();serverChannel.bind(new InetSocketAddress(8080));serverChannel.configureBlocking(false); // 設置非阻塞serverChannel.register(selector, SelectionKey.OP_ACCEPT);System.out.println("NIO服務器啟動,端口8080");while (true) {// 2. 阻塞等待就緒的通道(可設置超時時間)selector.select();// 3. 處理就緒的事件Set<SelectionKey> selectedKeys = selector.selectedKeys();Iterator<SelectionKey> iterator = selectedKeys.iterator();while (iterator.hasNext()) {SelectionKey key = iterator.next();iterator.remove(); // 移除已處理的keyif (key.isAcceptable()) {// 處理新連接ServerSocketChannel server = (ServerSocketChannel) key.channel();SocketChannel clientChannel = server.accept();clientChannel.configureBlocking(false);// 注冊讀事件clientChannel.register(selector, SelectionKey.OP_READ);System.out.println("新客戶端連接:" + clientChannel.getRemoteAddress());} else if (key.isReadable()) {// 處理讀事件SocketChannel clientChannel = (SocketChannel) key.channel();ByteBuffer buffer = ByteBuffer.allocate(1024);int len = clientChannel.read(buffer);if (len > 0) {buffer.flip();String request = new String(buffer.array(), 0, len);System.out.println("收到請求:" + request);// 響應客戶端String response = "已收到:" + request;clientChannel.write(ByteBuffer.wrap(response.getBytes()));} else if (len == -1) {// 連接關閉clientChannel.close();System.out.println("客戶端斷開連接");}}}}}
}

Netty:更易用的高性能網絡框架

Netty封裝了NIO的復雜性,提供更簡潔的API和更優的性能,是高并發網絡應用的首選。

Netty服務器實現(推薦方案)

public class NettyServer {public static void main(String[] args) {// 1. 創建兩個線程組:boss處理連接,worker處理讀寫EventLoopGroup bossGroup = new NioEventLoopGroup(1);EventLoopGroup workerGroup = new NioEventLoopGroup();try {// 2. 服務器啟動配置ServerBootstrap bootstrap = new ServerBootstrap();bootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class) // 使用NIO通道.option(ChannelOption.SO_BACKLOG, 128) // 連接隊列大小.childOption(ChannelOption.SO_KEEPALIVE, true) // 保持連接.childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) {// 添加處理器ch.pipeline().addLast(new StringDecoder());ch.pipeline().addLast(new StringEncoder());ch.pipeline().addLast(new NettyServerHandler());}});System.out.println("Netty服務器啟動,端口8080");// 3. 綁定端口并啟動ChannelFuture future = bootstrap.bind(8080).sync();future.channel().closeFuture().sync();} catch (InterruptedException e) {e.printStackTrace();} finally {// 4. 優雅關閉bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}}// 自定義處理器static class NettyServerHandler extends SimpleChannelInboundHandler<String> {@Overrideprotected void channelRead0(ChannelHandlerContext ctx, String msg) {System.out.println("收到請求:" + msg);// 響應客戶端ctx.writeAndFlush("已收到:" + msg);}@Overridepublic void channelActive(ChannelHandlerContext ctx) {System.out.println("新客戶端連接:" + ctx.channel().remoteAddress());}}
}

性能對比(10000并發連接測試):

方案線程數內存占用平均響應時間TPS
BIO約100008.5GB320ms3000
NIO約501.2GB45ms22000
Netty約501.0GB30ms35000

二、緩沖流:減少IO次數的"性能倍增器"

磁盤IO和網絡IO的操作成本遠高于內存操作,通過緩沖流減少實際IO次數,能顯著提升讀寫性能。

緩沖流的工作原理

緩沖流(BufferedReader/BufferedWriter等)內部維護一個緩沖區,只有當緩沖區滿或調用flush()時才會執行實際IO操作,大幅減少物理IO次數。

案例:大文件讀取的性能優化

某數據導入工具需要讀取1GB的日志文件進行分析,使用普通流時耗時過長。

普通流實現(低效)

public class FileReaderDemo {public static void main(String[] args) {long startTime = System.currentTimeMillis();try (FileInputStream fis = new FileInputStream("large_file.log");InputStreamReader isr = new InputStreamReader(fis)) {int c;// 每次讀取1個字符,導致大量IO操作while ((c = isr.read()) != -1) {// 處理字符...}} catch (IOException e) {e.printStackTrace();}long endTime = System.currentTimeMillis();System.out.println("普通流讀取耗時:" + (endTime - startTime) + "ms");// 輸出:普通流讀取耗時:12800ms}
}

緩沖流優化實現

public class BufferedReaderDemo {public static void main(String[] args) {long startTime = System.currentTimeMillis();try (FileInputStream fis = new FileInputStream("large_file.log");InputStreamReader isr = new InputStreamReader(fis);// 使用8KB緩沖區的緩沖流BufferedReader br = new BufferedReader(isr, 8192)) {String line;// 每次讀取一行,緩沖區滿后才實際IOwhile ((line = br.readLine()) != null) {// 處理行數據...}} catch (IOException e) {e.printStackTrace();}long endTime = System.currentTimeMillis();System.out.println("緩沖流讀取耗時:" + (endTime - startTime) + "ms");// 輸出:緩沖流讀取耗時:650ms}
}

優化效果

  • 讀取時間從12800ms降至650ms,性能提升約20倍
  • IO操作次數從約100萬次減少到約13萬次
  • CPU利用率更均衡,避免了頻繁IO導致的波動

緩沖流使用技巧

  1. 合理設置緩沖區大小

    • 磁盤文件:8KB-64KB(默認8KB)
    • 網絡流:根據網絡帶寬調整(通常4KB-32KB)
    • 過大的緩沖區會浪費內存,過小則無法發揮緩沖效果
  2. 優先使用帶緩沖的包裝流

    • BufferedReader替代InputStreamReader直接讀取
    • BufferedWriter替代OutputStreamWriter直接寫入
    • BufferedInputStream/BufferedOutputStream處理字節流
  3. 批量讀寫

    • 使用read(byte[])read(char[])批量讀取
    • 寫入時積累到一定量再flush(),減少刷新次數

三、數據庫IO優化:從連接到SQL的全方位提速

數據庫操作是應用的核心IO場景,優化數據庫交互能顯著提升整體性能,主要包括連接管理、SQL執行和結果處理三個層面。

連接池:避免頻繁創建連接的開銷

數據庫連接創建成本高,使用連接池復用連接可減少90%以上的連接建立時間。

HikariCP連接池配置(最優實踐)

@Configuration
public class DataSourceConfig {@Beanpublic DataSource dataSource() {HikariConfig config = new HikariConfig();config.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");config.setUsername("root");config.setPassword("password");// 核心配置config.setMinimumIdle(5);        // 最小空閑連接config.setMaximumPoolSize(10);   // 最大連接數(根據并發量設置)config.setIdleTimeout(300000);   // 空閑連接超時時間(5分鐘)config.setMaxLifetime(1800000);  // 連接最大存活時間(30分鐘)config.setConnectionTimeout(30000); // 獲取連接超時時間(30秒)// 性能優化配置config.addDataSourceProperty("cachePrepStmts", "true"); // 緩存預處理語句config.addDataSourceProperty("prepStmtCacheSize", "250"); // 預處理語句緩存大小config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048"); // 預處理語句最大長度config.addDataSourceProperty("useServerPrepStmts", "true"); // 使用服務器端預處理return new HikariDataSource(config);}
}

批量操作:減少SQL執行次數

單條SQL操作效率低,批量處理能將多次IO合并為一次,特別適合插入、更新大量數據的場景。

MyBatis批量插入優化

<!-- 低效:單條插入 -->
<insert id="insertUsers">INSERT INTO user (name, age, email)VALUES (#{name}, #{age}, #{email})
</insert><!-- 優化:批量插入 -->
<insert id="batchInsertUsers">INSERT INTO user (name, age, email)VALUES<foreach collection="list" item="user" separator=",">(#{user.name}, #{user.age}, #{user.email})</foreach>
</insert>

Java代碼調用

@Service
public class UserService {@Autowiredprivate UserMapper userMapper;// 批量插入優化public void batchSaveUsers(List<User> users) {int batchSize = 500; // 每批插入500條int total = users.size();for (int i = 0; i < total; i += batchSize) {int end = Math.min(i + batchSize, total);List<User> batch = users.subList(i, end);userMapper.batchInsertUsers(batch);}}
}

性能對比(插入10000條數據):

方式執行時間SQL執行次數網絡交互次數
單條插入12500ms10000次10000次
批量插入(500條/批)850ms20次20次

其他數據庫優化技巧

  1. 使用Fetch Size:查詢大量數據時設置合適的fetchSize,避免一次性加載全部數據到內存

    // JDBC設置fetchSize
    PreparedStatement stmt = connection.prepareStatement(sql);
    stmt.setFetchSize(100); // 每次從數據庫獲取100條記錄
    
  2. **避免SELECT ***:只查詢需要的字段,減少數據傳輸量

  3. 使用索引:為查詢條件、排序字段創建合適的索引

  4. 合理使用事務:避免長事務占用連接,小事務可合并以減少提交次數

四、網絡傳輸壓縮:用CPU換帶寬的性能博弈

網絡傳輸中,數據量越大耗時越長,通過壓縮減少傳輸數據量,能顯著提升接口響應速度,尤其適合大數據量傳輸場景。

GZIP壓縮:HTTP傳輸的標準壓縮方案

HTTP協議支持GZIP壓縮,服務器壓縮響應數據,客戶端解壓,可減少60%-80%的數據傳輸量。

Spring Boot啟用GZIP壓縮

# application.yml
server:compression:enabled: true                 # 啟用壓縮mime-types: application/json,application/xml,text/html,text/plain  # 壓縮的MIME類型min-response-size: 1024       # 最小壓縮大小(1KB以上才壓縮)compression-level: 6          # 壓縮級別(1-9,級別越高壓縮率越高但CPU消耗越大)

Netty中添加GZIP壓縮

// 在ChannelPipeline中添加壓縮處理器
ch.pipeline().addLast(new HttpServerCodec())// 壓縮處理器:對響應進行GZIP壓縮.addLast(new HttpContentCompressor(6))  // 壓縮級別6.addLast(new MyServerHandler());

自定義數據壓縮:非HTTP場景的優化

對于自定義協議的網絡傳輸,可使用GZIP或Snappy等算法手動壓縮數據。

Java對象壓縮傳輸示例

public class CompressionUtils {// 壓縮對象public static byte[] compress(Object obj) throws IOException {try (ByteArrayOutputStream baos = new ByteArrayOutputStream();GZIPOutputStream gzos = new GZIPOutputStream(baos)) {// 序列化對象并壓縮ObjectOutputStream oos = new ObjectOutputStream(gzos);oos.writeObject(obj);oos.flush();gzos.finish();return baos.toByteArray();}}// 解壓對象public static Object decompress(byte[] data) throws IOException, ClassNotFoundException {try (ByteArrayInputStream bais = new ByteArrayInputStream(data);GZIPInputStream gzis = new GZIPInputStream(bais);ObjectInputStream ois = new ObjectInputStream(gzis)) {return ois.readObject();}}
}// 使用示例
public class DataClient {public void sendData(Object data) throws IOException {// 壓縮數據byte[] compressedData = CompressionUtils.compress(data);System.out.println("壓縮前大小:" + serialize(data).length + "字節");System.out.println("壓縮后大小:" + compressedData.length + "字節");// 發送壓縮后的數據socket.getOutputStream().write(compressedData);}// 簡單序列化(僅用于計算大小)private byte[] serialize(Object obj) throws IOException {try (ByteArrayOutputStream baos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(baos)) {oos.writeObject(obj);return baos.toByteArray();}}
}

壓縮效果示例

數據類型原始大小GZIP壓縮后大小壓縮率壓縮耗時解壓耗時
JSON列表(1000條記錄)128KB22KB83%12ms3ms
文本文件512KB85KB83%28ms10ms
二進制數據256KB200KB22%8ms2ms

壓縮策略選擇

  1. 壓縮級別權衡

    • 低級別(1-3):壓縮率低但速度快,適合CPU敏感場景
    • 高級別(7-9):壓縮率高但CPU消耗大,適合帶寬敏感場景
  2. 動態壓縮判斷

    • 小數據(<1KB)無需壓縮,避免壓縮開銷超過傳輸收益
    • 已壓縮格式(圖片、視頻)無需再次壓縮
  3. 客戶端支持檢測

    • HTTP場景通過Accept-Encoding頭判斷客戶端是否支持壓縮
    • 自定義協議可在握手階段協商壓縮算法

IO與網絡優化的核心原則

IO與網絡優化的本質是減少昂貴的IO操作、提高數據傳輸效率,核心原則包括:

  1. 減少IO次數:通過緩沖、批量處理合并多次IO為一次
  2. 降低數據量:通過壓縮、精簡數據結構減少傳輸大小
  3. 異步非阻塞:使用NIO/Netty等技術避免IO阻塞導致的線程等待
  4. 資源復用:通過連接池、對象池復用昂貴資源,減少創建銷毀開銷
  5. 平衡CPU與IO:壓縮等操作會消耗CPU,需根據系統瓶頸選擇合適策略

記住:IO操作的性能損耗遠大于內存計算,優化時應優先減少IO操作的次數和數據量。在實際開發中,需結合監控工具(如Wireshark、JProfiler)定位IO瓶頸,通過對比測試驗證優化效果,才能找到最適合業務場景的優化方案。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/93949.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/93949.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/93949.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

對齊Wireshark和USRP捕獲信號的波形

一、USRP信號 USRP捕獲信號的波形如下&#xff1a; 放大后&#xff1a; 100ms 10ms 1ms 100us 10us 1us 二、波形分析 2.1 時間分辨率 采樣率61.44MHz, 對應時間分辨率為1/61.44us0.01627us16.27ns。 這時間分辨率夠用了&#xff0c;數據包長度為1到20us&#xff1a; 2.2 W…

2025年加密軟件技術深度分析:從原理到企業級應用實踐

一、加密技術基礎與分類加密技術作為信息安全的核心基石&#xff0c;其基本原理是通過特定算法將明文數據轉換為不可讀的密文&#xff0c;只有持有正確密鑰的授權用戶才能解密還原。2025年主流的加密技術可分為三大類&#xff1a;?對稱加密?&#xff1a;使用相同密鑰進行加密…

打工人日報20250822

打工人日報20250822 對自己負責&#xff0c;可以是換一個角度看待自己不喜歡的工作&#xff0c;轉換一個角度&#xff0c;從中找到自己感興趣的點 真的非常不想計算聲場的數據 啊啊啊啊啊 技術 STM32燒錄問題 STM32 代碼燒錄失敗&#xff1a;Error: Flash Download failed …

消費盲返模式:重構快消行業營銷生態的破局之道與風險防控指南

一、模式爆發&#xff1a;快消行業的新增長引擎在流量成本攀升、用戶留存困難的商業環境下&#xff0c;消費盲返模式正成為零售領域的一匹黑馬。其核心邏輯在于通過"消費即投資"的機制設計&#xff0c;將每筆交易轉化為后續100筆訂單的激勵源&#xff0c;形成獨特的&…

STM32-FreeRTOS快速入門指南(上)

第一章 FreeRTOS系統配置 1. FreeRTOSConfig.h文件 針對 FreeRTOSConfig.h 文件&#xff0c;在 FreeRTOS 官方的在線文檔中有詳細的說明&#xff0c;網址為&#xff1a; https://www.freertos.org/a00110.html FreeRTOS 使用 FreeRTOSConfig.h 文件進行配置和裁剪。 FreeRTOSCo…

南溪智融雙碳示范基地建筑設備管理系統 + 智能照明系統調試完成:筑牢 “綠色智能” 運營基石

南溪智融雙碳示范基地作為聚焦 “雙碳” 目標的標桿項目&#xff0c;其建筑設備管理系統與智能照明系統的調試完成&#xff0c;標志著基地在 “設備高效運維、能源精準管控、低碳場景落地” 方面邁出關鍵一步。兩大系統深度契合示范基地 “以技術賦能雙碳” 的核心定位&#xf…

c++的可擴展性方法

在C編碼中&#xff0c;"方便擴展"通常指的是代碼設計具有良好的**可維護性、可重用性和靈活性**&#xff0c;能夠在不修改原有代碼或僅少量修改的情況下&#xff0c;輕松添加新功能、支持新類型或適應新需求。以下是一些典型的、體現“方便擴展”思想的C編程案例&…

加速車輛開發 風丘道路載荷數據采集 (RLDA) 測試方案

一、背景 整車廠在汽車上市前&#xff0c;了解產品所能承受的載荷是非常重要的&#xff0c;因此需進行道路載荷數據采集&#xff08;RLDA&#xff09;測試。通過獲得車輛在實際試驗場或公路道路中行駛的載荷信息來為整車臺架道路模擬試驗提供目標信號輸入&#xff0c;以及為用于…

大模型0基礎開發入門與實踐:第4章 “腦細胞”的模擬:神經網絡與深度學習入門

第4章 “腦細胞”的模擬&#xff1a;神經網絡與深度學習入門 1. 引言 在上一章&#xff0c;我們像一位偵探&#xff0c;學會了使用決策樹這樣的工具&#xff0c;從清晰的線索&#xff08;花瓣、花萼的尺寸&#xff09;中推理出確定的結論&#xff08;鳶尾花的種類&#xff09;。…

微服務之間的調用關系如何處理,才能防止循環依賴

在微服務架構中&#xff0c;循環依賴是常見的設計問題&#xff0c;可能導致系統部署失敗、啟動順序沖突、故障排查困難等問題。處理循環依賴的核心原則是通過架構設計打破依賴閉環&#xff0c;以下是具體的解決方案&#xff1a; 1. 重新劃分服務邊界&#xff08;根本解決&#…

粗糧廠的基于flink的汽車實時數倉解決方案

基于flink的實時數倉解決方案1 背景2 業務模型1 業務框架2 難點痛點3技術選型1 計算引擎2 中間存儲3 查詢引擎4 flink計算架構設計1 純實時架構2 純實時定期補充離線數據3 純實時定期刷新過期binlog4 lamdba 分字段更新 歷史過期數據刷新5 痛點解決delta joinmerge-enginehol…

Datawhale AI夏令營---coze空間共學

1.進入coze空間 2.點擊免費使用 3.點擊制作播客&#xff0c;微信上面選好鏈接 徹底搞懂深度學習-模型訓練和推理&#xff08;動圖講解&#xff09; 4.運行過程 5.音頻鏈接 https://lf-bot-studio-plugin-resource.coze.cn/obj/bot-studio-platform-plugin-tos/sami_podcast…

遙感機器學習入門實戰教程|Sklearn案例⑥:網格搜索與超參數優化

在前幾篇案例中&#xff0c;有同學在后臺留言&#xff1a;“模型的參數到底怎么調&#xff1f;比如 SVM 的 C 和 γ&#xff0c;隨機森林的樹數和深度&#xff0c;要怎么選才能得到最優結果呢&#xff1f;”這是一個非常經典的問題&#xff1a;參數選不好&#xff0c;模型效果差…

論文精讀(三)|智能合約漏洞檢測技術綜述

筆者鏈接&#xff1a;撲克中的黑桃A 專欄鏈接&#xff1a;論文精讀 本文關鍵詞&#xff1a;智能合約;合約安全;合約可靠性;合約質量保障;漏洞檢測;合約程序分析 引 諸位技術同仁&#xff1a; 本系列將系統精讀的方式&#xff0c;深入剖析計算機科學頂級期刊/會議論文&#…

YOLO --- YOLO11模型以及項目詳解

YOLO — YOLO11模型以及項目詳解 文章目錄YOLO --- YOLO11模型以及項目詳解一&#xff0c;開源地址二&#xff0c;重要模塊2.1 C3K22.2 C2PSA2.3 檢測頭三&#xff0c;網絡結構3.1 整體結構劃分3.2 Backbone 結構分析&#xff08;從下往上看&#xff09;3.3 結構分析&#xff0…

Debezium監聽MySQL binlog并實現有狀態重啟

Debezium實現MySQL數據監聽了解Debezium? 本期主要內容實現步驟1. 新建Maven工程2.導入依賴3.核心代碼編寫4.offset的存儲5.OffsetBackingStore實現jdbc模式6.運行結果總結了解Debezium 官網&#xff1a;https://debezium.io/ Debezium是一組分布式服務&#xff0c;用于捕獲數…

InfluxDB 存儲優化:TSM 文件管理與空間回收(一)

一、InfluxDB 與 TSM 文件初相識**在數字化時代&#xff0c;數據量呈爆發式增長&#xff0c;尤其是時間序列數據&#xff0c;如服務器監控指標、傳感器讀數、金融交易記錄等&#xff0c;它們都帶有時間戳&#xff0c;記錄著事物隨時間的變化。InfluxDB 作為一款高性能的開源時序…

macos使用FFmpeg與SDL解碼并播放H.265視頻

效果: 安裝依賴: brew install ffmpeg brew install sdl2 brew install x265 確認x265已啟用 查看x265版本 工程CMakeLists.txt

C#開源庫ACadSharp讀取dwg圖元的示例

文章目錄介紹數據示例讀取圖元屬性介紹 開源庫ACadSharp的地址&#xff1a;https://github.com/DomCR/ACadSharp 可以在NuGet中搜索到該庫并安裝。 數據示例 數據是一個繪制了以下簡單圖元的dwg數據&#xff1a; 讀取圖元屬性 創建了.net6控制臺項目&#xff0c;通過NuG…

【UniApp打包鴻蒙APP全流程】如何配置并添加UniApp API所需的鴻蒙系統權限

一、前言&#xff1a;為什么選擇 UniApp 打包鴻蒙應用&#xff1f; 隨著鴻蒙生態的快速發展&#xff0c;越來越多開發者希望將現有跨平臺項目快速接入鴻蒙系統。而 UniApp 作為國內領先的跨平臺開發框架&#xff0c;憑借其“一次開發&#xff0c;多端發布”的特性&#xff0c;…