1 编写以下案例(下面的三个按钮都调用了底层的C语言):
项目案例的代码结构如下:
2 编写DataProvider的代码:
package com.example.ndkpassdata;
public class DataProvider {
/**
* 计算x和y的加法 apktools
*
* @param x
* @param y
* @return
*/
public native int add(int x,int y);
/**
* 给字符串后面拼接字符串 加密运算 web url
* @param s
* @return
*/
public native String sayHelloInC(String s);
/**
* 给C代码传递int数组 让C代码给这个数组进行操作
* 图形 声音的处理
*
* @param iNum
* @return
*/
public native int[] intMethod(int[] iNum);
}
3 MainActivity的代码如下:
package com.example.ndkpassdata;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
public class MainActivity extends Activity {
DataProvider provider;
static{
System.loadLibrary("hello");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
provider=new DataProvider();
}
@SuppressLint("ShowToast")
public void click1(View view){
int result=provider.add(3, 5);
Toast.makeText(getApplicationContext(), "provider.add(3, 5) = " + result, 0).show();
}
@SuppressLint("ShowToast")
public void click2(View view){
String str=provider.sayHelloInC("yll");
Toast.makeText(getApplicationContext(), str, 0).show();
}
public void click3(View view){
int[] arr=new int[]{1,2,3,4,5};
provider.intMethod(arr);
for(int i:arr){
System.out.println(i);
}
}
}
4 通过DataProvider生成C语言的头文件
进入/cygdrive/e/workspace/Android/NdkPassData/src目录下:
查看DataProvider的全路径:
在项目中生成头文件
头文件的内容如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_ndkpassdata_DataProvider */
#ifndef _Included_com_example_ndkpassdata_DataProvider
#define _Included_com_example_ndkpassdata_DataProvider
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_example_ndkpassdata_DataProvider
* Method: add
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_example_ndkpassdata_DataProvider_add
(JNIEnv *, jobject, jint, jint);
/*
* Class: com_example_ndkpassdata_DataProvider
* Method: sayHelloInC
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_example_ndkpassdata_DataProvider_sayHelloInC
(JNIEnv *, jobject, jstring);
/*
* Class: com_example_ndkpassdata_DataProvider
* Method: intMethod
* Signature: ([I)[I
*/
JNIEXPORT jintArray JNICALL Java_com_example_ndkpassdata_DataProvider_intMethod
(JNIEnv *, jobject, jintArray);
#ifdef __cplusplus
}
#endif
#endif
5 在项目下创建一个文件夹jni,将上面生成的头文件拷贝到这个文件夹下:
然后按照头文件,编写hello.c,hello.c的代码如下:
#include <stdio.h>
#include "com_example_ndkpassdata_DataProvider.h"
#include <android/log.h>
#include <string.h>
#define LOG_TAG "clog"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
/**
* 将Java中的字符串转换成为C语言的字符
*/
char* Jstring2CStr(JNIEnv* env, jstring jstr)
{
char* rtn = NULL;
jclass clsstring = (*env)->FindClass(env,"java/lang/String");
jstring strencode = (*env)->NewStringUTF(env,"GB2312");
jmethodID mid = (*env)->GetMethodID(env,clsstring, "getBytes", "(Ljava/lang/String;)[B");
jbyteArray barr= (jbyteArray)(*env)->CallObjectMethod(env,jstr,mid,strencode); // String .getByte("GB2312");
jsize alen = (*env)->GetArrayLength(env,barr);
jbyte* ba = (*env)->GetByteArrayElements(env,barr,JNI_FALSE);
if(alen > 0)
{
rtn = (char*)malloc(alen+1); //"\0"
memcpy(rtn,ba,alen);
rtn[alen]=0;
}
(*env)->ReleaseByteArrayElements(env,barr,ba,0); //
return rtn;
}
JNIEXPORT jint JNICALL Java_com_example_ndkpassdata_DataProvider_add
(JNIEnv * env, jobject jobject, jint x, jint y){
// 想在logcat控制台上 打印日志
LOGD("x=%d",x);
LOGI("y=%d",y);
// log.i(TAG,"sss");
return x+y;
}
JNIEXPORT jstring JNICALL Java_com_example_ndkpassdata_DataProvider_sayHelloInC
(JNIEnv * env, jobject jobject, jstring str){
char* c="hello";
// 在C语言中不能直接操作java中的字符串
// 把java中的字符串转换成c语言中 char数组
char* cstr=Jstring2CStr(env,str);
strcat(cstr,c);
LOGD("%s",cstr);
return (*env)->NewStringUTF(env,cstr);
}
JNIEXPORT jintArray JNICALL Java_com_example_ndkpassdata_DataProvider_intMethod
(JNIEnv * env, jobject jobject, jintArray jarray){
// jArray 遍历数组 jint* (*GetIntArrayElements)(JNIEnv*, jintArray, jboolean*);
// 数组的长度 jsize (*GetArrayLength)(JNIEnv*, jarray);
// 对数组中每个元素 +5
int length=(*env)->GetArrayLength(env,jarray);
int* array=(*env)->GetIntArrayElements(env,jarray,0);
int i=0;
for(;i<length;i++){
*(array+i)+=5;
}
return jarray;
}
其中,上面的代码中使用了NDK中打印日志的库,Log共享库库的位置在:
6 编写Android.mk文件,内容如下:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
#测试写了lib前缀的情况,如果不加lib,生成.so的时候回自动加上lib前缀
LOCAL_MODULE := libhello
LOCAL_SRC_FILES := hello.c
#这里使用C语言打印日志,要引用log的共享库
#共享库的位置在:\android-ndk-r9d\platforms\android-19\arch-arm\usr\lib
LOCAL_LDLIBS += -llog
include $(BUILD_SHARED_LIBRARY)
7 使用ndk-build进行交叉编译:
F5刷新项目,发现项目下多了:
8 编写布局文件:
<RelativeLayout 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"
tools:context=".MainActivity" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:text="@string/hello_world" />
<Button
android:onClick="click1"
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:text="C语言实现第一个方法" />
<Button
android:onClick="click2"
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="@+id/button1"
android:text="C语言实现第二个方法" />
<Button
android:onClick="click3"
android:id="@+id/button3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="@+id/button2"
android:text="C语言实现第三个方法" />
</RelativeLayout>
9 编写Android清单文件:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.ndkpassdata"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="19" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.example.ndkpassdata.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
接下来运行应用,就可以看到如下:
同时注意控制台,可以打印出日志信息。