Android:JNA實踐(附Demo)


一、JNA和JNI的對比

  1.JNI的調用流程
  Android應用開發中要實現Java和C,C++層交互時,想必首先想到的是JNI,但是JNI的使用過程十分繁瑣,需要自己再封裝一層JNI接口進行轉換(使用SUN規定的數據結構去替代C語言的數據結構),包名、函數名等都要匹配,難以閱讀和更新。
  如下圖是通過JNI實現Java調用C層的方法流程:
  

  2.什么是JNA?與JNI有什么差異?

  JNA(Java Native Access):建立在JNI之上的Java開源框架,SUN主導開發,用來調用C、C++代碼,尤其是底層庫文件(windows中叫dll文件,linux下是so【shared object】文件)。JNA簡化了Java調用原生函數的過程,原理是提供了一個動態的C語言編寫的轉發器(實際上也是一個動態鏈接庫,在Linux-i386中文件名是:libjnidispatch.so)可以自動實現Java與C之間的數據類型映射。
  相比JNI,JNA只需要導入一個.jar和一個.so,然后就可以在Java中直接聲明so中公開的函數並調用,十分方便。而JNA有兩個小缺點:(1)性能上會比通過JNI調用動態鏈接庫要稍低,但總體影響不大,因為JNA也避免了JNI的一些平台配置的開銷。(2)因為JNA調用是直接在Java層實現,所以反過來C層無法直接通過JNA調用Java方法,但是Java層可以把方法模擬函數指針傳到c層進行回調。
 
  

 

  二、JNA實踐
 
  1. jna.jar包和libjnidispatch.so庫文件下載
  
     JNA項目的Git Hub地址: https://github.com/java-native-access/jnalibjnidispatch.so 包含在android-armv7.jar中,以及 jna.jar 都在 dist 目錄下。
  
  2.導入jar包和so到Android studio工程
  (1)目錄結構
    

 

  (2)build.gradle 配置

apply plugin: 'com.android.library'

android {
    ......
    sourceSets{
        main{
            jniLibs.srcDirs = ['src/main/jniLibs']
        }
    }
    ......
}

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    ......      
}

 

    3.代碼實現
     (1)新建一個Java接口直接加載第三方動態庫 libnanovoice.so

    其中libnanovoice.so對於應的頭文件(.h)內容如下:

#ifndef __JB_NANOSIC__H__
#define __JB_NANOSIC__H__

#ifdef __cplusplus
extern "C" { 
#endif

typedef void (*AppCallback) (char* data,int datalen);

int nano_open(AppCallback cb);
int nano_close(void);

#ifdef __cplusplus
}
#endif


#endif

    根據動態庫提供的頭文件里函數聲明,編寫JnaNanovoice.java 內容如下:

package com.lxl.nanosic.voice;

import com.sun.jna.Callback;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.NativeLibrary;
import com.sun.jna.Pointer;

public interface JnaNanovoice extends Library {
    public static final String JNA_LIBRARY_NAME = "nanovoice";
    public static final NativeLibrary JNA_NATIVE_LIB = NativeLibrary.getInstance(JnaNanovoice.JNA_LIBRARY_NAME);
    public static final JnaNanovoice INSTANCE = (JnaNanovoice)Native.loadLibrary(JnaNanovoice.JNA_LIBRARY_NAME, JnaNanovoice.class); //直接加載第三方動態庫

    //定義接口AppCallback,繼承自com.sun.jna.Callback
    public interface AppCallback extends Callback {
        void dataReceived(Pointer data, int datalen);
    }

    //動態庫的函數聲明
    int nano_open(AppCallback cb);
    int nano_close();
}

  c層和JNA層變量類型有所差異,需要轉換:

  推薦一個轉換工具 :jnaeratorStudio.jar   使用方法:java -jar jnaeratorStudio.jar

 

  (2)調用c庫函數,其中Java回調接口需要進一步實現, NanoVoiceRecord.java 中JNA調用部分代碼截取如下:

package com.lxl.nanosic.voice;

import android.util.Log;
import com.sun.jna.Pointer;
import java.util.LinkedList;

public class NanoVoiceRecord {

    private final String TAG = "NanoVoiceRecord";/**
     * 回調函數
     */
    private static JnaNanovoice.AppCallback mDataCallback; ......

  /** API 2:開始錄音 **/ public void start() {   ......
    // JNA接口 mDataCallback = new JnaNanovoice.AppCallback() { // 實現接口中的回調 public void dataReceived (Pointer data, int datalen){ if(null != data){ byte[] buffer = data.getByteArray(0, datalen); int res = OnDataReceived(buffer, datalen); Log.w(TAG, "...OnDataReceived = " + res + "m_in_q.size=" + m_in_q.size()); } else { Log.e(TAG, "...Callback data is null !!!"); } } }; // 開始錄音並傳入回調 JnaNanovoice.INSTANCE.nano_open(mDataCallback); } /** API 3:停止錄音 **/ public void stop() { JnaNanovoice.INSTANCE.nano_close(); //調用JNA接口退出錄音 isRecording=false; } ......

  /** * 處理c層回調上來的遙控器語音數據方法 */ public int OnDataReceived(byte[] buffer, int size) { byte[] bytes_pkg; int bufferLength; if(m_in_q==null){ return 0; } bytes_pkg = buffer.clone(); if (m_in_q.size() > 6) { //最多緩存6個包 m_in_q.removeFirst(); } m_in_q.add(bytes_pkg); ...... }

  

  至此便實現了Java層通過JNA對c層函數的直接調用:

 

   JNA實踐Demo(包含轉換jar工具)已傳至Git Hub : https://github.com/dragonforgithub/JnaDemo
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM