Android jni本地编程入门

在某些情况下,java编程已经不能满足我们的需要,比如一个复杂的算法处理,这时候就需要用到jni技术;

  • jni : java native interface
  • jni 其实就是java和c/cpp之间进行通信的一个接口规范,java可以调用c/cpp里面的函数,同样,c/cpp也可以调用java类的方法;

jni开发工具ndk的安装:
在最新的ndk版本中,安装ndk很简单,只需要装ndk的路径配置到系统环境变量中即可;
在编译的时候,进入工程根目录;执行命令  ndk-build  即可完成编译;

一、cpp与java

>>1.java文件:TextOutput.java

Android jni本地编程入门
package com.jnitest;

import android.util.Log;

public class TextOutput {
    private native String init();
    static {
        System.loadLibrary("text");
    }

    public String getString() {
        return init();
    }

    public void sayHello() {
        Log.e("tag", "void sayHello");
    }

    public String sayHello_() {
        Log.e("tag", "string sayHello_");
        return "return string sayHello_";
    }

    public static void sayHello__() {
        Log.e("tag", "static sayHello__");
    }
}
Android jni本地编程入门

在该类中定义了一个本地方法init[native init],方法实现由接下来的cpp函数来实现;以及定义了其它几个方法,由cpp文件来调用;

>>2.cpp文件:test.cpp

Android jni本地编程入门
#include <jni.h>
#include <android/log.h>
#include <string.h>

#ifndef _Included_com_jnitest_TextOutput
#define _Included_com_jnitest_TextOutput
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT jstring JNICALL Java_com_jnitest_TextOutput_init(JNIEnv *, jobject);
#ifdef __cplusplus
}
JNIEXPORT jstring JNICALL Java_com_jnitest_TextOutput_init(JNIEnv * env,
        jobject obj) {
    jmethodID mid; // 方法标识id
    jclass cls = env->GetObjectClass(obj); // 类的对象实例
    mid = env->GetMethodID(cls, "sayHello", "()V"); 
    env->CallVoidMethod(obj, mid);

    jmethodID mid1;
    mid1 = env->GetMethodID(cls, "sayHello_", "()Ljava/lang/String;");// @1
    jstring s1 = (jstring) env->CallObjectMethod(obj, mid1); // @2

    jmethodID mid2;
    mid2 = env->GetStaticMethodID(cls, "sayHello__", "()V");
    env->CallStaticVoidMethod(cls, mid2);

    return s1;
}
#endif
#endif
Android jni本地编程入门

先来看在cpp中定义的函数名:Java_com_jnitest_TextOutput_init
其实不难看出,java文件与cpp文件中函数名的配对定义方式为Java + 包名 + java类名 + 方法/函数名,中间用_分隔;其中两个参数分别是:

  • env:当前该线程的内容,包含线程里面全部内容;
  • obj:当前类的实例,指.java文件的内容(在该例子中即是TextOutput类);

@1:获取MethodId,该方法需要三个参数,分别jclass类的对象实例,函数名,函数签名;其中函数签名由两部分构成(参数+函数返回值); 
查看签名可以通过先进入到java文件生成的class文件目录,执行命令:javap -s -p ClassName
Android jni本地编程入门

生成如上图所示的函数签名;

@2:执行该方法,在上面的例子中,将执行sayHello_方法

运行程序:

Android jni本地编程入门
TextOutput to = new TextOutput();
Log.e("tag", "" + to.getString());
Android jni本地编程入门

Android jni本地编程入门

二.c与java
在Activity中有一个int字段,通过callback赋值

Android jni本地编程入门
package com.example.hellojni;

import android.app.Activity;
import android.util.Log;
import android.widget.TextView;
import android.os.Bundle;

public class HelloJni extends Activity {
    private static int si;

    private static void callback() {
        si = 123;
    }

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        TextView tv = new TextView(this);
        tv.setText(stringFromJNI());
        setContentView(tv);
        Log.e("tag", "si=" + si);
    }

    public native String stringFromJNI();

    static {
        System.loadLibrary("hello-jni");
    }
}
Android jni本地编程入门

hello-jni.c

Android jni本地编程入门
#include <string.h>
#include <jni.h>

/* 
 * */
jstring Java_com_example_hellojni_HelloJni_stringFromJNI(JNIEnv* env,
        jobject thiz) //env:当前该线程的内容,包含线程全部的东西;thiz:当前类的实例,指.java文件的内容
{
    jint si;
    jfieldID fid; // 一个字段,实际上对应java类里面的一个字段或属性;
    jclass cls = (*env)->GetObjectClass(env, thiz); // 类的对象实例
    jmethodID mid = (*env)->GetStaticMethodID(env, cls, "callback", "()V"); // 一个方法的id
    //(I)V  (I)I
    if (mid == NULL) {
        return (*env)->NewStringUTF(env, "mid=null");
    }
    (*env)->CallStaticVoidMethod(env, cls, mid); //调用callback方法
    fid = (*env)->GetStaticFieldID(env, cls, "si", "I"); //取出si字段
    if (fid == NULL) {
        return (*env)->NewStringUTF(env, "fid=null");
    }
    si = (*env)->GetStaticIntField(env, cls, fid); //取出字段对应的值(fid字段对应的值)

    return (*env)->NewStringUTF(env, "init success");
}
Android jni本地编程入门

运行上面的Activity,即可实现对si的赋值

Android jni本地编程入门

3.加入链接库

在程序开发过程中,会频繁的用到调试,方式有很多种,下面要讲的这一种是通过log打印信息来打印程序运行时的一些状态数值;

修改Android.mk文件,添加一句代码

Android jni本地编程入门
include $(CLEAR_VARS)
LOCAL_LDLIBS += -llog //LDLIBS:连接libs,后面跟的参数为需要链接的libs,-llog表示Android中的Log库;
include $(BUILD_SHARED_LIBRARY)
Android jni本地编程入门

加入了log库之后

即可以在c文件中调用log打印输出信息:

Android jni本地编程入门
__android_log_print(ANDROID_LOG_ERROR, "hello", "livingstone");    
__android_log_print(ANDROID_LOG_DEBUG, "hello", "livingstone %d" ,23);
Android jni本地编程入门

 

 

Android jni本地编程入门,布布扣,bubuko.com

Android jni本地编程入门

上一篇:Javascript作用域链


下一篇:C++的允诺 ---- 默认构造函数 真的如你所愿吗