在Android中使用JNA

一.JNA简述

略。

二.so文件的编译

本文以C语言为例。

1.C源文件

1.  #include<stdio.h>
2.  int add(int a,int b);
3.  int add(int a,int b){
4.  int c = a + b ;
5.  return c ;
6.  }  

2.Android.mk文件


1.  LOCAL_PATH := $(call my-dir)  # 执行编译的工作路径在当前路径
2.  include $(CLEAR_VARS)  # 固定写法 :>

4.  LOCAL_MODULE := jnatest # 自定义的编译成的so文件名
5.  CODE_PATH :=  ./  # 源码文件目录

7.  LOCAL_SRC_FILES := $(CODE_PATH)/jnatest.c # 要编译的c或cpp源码文件,多个文件用空格分开
8.  LOCAL_C_INCLUDES := $(LOCAL_PATH)/$(CODE_PATH)  # 头文件所在目录

10.  include $(BUILD_SHARED_LIBRARY)  # 生成so文件

12.  ########################## 或者以下简单方式 #########################

14.  LOCAL_PATH := $(call my-dir)
15.  include $(CLEAR_VARS)

17.  LOCAL_MODULE :=  自定义so文件名
18.  LOCAL_SRC_FILES :=  源码.c

20.  include $(BUILD_SHARED_LIBRARY)

3.Application.mk文件


1.  APP_BUILD_SCRIPT :=  Android.mk # 指明同目录下的Android.mk
2.  APP_STL := gnustl_shared # 运行库,一般安卓使用stlport_static
3.  APP_ABI := armeabi armeabi-v7a x86 # 目标平台,多个用空格

4.使用NDK编译

有些网络文章中讲到的,可以不用App.mk文件。
这里只使用NDK进行编译。即 你电脑上没有安装AndroidStudio和Eclipse也无所谓。
建议将NDK的根路径配置到系统的环境变量,在cmd中输入ndk-build能看到如下信息:


image

这里以csource文件夹为例,将源码和mk文件放入,然后cmd的工作路径也切换到这里:


image

执行命令 ndk-build 进行编译,如果这时你还看到上图所示的2行信息,说明编译失败,Could not find application project directory !

此时可以直接指定编译入口:


1.  ndk-build NDK_PROJECT_PATH=./ NDK_APPLICATION_MK=Application.mk 

image

当前文件夹里生成新的目录,libs,其中就是我们的目标so文件。


image

三.JNA依赖的准备

前往 https://github.com/java-native-access/jna/releases ,下载最新的zip包。

image

将zip文件解码,打开 dist 目录,找到7个android-*.jar文件,解压得到其中的so库,并对应的放到7个平台目录中。当然这7个并非都需要,armeabi、armeabi-v7a是最常用的。


image

除了这些so文件,还需要2个jar。jna-min.jar 和 jna-platform.jar 。


image

四.在AndroidStudio中集成so的形式

按照平常的路子创意几个普通的AS项目。


image

1.libs方式

常用的方式,就是将so、jar、arr等依赖一股脑儿放到项目默认的libs目录里。直接强硬干脆利落。通常集成第三方的地图、推送、一些功能框架时这么做。


image

将第三方依赖加入libs后不用做其它过多配置,就可以在java代码中直接使用了。因为gradle里默认加载此目录中的依赖:


image

现在,在libs下放入我们需要的JNA依赖和之前编译好的so文件:


image

往常就可以直接java开黑,没有任何问题。但JNA的特殊性会导致一个异常:

com.cuiweiyou.jnaprj E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.cuiweiyou.jnaprj, PID: 31846
java.lang.UnsatisfiedLinkError: Native library (com/sun/jna/android-arm/libjnidispatch.so) not found in resource path (.)
at com.sun.jna.Native.loadNativeDispatchLibraryFromClasspath(Native.java:962)
at com.sun.jna.Native.loadNativeDispatchLibrary(Native.java:922)
at com.sun.jna.Native.<clinit style="margin: 0px; padding: 0px; outline: 0px; border: 0px; vertical-align: baseline; word-wrap: break-word; -webkit-appearance: none; box-sizing: border-box; background: transparent;">(Native.java:190)
at com.sun.jna.Native.loadLibrary(Native.java:544)</clinit>

此时,需要在Module的gradle里配置一下:


image

这样,无论是JNA的还是我们自己的so都能比较统一的管理。

2.jniLibs方式

相较于上面的方式1,这个多了一个目录,但gradle里不用过多配置。
jar包仍然放在默认的libs里。
然后在 src/main/ 下新建“jniLibs”目录,将so库文件放进去。


image

Module的gradle按照默认配置,无修改。


image

五.在Java中使用JNA

相较于JNI省事多了,JNA直接api调用即可。


import com.sun.jna.Library;
import com.sun.jna.Native;
public class MainActivity extends AppCompatActivity {
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    
        new AsyncTask<Void, Void, Void>() {
            @Override
            protected Void doInBackground(Void... params) {
    
                int c = JNATest.INSTANCE.add(22, 33);
                Log.e("ard", "JNA返回:" + c);
    
                return null;
            }
        }.execute();
    }
    
    interface JNATest extends Library {
        JNATest INSTANCE = (JNATest) Native.loadLibrary("jnatest", JNATest.class);
        public int add(int a, int b);
    }
}

接口的属性是public公共的、static静态的、final最终的,相当于全局常量。

| 1. 接口JNATest继承自sun的Library,这个Library也是个接口。 |
| 2. JNATest内部通过sun的Native调用了loadLibrary方法,传入的第一个参数就是我们自己编译的so文件名(去掉‘lib’和后缀)。方法内部调用了第2个参数JNATest.class的类加载器,并为这个class创建了一个InvocationHandler,这个Handler去加载我们的自己的so。最终使用Proxy将准备好的种种生成一个代理使用。 |
| 3. INSTANCE这个代理就是实现了“add”方法的一个JNATest的实例。JNATest的add方法对应c代码中的add函数。须注意java的数据类型和c的数据类型的差异。本文为了简便而仅涉及int类型。 |

如此,当java调用INSTANCE的add时,最终通过代理反射去执行C定义的原生代码

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,695评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,569评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 168,130评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,648评论 1 297
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,655评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,268评论 1 309
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,835评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,740评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,286评论 1 318
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,375评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,505评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,185评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,873评论 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,357评论 0 24
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,466评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,921评论 3 376
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,515评论 2 359

推荐阅读更多精彩内容