Idea开发Jni程序(Windows+Android)

本篇介绍开发一个最简单的JNI sample,内容包括:
1. 环境搭建与配置
2. 代码实现
3. 调试与运行
4. 编译安卓可用的jni程序

其实AndroidStudio已经集成了Cmake,这是一个相当方便快捷的工具,但是我不用,原因有两点:
1. 用不了(我的电脑上创建完包含c++的文件的示例程序,移植sync不成功);
2. 更加容易理解原理性的东西;

那么,套用小红母鸡说的,“既然这样,那就我自己来吧!”

搭建GCC编译环境

如果你是在linux下开发,可以跳过这一步,搭建这个环境是为了模拟linux环境编译底层代码(相当于开了个虚拟机,效率肯定会比直接在linux下编译慢很多);

Windows下有两种搭建gcc的方式:Cygwin和minGW,使用都差不多,我这里使用minGW;

关于minGW的环境搭建,github上面有一篇很详细的关于gcc环境搭建的教程,
https://github.com/cpluspluscom/ChessPlusPlus/wiki/MinGW-Build-Tutorial
说的超级详细,这里我制作一个简短的翻译,简化起来就三条:
1. 下载完得到exe后运行解压到指定目录(建议不要有空格和中文);
2. make.exe复制一份到改名为mingw32-make.exe;
3. 将C:\MinGW\bin配置到path环境变量,并在cmd下输入g++ –version检验;

注意:如果你的jdk是64为别下载的mingGW是32位的;

编码实现

想平常一样,创建一个普通的Android应用程序,为了叙述方便,我创建了一个HelloJni的类:

java实现

package com.larson.jni;
/**
 * @author Larsonzhong (larsonzhong@163.com)
 * @since 2017-12-07 11:14
 */
public class HelloJni {
    // 编译后会生成 libhello.so ,然后添加到工程中引用该so文件的时候添加以下这段
    static {
        // hello 为本地库名,在编写make文件的时候指定,了解make参考我的其他博客
        System.loadLibrary("hello");
    }

    // native方法用于生成.h头文件及给java调用
    private native void sayHelloFromC();

    // 调用native方法来访问so文件里面的方法
    public static void main(String[] args) {
        new HelloJni().sayHelloFromC();  // invoke the native method
    }
}

生成头文件

有两种方式,如果你开发jni的程序比较多,建议采用第二种;

方式一:命令行使用javah

javah -jni -classpath (搜寻类目录) -d (输出目录) (类名)

搜寻类目录:你的需要生成头文件的java文文件对应生成的class文件所在目录,路径可以是相对路径;我的是

E:SampleProject/testmodule/build/intermediates/classes/[packagename]/

输出目录:存放目标头文件所在的目录,一般放在需要在项目中放置的位置,如果不确定,你可以使用ide自带的new->folder->jni folder,ide会自动创建好这个目录,然后你把这个目录作为输出目录即可;

方式二:使用IDE

打开File->Setting->Tools->Enternal Tools
这里写图片描述

$Argument$用来取ide中定义好的值;你照着写就行,不过我觉得把这几个输入想完全弄明白并非难事,我这里是直接把输出目录设置成了java文件所在目录,你也可以直接设置成jni目录(自己摸索,我觉得生成好了拖过去也不费事);

在HelloJNI.java文件中点击右键>External Tools>Generate Header File

编写C并编译成So文件

#include<jni.h>
#include <stdio.h>
#include "com_larson_jni_HelloJni.h"
//Java_com_larson_jni_HelloJni_sayHelloFromC对应头文件中声明的方法
JNIEXPORT void JNICALL Java_com_larson_jni_HelloJni_sayHelloFromC(JNIEnv *env, jobject thisObj) {
   printf("Hello World from C !\n");
   return;
}

方法一:

接下来在idea自带的Terminal中输入:

gcc -c [c文件所在路径]
文件所在路径可以通过右键该c文件,复制path获得;

比如我的是gcc -c testmodule/src/main/jni/HelloJni.c
然后,会得到一个错误:

F:\LarsonDocument\github\XCar>gcc -c testmodule/src/main/jni/HelloJni.c
testmodule/src/main/jni/HelloJni.c:1:16: fatal error: jni.h: No such file or directory
 #include<jni.h>

我们吧jdk下的include目录以及win32目录包含进来:

> gcc -c -I"D:\Program Files\jdk1.8.0_77\include" -I"D:\Program  Files\jdk1.8.0_77\include\win32" testmodule/src/main
/jni/HelloJni.c

目测这样有点繁琐,我们来一键生成吧;

方法二:一键生成

前面已经有使用external tool了,所以这里不多废话,直接上图:
这里写图片描述

编译安卓可用的jni程序

安卓针对c代码的编译开发有一套ndk,这个ndk其实也是像cgywin以及mingGW一样,用于交叉编译的,之所以写在后面,是因为很多小伙伴一上来就只要结果,其实和mingGW一比较对比会发现,这都是有很多共通之处,也便于触类旁通,说的通俗点,这套方法不仅仅在eclipse上可用,在idea上可用,在AndroidStudio上可用,你甚至不需要IDE也能进行交叉编译(当然效率会低很多)。

本人电脑的原因导致AndroidStudio2.2版本以后的ide在我公司的电脑上死活同步不成功,我甚至花了一天时间在搞这个东西,后来意识到效率太低,我像是面对一个黑盒子在各种盲测,但是就是不知道怎么就出问题了。想到这里我才开了这一贴,一方面帮助自己理清思路,一方面帮助更多的人从依赖IDE这个死胡同走出来;

我们生产代码,我们不做代码的搬运工!

言归正传,如果看这篇帖子的同学是安卓开发者,会发现上面的方法编译成功后会导致应用打开后崩溃,加载so文件出错,这是因为平台不一样导致的,上面使用mingGW编译出来的是在Windows下通用的so文件,而安卓多半使用的arm架构,而且细心的朋友会发现一般的ndk编译都会有各种平台指定,而mingGW并没有,其实要改成ndk很简单,有了上面的基础,分分钟搞定;

准备ndk

下载ndk可以去AndroidDevTools这个网站下载,其实我作为安卓开发者,很多相关软件都不是翻墙得到的,而是直接从这个网站拿来,当然拿来主义很好,翻翻墙也是很好的;

配置external Tools

我取名为ndkbuild,配置和上面差不多,只是使用的命令是ndk根目录下的ndk-build.bat,界面如下:
这里写图片描述

编写编译脚本Android.mk

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
#生成libNativeLibTest.so和上面例子有区别
LOCAL_MODULE    := NativeLibTest
LOCAL_SRC_FILES := NativeLibTest.cpp

include $(BUILD_SHARED_LIBRARY)

编写编译配置Application.mk

APP_ABI := all#编译所有平台下的so
#如果只是编译指定平台,则把all替换成指定平台比如arm64-v8a armeabi ,不同平台中间空格隔开
APP_STL := stlport_static

提示

在配置参数的时候有几点需要注意的:
1. 在没有接触ide的时候我们基本都是配置ndk环境变量,然后cmd进入到需要编译的目录,输入ndk-build就开始编译了,这里是不需要配置环境变量的,因为通过Program已经指定了;
2. paraments配置出错会出现Please define the NDK_PROJECT_PATH variable to point to it错误,ndk会自动搜索配置目录下的jni文件夹并编译,因此只需要指定module所在的目录即可,即$FileDir$
3. 可选参数有APP_BUILD_SCRIPT=./Android.mk NDK_APPLICATION_MK=./Application.mk就是指定编译脚本和编译配置(Application.mk)配置指定运行平台(abi,比如x86,armeabi等);
4. 如果提示include相关的错误,比如找不到iostream,则属于编译配置相关的,在Application.mk下配置APP_STL := stlport_static即可;

然后,编译,会自动把target成的so文件添加到libs/下各个平台中,运行即可;

本文系原创,欢迎传播,请尊重博主的认真和真诚,装载注明出处,谢谢;

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 酷酷鲨 设计师:CSDN官方博客 返回首页