jni备忘:jni调用java方法显示一个Dialog

6 篇文章 0 订阅

jni备忘:jni调用java方法显示一个Dialog

写在前面

之前研究过jni,发现一段时间不用就基本忘干净了,现在在这里通过一个简单的例子记录一下。
完整代码下载

1、环境搭建

开发ndk需要下载ndk的开发包,我下载的是android-ndk32-r10-windows-x86.zip,这个版本中自带了c的编译链,可以不使用cygwin。
将ndk解压到一个目录以后,文件夹中有一个批处理文件ndk-build.cmd,在jni文件夹所在的目录执行这个命令就能进行编译。
为了方便敲命令,把ndk文件夹的根目录加入到环境变量中。

2、编写java代码

例子期望的效果是,显示一个普通的Dialog,有标题,有提示信息,有按钮。
首先需要开发java的代码,把ndk中的例子HelloJni代码稍微改动一下:

import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;

public class HelloJni extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        LinearLayout ll = new LinearLayout(this);
        ll.setOrientation(LinearLayout.VERTICAL);

        Button btn = null;

        btn = new Button(this);
        btn.setText("show Dialog by jni");
        btn.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                test1(HelloJni.this);
            }
        });
        ll.addView(btn);

        btn = new Button(this);
        btn.setText("show Dialog by java");
        btn.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                Context context = HelloJni.this;

                new AlertDialog.Builder(context).setTitle("标题")
                        .setMessage("提示信息").setPositiveButton("确定", null)
                        .show();
            }
        });
        ll.addView(btn);

        setContentView(ll);

    }

    public native void test1(Context context);

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

最后一句”System.loadLibrary(“test_jni”);”是加载so库的代码

3、生成jni头文件

在上面的java代码中声明了一个native函数,这个native函数就是需要在jni中实现的接口函数。
jni编写遵循固定的格式,详细查看:https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/design.html
为了方便,可以使用javah命令生成对应的头文件,这样就不怕写错了。
命令为

javah com.example.hellojni.HelloJni

执行命令以后报错:

D:\android\worksapce\TestJni\src>javah com.example.hellojni.HelloJni
错误: 无法确定Context的签名

这个错误是由于Context不能被jdk环境识别造成的,可以先将Context更改为Object,生成头文件以后再改回来即可。
生成的头文件如下:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_hellojni_HelloJni */

#ifndef _Included_com_example_hellojni_HelloJni
#define _Included_com_example_hellojni_HelloJni
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_hellojni_HelloJni
 * Method:    test1
 * Signature: (Ljava/lang/Object;)V
 */
JNIEXPORT void JNICALL Java_com_example_hellojni_HelloJni_test1
  (JNIEnv *, jobject, jobject);

#ifdef __cplusplus
}
#endif
#endif

一个小知识点:如果声明的native方法存在下划线的话,用转义字符”_1”代替。比如set_name,应该写成set_1name。

4、编写jni实现

在Android项目的根目录创建文件夹”jni”,新建test_jni.cpp文件
编写的jni代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <jni.h>

#include <com_example_hellojni_HelloJni.h>

//#include <com_hengbao_trustedenv_ccbhce_TuiAPI.h>

#ifdef __cplusplus
extern "C" {
#endif

/*
 * Class:     com_example_hellojni_HelloJni
 * Method:    test1
 * Signature: (Ljava/lang/Object;)V
 */
JNIEXPORT void JNICALL Java_com_example_hellojni_HelloJni_test1
  (JNIEnv * env, jobject obj, jobject context) {
    /*
     new AlertDialog.Builder(context)
            .setTitle("标题")
            .setMessage("提示信息")
            .setPositiveButton("确定", null)
            .show();
     */

    jmethodID jmethod_tem;
    // AlertDialog.Builder builder = new AlertDialog.Builder(context);
    jclass jclass_AlertDialogBuilder = env->FindClass("android/app/AlertDialog$Builder");
    jmethod_tem = env->GetMethodID(jclass_AlertDialogBuilder, "<init>", "(Landroid/content/Context;)V");
    jobject jobject_AlertDialogBuilder = env->NewObject(jclass_AlertDialogBuilder, jmethod_tem, context);

    // builder.setTitle("text"); public Builder setTitle(java.lang.CharSequence message)
    jmethod_tem = env->GetMethodID(jclass_AlertDialogBuilder, "setTitle",
            "(Ljava/lang/CharSequence;)Landroid/app/AlertDialog$Builder;");
    env->CallObjectMethod(jobject_AlertDialogBuilder, jmethod_tem, env->NewStringUTF("标题"));

    // builder.setMessage("text"); public Builder setMessage(CharSequence message)
    jmethod_tem = env->GetMethodID(jclass_AlertDialogBuilder, "setMessage",
            "(Ljava/lang/CharSequence;)Landroid/app/AlertDialog$Builder;");
    env->CallObjectMethod(jobject_AlertDialogBuilder, jmethod_tem, env->NewStringUTF("提示信息"));

    // builder..setPositiveButton("确定", null);
    // public Builder setPositiveButton(CharSequence text, final android.content.DialogInterface.OnClickListener listener)
    jmethod_tem = env->GetMethodID(jclass_AlertDialogBuilder, "setPositiveButton",
            "(Ljava/lang/CharSequence;Landroid/content/DialogInterface$OnClickListener;)Landroid/app/AlertDialog$Builder;");
    env->CallObjectMethod(jobject_AlertDialogBuilder, jmethod_tem, env->NewStringUTF("确定"), NULL);

    // builder.show();  public AlertDialog show()
    jmethod_tem = env->GetMethodID(jclass_AlertDialogBuilder, "show", "()Landroid/app/AlertDialog;");
    env->CallObjectMethod(jobject_AlertDialogBuilder, jmethod_tem);
}

#ifdef __cplusplus
}
#endif

编写中总结的知识点:

1、调用一个类的构造方法,方法的名称填写””
2、如果是内部类的话,应该用$分割,比如”android/app/AlertDialog$Builder”
3、使用c和c++开发的话,jni的函数调用形式略有不同,c中调用的话是”(*env)->FindClass(env, “xxxx”);”,而c++中是”env->FindClass(“xxxx”);”
4、写方法签名时,如果是类对象的话,用”Ljava/lang/String;”的形式,分号不能省。
如果是基本类型的话,根据规则用”Z”表示boolean等
有关方法的签名详细写法参见:Type Signatures
5、编写代码中发现,jni的代码方法签名很容易出错,使用javap可以查看java类中所有方法的签名,不过必须是对编译后的class文件进行操作,详见利用javap生成方法签名

5、编译运行

编译文件时需要额外两个配置文件:Android.mk和Application.mk。
Application.mk描述要生成so库的对应cpu指令集,默认为全部,即armeabi、x86等,这里指定只生成armeabi。

APP_ABI := armeabi

Android.mk负责描述编译时的信息。

LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)

#编译的文件,不包括.h头文件
LOCAL_SRC_FILES := test_jni.cpp
#将inc文件夹整个包含进来,这样导头文件就可以直接引用,不用带文件夹名字inc/xxx.h
LOCAL_C_INCLUDES:= inc
LOCAL_MODULE    := test_jni

LOCAL_LDLIBS += -llog

include $(BUILD_SHARED_LIBRARY)

接下来启动一个cmd命令窗口,cd到jni文件夹目录下,执行命令:

ndk-build

执行结果如下:

D:\android\worksapce\TestJni\jni>ndk-build
[armeabi] Compile++ thumb: test_jni <= test_jni.cpp
[armeabi] SharedLibrary  : libtest_jni.so
[armeabi] Install        : libtest_jni.so => libs/armeabi/libtest_jni.so

运行结果:
例子运行结果

6、最后

在上面的例子中,Dialog的确定按钮并没有设置点击事件,如果要在jni中设置点击事件的话,估计需要在jni中定义一个c++的类,继承DialogInterface.OnClickListener才能做到,现在先空着。
完整代码下载

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值