«

Android jni 应用初探 (MAC 环境)

时间:2024-3-2 17:52     作者:韩俊     分类: Android


<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 := all
Android.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
[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$ 
执行完命令会生成libs和obj文件夹

4.善后工作

按道理现在应该可以运行了,可是在实验过程中运行报错(可能和androidStudio的文件组织方式有关,没有深究),在网上找到解决方案:

在app/src/main 目录下新建jniLibs文件夹,并将3中生成的libs文件夹下的所有内容拷贝到此文件夹下,完成后的目录结构:



此时就可以运行了!


一直以来就像搞明白到底该怎么去写,每次实验总是出各种各样的问题,现在终于搞定了! 不一样的环境下,操作应该有差异,不过大概流程就是这样了。

标签: android

热门推荐