在android应用开发过程中,从Java调用C/C++函数主要经历以下几个步骤:
1. 必须在java代码中声明本地方法。
2.需要实现java本地接口(JNI)粘合层。
3.必须创建Android makefile文件。
4.必须用C/C++实现本地方法。
5.必须编译本地库。
6.必须加载本地库。
android-NDK为我们提供了一整套的机制来完成这些步骤。我采用的是NDKr8版本,完全不用Cygwin(貌似从NDKr7开始就可以完全不用Cygwin了)。
前篇:新建android工程MyJNITest,步骤省略,示例代码如下:
代码清单1: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: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=".MainActivity"
android:orientation="vertical">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="加法:20+70="/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/addition"
android:text="计算"
android:onClick="calculate"/>
</LinearLayout>
<EditText
android:id="@+id/addtionResult"
android:layout_width="fill_parent"
android:layout_height="40dip"
android:hint="点击计算按钮,进行加法运算"/>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="乘法:20*70="/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/multiplication"
android:text="计算"
android:onClick="calculate"/>/>
</LinearLayout>
<EditText
android:id="@+id/multiplicationResult"
android:layout_width="fill_parent"
android:layout_height="40dip"
android:hint="点击计算按钮,进行乘法运算"/>
</LinearLayout>
代码清单2:MainActivity.java
package com.example.myjnitest;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
public class MainActivity extends Activity {
private EditText additionResult,multiplicationResult;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
additionResult=(EditText)findViewById(R.id.addtionResult);
multiplicationResult=(EditText)findViewById(R.id.multiplicationResult);
}
public void calculate(View view)
{
int id=view.getId();
switch (id) {
case R.id.addition:
long addResult=addition(20, 70);
additionResult.setText("20+70="+addResult);
break;
case R.id.multiplication:
long mulResult=multiplication(20, 70);
multiplicationResult.setText("20*70="+mulResult);
break;
}
}
//本地方法,由java调用
private native long addition(int i,int j);
public static native long multiplication(int a,int b);
}
1. 声明本地方法:
示例代码2中有两个方法,addition()和multiplication()方法。这就是java中声明的本地方法,只是比普通的方法多了native关键字(native关键字不可省略),这些方法并没有具体的实现。从调用者角度来看,一旦声明了本地方法就可以在java代码中调用,会顺利通过编译。不过,如果应用运行并调用这两个本地方法的话,就会蹦出IllegalStateException异常,这很好理解,因为本地方法还没有具体实现。
本地方法可以是公共的,也可以是受保护的、私有的或包作用域的,如同任何其他java方法一样。同样,本地方法不一定必须是静态的,也不是必须使用基本类型。
2.实现JNI粘合层:
java通过JNI框架调用库里的C/C++方法。可以利用JDK(java开发工具包)建立JNI粘合层。首先,需要在C/C++头文件中声明要实现的函数。建议使用JDK的javah工具自动生成头文件。
在cmd中,进入MyJNITest工程根目录,例如我的工程根目录为 E:\workSpace\MyJNITest,然后在此目录下使用javah命令:
将本地方法放在了MainActivity.java类中,使用javah命令:
javah -classpath ./bin/classes -d jni com.example.myjnitest.MainActivity
会蹦出如此错误,找不到android.app.Activity的类文件。跳出该错误,猜想可能得原因是由于没有找到android.jar包,可能得在某个参数中指定下路径,在此不纠结其原因,以后的文章中再详细解释吧。
避免该问题有两种解决办法,仅是避免而已:
方法一: 在cmd中进入工程的src目录下,执行javah命令:
javah -d jni com.example.myjnitest.MainActivity
这样会在src目录下成功生成一个jni文件夹,头文件就在该jni文件夹中。
方法二:不要将本地方法写在MainActivity.java类中,而是单独放在一个普通类中
代码清单3:NativeMethodsClass.java
package com.example.myjnitest;
public class NativeMethodsClass {
//本地方法,由java调用
private native long addition(int i,int j);
public static native long multiplication(int a,int b);
}
再执行javah命令:
javah -classpath ./bin/classes -d jni com.example.myjnitest.NativeMethodsClass
就会在工程的根目录下生成jni文件夹,该文件夹中包含头文件。
采用方法一,生成头文件,
代码清单4:com_example_myjnitest_MainActivity.h