Android进阶之路(二) -- NDK初探

继续学习NDK开发,今天来实现一个简单的计算器功能,NativeUtil类中有一个静态的native方法,它接收三个参数,分别是两个操作数和一个操作符,并且返回C的计算结果。


NativeUtil类定义如下

public class NativeUtil {
    static {
        System.loadLibrary("native-lib");
    }

    public static int ADD = 0;
    public static int SUB = 1;
    public static int MULTI = 2;
    public static int DIVISION = 3;

    public static native int calculate(int arg0,int arg1,int symbol);
}
其中,ADD,SUB,MULTI,DIVISION,分别表示加减乘除,C中取出arg0和arg1的值,根据symbol和NativeUtil的静态成员变量对比,判断进行哪种操作,然后返回计算结果。

在动手之前,我们先要解决几个问题:
1、C怎么获取Java层的数据类型?
2、C怎么访问NativeUtil的成员变量?


要解决第一个问题,我们得先了解C与Java数据类型的转换,转换表如下:

Java 类型本地类型描述
booleanjbooleanC/C++8位整型
bytejbyteC/C++带符号的8位整型
charjcharC/C++无符号的16位整型
shortjshortC/C++带符号的16位整型
intjintC/C++带符号的32位整型
longjlongC/C++带符号的64位整型e
floatjfloatC/C++32位浮点型
doublejdoubleC/C++64位浮点型
Objectjobject任何Java对象,或者没有对应java类型的对象
ClassjclassClass对象
Stringjstring字符串对象
Object[]jobjectArray任何对象的数组
boolean[]jbooleanArray布尔型数组
byte[]jbyteArray比特型数组
char[]jcharArray字符型数组
short[]jshortArray短整型数组
int[]jintArray整型数组
long[]jlongArray长整型数组
float[]jfloatArray浮点型数组
double[]jdoubleArray双浮点型数组

Java中int型的数据,在C中就会用jint表示。

还有很关键的一点是方法签名,方法签名的作用是唯一确定一个方法,因为方法可以有重载,所以仅靠方法的名称是无法准确定位一个方法的,这个时候就需要方法签名了,方法签名实际上就是方法返回值和参数表的组合,它的规则如下:


使用时的形式是(参数类型1参数类型2……)返回类型
比如:
public int calculate(int arg0,int arg1,int symbol);
它的方法签名就是(III)I

C如何访问NativeUtil的成员变量呢?
这就需要请出JNI大总管JNIEnv了,JNIEnv负责Java与C的交互,比如从调用Java层的方法、访问Java层的变量,都需要JNIEnv的参与。
大致阅读源码可知JNIEnv在C和C++中分别有不同实现,在C中JNIEnv是JNINativeInterface的一个指针类型,在C++中JNIEnv是一个结构体,它们的共同点是都与JNINativeInterface有关,JNINativeInterface中定义了很多指针,C/C++就通过这些指针调用Java层函数,详细的内容会在后面源码部分介绍。
查阅文档可以知道,JNIEnv中有一个 GetStaticIntField(jclass clazz, jfieldID fieldID)函数,第一个参数是需要获取成员变量的类,第二个参数是字段ID。

字段ID如何获取呢?继续查阅文档,我们又找到了GetStaticFieldID(jclass clazz, const char* name, const char* sig)函数,它的三个参数分别是需要获取成员变量的类,成员变量名和签名。

与此相同,JNIEnv中还提供了很多很多获取变量信息、获取方法信息的函数。


由此,就可以开始编写C代码了,写出的的代码如下:
#include <jni.h>

extern "C"
JNIEXPORT jint JNICALL
Java_com_tustcs_ndktest_NativeUtil_calculate(JNIEnv *env, jclass type, jint arg0, jint arg1,
                                             jint symbol) {
    if(symbol == env->GetStaticIntField(type,env->GetStaticFieldID(type,"ADD","I")))
        return arg0 + arg1;
    else if(symbol == env->GetStaticIntField(type,env->GetStaticFieldID(type,"SUB","I")))
        return arg0 - arg1;
    else if(symbol == env->GetStaticIntField(type,env->GetStaticFieldID(type,"MULTI","I")))
        return arg0 * arg1;
    else if(symbol == env->GetStaticIntField(type,env->GetStaticFieldID(type,"DIVISION","I")))
        return arg0 / arg1;
}

然后在CMakeLists.txt中添加路径,再去MainActivity调用。。嗯,大功告成。



今天附上关于静态块和成员变量初始化的小知识:

public class InitTest {
	public static int sInt1 = 10;
	public static int sInt2;
	public int mInt1 = 30;
	public int mInt2;
	//局部代码块
	{
		if(mInt1 == 30) {
			System.out.println("成员变量mInt1已完成初始化:mInt1 = " + mInt1);
		}
		mInt2 = 40;
		System.out.println("代码块mInt2初始化:mInt2 = " + mInt2);
	}
	//静态块
	static{
		sInt2 = 20;
		System.out.println("静态块sInt2初始化:sInt2 = " + sInt2);
	}
	public static void print() {
		System.out.println("静态方法:print()调用");
	}	
}
主方法执行:
public static void main(String[] args) {
		InitTest.print();
		//InitTest test = new InitTest(); 
}
在主函数调用InitTest的静态方法,结果如下:


我们可以看到,调用类的静态方法不会调用局部代码块,而会先调用静态块。

去掉注释,实例化一个类试试

public static void main(String[] args) {
		//InitTest.print();
		InitTest test = new InitTest(); 
}
运行结果如下:

我们可以看到,实例化一个类时,会优先调用静态块,再调用代码块和给成员变量赋值。

那么,代码块和给成员变量赋值哪个先执行呢?

答案是从头开始执行,程序运行到哪里,就先调用哪里,按顺序执行。

### 回答1: Android C 高级编程是指在Android开发中使用C语言进行高级编程的技术。而使用NDK(Native Development Kit)可以使开发者在Android应用中使用C/C++等本地语言进行编程。 NDK一个工具集,它允许开发者在Android应用中嵌入本地代码,并且提供了一系列的开发工具和库,以便开发者能够在Android应用中使用C/C++进行高级编程。使用NDK可以提供更高的性能和更低的内存占用,适用于需要处理大量数据和高性能计算的应用场景。 在使用NDK进行Android C高级编程时,可以使用PDF(Portable Document Format)作为文档格式,以便对代码和项目进行更好的管理和文档化。在NDK开发过程中,可以使用PDF文档记录关键的设计思路、代码逻辑、接口定义等信息,以方便团队协作和后续的维护。 使用NDK进行Android C高级编程的步骤大致如下: 1. 准备开发环境:安装NDK并配置好开发环境,包括设置NDK的路径和编译器等。 2. 创建新项目:使用Android Studio创建一个新的Android项目,并在项目中引入NDK的支持。 3. 编写C代码:使用C/C++语言编写需要调用的函数、算法或者数据结构等代码,并将其保存在适当的目录下。 4. 编写JNI接口:在生成的Java代码中,使用JNI(Java Native Interface)定义对应C代码的接口,以便在Java层调用C代码。 5. 编译和构建:使用NDK的工具集进行编译和构建,将C代码编译成适合Android平台使用的库文件(.so文件)。 6. 在Java代码中调用C代码:在需要调用C代码的地方,使用JNI接口调用对应的C函数,以实现和C代码的交互和调用。 使用PDF文档进行文档化可以帮助开发者更好地组织和管理代码、接口和设计文档等,方便后续的代码维护和项目协作。同时,也可以作为项目的参考文档,方便其他开发人员了解和使用项目。 ### 回答2: Android C 高级编程是针对使用NDK(Native Development Kit)的一种高级编程技术。NDKAndroid开发工具包中的一个工具,允许开发者使用C、C++或其他本地编程语言编写Android应用程序的部分或全部代码。 使用NDK进行Android C高级编程有许多优点。首先,NDK提供了更高的性能和更好的控制权,特别是在处理图形、音频和计算密集型任务时。通过使用本地编程语言,开发者能够更好地利用底层系统资源,提高应用程序的执行效率和速度。 其次,NDK还提供了对现有C和C++库的支持。这意味着开发者可以使用许多已经存在的库和功能来加快开发进程。无需重新编写现有的代码,直接使用NDK与这些库进行集成即可。 在使用NDK进行Android C高级编程时,一种常见的用途是开发游戏。使用C或C++编写游戏代码可以获得更好的性能和更低的延迟,这对于游戏的流畅运行至关重要。 此外,开发者还可以使用NDK为现有的Java应用程序添加本地本地扩展。这样可以通过使用C或C++编写某些关键组件,以改进应用程序的性能或添加新的功能。 总的来说,通过使用NDK进行Android C高级编程,开发者可以获得更高的性能、更好的控制权和更好的资源利用。无论是开发游戏还是优化应用程序,使用NDK都是提高性能和扩展功能的好方法。通过阅读相关的PDF文档,开发者可以更深入地了解如何使用NDK进行Android C高级编程。 ### 回答3: Android NDK (Native Development Kit) 是一个用于开发 Android 应用程序的工具集,它使开发者能够使用 C 或 C++ 编写原生代码,并将其与 Java 编写的 Android 应用程序一起使用。使用 NDK 可以达到增加性能、复用现有的 C/C++ 代码以及访问底层硬件等目的。 在 Android C 高级编程中,使用 NDK 商用 PDF 库可以实现Android 应用程序中处理 PDF 文件的功能。PDF 文件是一种常见的电子文档格式,使用 PDF 库可以读取、编辑和生成 PDF 文件。 使用 NDK 进行 PDF 处理的一般步骤如下: 1. 集成 PDF 库:首先,需要将商用的 PDF 库 (.so 文件) 集成到 Android 项目中。可以通过在 Android.mk 文件中添加相关配置,确保 .so 文件正确地被编译和链接到应用程序中。 2. 创建 JNI 接口:为了在 Java 层与 C/C++ 层之间进行通信,需要创建 JNI (Java Native Interface) 接口。可以在创建 JNI 方法时使用 JNAerator 或者手动编写 JNI 代码,以便在 Java 层调用 C/C++ 的功能。 3. 对 PDF 文件进行处理:在 C/C++ 层,可以使用 PDF 库提供的功能来处理 PDF 文件。例如,可以使用库提供的函数来解析、渲染、添加标注、提取内容等。 4. 将数据返回给 Java 层:在 C/C++ 层处理完之后,可以通过 JNI 接口将处理后的数据返回给 Java 层。这样就可以在 Android 应用程序中显示或者存储处理后的 PDF 文件。 需要注意的是,在使用商用 PDF 库时,需要遵循相关的许可协议,并确保在开发和分发过程中合法使用该库。 总之,通过使用 NDK 和商用 PDF 库,可以使 Android 应用程序具有处理 PDF 文件的高级编程能力。同时,开发者需要具备 C/C++ 编程和 JNI 接口的使用经验,以便顺利地进行开发工作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值