最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

[鴻蒙PC三方庫適配]Java本地訪問庫JNA適配到鴻蒙PC平臺(tái)

 更新時(shí)間:2026年06月27日 13:46:39   作者:程序山海  
本文詳細(xì)介紹了如何將 Java Native Access (JNA) 本地庫適配到 鴻蒙PC 平臺(tái),文章將系統(tǒng)性地講解如何利用 lycium_plusplus 構(gòu)建框架,處理 Java JNI 本地庫在鴻蒙環(huán)境下的交叉編譯流程

本文是軟件鴻蒙化遷移實(shí)踐系列文章之一,專注于 Java JNI 本地庫的鴻蒙適配,為開發(fā)者提供完整的遷移指南。

歡迎加入開源鴻蒙PC社區(qū):https://harmonypc.csdn.net/

歡迎在PC社區(qū)平臺(tái)申請(qǐng)新建項(xiàng)目:https://atomgit.com/OpenHarmonyPCDeveloper

AtomGit 倉庫地址:https://atomgit.com/OpenHarmonyPCDeveloper/ohos_jna_pc

項(xiàng)目信息說明

項(xiàng)目說明
名稱JNA (Java Native Access)
開源協(xié)議LGPL-2.1 / Apache-2.0
源碼版本5.14.0
目標(biāo)平臺(tái)鴻蒙 PC
依賴項(xiàng)JDK 11, Ant, libffi
操作系統(tǒng)平臺(tái)WSL Ubuntu 24.04

一、背景介紹

1.0 功能與效果

JNA 本地庫在本實(shí)踐中的預(yù)期能力如下:

功能:為 Java 程序提供訪問本地共享庫的能力,無需編寫 JNI 代碼。JNA 通過 libjnidispatch.so 實(shí)現(xiàn) Java 與本地 C/C++ 庫之間的自動(dòng)調(diào)度。

效果:在鴻蒙 PC 上提供與標(biāo)準(zhǔn) Linux 環(huán)境相近的 JNA 使用體驗(yàn),便于在鴻蒙 Java 應(yīng)用開發(fā)中實(shí)現(xiàn)本地庫調(diào)用、系統(tǒng) API 訪問、硬件接口交互等場(chǎng)景的本地調(diào)度功能。

1.1 什么是 鴻蒙PC HNP 生態(tài)

HNP(Harmony Native Package)是 鴻蒙PC 的原生包格式,lycium 是增強(qiáng)型構(gòu)建框架,支持自動(dòng)下載源碼、交叉編譯(arm64-v8a、armeabi-v7a)、一鍵生成 HNP 包以及開源聲明聚合。C/C++ 原生庫的適配是鴻蒙系統(tǒng)生態(tài)建設(shè)中的重要一環(huán)。

為什么 JNA 需要 C/C++ 構(gòu)建?

雖然 JNA 是 Java 庫,但它包含一個(gè)核心的本地調(diào)度庫 libjnidispatch.so(C 代碼實(shí)現(xiàn))。這個(gè) so 文件負(fù)責(zé):

  • Java 與本地 C/C++ 庫之間的自動(dòng)調(diào)度
  • 數(shù)據(jù)類型轉(zhuǎn)換和內(nèi)存管理
  • 函數(shù)調(diào)用的底層實(shí)現(xiàn)

因此,我們需要使用 lycium_plusplus 框架交叉編譯這個(gè) C 語言本地庫。

1.2 為什么適配 JNI 本地庫會(huì)有難度

常見挑戰(zhàn)包括:

  • 構(gòu)建系統(tǒng)差異:JNA 使用 Makefile 構(gòu)建系統(tǒng),需要配置交叉編譯工具鏈(aarch64-linux-ohos-clang/clang++)。
  • X11 圖形依賴:JDK 的 jawt_md.h 硬編碼需要 X11/Xlib.h,但 鴻蒙PC 是無頭系統(tǒng)(headless),不需要圖形界面。
  • libffi 交叉編譯:JNA 依賴 libffi 靜態(tài)庫,老版本 libffi 不認(rèn)識(shí) ohos 目標(biāo),需要使用 android 兼容方案。
  • JNI 頭文件生成:需要 JDK 11 和 Ant 工具,通過 ant javah 自動(dòng)生成 JNI 頭文件。
  • 共享庫****鏈接配置:Makefile 默認(rèn)使用 -shared 標(biāo)志,但環(huán)境變量 LDFLAGS 會(huì)覆蓋 Makefile 的默認(rèn)值,需要特殊處理。
  • Windows 元數(shù)據(jù)干擾:從 Windows 環(huán)境復(fù)制的源碼可能包含 :Zone.Identifier 等區(qū)域標(biāo)識(shí)文件,需要在打包前清理。
  • 構(gòu)建緩存問題:lycium 使用 hpk_build.csv 跟蹤構(gòu)建狀態(tài),需要正確清理緩存才能重新構(gòu)建。

1.3 JNA 簡(jiǎn)介

JNA (Java Native Access) 是一款由 Sun Microsystems(現(xiàn) Oracle)開發(fā)的 Java 庫,提供訪問本地共享庫的能力而無需編寫 JNI 代碼。其主要特點(diǎn)包括:

  • 零 JNI 代碼:Java 開發(fā)者無需編寫 C/C++ 代碼即可調(diào)用本地庫。
  • 自動(dòng)類型映射:自動(dòng)處理 Java 類型與 C 類型之間的轉(zhuǎn)換。
  • 跨平臺(tái)特性:原生支持 Windows、macOS、Linux,本次適配擴(kuò)展到 鴻蒙PC 平臺(tái)。
  • 廣泛使用:被 NetBeans、Eclipse、IntelliJ IDEA 等知名項(xiàng)目使用。
  • 開源地址
    • GitHub:https://github.com/java-native-access/jna
    • AtomGit:https://atomgit.com/weixin_62765017/ohos_jna.git

二、環(huán)境準(zhǔn)備

2.1 系統(tǒng)要求

  • 開發(fā)環(huán)境:Ubuntu 24.04(推薦 WSL 2)
  • 核心工具:Make、GCC/G++、Git、Python3、JDK 11、Ant
  • 構(gòu)建框架:lycium_plusplus
  • 鴻蒙 SDK:鴻蒙PC SDK(提供交叉編譯工具鏈 aarch64-linux-ohos-clang/clang++)
  • 目標(biāo)架構(gòu):arm64-v8a(AArch64)

2.2 擴(kuò)展閱讀與參考教程

下方匯總展示了多位老師在鴻蒙 鴻蒙PC 適配方面的高質(zhì)量教程。若在前提準(zhǔn)備(環(huán)境、工具鏈、框架)部分還有不清楚的地方,可參考這些文章進(jìn)一步學(xué)習(xí)。 以下資源不分先后順序,均具有參考價(jià)值。

資源類型描述鏈接
三方庫交叉編譯環(huán)境(Ubuntu)在 Ubuntu 中搭建鴻蒙PC 三方庫交叉編譯構(gòu)建開發(fā)環(huán)境?? 點(diǎn)擊查看
三方庫交叉編譯環(huán)境(macOS)在 macOS 中搭建鴻蒙PC 三方庫交叉編譯開發(fā)環(huán)境?? 點(diǎn)擊查看
基礎(chǔ)環(huán)境搭建Windows 10 上安裝和使用 WSL 2、安裝 Ubuntu 24 詳細(xì)指南?? 點(diǎn)擊查看
Mac 移植指南鴻蒙PC命令行適配指南(Mac 版)?? 點(diǎn)擊查看
Win 移植指南鴻蒙PC 生態(tài)三方軟件移植:開發(fā)環(huán)境搭建及三方庫移植指南?? 點(diǎn)擊查看
全流程適配指南OpenHarmony Linux 命令行工具適配實(shí)戰(zhàn):基于 Cursor × WSL 的 tree 2.2.1 交叉編譯與 HNP 打包全流程指南?? 點(diǎn)擊查看
官方構(gòu)建文檔新腳手架:社區(qū)維護(hù)的鴻蒙PC 生態(tài)命令行工具構(gòu)建框架 lycium_plusplus(原 build 倉庫為舊方式,請(qǐng)以本倉庫為準(zhǔn))?? 點(diǎn)擊查看

2.3 配置 鴻蒙PC SDK 環(huán)境變量

在開始之前,需要先配置 鴻蒙PC SDK 路徑。這是所有后續(xù)操作的基礎(chǔ)。

# 配置 鴻蒙PC SDK 環(huán)境變量(請(qǐng)根據(jù)實(shí)際路徑修改)
export OHOS_SDK=/home/weishuo/ohos-sdk/linux

# 驗(yàn)證 SDK 是否存在
ls ${OHOS_SDK}/native/llvm/bin/aarch64-linux-ohos-clang
# 應(yīng)該輸出:/home/weishuo/ohos-sdk/linux/native/llvm/bin/aarch64-linux-ohos-clang

注意:如果 SDK 路徑不同,請(qǐng)修改為你的實(shí)際路徑。

2.4 lycium_plusplus 框架

lycium_plusplus 是本次適配工作的核心工具,主要用于統(tǒng)一管理各類第三方庫的構(gòu)建流程,通過規(guī)范編譯、依賴與打包邏輯,實(shí)現(xiàn)三方庫在目標(biāo)平臺(tái)上高效、穩(wěn)定地編譯與集成,是整個(gè)適配環(huán)節(jié)中保障構(gòu)建一致性與可維護(hù)性的關(guān)鍵支撐。

# 克隆 lycium_plusplus 項(xiàng)目
git clone https://gitcode.com/OpenHarmonyPCDeveloper/lycium_plusplus.git
cd lycium_plusplus

2.5 JDK 11 與 Ant 安裝

由于 JNA 需要生成 JNI 頭文件,需要安裝 JDK 11 和 Ant 工具。

# 安裝 JDK 11
sudo apt-get install -y openjdk-11-jdk

# 安裝 Ant
sudo apt-get install -y ant

# 驗(yàn)證安裝
java -version
javac -version
ant -version

# 配置 鴻蒙PC SDK 環(huán)境變量
export OHOS_SDK=/home/weishuo/ohos-sdk/linux

三、實(shí)戰(zhàn):以 JNA 為例的適配步驟

本章節(jié)將為新人開發(fā)者提供完整的、可復(fù)現(xiàn)的適配步驟。我們將從零開始,逐步完成 JNA 本地庫的鴻蒙適配工作。每個(gè)步驟都包含詳細(xì)的說明、命令示例和注意事項(xiàng)。

3.1 創(chuàng)建項(xiàng)目目錄結(jié)構(gòu)

步驟說明

在 lycium_plusplus 框架中,每個(gè)三方庫都需要在 thirdparty/ 目錄下?lián)碛歇?dú)立的目錄。這個(gè)目錄將存放該庫的所有構(gòu)建配置文件(HPKBUILD、hnp.json、README.OpenSource、HPKCHECK 等)。

詳細(xì)操作流程

# 1. 進(jìn)入 lycium_plusplus 項(xiàng)目根目錄
cd /home/weishuo/lycium_plusplus

# 2. 進(jìn)入 thirdparty 目錄
cd thirdparty

# 3. 創(chuàng)建 jna 目錄
mkdir -p jna

# 4. 進(jìn)入新創(chuàng)建的目錄
cd jna

# 5. 驗(yàn)證目錄創(chuàng)建成功
pwd
# 輸出:/home/weishuo/lycium_plusplus/thirdparty/jna

# 6. 查看目錄結(jié)構(gòu)
ls -la

目錄結(jié)構(gòu)說明

lycium_plusplus/
├── thirdparty/
│   ├── jna/              ← 我們剛創(chuàng)建的目錄
│   │   ├── HPKBUILD        ← 將要?jiǎng)?chuàng)建的構(gòu)建腳本
│   │   ├── hnp.json        ← 將要?jiǎng)?chuàng)建的包元數(shù)據(jù)
│   │   ├── README.OpenSource  ← 將要?jiǎng)?chuàng)建的開源聲明
│   │   └── HPKCHECK        ← 將要?jiǎng)?chuàng)建的檢查腳本
│   ├── mediainfo/          ← 其他三方庫示例
│   ├── fuse3/              ← 其他三方庫示例
│   └── ...
├── lycium/
│   ├── build.sh            ← lycium 構(gòu)建入口腳本
│   └── usr/                ← 構(gòu)建產(chǎn)物輸出目錄
└── Projects/
    └── jna/                ← JNA 源碼目錄(需提前準(zhǔn)備)

注意事項(xiàng)

  • 目錄名稱必須與 pkgname 變量保持一致(本例中為 jna)
  • 確保 Projects/jna/ 目錄中已經(jīng)有 JNA 的源碼
  • 如果源碼還未準(zhǔn)備,需要先下載或克隆源碼到 Projects/jna/ 目錄(參考 3.0 節(jié))

3.2 禁用 JAWT 依賴的 sed 方案(推薦)

# 1. 在 #include <wchar.h> 后添加 NO_JAWT 宏定義
sed -i '/#include <wchar.h>/a\
\
/* OpenHarmony: Disable JAWT to avoid X11 dependency */\
#ifndef NO_JAWT\
#define NO_JAWT 1\
#endif' native/dispatch.c

# 2. 將 #include <jni.h> 替換為直接包含 JNI 頭文件(跳過 jni_md.h)
sed -i 's/^#include <jni.h>$/#include "com_sun_jna_Native.h"\n#include "com_sun_jna_Function.h"/' native/dispatch.c

# 3. 修改 JAWT 條件編譯(禁用 JAWT 代碼)
sed -i 's/^#ifndef NO_JAWT$/#ifdef DISABLE_JAWT_COMPLETELY/' native/dispatch.c

為什么推薦 sed 而不是 patch

方式優(yōu)點(diǎn)缺點(diǎn)
patch 文件直觀、易讀依賴行號(hào),版本不同可能失敗
sed 命令靈活、不依賴行號(hào)語法稍復(fù)雜

本文選擇:使用 sed 命令,直接在 HPKBUILD 的 prepare() 函數(shù)中修改源碼,無需額外創(chuàng)建 patch 文件。

注意事項(xiàng)

  • 這三條 sed 命令會(huì)在后面的 HPKBUILD 的 prepare() 函數(shù)中自動(dòng)執(zhí)行
  • 你不需要手動(dòng)運(yùn)行這些命令,只需要理解它們的原理即可

3.3 創(chuàng)建 HPKBUILD 文件(核心構(gòu)建腳本)

什么是 HPKBUILD?

HPKBUILD 是 lycium 框架的核心構(gòu)建腳本,可以理解為一個(gè)"構(gòu)建配方"。它告訴 lycium 框架:

  • 這個(gè)庫叫什么、什么版本、什么許可證(元信息)
  • 如何準(zhǔn)備源碼、如何編譯、如何打包(構(gòu)建流程)
  • 使用什么編譯器和編譯參數(shù)(環(huán)境配置)

創(chuàng)建方法

#!/bin/bash

# -----------------------------------------------------------------------------
# JNA (Java Native Access) HPKBUILD - OpenHarmony 鴻蒙適配
# 適配本地庫 libjnidispatch.so
# -----------------------------------------------------------------------------

pkgname=jna
pkgver=5.14.0
pkgrel=0
pkgdesc="Java Native Access - native dispatch library for OpenHarmony"
url="https://github.com/java-native-access/jna"
archs=("arm64-v8a")
license=("LGPL-2.1")
depends=()
makedepends=()

autounpack=false
downloadpackage=false
buildtools="make"

srcpath="${LYCIUM_ROOT}/../Projects/jna"
builddir="jna-${pkgver}"

# -----------------------------------------------------------------------------
# prepare():準(zhǔn)備源碼
# -----------------------------------------------------------------------------
prepare() {
    if [ -d "$srcpath" ]; then
        echo "Using local source from: $srcpath"
        mkdir -p "$builddir"
        cp -rf "$srcpath"/* "$builddir/"
        
        # 清理 Windows 元數(shù)據(jù)
        find "$builddir" -name "*.bak" -type f -delete 2>/dev/null || true
        find "$builddir" -name "*:Zone.Identifier" -type f -delete 2>/dev/null || true
        
        # 修改 dispatch.c:禁用 JAWT 避免 X11 依賴(OpenHarmony 不需要圖形界面)
        cd "$builddir"
        if [ -f "native/dispatch.c" ]; then
            echo "Patching dispatch.c to disable JAWT..."
            # 在 #include <wchar.h> 后添加 NO_JAWT 定義
            sed -i '/#include <wchar.h>/a\
\
/* OpenHarmony: Disable JAWT to avoid X11 dependency */\
#ifndef NO_JAWT\
#define NO_JAWT 1\
#endif' native/dispatch.c
            # 將 #include <jni.h> 替換為直接包含 JNI 頭文件(跳過 jni_md.h 的 X11 依賴)
            sed -i 's/^#include <jni.h>$/#include "com_sun_jna_Native.h"\n#include "com_sun_jna_Function.h"/' native/dispatch.c
            # 將 #ifndef NO_JAWT 改為 #ifdef DISABLE_JAWT_COMPLETELY(永不成立)
            sed -i 's/^#ifndef NO_JAWT$/#ifdef DISABLE_JAWT_COMPLETELY/' native/dispatch.c
            echo "dispatch.c patched successfully"
        fi
        cd "$OLDPWD"
        
        # 生成 JNI 頭文件(關(guān)鍵步驟?。?
        echo "Generating JNI headers..."
        cd "$builddir"
        ant javah > "${LYCIUM_ROOT}/log/jna-javah.log" 2>&1
        ret=$?
        if [ $ret -ne 0 ]; then
            echo "ERROR: Failed to generate JNI headers"
            cat "${LYCIUM_ROOT}/log/jna-javah.log" >&2
            cd "$OLDPWD"
            return $ret
        fi
        
        # 復(fù)制 JNI 頭文件到 build/native 目錄(Makefile 期望的位置)
        mkdir -p build/native
        if [ -d "build/headers" ]; then
            cp -f build/headers/*.h build/native/
            echo "JNI headers copied to build/native/"
            ls -la build/native/*.h
        fi
        echo "JNI headers generated successfully"
        cd "$OLDPWD"
        
        echo "Prepare completed in: $builddir"
    else
        echo "ERROR: Source not found at $srcpath"
        exit 1
    fi
}

# -----------------------------------------------------------------------------
# build():編譯構(gòu)建
# -----------------------------------------------------------------------------
build() {
    cd "$builddir/native"
    
    # 設(shè)置 buildlog
    buildlog="${LYCIUM_ROOT}/log/${pkgname}-build.log"
    mkdir -p "${LYCIUM_ROOT}/log"
    
    # 設(shè)置 OpenHarmony 交叉編譯工具鏈
    export CC="${OHOS_SDK}/native/llvm/bin/aarch64-linux-ohos-clang"
    export CXX="${OHOS_SDK}/native/llvm/bin/aarch64-linux-ohos-clang++"
    export AR="${OHOS_SDK}/native/llvm/bin/llvm-ar"
    export RANLIB="${OHOS_SDK}/native/llvm/bin/llvm-ranlib"
    export STRIP="${OHOS_SDK}/native/llvm/bin/llvm-strip"
    export NM="${OHOS_SDK}/native/llvm/bin/llvm-nm"
    
    # 設(shè)置編譯標(biāo)志(注意:不要設(shè)置 LDFLAGS 環(huán)境變量,讓 Makefile 使用自己的默認(rèn)值)
    # 添加 JNI 頭文件路徑(build/headers 和 build/native 都要包含)
    # 添加 JDK include 路徑(jni.h 和 jni_md.h)
    export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64
    export CFLAGS="--target=aarch64-linux-ohos --sysroot=${OHOS_SDK}/native/sysroot -O2 -fPIC -fno-strict-aliasing -I../build/native/libffi/include -I../build/native -I../build/headers -I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux"
    
    # 設(shè)置 libffi 交叉編譯參數(shù)(關(guān)鍵?。?
    # 使用 aarch64-linux-android 因?yàn)槔习姹?libffi 不認(rèn)識(shí) ohos
    export FFI_CONFIG="--enable-static --disable-shared --with-pic=yes --host=aarch64-linux-android"
    export FFI_ENV="CC=\"$CC\" CFLAGS=\"$CFLAGS\" CPPFLAGS=\"$CFLAGS\""
    export FFI_BUILD="../build/native/libffi"
    
    # 創(chuàng)建構(gòu)建目錄
    mkdir -p ../build/native
    
    # 使用 Makefile 構(gòu)建(參考 Android 交叉編譯方式)
    make clean > "$buildlog" 2>&1 || true
    
    # make clean 會(huì)刪除 build/native 目錄,需要重新復(fù)制 JNI 頭文件
    mkdir -p ../build/native
    if [ -d "../build/headers" ]; then
        cp -f ../build/headers/*.h ../build/native/
        echo "JNI headers re-copied to build/native/ after clean"
    fi
    
    make \
        OS=linux \
        ARCH=aarch64 \
        CC="$CC" \
        CXX="$CXX" \
        AR="$AR" \
        RANLIB="$RANLIB" \
        STRIP="$STRIP" \
        CFLAGS="$CFLAGS" \
        CPPFLAGS="-DNO_JAWT -DNO_WEAK_GLOBALS -DFFI_STATIC_BUILD" \
        CDEFINES="-DFFI_STATIC_BUILD -DNO_JAWT -DNO_WEAK_GLOBALS -DFFI_MMAP_EXEC_WRIT=1 -DFFI_MMAP_EXEC_SELINUX=0" \
        HOST_CONFIG="--host=aarch64-linux-android" \
        FFI_CONFIG="--enable-static --disable-shared --with-pic=yes --host=aarch64-linux-android" \
        JAVA_HOME="" \
        JAVAH="../build/native" \
        BUILD="../build/native" \
        INSTALLDIR="../build/linux-aarch64" \
        >> "$buildlog" 2>&1
    
    ret=$?
    if [ $ret -ne 0 ]; then
        echo "Make build failed!"
        cat "$buildlog" >&2
        cd "$OLDPWD"
        return $ret
    fi
    
    cd "$OLDPWD"
    return $ret
}

# -----------------------------------------------------------------------------
# check():驗(yàn)證構(gòu)建產(chǎn)物
# -----------------------------------------------------------------------------
check() {
    echo "The test must be on an OpenHarmony device!"
}

# -----------------------------------------------------------------------------
# package():打包產(chǎn)物
# -----------------------------------------------------------------------------
package() {
    : ${destdir:=${LYCIUM_ROOT}/usr/${pkgname}/${ARCH}}
    
    # 只復(fù)制 libjnidispatch.so
    mkdir -p "${destdir}/usr/lib"
    
    _lib_path="${LYCIUM_ROOT}/../thirdparty/${pkgname}/${builddir}/build/native"
    
    if [ -f "$_lib_path/libjnidispatch.so" ]; then
        cp -f "$_lib_path/libjnidispatch.so" "${destdir}/usr/lib/"
        chmod 755 "${destdir}/usr/lib/libjnidispatch.so"
        echo "  ? Installed libjnidispatch.so"
    else
        echo "  ? libjnidispatch.so not found at $_lib_path"
        return 1
    fi
    
    # 清理不需要的文件
    find "${destdir}" -name "*.bak" -type f -delete 2>/dev/null || true
    find "${destdir}" -name "*:Zone.Identifier" -type f -delete 2>/dev/null || true
    
    return 0
}

# -----------------------------------------------------------------------------
# archive():生成歸檔包
# -----------------------------------------------------------------------------
archive() {
    export HNP_TOOL="${HNP_TOOL:-${OHOS_SDK}/toolchains/hnpcli}"
    
    mkdir -p ${LYCIUM_ROOT}/output/$ARCH
    
    # 打包 tar.gz
    pushd ${LYCIUM_ROOT}/usr/${pkgname}/${ARCH} > /dev/null 2>&1
    tar -zcf ${LYCIUM_ROOT}/output/$ARCH/${pkgname}_${pkgver}.tar.gz .
    echo "Archive completed: ${LYCIUM_ROOT}/output/$ARCH/${pkgname}_${pkgver}.tar.gz"
    popd > /dev/null 2>&1
    
    # 打包 HNP
    if [ -f "${HNP_TOOL}" ]; then
        cp ${LYCIUM_ROOT}/../thirdparty/${pkgname}/hnp.json ${LYCIUM_ROOT}/usr/${pkgname}/${ARCH}/
        ${HNP_TOOL} pack \
            -i ${LYCIUM_ROOT}/usr/${pkgname}/${ARCH} \
            -o ${LYCIUM_ROOT}/output/$ARCH/
        echo "Archive completed: ${LYCIUM_ROOT}/output/$ARCH/${pkgname}.hnp"
    else
        echo "Warning: hnpcli not found at ${HNP_TOOL}, skipping HNP generation"
    fi
}

# -----------------------------------------------------------------------------
# cleanbuild():清理構(gòu)建產(chǎn)物
# -----------------------------------------------------------------------------
cleanbuild() {
    echo "Cleaning build artifacts for ${pkgname}..."
    
    # 清理構(gòu)建目錄
    rm -rf "${LYCIUM_ROOT}/../thirdparty/${pkgname}/${builddir}"
    
    # 清理 output
    rm -rf "${LYCIUM_ROOT}/output/${pkgname}"*
    
    # 清理 usr 產(chǎn)物
    rm -rf "${LYCIUM_ROOT}/usr/${pkgname}"
    
    echo "Clean completed"
}

HPKBUILD 核心結(jié)構(gòu)說明

  • 第 1 部分:元信息:庫名稱定義為 jna,運(yùn)行時(shí)依賴為空,JNA 無需額外 HNP 依賴包
  • 第 2 部分:prepare () 準(zhǔn)備函數(shù):完成源碼復(fù)制至編譯目錄,關(guān)閉 JAWT 組件以規(guī)避 X11 依賴,通過 ant javah 指令編譯生成 JNI 頭文件
  • 第 3 部分:build () 構(gòu)建函數(shù):配置 aarch64-linux-ohos-clang 交叉編譯工具鏈,采用安卓主機(jī)三元組編譯 libffi 靜態(tài)庫,最終完成 libjnidispatch.so 動(dòng)態(tài)庫編譯
  • 第 4 部分:package () 打包函數(shù):將編譯產(chǎn)出的 libjnidispatch.so 庫文件,拷貝至系統(tǒng) usr/lib 目錄完成部署
  • 第 5 部分:archive () 歸檔函數(shù):輸出 tar.gz 格式壓縮包,檢測(cè) hnpcli 工具是否存在,按需同步生成 hnp 安裝包

構(gòu)建流程

prepare()   →  準(zhǔn)備源碼、修改代碼、生成 JNI 頭文件
    ↓
build()     →  交叉編譯 libffi、編譯 libjnidispatch.so
    ↓
package()   →  安裝 .so 文件到 usr/lib/
    ↓
archive()   →  生成 tar.gz 和 hnp 包

3.4 創(chuàng)建 jna-disable-jawt.patch 補(bǔ)丁文件

什么是 patch 文件?

patch 文件是用于修改源碼的文本文件,記錄了需要修改的文件位置和修改內(nèi)容。在 JNA 適配中,我們需要通過 patch 修改 dispatch.c 來禁用 JAWT 依賴。

為什么需要這個(gè)補(bǔ)丁

  1. X11 依賴問題:JDK 的 jawt_md.h 硬編碼包含 #include <X11/Xlib.h>
  2. 鴻蒙PC 無 X11:鴻蒙PC 是無頭系統(tǒng)(headless),沒有圖形界面,不提供 X11 庫
  3. JAWT 非必需:JNA 的核心功能不需要 JAWT,只有需要 Java GUI 組件才需要
  4. 編譯阻斷:不修改會(huì)導(dǎo)致編譯錯(cuò)誤 fatal error: ‘X11/Xlib.h’ file not found

創(chuàng)建方法

--- a/native/dispatch.c
+++ b/native/dispatch.c
@@ -111,9 +111,15 @@
 #include <stdlib.h>
 #include <wchar.h>
-#include <jni.h>
+
+/* OpenHarmony: Disable JAWT to avoid X11 dependency */
+#ifndef NO_JAWT
+#define NO_JAWT 1
+#endif
+
+#include "com_sun_jna_Native.h"
+#include "com_sun_jna_Function.h"
 
-#ifndef NO_JAWT
+#ifdef DISABLE_JAWT_COMPLETELY
 #include <jawt.h>
 #include <jawt_md.h>
 #endif

補(bǔ)丁修改說明

  1. 第一處修改:在 #include <jni.h> 之前定義 NO_JAWT 宏
    1. 這個(gè)宏會(huì)告知 JNA 代碼禁用 JAWT 相關(guān)功能
  2. 第二處修改:將 #include <jni.h> 替換為直接包含 JNI 頭文件
    1. 跳過 jni.h 間接包含 jawt_md.h 的鏈條
    2. 直接包含 com_sun_jna_Native.h 和 com_sun_jna_Function.h
  3. 第三處修改:將 #ifndef NO_JAWT 改為 #ifdef DISABLE_JAWT_COMPLETELY
    1. DISABLE_JAWT_COMPLETELY 宏永遠(yuǎn)不定義,所以 JAWT 代碼永遠(yuǎn)不會(huì)編譯

使用方式

# 在 HPKBUILD 的 prepare() 中應(yīng)用 patch
cd "$builddir"
patch -p1 < "../jna-disable-jawt.patch"

注意事項(xiàng)

  • patch 文件可能因 JNA 版本不同而需要調(diào)整行號(hào)
  • 本文 HPKBUILD 示例使用 sed 方式(更靈活),無需 patch 文件
  • 如果你更喜歡使用 patch 文件,可以參考本節(jié)的創(chuàng)建方法

3.5 創(chuàng)建 hnp.json(包元數(shù)據(jù))

什么是 hnp.json?

hnp.json 是鴻蒙 HNP 包的元數(shù)據(jù)文件,類似于 Node.js 的 package.json。它告訴系統(tǒng):

  • 這個(gè)包叫什么、什么版本、什么許可證
  • 包含哪些文件、安裝到哪些目錄
  • 依賴哪些其他包

創(chuàng)建方法

{
    "type": "hnp-config",
    "name": "jna-native",
    "version": "5.14.0",
    "description": "Java Native Access native library for OpenHarmony",
    "license": "LGPL-2.1",
    "arch": "arm64-v8a",
    "install": {
        "lib": ["usr/lib/libjnidispatch.so"]
    }
}

字段說明

  • type:固定為 “hnp-config”
  • name:包名稱(可以與 pkgname 不同)
  • version:版本號(hào),與 HPKBUILD 中的 pkgver 一致
  • arch:目標(biāo)架構(gòu),arm64-v8a 表示 64 位 ARM
  • install.lib:要安裝的庫文件列表

3.6 創(chuàng)建 README.OpenSource(開源聲明)

什么是 README.OpenSource?

README.OpenSource 是開源合規(guī)聲明文件,記錄:

  • 使用了哪些開源組件
  • 每個(gè)組件的許可證類型
  • 上游源碼地址和版本

這是鴻蒙生態(tài)的必備文件,用于滿足開源許可證的法律要求。

創(chuàng)建方法

[
    {
        "Name": "JNA (Java Native Access)",
        "License": "LGPL-2.1",
        "License File": "https://github.com/java-native-access/jna/blob/master/LICENSE",
        "Version Number": "5.14.0",
        "Owner": "your-email@example.com",
        "Upstream URL": "https://github.com/java-native-access/jna",
        "Description": "JNA provides Java programs easy access to native shared libraries without writing JNI code. This package contains the native dispatch library (libjnidispatch.so) for OpenHarmony."
    }
]

注意:雖然文件名是 .OpenSource,但內(nèi)容必須是合法的 JSON 數(shù)組格式。

3.7 創(chuàng)建 HPKCHECK 檢查腳本

什么是 HPKCHECK?

HPKCHECK 是自動(dòng)驗(yàn)證腳本,在構(gòu)建后檢查:

  • 產(chǎn)物是否存在(.so 文件)
  • 文件格式是否正確(ELF)
  • 架構(gòu)是否匹配(ARM64)
  • 是否使用 musl libc
#!/bin/bash

HPK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "${HPK_DIR}" || exit 1
source ./HPKBUILD > /dev/null 2>&1

logfile="${HPK_DIR}/${pkgname}_${ARCH}_test.log"

checkprepare() {
    return 0
}

openharmonycheck() {
    res=0
    inst_lib="${LYCIUM_ROOT}/usr/${pkgname}/${ARCH}/lib"
    
    if [ -d "${inst_lib}" ]; then
        echo "start test times: $(date)" >> "${logfile}" 2>&1
        
        # 檢查 libjnidispatch.so
        if [ -f "${inst_lib}/libjnidispatch.so" ]; then
            echo "? libjnidispatch.so exists" >> "${logfile}" 2>&1
            
            # 檢查 ELF 格式
            file "${inst_lib}/libjnidispatch.so" >> "${logfile}" 2>&1
            ret=$?
            res=$(( res | $ret ))
            
            # 檢查架構(gòu)
            if grep -q "ARM aarch64" "${logfile}"; then
                echo "? Architecture is ARM64" >> "${logfile}" 2>&1
            else
                echo "? Architecture mismatch" >> "${logfile}" 2>&1
                res=1
            fi
            
            # 檢查 musl
            if grep -q "musl" "${logfile}"; then
                echo "? Using musl libc" >> "${logfile}" 2>&1
            else
                echo "?? Not using musl libc" >> "${logfile}" 2>&1
            fi
        else
            echo "? libjnidispatch.so not found" >> "${logfile}" 2>&1
            res=1
        fi
        
        echo "end test times: $(date)" >> "${logfile}" 2>&1
    else
        echo "? ${inst_lib} directory not found" >> "${logfile}" 2>&1
        res=1
    fi
    
    return $res
}

步驟說明

HPKCHECK 是 lycium 框架的構(gòu)建檢查腳本,用于在編譯前驗(yàn)證環(huán)境是否滿足構(gòu)建要求,以及在編譯后驗(yàn)證產(chǎn)物是否正確。它相當(dāng)于一個(gè)"自動(dòng)化測(cè)試腳本",確保構(gòu)建質(zhì)量。

HPKCHECK 的作用

  1. 環(huán)境驗(yàn)證:檢查編譯環(huán)境是否滿足要求
  2. 產(chǎn)物驗(yàn)證:驗(yàn)證生成的庫文件是否存在且格式正確
  3. 架構(gòu)檢查:驗(yàn)證 ELF 文件格式和目標(biāo)架構(gòu)
  4. 日志記錄:記錄測(cè)試結(jié)果到日志文件

四、編譯流程與完整示例

4.1 環(huán)境準(zhǔn)備

配置用于前置環(huán)境檢查與變量設(shè)置,通過指定 鴻蒙PC SDK 路徑、驗(yàn)證 JDK 和 Ant 環(huán)境,為 lycium_plusplus 構(gòu)建 JNA 三方庫提供基礎(chǔ)運(yùn)行環(huán)境

# 1. 確保 鴻蒙PC SDK 已安裝
export OHOS_SDK=/home/weishuo/ohos-sdk/linux

# 2. 確保 JDK 11 和 Ant 已安裝
java -version
javac -version
ant -version

# 3. 驗(yàn)證交叉編譯工具鏈
ls ${OHOS_SDK}/native/llvm/bin/aarch64-linux-ohos-clang
# 應(yīng)該輸出:/home/weishuo/ohos-sdk/linux/native/llvm/bin/aarch64-linux-ohos-clang

4.2 創(chuàng)建項(xiàng)目結(jié)構(gòu)并執(zhí)行編譯

通過目錄操作、腳本創(chuàng)建、清理緩存、執(zhí)行構(gòu)建指令,完成 lycium_plusplus 中 JNA 三方庫的從零構(gòu)建全流程

# 1. 進(jìn)入 thirdparty 目錄并創(chuàng)建 jna 目錄
cd /home/weishuo/lycium_plusplus/thirdparty
mkdir -p jna
cd jna

# 2. 創(chuàng)建 4 個(gè)核心文件(內(nèi)容見第三章)
# HPKBUILD、hnp.json、README.OpenSource、HPKCHECK
# 可以使用 cat heredoc 方式創(chuàng)建(避免 CRLF 問題)

# 3. 進(jìn)入 lycium 目錄
cd /home/weishuo/lycium_plusplus/lycium

# 4. 清理歷史構(gòu)建記錄(可選,但推薦)
grep -v 'jna' usr/hpk_build.csv > usr/hpk_build.csv.tmp 2>/dev/null || true
mv usr/hpk_build.csv.tmp usr/hpk_build.csv 2>/dev/null || true
rm -rf ../thirdparty/jna/jna-5.14.0
rm -rf output/*/jna*

# 5. 開始構(gòu)建
./build.sh jna

4.3 構(gòu)建成功輸出示例

構(gòu)建過程中,lycium 框架會(huì)依次執(zhí)行 prepare()、build()、package()、archive() 四個(gè)階段:

關(guān)鍵步驟說明

  1. prepare() 階段
    1. 復(fù)制源碼到 jna-5.14.0/ 目錄
    2. 使用 sed 修改 dispatch.c 禁用 JAWT
    3. 運(yùn)行 ant javah 生成 6 個(gè) JNI 頭文件
    4. 復(fù)制頭文件到 build/native/ 目錄
  2. build() 階段
    1. 配置并交叉編譯 libffi 靜態(tài)庫(使用 aarch64-linux-android)
    2. 編譯 dispatch.c 和 closures.c
    3. 鏈接生成 libjnidispatch.so
  3. package() 階段
    1. 將 libjnidispatch.so 復(fù)制到 usr/lib/ 目錄
  4. archive() 階段
    1. 生成 jna_5.14.0.tar.gz 標(biāo)準(zhǔn)壓縮包
    2. 生成 jna-native.hnp 鴻蒙安裝包

4.4 驗(yàn)證產(chǎn)物

構(gòu)建成功后,編譯產(chǎn)物會(huì)統(tǒng)一輸出至 output 目錄,包含標(biāo)準(zhǔn) tar 壓縮包 與鴻蒙專用 hnp 格式包

查看產(chǎn)物文件

# 1. 查看 output 目錄
cd /home/weishuo/lycium_plusplus/lycium/output/arm64-v8a
ls -lh

驗(yàn)證 tar.gz 內(nèi)容

# 查看壓縮包內(nèi)容
tar tzf jna_5.14.0.tar.gz

五、鴻蒙 PC 真機(jī)驗(yàn)證:JNA

鴻蒙PC 環(huán)境下完成 JNA 動(dòng)態(tài)庫編譯后,需要將 libjnidispatch.so 部署到設(shè)備并做全面校驗(yàn),確保其格式、架構(gòu)、依賴和導(dǎo)出符號(hào)均滿足運(yùn)行要求。下面分步介紹驗(yàn)證流程及所用命令,并提供一個(gè)自動(dòng)化驗(yàn)證腳本

5.1 部署庫文件:解壓與自簽名

首先將編譯產(chǎn)物放入設(shè)備,解壓歸檔文件,為 .so 文件添加鴻蒙系統(tǒng)所需的簽名和執(zhí)行權(quán)限。

# 查看當(dāng)前目錄文件(確認(rèn)壓縮包存在)
ls

# 靜默解壓 jna 壓縮包(無警告輸出)
tar -zxf jna_5.14.0.tar.gz 2>/dev/null

# 進(jìn)入庫文件目錄
cd usr/lib

# 鴻蒙系統(tǒng)二進(jìn)制文件自簽名
binary-sign-tool sign -inFile libjnidispatch.so -outFile libjnidispatch.so -selfSign "1"

# 添加可執(zhí)行權(quán)限(必須,否則系統(tǒng)無法加載)
chmod +x libjnidispatch.so

# 驗(yàn)證最終文件狀態(tài)
ls -l

說明

  • tar -zxf … 2>/dev/null:靜默解壓,避免輸出干擾
  • binary-sign-tool sign -selfSign “1”:為動(dòng)態(tài)庫添加 鴻蒙PC 自簽名,系統(tǒng)在加載時(shí)會(huì)校驗(yàn)簽名,未簽名文件會(huì)被拒絕
  • chmod +x:確保運(yùn)行時(shí)加載器能夠映射并執(zhí)行該文件

5.2 基礎(chǔ)信息校驗(yàn):文件類型與動(dòng)態(tài)依賴

確認(rèn)庫文件是合法的 ELF 動(dòng)態(tài)庫,并檢查其依賴關(guān)系,排除對(duì)圖形庫(如 X11)的非預(yù)期依賴。

# 查看 libjnidispatch.so 的文件類型
# 輸出顯示它是 aarch64 架構(gòu)的 ELF 動(dòng)態(tài)庫,格式本身沒問題
file libjnidispatch.so

# 讀取 ELF 文件的動(dòng)態(tài)節(jié)信息
# 這里可以看到它只依賴了 libc.so,SONAME 是 ../build/native/libjnidispatch.so
readelf -d libjnidispatch.so

分析

  • file 輸出確認(rèn)該文件為 64?bit aarch64 ELF 共享對(duì)象,格式正確,無損壞。
  • readelf -d 顯示的 NEEDED 列表僅包含 libc.so,說明該庫除了標(biāo)準(zhǔn) C 庫外沒有其他運(yùn)行時(shí)依賴(如 libm、libdl 等),在 鴻蒙PC 環(huán)境下加載風(fēng)險(xiǎn)極低。
  • SONAME 字段為 …/build/native/libjnidispatch.so,雖帶相對(duì)路徑,但不影響 Native.loadLibrary() 找到同名文件,屬于正常。

5.3 導(dǎo)出符號(hào)檢查:確認(rèn) JNI 接口完整

JNA 通過 JNI 調(diào)用本地方法,因此必須確保 libjnidispatch.so 正確導(dǎo)出了所有 Java_ 開頭的橋接函數(shù)。

# nm 命令:列出目標(biāo)文件/庫的符號(hào)表
# -D 參數(shù):只列出動(dòng)態(tài)符號(hào)(即對(duì)外導(dǎo)出、運(yùn)行時(shí)可見的符號(hào))
nm -D libjnidispatch.so | grep "T Java_"
#  | grep "T Java_":過濾出類型為 T(text,即代碼段)且以 Java_ 開頭的符號(hào)
# 這些符號(hào)是 JNA 橋接層的核心本地方法,Java 側(cè)要調(diào)用它們,必須在 .so 里存在且導(dǎo)出

說明

  • nm -D 僅顯示動(dòng)態(tài)符號(hào)表,T 表示全局代碼符號(hào)
  • 過濾出的 Java_com_sun_jna_* 均為 JNA 的核心本地接口,全部以 T 形式存在,說明導(dǎo)出表完整,Java 層調(diào)用不會(huì)因符號(hào)丟失而失敗。

5.4 架構(gòu)匹配確認(rèn)

通過 ELF 文件頭再次驗(yàn)證目標(biāo)架構(gòu),確保庫與設(shè)備 CPU 完全匹配。

# 讀取 ELF 文件頭信息,過濾出 Class 和 Machine 字段
# -h 表示讀取文件頭(Header),grep 用來篩選關(guān)鍵信息
readelf -h libjnidispatch.so | grep "Class\|Machine"

輸出解釋

  • Class: ELF64:說明該庫為 64 位格式,只能運(yùn)行在 64 位系統(tǒng)上,無法兼容 32 位環(huán)境。
  • Machine: AArch64:表示目標(biāo)指令集是 ARM64,與鴻蒙 PC 設(shè)備的 aarch64 架構(gòu)一致,不存在交叉編譯錯(cuò)誤。

5.5 自動(dòng)化驗(yàn)證腳本

為了快速完成上述檢查并生成測(cè)試代碼,可以編寫一個(gè)驗(yàn)證腳本 verify_jna.sh,它會(huì)依次檢驗(yàn)環(huán)境、格式、依賴、導(dǎo)出符號(hào)和 Java 運(yùn)行時(shí),并在條件滿足時(shí)幫助編譯、運(yùn)行一個(gè)簡(jiǎn)單的 JNA 測(cè)試程序。

腳本內(nèi)容

#!/bin/sh
# JNA OpenHarmony 真機(jī)驗(yàn)證腳本
# 使用方法:./verify_jna.sh
# 注意:使用 /bin/sh 確保鴻蒙 PC 兼容性

echo "============================================================"
echo "JNA OpenHarmony 真機(jī)驗(yàn)證"
echo "============================================================"

# 1. 檢查當(dāng)前目錄
echo ""
echo "[1/6] 檢查當(dāng)前環(huán)境..."
CURRENT_DIR=$(pwd)
echo "當(dāng)前目錄: $CURRENT_DIR"

# 檢查 libjnidispatch.so 是否存在
if [ -f "libjnidispatch.so" ]; then
    echo "? 找到 libjnidispatch.so"
else
    echo "? 未找到 libjnidispatch.so"
    echo "   請(qǐng)?jiān)?libjnidispatch.so 所在目錄執(zhí)行此腳本"
    exit 1
fi

# 2. 檢查 ELF 格式
echo ""
echo "[2/6] 檢查庫文件格式..."
FILE_OUTPUT=$(file libjnidispatch.so)
echo "$FILE_OUTPUT"

# 檢查是否為 ELF 格式
echo "$FILE_OUTPUT" | grep -q "ELF"
if [ $? -eq 0 ]; then
    echo "? ELF 格式正確"
else
    echo "? 不是 ELF 格式"
    exit 1
fi

# 檢查是否為 64 位
echo "$FILE_OUTPUT" | grep -q "64-bit"
if [ $? -eq 0 ]; then
    echo "? 64 位庫"
else
    echo "? 不是 64 位庫"
    exit 1
fi

# 檢查是否為 ARM64
echo "$FILE_OUTPUT" | grep -q "arm64\|aarch64\|AArch64"
if [ $? -eq 0 ]; then
    echo "? ARM64 架構(gòu)"
else
    echo "??  架構(gòu)可能不匹配(期望 ARM64)"
fi

# 3. 檢查動(dòng)態(tài)依賴
echo ""
echo "[3/6] 檢查動(dòng)態(tài)庫依賴..."
echo ""
readelf -d libjnidispatch.so | grep "NEEDED"

# 檢查是否依賴 libc
readelf -d libjnidispatch.so | grep -q "libc.so"
if [ $? -eq 0 ]; then
    echo ""
    echo "? 依賴 libc.so(正常)"
fi

# 檢查是否有 X11 依賴(不應(yīng)該有)
readelf -d libjnidispatch.so | grep -q "X11\|libX"
if [ $? -eq 0 ]; then
    echo "? 發(fā)現(xiàn) X11 依賴(JAWT 禁用失敗)"
    exit 1
else
    echo "? 無 X11 依賴(JAWT 已禁用)"
fi

# 4. 檢查 JNI 導(dǎo)出符號(hào)
echo ""
echo "[4/6] 檢查 JNI 導(dǎo)出符號(hào)..."
JNI_COUNT=$(nm -D libjnidispatch.so 2>/dev/null | grep "T Java_" | wc -l)

if [ "$JNI_COUNT" -gt 0 ]; then
    echo "? 發(fā)現(xiàn) $JNI_COUNT 個(gè) JNI 導(dǎo)出符號(hào)"
    echo ""
    echo "前 10 個(gè) JNI 函數(shù):"
    nm -D libjnidispatch.so | grep "T Java_" | head -10
else
    echo "??  未發(fā)現(xiàn) JNI 導(dǎo)出符號(hào)(可能 nm 命令不可用)"
    echo "   嘗試使用 readelf 檢查..."
    readelf -s libjnidispatch.so | grep "Java_" | head -10
fi

# 5. 檢查架構(gòu)詳情
echo ""
echo "[5/6] 檢查架構(gòu)詳情..."
readelf -h libjnidispatch.so 2>/dev/null | grep "Class\|Machine"

# 6. 檢查 Java 環(huán)境
echo ""
echo "[6/6] 檢查 Java 環(huán)境..."
if command -v java >/dev/null 2>&1; then
    JAVA_VERSION=$(java -version 2>&1 | head -1)
    echo "? Java 已安裝: $JAVA_VERSION"
    
    # 檢查是否有 JNA jar 包
    echo ""
    echo "檢查 JNA jar 包..."
    JNA_JAR=$(find . -name "jna-*.jar" -type f | head -1)
    
    if [ -n "$JNA_JAR" ]; then
        echo "? 找到 JNA jar 包: $JNA_JAR"
        
        # 創(chuàng)建測(cè)試程序
        echo ""
        echo "創(chuàng)建測(cè)試程序..."
        cat > JNATest.java << 'JAVA_EOF'
import com.sun.jna.Library;
import com.sun.jna.Native;

public class JNATest {
    public interface CLibrary extends Library {
        CLibrary INSTANCE = Native.load("c", CLibrary.class);
        int printf(String format, Object... args);
    }
    
    public static void main(String[] args) {
        System.out.println("========================================");
        System.out.println("JNA OpenHarmony 驗(yàn)證測(cè)試");
        System.out.println("========================================");
        
        try {
            System.out.println("\n[測(cè)試] 調(diào)用 libc.printf");
            CLibrary.INSTANCE.printf("  Hello from JNA on OpenHarmony!\n");
            System.out.println("\n? JNA 工作正常!");
            System.out.println("========================================");
        } catch (UnsatisfiedLinkError e) {
            System.err.println("\n? 本地庫加載失敗: " + e.getMessage());
            System.err.println("請(qǐng)?jiān)O(shè)置 LD_LIBRARY_PATH:");
            System.err.println("  export LD_LIBRARY_PATH=$(pwd):$LD_LIBRARY_PATH");
            System.exit(1);
        } catch (Exception e) {
            System.err.println("\n? 測(cè)試失敗: " + e.getMessage());
            e.printStackTrace();
            System.exit(1);
        }
    }
}
JAVA_EOF
        
        echo "? 測(cè)試程序創(chuàng)建成功"
        echo ""
        echo "============================================================"
        echo "? 所有檢查通過!運(yùn)行測(cè)試:"
        echo "============================================================"
        echo ""
        echo "  export LD_LIBRARY_PATH=$(pwd):\$LD_LIBRARY_PATH"
        echo "  javac -cp $JNA_JAR JNATest.java"
        echo "  java -cp $JNA_JAR:. JNATest"
        echo ""
    else
        echo "??  未找到 JNA jar 包"
        echo "   請(qǐng)下載: https://repo1.maven.org/maven2/net/java/dev/jna/jna/5.14.0/jna-5.14.0.jar"
        echo ""
        echo "  下載后運(yùn)行:"
        echo "  export LD_LIBRARY_PATH=$(pwd):\$LD_LIBRARY_PATH"
        echo "  javac -cp jna-5.14.0.jar JNATest.java"
        echo "  java -cp jna-5.14.0.jar:. JNATest"
    fi
else
    echo "??  Java 未安裝或不在 PATH 中"
    echo "   需要 Java 運(yùn)行時(shí)才能測(cè)試 JNA"
fi

# 最終總結(jié)
echo ""
echo "============================================================"
echo "驗(yàn)證總結(jié)"
echo "============================================================"
echo ""
echo "? libjnidispatch.so 文件格式正確"
echo "? ARM64 架構(gòu)匹配"
echo "? 無 X11 依賴(JAWT 已禁用)"
echo "? 依賴干凈(僅 libc.so)"
echo ""
if [ "$JNI_COUNT" -gt 0 ]; then
    echo "? JNI 導(dǎo)出符號(hào)正常($JNI_COUNT 個(gè))"
fi
echo ""
echo "結(jié)論:JNA 本地庫已成功適配到 OpenHarmony!"
echo "============================================================"

腳本分析

該腳本分為六個(gè)檢查步驟:

  1. 環(huán)境檢查 —— 確認(rèn)當(dāng)前目錄包含 libjnidispatch.so
  2. ELF** 格式檢驗(yàn)** —— 利用 file 命令驗(yàn)證格式是否為 64 位 ARM64 ELF,防止文件損壞或架構(gòu)錯(cuò)誤
  3. 動(dòng)態(tài)依賴掃描 —— 通過 readelf -d 列出 NEEDED 項(xiàng),確保沒有 X11 等不該出現(xiàn)的依賴,僅依賴 libc.so。
  4. JNI 符號(hào)導(dǎo)出驗(yàn)證 —— 統(tǒng)計(jì)并展示 Java_ 開頭的全局符號(hào),確認(rèn)橋接層接口完整
  5. 架構(gòu)詳情確認(rèn) —— 再次用 readelf -h 展示 ELF 頭中的 Class 和 Machine 字段,增強(qiáng)可讀性
  6. Java 環(huán)境與測(cè)試 —— 檢測(cè) Java 是否可用,查找 JNA JAR 包,生成并編譯 JNATest.java,給出運(yùn)行指令。若庫文件和 Jar 包就緒,可直接編譯運(yùn)行一個(gè)調(diào)用 libc.printf 的簡(jiǎn)單示例來最終驗(yàn)證 JNA 是否能正常加載并工作

通過這套自動(dòng)化和人工結(jié)合的校驗(yàn)流程,可以確定 libjnidispatch.so 已成功適配至 鴻蒙PC 平臺(tái),格式完整、依賴純凈、接口齊全,能夠?yàn)樯蠈?Java 應(yīng)用提供可靠的 JNA 本地調(diào)用支持

六、常見問題與解決方案(FAQ)

6.1 編譯錯(cuò)誤類

Q1:構(gòu)建時(shí)報(bào)錯(cuò) Hunk #1 FAILED at 113 或 malformed patch at line 20?

A:這是 patch 文件上下文行號(hào)不匹配導(dǎo)致的。原因可能是:

  1. JNA 版本不同,dispatch.c 的實(shí)際行號(hào)有差異
  2. 文件有 CRLF 換行符,導(dǎo)致 patch 解析失敗
  3. 之前的修改已經(jīng)改變了文件內(nèi)容

解決方案:使用 sed 命令代替 patch 文件,更加靈活可靠。

# 在 HPKBUILD 的 prepare() 函數(shù)中使用 sed
# 1. 添加 NO_JAWT 定義
sed -i '/#include <wchar.h>/a\
\
/* OpenHarmony: Disable JAWT to avoid X11 dependency */\
#ifndef NO_JAWT\
#define NO_JAWT 1\
#endif' native/dispatch.c

# 2. 替換 jni.h 包含
sed -i 's/^#include <jni.h>$/#include "com_sun_jna_Native.h"\n#include "com_sun_jna_Function.h"/' native/dispatch.c

# 3. 修改 JAWT 條件編譯
sed -i 's/^#ifndef NO_JAWT$/#ifdef DISABLE_JAWT_COMPLETELY/' native/dispatch.c

Q2:編譯時(shí)報(bào)錯(cuò) fatal error: ‘X11/Xlib.h’ file not found?

A:原因是 JDK 的 jawt_md.h 硬編碼需要 X11 頭文件,但 OpenHarmony 是無頭系統(tǒng)(headless),不需要圖形界面。HPKBUILD 中已包含自動(dòng)修復(fù)邏輯,通過 sed 修改 dispatch.c 禁用 JAWT。

# HPKBUILD 的 prepare() 函數(shù)中已包含自動(dòng)修復(fù)代碼
sed -i '/#include <wchar.h>/a\
\
/* OpenHarmony: Disable JAWT to avoid X11 dependency */\
#ifndef NO_JAWT\
#define NO_JAWT 1\
#endif' native/dispatch.c
sed -i 's/^#ifndef NO_JAWT$/#ifdef DISABLE_JAWT_COMPLETELY/' native/dispatch.c

原理說明

  • dispatch.c 第 114 行有 #include <jni.h>
  • 在 Linux 上,jni.h 會(huì)包含 jawt_md.h
  • jawt_md.h 第 29 行有 #include <X11/Xlib.h>
  • 通過定義 NO_JAWT 宏并修改條件編譯,跳過 JAWT 相關(guān)代碼

Q3:編譯時(shí)報(bào)錯(cuò) configure: error: cannot run C compiled programs. If you meant to cross compile, use ‘–host’?

A:原因是 libffi 的 configure 腳本未識(shí)別交叉編譯環(huán)境。需要在 make 命令中添加 HOST_CONFIG 和 FFI_CONFIG 參數(shù),使用 aarch64-linux-android 而不是 aarch64-linux-ohos(老版本 libffi 不認(rèn)識(shí) ohos)。

# HPKBUILD 的 build() 函數(shù)中已配置
export FFI_CONFIG="--enable-static --disable-shared --with-pic=yes --host=aarch64-linux-android"
export HOST_CONFIG="--host=aarch64-linux-android"

# 傳遞給 make 命令
make \
    HOST_CONFIG="--host=aarch64-linux-android" \
    FFI_CONFIG="--enable-static --disable-shared --with-pic=yes --host=aarch64-linux-android"

為什么使用 android

  • 老版本 libffi 的 config.sub 不認(rèn)識(shí) ohos 目標(biāo)
  • aarch64-linux-android 與 鴻蒙PC 兼容(都是 musl libc)
  • 這是經(jīng)過驗(yàn)證的可行方案

Q4:編譯時(shí)報(bào)錯(cuò) libtool: error: cannot build a shared library?

A:原因是 LDFLAGS 環(huán)境變量中包含了 -shared 標(biāo)志,傳遞給了 libffi 的構(gòu)建,但 libffi 配置為靜態(tài)庫(–disable-shared),產(chǎn)生沖突。

解決方案:不設(shè)置全局 LDFLAGS 環(huán)境變量,讓 Makefile 使用自己的默認(rèn)值。

# ? 錯(cuò)誤做法:設(shè)置 LDFLAGS 會(huì)影響 libffi
export LDFLAGS="--target=aarch64-linux-ohos --sysroot=${OHOS_SDK}/native/sysroot -shared"

# ? 正確做法:不設(shè)置 LDFLAGS,讓 Makefile 處理
# Makefile 中已有:LDFLAGS=-o $@ -shared (用于鏈接 libjnidispatch.so)

Q5:編譯時(shí)報(bào)錯(cuò) fatal error: ‘com_sun_jna_Function.h’ file not found?

A:原因是 JNI 頭文件未生成。需要安裝 JDK 11 和 Ant,并在 prepare() 階段運(yùn)行 ant javah 生成頭文件。

# 1. 安裝 JDK 11 和 Ant
sudo apt install -y openjdk-11-jdk
sudo apt install -y ant

# 2. 設(shè)置 JAVA_HOME
export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64

# 3. 生成 JNI 頭文件
cd jna-5.14.0
ant javah

# 4. 復(fù)制到頭文件目錄(Makefile 期望的位置)
mkdir -p build/native
cp -f build/headers/*.h build/native/

注意:make clean 會(huì)刪除 build/native 目錄,需要在 make clean 后重新復(fù)制頭文件。

Q6:編譯時(shí)報(bào)錯(cuò) fatal error: ‘jni.h’ file not found?

A:原因是 CFLAGS 中缺少 JDK include 路徑。需要添加 JDK 的 include 和 include/linux 目錄。

export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64
export CFLAGS="... -I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux"

完整 CFLAGS 示例

export CFLAGS="--target=aarch64-linux-ohos \
  --sysroot=${OHOS_SDK}/native/sysroot \
  -O2 -fPIC -fno-strict-aliasing \
  -I../build/native/libffi/include \
  -I../build/native \
  -I../build/headers \
  -I${JAVA_HOME}/include \
  -I${JAVA_HOME}/include/linux"

Q7:編譯時(shí)報(bào)錯(cuò) ld.lld: error: undefined symbol: main?

A:原因是鏈接共享庫時(shí)缺少 -shared 標(biāo)志,鏈接器誤以為在構(gòu)建可執(zhí)行文件。這是因?yàn)?LDFLAGS 環(huán)境變量覆蓋了 Makefile 的默認(rèn)值。

解決方案:刪除 LDFLAGS 環(huán)境變量,讓 Makefile 使用默認(rèn)配置。

Q8:構(gòu)建時(shí)報(bào)錯(cuò) $‘\r’: command not found?**

A:這是 Windows 換行符(CRLF)導(dǎo)致的問題。在 WSL + Windows 共享目錄環(huán)境下,使用 Windows 編輯器創(chuàng)建的文件會(huì)自動(dòng)帶有 CRLF 換行符。解決方案是使用 Python 腳本修復(fù)。

# 創(chuàng)建 fix_crlf.py 腳本
python3 << 'EOF'
import os

files = [
    "/home/weishuo/lycium_plusplus/thirdparty/jna/HPKBUILD",
    "/home/weishuo/lycium_plusplus/thirdparty/jna/HPKCHECK"
]

for filepath in files:
    with open(filepath, "rb") as f:
        content = f.read()
    content = content.replace(b"\r\n", b"\n").replace(b"\r", b"\n")
    with open(filepath, "wb") as f:
        f.write(content)
    print(f"Fixed: {filepath}")

print("All files fixed!")
EOF

6.2 環(huán)境問題類

Q9:執(zhí)行 ./build.sh jna 后,日志顯示 ALL JOBS DONE!!! 但 output 目錄為空?

A:這通常是因?yàn)?hpk_build.csv 中已有該包的構(gòu)建記錄,導(dǎo)致構(gòu)建被跳過。需要手動(dòng)清理構(gòu)建記錄和緩存。

cd lycium_plusplus/lycium
grep -v 'jna' usr/hpk_build.csv > usr/hpk_build.csv.tmp
mv usr/hpk_build.csv.tmp usr/hpk_build.csv
rm -rf ../thirdparty/jna/jna-5.14.0
rm -rf output/*/jna*
./build.sh jna

Q10:編譯時(shí)報(bào)錯(cuò) fatal error: ‘ffi.h’ file not found?

A:原因是 CFLAGS 中缺少 libffi 頭文件路徑。需要添加 libffi 編譯后的 include 目錄。

export CFLAGS="... -I../build/native/libffi/include"

完整構(gòu)建路徑說明

  • libffi 頭文件:…/build/native/libffi/include/ffi.h
  • JNI 頭文件:…/build/native/com_sun_jna_*.h
  • JDK 頭文件:${JAVA_HOME}/include/jni.h

Q11:如何驗(yàn)證生成的庫文件是否正確?

A:使用 file 和 nm 命令檢查 ELF 文件格式和導(dǎo)出符號(hào)。

# 1. 檢查 ELF 格式
file ~/lycium_plusplus/lycium/usr/jna/arm64-v8a/usr/lib/libjnidispatch.so

# 正確輸出:
# ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), 
# dynamically linked, stripped

# 2. 檢查 JNI 導(dǎo)出符號(hào)
nm -D ~/lycium_plusplus/lycium/usr/jna/arm64-v8a/usr/lib/libjnidispatch.so | grep "T Java_"

# 應(yīng)該看到類似輸出:
# 000000000000c454 T Java_com_sun_jna_Native__1getDirectBufferPointer
# 000000000000b42c T Java_com_sun_jna_Native__1getPointer
# 000000000000ad54 T Java_com_sun_jna_Native_close
# ...

6.3 使用與驗(yàn)證類

Q13:如何在鴻蒙 PC 上使用 JNA?

A:JNA 是純運(yùn)行時(shí) Java 庫,Java 端調(diào)用完全不需要 C 頭文件。只需提供 libjnidispatch.so 文件,配合 jna.jar 使用。

// Java 代碼,不需要任何 C 頭文件
import com.sun.jna.Library;
import com.sun.jna.Native;

public class MyProgram {
    public interface CLibrary extends Library {
        CLibrary INSTANCE = Native.load("c", CLibrary.class);
        int printf(String format, Object... args);
    }
    
    public static void main(String[] args) {
        CLibrary.INSTANCE.printf("Hello from JNA on OpenHarmony!\n");
    }
}
# 運(yùn)行 Java 程序
export LD_LIBRARY_PATH=./usr/lib:$LD_LIBRARY_PATH
javac -cp jna-5.14.0.jar MyProgram.java
java -cp jna-5.14.0.jar:. MyProgram

Q14:JNA 需要安裝頭文件嗎?

A:不需要。JNA 是純運(yùn)行時(shí) Java 庫,其 Java 端調(diào)用完全不需要 C 頭文件(如 dispatch.h、protect.h、ffi.h)。libjnidispatch.so 已靜態(tài)鏈接 libffi,所有底層調(diào)度由 JNA jar 包自動(dòng)完成。用戶只需提供 .so 文件,無需打包或安裝任何 .h 頭文件。

Q15:如何系統(tǒng)排查構(gòu)建錯(cuò)誤?

A:按以下步驟排查:

# 1. 查看完整日志
./build.sh jna 2>&1 | tee build.log

# 2. 確認(rèn)失敗階段:prepare / build / package / archive

# 3. 檢查 prepare()
ls -la thirdparty/jna/jna-5.14.0/
cat log/jna-javah.log

# 4. 檢查 build()
cat log/jna-build.log

# 5. 檢查 package() - 確認(rèn) destdir 路徑和庫文件是否存在

# 6. 檢查 archive() - 確認(rèn)打包路徑是否正確

Q16:為什么選擇本地源碼而非 Git 下載?

A:本地源碼模式更適合快速迭代開發(fā),避免每次構(gòu)建都重新下載。JNA 包含 native 目錄和 Java 代碼,本地源碼可以確保版本一致性。對(duì)于已經(jīng)驗(yàn)證過的穩(wěn)定版本,可以改為從 GitHub 下載。

# 改為從 GitHub 下載(修改 HPKBUILD)
source="https://github.com/java-native-access/jna/archive/refs/tags/${pkgver}.tar.gz"
autounpack=true
downloadpackage=true

Q17:測(cè)試時(shí)遇到 UnsatisfiedLinkError 怎么辦?

A:這通常是庫路徑設(shè)置問題。按以下步驟排查:

# 1. 檢查 LD_LIBRARY_PATH
echo $LD_LIBRARY_PATH
# 應(yīng)該包含 ./usr/lib

# 2. 檢查庫文件是否存在
ls -l ./usr/lib/libjnidispatch.so

# 3. 檢查架構(gòu)是否匹配
file ./usr/lib/libjnidispatch.so
# 必須是 ARM64

# 4. 檢查依賴庫
ldd ./usr/lib/libjnidispatch.so
# 確保所有依賴都找到

Q18:可以在其他架構(gòu)(如 armeabi-v7a)上構(gòu)建嗎?

A:可以。修改 HPKBUILD 中的 archs 變量,并調(diào)整工具鏈:

# 修改 HPKBUILD
archs=("armeabi-v7a")

# 修改工具鏈(在 build() 函數(shù)中)
export CC="${OHOS_SDK}/native/llvm/bin/armv7a-linux-ohos-clang"
export CFLAGS="--target=armv7a-linux-ohos ..."

七、技術(shù)總結(jié)

本次將 JNA 本地庫適配至 鴻蒙PC 平臺(tái),完整驗(yàn)證了 Java JNI 本地庫在鴻蒙環(huán)境下的交叉編譯、構(gòu)建打包與依賴處理流程,形成了可復(fù)用的適配范式。通過規(guī)范 HPKBUILD 配置、禁用 JAWT 避免 X11 依賴、使用 android 兼容方案交叉編譯 libffi、自動(dòng)生成 JNI 頭文件等關(guān)鍵處理,實(shí)現(xiàn)了庫正常編譯運(yùn)行與輕量化部署,相關(guān)思路可廣泛遷移至各類 Java JNI 本地庫的鴻蒙移植工作。

  • 建立了 Makefile 類項(xiàng)目標(biāo)準(zhǔn)化的 HPKBUILD 適配模板,明確交叉編譯與 JNI 頭文件生成要點(diǎn)
  • 解決 X11 圖形依賴、libffi 交叉編譯、JNI 頭文件自動(dòng)生成、共享庫鏈接配置等常見適配問題
  • 通過純運(yùn)行時(shí)庫設(shè)計(jì)實(shí)現(xiàn)零頭文件交付,提升在鴻蒙設(shè)備上的部署效率
  • 適配方案具備通用性,可直接用于其他 Java JNI 本地庫(如 JNR、JNIWrapper)移植
  • 為后續(xù)多架構(gòu)擴(kuò)展、Java 應(yīng)用集成、自動(dòng)化適配工具開發(fā)奠定基礎(chǔ)

八、結(jié)語

本次實(shí)踐成功將 JNA 本地庫移植至 鴻蒙PC 平臺(tái),充分體現(xiàn)了 lycium_plusplus 框架對(duì) Java JNI 項(xiàng)目交叉編譯的支撐能力。通過合理運(yùn)用 HPKBUILD 構(gòu)建流程、禁用 JAWT 避免 X11 依賴、使用 android 兼容方案、規(guī)范 Make 編譯配置、自動(dòng)生成 JNI 頭文件等關(guān)鍵措施,有效解決了適配中的兼容性與構(gòu)建問題,為同類開源 Java JNI 本地庫遷移至鴻蒙生態(tài)提供了可復(fù)用的思路與實(shí)踐參考,也助力開源鴻蒙原生工具生態(tài)的完善與發(fā)展。

提示:本文基于 JNA 5.14.0 版本進(jìn)行適配。不同版本的依賴和構(gòu)建腳本可能有所差異,建議在適配前先熟悉目標(biāo)項(xiàng)目的 Makefile 和構(gòu)建配置文件。JNA 是純運(yùn)行時(shí)庫,Java 端使用不需要任何 C 頭文件,只需提供 libjnidispatch.so 配合 jna.jar 即可。

到此這篇關(guān)于[鴻蒙PC三方庫適配]Java本地訪問庫JNA適配到鴻蒙PC平臺(tái)的文章就介紹到這了,更多相關(guān)JNA本地庫適配到鴻蒙PC平臺(tái)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 基于Failed?to?load?ApplicationContext異常的解決思路

    基于Failed?to?load?ApplicationContext異常的解決思路

    這篇文章主要介紹了基于Failed?to?load?ApplicationContext異常的解決思路,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-01-01
  • SpringBoot詳解如何實(shí)現(xiàn)讀寫分離

    SpringBoot詳解如何實(shí)現(xiàn)讀寫分離

    當(dāng)響應(yīng)的瓶頸在數(shù)據(jù)庫的時(shí)候,就要考慮數(shù)據(jù)庫的讀寫分離,當(dāng)然還可以分庫分表,那是單表數(shù)據(jù)量特別大,當(dāng)單表數(shù)據(jù)量不是特別大,但是請(qǐng)求量比較大的時(shí)候,就要考慮讀寫分離了.具體的話,還是要看自己的業(yè)務(wù)...如果還是很慢,那就要分庫分表了...我們這篇就簡(jiǎn)單講一下讀寫分離
    2022-05-05
  • 淺談Java枚舉的作用與好處

    淺談Java枚舉的作用與好處

    下面小編就為大家?guī)硪黄獪\談Java枚舉的作用與好處。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2016-07-07
  • Java程序開發(fā)環(huán)境配置圖文教程

    Java程序開發(fā)環(huán)境配置圖文教程

    這篇文章主要為大家詳細(xì)介紹了Java程序開發(fā)環(huán)境配置圖文教程,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-07-07
  • Java中的參數(shù)傳遞詳細(xì)介紹

    Java中的參數(shù)傳遞詳細(xì)介紹

    大家好,本篇文章主要講的是Java中的參數(shù)傳遞詳解,感興趣的同學(xué)趕快來看一看吧,對(duì)你有幫助的話記得收藏一下,方便下次瀏覽
    2022-01-01
  • Spring Security基于json登錄實(shí)現(xiàn)過程詳解

    Spring Security基于json登錄實(shí)現(xiàn)過程詳解

    這篇文章主要介紹了Spring Security基于json登錄實(shí)現(xiàn)過程詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-08-08
  • springboot配置多數(shù)據(jù)源(靜態(tài)和動(dòng)態(tài)數(shù)據(jù)源)

    springboot配置多數(shù)據(jù)源(靜態(tài)和動(dòng)態(tài)數(shù)據(jù)源)

    在開發(fā)過程中,很多時(shí)候都會(huì)有垮數(shù)據(jù)庫操作數(shù)據(jù)的情況,需要同時(shí)配置多套數(shù)據(jù)源,本文主要介紹了springboot配置多數(shù)據(jù)源(靜態(tài)和動(dòng)態(tài)數(shù)據(jù)源),感興趣的可以了解一下
    2023-09-09
  • JAVA實(shí)現(xiàn)經(jīng)典游戲坦克大戰(zhàn)的示例代碼

    JAVA實(shí)現(xiàn)經(jīng)典游戲坦克大戰(zhàn)的示例代碼

    小時(shí)候大家都玩過坦克大戰(zhàn)吧,熟悉的旋律和豐富的關(guān)卡陪伴了我們一整個(gè)寒暑假。本文將通過Java+Swing實(shí)現(xiàn)這一經(jīng)典游戲,感興趣的可以學(xué)習(xí)一下
    2022-01-01
  • springboot security之前后端分離配置方式

    springboot security之前后端分離配置方式

    這篇文章主要介紹了springboot security之前后端分離配置方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2025-03-03
  • Java中冒泡排序的原生實(shí)現(xiàn)方法(正序與逆序)

    Java中冒泡排序的原生實(shí)現(xiàn)方法(正序與逆序)

    這篇文章主要給大家介紹了關(guān)于Java中冒泡排序的原生實(shí)現(xiàn)方法(正序與逆序)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-11-11

最新評(píng)論

白水县| 丹棱县| 兴城市| 灵璧县| 孙吴县| 柳河县| 鹿邑县| 潼关县| 孙吴县| 洪雅县| 东城区| 蒙自县| 桓台县| 安宁市| 滨海县| 新泰市| 景德镇市| 十堰市| 隆昌县| 巴彦县| 玉溪市| 正安县| 陇西县| 申扎县| 贵阳市| 博白县| 罗源县| 瑞金市| 南开区| 太保市| 巴里| 潜江市| 万安县| 南投市| 康定县| 铁力市| 镇江市| 大英县| 肥东县| 格尔木市| 泗洪县|