Android jni 应用初探 (MAC 环境)

时间:2021-11-23 12:39:14
<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 文件夹,以备后用


Android jni 应用初探 (MAC 环境)

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。

Android jni 应用初探 (MAC 环境)

(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 文件内容:

Android jni 应用初探 (MAC 环境)            

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文件夹

 Android jni 应用初探 (MAC 环境)

4.善后工作

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

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

Android jni 应用初探 (MAC 环境)

此时就可以运行了!


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