<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">Android JNI HEllOWORD 初探</span>
目标:自己生成so库并进行调用。
**************************
环境:
Mac 10.10.3
Android studio 1.2
NDK (这个不用说,一定要有,,ndk-build 一下包中自带的example,看是否配置好)
**************************
经过作者实践之后总结出的流程:
(1)得到.c 文件
1.首先要写一个java文件,在java文件中加载so库,并调用native方法。
2.执行$javah 包命.文件名 生成.h文件(执行此命令应在包命最外层的同级目录)
3.在新建的.c文件中,include<.h文件>中的方法,并实现。
(2)建立目录结构:在项目的根目录下新建JNI 目录,JNI中放置.c文件、mk文件。
(3)mk文件内容(Android.mk,Application.mk)
Android.mk文件是用来定义编译规则的文件,描述了编译选项、头文件、源文件、依赖库等。文件用到的主要字段及含义有:
LOCAL_PATH := $(call my-dir)
*local_path 定义了本地源码路径 指定调用my-dir
include $(CLEAR_VARS)
*清除掉系统的宏定义
LOCAL_MODULE := hello-jni
*指定模块的名字,源文件生成的文件名,若生成so文件则在生成的文件前面会自动加上lib。(这个字段的值 也就是在java中加载的so库的名字)
LOCAL_SRC_FILES := hello-jni.c
*指定的C/C++源文件
include $(BUILD_SHARED_LIBRARY)
*指定生成的文件类型(BUILD_EXECUTABLE 表示生成可执行文件、)(BUILD_SHARED_LIBRARY 生成动态库)(BUILD_STATIC_LIBRARY 生成静态库)
Application.mkm描述原生程序的特性
(4)编译:在项目的根目录下执行ndk—build。
if(在Android studio环境中)
{
在main 目录下新建 jniLibs,
将libs 中生成的内容 全部拷贝到jniLibs目录中
}
***************************
java 调用c/c++,会根据自身的包名,到特定的文件中去找特定的方法。例如:
package com.a.b;
class c
{
public static native String MethodName();
public static void main(String[] args)
{
System.loadlibrary("Jni-test");
MethodName();
}
}
上面这段程序执行时,便会到特定的so库中寻找Java_com_a_b_c_MethodName( JNIEnv* env,
jobject thiz)
如果在c文件中这么长的方法名自己去写 避免不了麻烦和出错,有没有方便的方法?
有就是第一步,利用Javah 生成.h文件,然后把其中的方法拷贝到 实现的c文件就可以了。
***************************
实践Part:
1.新建android应用JniTest(一路next),然后在根目录(JniTest)下新建jni 文件夹,以备后用
2.修改MainActivity内容
public native String getStringFromNative();
private TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
System.loadLibrary("hello");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv=(TextView)findViewById(R.id.tv_word);
tv.setText(getStringFromNative());
Log.i("HelloFromJni----->",getStringFromNative());
}
public native String getStringFromeNative(),声明了getStringFromeNative()这个Native方法
3.生成.c
(1)$cd 到项目目录,这个可以直接在AndroidStudio中直接选择terminal。
(2 )cd 到新建的jni目录 执行下面的命令:
$ javah -classpath ../app/src/main/java/ com.example.apple.jnitest.MainActivity
com.example.apple.jnitest.MainActivity 应当替换成你的java文件的 包命.类名。执行完这句话后,会在新建的jni目录下生成com_example_apple_jnitest_MainActivity.h文件。
-classpath 后面跟的路径名 应当是从当前文件夹出发,能够找到你的包做外层的目录的路径。既我的目录结构是/app/src/main/java/com/example/apple/jnitest/MainActivity.java,我的classpath只要能找到com 即可。
(3)编写.c
这个可以把(2)生成的.h import,然后实现方法就好。我是直接把google给的ndk包example中的代码拷贝修改了。
.h 文件内容:
hello.c 文件代码:
#include<string.h>
#include<jni.h>
#include<com_example_apple_jnitest_MainActivity.h>
jstring JNICALL Java_com_example_apple_jnitest_MainActivity_getStringFromNative
(JNIEnv* env , jobject thiz)
{
#if defined(__arm__)
#if defined(__ARM_ARCH_7A__)
#if defined(__ARM_NEON__)
#if defined(__ARM_PCS_VFP)
#define ABI "armeabi-v7a/NEON (hard-float)"
#else
#define ABI "armeabi-v7a/NEON"
#endif
#else
#if defined(__ARM_PCS_VFP)
#define ABI "armeabi-v7a (hard-float)"
#else
#define ABI "armeabi-v7a"
#endif
#endif
#else
#define ABI "armeabi"
#endif
#elif defined(__i386__)
#define ABI "x86"
#elif defined(__x86_64__)
#define ABI "x86_64"
#elif defined(__mips64) /* mips64el-* toolchain defines __mips__ too */
#define ABI "mips64"
#elif defined(__mips__)
#define ABI "mips"
#elif defined(__aarch64__)
#define ABI "arm64-v8a"
#else
#define ABI "unknown"
#endif
return (*env)->NewStringUTF(env, "Hello from JNI ! Compiled with ABI " ABI ".");
}
3.定义mk
这里在jni 新建两个mk文件 ,Application.mk和 Android.mk内容分别如下:
Application.mk:
APP_ABI := allAndroid.mk:
# Copyright (C) 2009 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := hello
LOCAL_SRC_FILES := hello.c
include $(BUILD_SHARED_LIBRARY)
4.在项目根目录下执行命令ndk-build
terminal 输出如下:
appledeMacBook-Pro:JniTest apple$ ndk-build执行完命令会生成libs和obj文件夹
[arm64-v8a] Install : libhello.so => libs/arm64-v8a/libhello.so
[x86_64] Install : libhello.so => libs/x86_64/libhello.so
[mips64] Install : libhello.so => libs/mips64/libhello.so
[armeabi-v7a] Install : libhello.so => libs/armeabi-v7a/libhello.so
[armeabi] Install : libhello.so => libs/armeabi/libhello.so
[x86] Install : libhello.so => libs/x86/libhello.so
[mips] Install : libhello.so => libs/mips/libhello.so
appledeMacBook-Pro:JniTest apple$
4.善后工作
按道理现在应该可以运行了,可是在实验过程中运行报错(可能和androidStudio的文件组织方式有关,没有深究),在网上找到解决方案:
在app/src/main 目录下新建jniLibs文件夹,并将3中生成的libs文件夹下的所有内容拷贝到此文件夹下,完成后的目录结构:
此时就可以运行了!
一直以来就像搞明白到底该怎么去写,每次实验总是出各种各样的问题,现在终于搞定了! 不一样的环境下,操作应该有差异,不过大概流程就是这样了。