您的位置:首页 > 移动开发 > Android开发

Android JNI 在C中调用Java(包括自定义的Java方法和Log)

2017-04-22 09:00 519 查看

项目简介:

该项目为在C中调用Java的方法

详细介绍:

用户点击按钮后,将会弹出一个对话框,但是该对话框不是由Java方法调用的,而是由C方法调用的。如下所示,点击按钮,屏幕弹出对话框:



点击对话框中的ok按钮,对话框就消失了。并吐司弹出一段文字“知道了”

该应用涉及到的知识有:

1.如何在C中使用android 的 log输出

其实这些方法在C中已经做好了,只需要导入Android的Log类库,并在C中宏定义(也可以不用define定义别名,

但是这些方法名字太长,不好写,并且不是所有的方法都要宏定义,只需要定义自己需要用的就行了。比如我只定义LOGI)

#include <android/log.h>
#define LOG_TAG    "HHH"
#define LOGI(...)  __android_log_print(ANDROID_LOG_INFO,LOG_TAG, __VA_ARGS__)
#define LOGD(...)  __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG, __VA_ARGS__)
#define LOGW(...)  __android_log_print(ANDROID_LOG_WARN,LOG_TAG, __VA_ARGS__)
#define LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG, __VA_ARGS__)
#define LOGF(...)  __android_log_print(ANDROID_LOG_FATAL,LOG_TAG, __VA_ARGS__)


通过
#include <android/log.h>
点击进入log.h文件,可以看到具体的方法,导入完成后,一定要Android.mk文件中写入LOCAL_LDLIBS += -llog,这样写表示在编译的时候加载Log类库

2 .如何在C中调用Java中的方法

与Java的反射机制十分相似

1) 获取类的字节码文件

(*env)->FindClass(…)

2) 获取方法名(即方法的id)

(*env)->GetMethodID(…);

3) 调用方法

(*env)->CallVoidMethod(…)

(*env)->CallByteMethod(…)

(*env)->CallCharMethod(…)

等等。需要根据返回值调用不同的方法

注意:

1.无论是log输入日志,还是C调用了Java方法,应为这都要把字符串传递个Java去处理,所以不要有中文,否则程序崩溃。在
Android JNI 调用C语言
这篇文章中有解决办法。文章链接在下面步骤中有。

步骤:

1.创建Android项目

准备工作。先Add Native Support,将类库名命名为Hello,然后该C文件后缀和Android.mk文件(详情参考这篇文章Android JNI 调用C语言)。C文件内容暂时不需要些。

2.布局文件

编写activity_main.xml文件:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="hhh.exercise.ujni_e.MainActivity" >

<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="click1"
android:text="C调用Java方法" />

</LinearLayout>


布局文件就一个按钮

3.Activity

编写MianActivity.java文件:

import android.app.Activity;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;

public class MainActivity extends Activity {

static{
System.loadLibrary("Hello");
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}

public void click1(View v) {
helloC();
}

public native void helloC();

// 该方法让C语言调用,然后在界面上显示一个对话框
public void show(String message) {
Builder builder = new Builder(this);
builder.setTitle("C调用Java中的方法");
builder.setMessage(message);

// 设置取消按钮
builder.setNegativeButton("ok", new DialogInterface.OnClickListener() {

@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(MainActivity.this, "知道了", 1).show();
}
});
AlertDialog dialog = builder.create();
dialog.show();
}

}


从代码中可以看出click1方法并没有调用show方法,也就是说,如果点击按钮后显示了对话框,说明是本地方法helloC调用的show方法。

4.C文件

#include <jni.h>
#include <android/log.h>
// 宏定义类似java 层的定义,不同级别的Log LOGI, LOGD, LOGW, LOGE, LOGF。 对就Java中的 Log.i log.d
#define LOG_TAG    "HHH"
#define LOGI(...)  __android_log_print(ANDROID_LOG_INFO,LOG_TAG, __VA_ARGS__)
#define LOGD(...)  __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG, __VA_ARGS__)
#define LOGW(...)  __android_log_print(ANDROID_LOG_WARN,LOG_TAG, __VA_ARGS__)
#define LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG, __VA_ARGS__)
#define LOGF(...)  __android_log_print(ANDROID_LOG_FATAL,LOG_TAG, __VA_ARGS__)

JNIEXPORT void JNICALL Java_hhh_exercise_ujni_1e_MainActivity_helloC
(JNIEnv * env, jobject obj){

LOGD("Hello Java");
LOGI("hello C ");

//使用反射技术,获取Java中的方法

// jclass      (*FindClass)(JNIEnv*, const char*);
//第二个参数:Java类的类名,要包含前面的包名
jclass clazz=(*env)->FindClass(env,"hhh/exercise/ujni_e/MainActivity");
LOGI("获得字节码文件: ");

//拿到方法的ID
//jmethodID   (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
//第三个参数:传入需要反射的方法名
//第四个参数:方法的签名
jmethodID methodID=(*env)->GetMethodID(env,clazz,"show","(Ljava/lang/String;)V");

//调用方法
//void        (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
//后面的三个点表示参数,可以输入任意多个。但是要注意,一定要输入Java类型的参数.不要输入中文,会乱码到导致错误(如果需要输入中文,就要进行转码)
(*env)->CallVoidMethod(env,obj,methodID,(*env)->NewStringUTF(env,"what fuck"));
}


可以看到,C中调用了Java中自定义的show方法,实际上就是用了反射技术。还有调用了系统已经做好的Log。

5.Android.mk

因为在C中调用了Java的Log,需要在Android.mk文件中配置:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_LDLIBS += -llog

LOCAL_MODULE    := Hello
LOCAL_SRC_FILES := Hello.c

include $(BUILD_SHARED_LIBRARY)


即在文件中添加一句
LOCAL_LDLIBS += -llog
即可。

整个demo完成

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐