Eclipse开发NDK

1. 概念了解:

   交叉编译:
 在一个平台下,编译另外一个平台能够执行二进制的代码
 windows下编译 成android 平台可以运行的汇编 

工具: ndk
            cdt:   Eclipse开发c/c++插件 , 如果eclipse没有安装需要安装,查看是否安装该插件

##NDK目录结构
* docs:帮助文档: HOW TO:相当于index
* build/tools:linux的批处理文件: .sh文件
* platforms:编译c代码需要使用的头文件和类库
D:android-ndk-r9d\platforms\android-18\arch-arm\usr\include: 类型对应、env中方法


* prebuilt:预编译使用的二进制可执行文件,相当于window下的exe
* sample:jni的使用例子
* source:ndk的源码
* toolchains:工具链
* ndk-build.cmd:编译打包c代码的一个指令

2.  NDK开发流程:

2.1. 新建一个jni文件夹,编写c代码

public native String Hello();   // 定义native方法,c实现,Java中

***** javah的使用,如果嫌弃写c实现方法名称太长,可以使用javah
 1.7:在src目录下执行javah 包名.类名
 javah com.example.hava.MainActivity   
 然后在同级目录下找到生成文件类,拷贝对应方法即可 

hello.c代码实现:

扫描二维码关注公众号,回复: 9345291 查看本文章
#include <stdio.h>
#include <stdlib.h>
#include <jni.h>

JNIEXPORT jstring JNICALL Java_com_example_hava_MainActivity_Hello  // 返回值+ 全类名
	 (JNIEnv * env, jobject obj){
	char* cstr = "hello from a";
    // env 是二级指针,NewStringUTF 到  jni.h中去找  对应函数
	jstring jstr = (*env)->NewStringUTF(env, cstr);
    return jstr;
}

2.2 定义 Android.mk 文件,指定编译源,和hello.c同级别目录

 LOCAL_PATH := $(call my-dir)

    include $(CLEAR_VARS)
    
    LOCAL_LDLIBS += -llog
    
    #编译生成的文件的类库叫什么名字
    LOCAL_MODULE    := hello
    #要编译的c文件
    LOCAL_SRC_FILES := hello.c

    include $(BUILD_SHARED_LIBRARY)

3. 编译: 

在jni目录下执行, ndk-build   在libs生产so库 , 需要配置ndk环境变量,使用ndk-build.cmd 命令

 

4.  调用调用使用

public class MainActivity extends Activity {

	Button button1;

	static {
		// 加载打包完毕的so类库,掐头lib 无尾.so
		System.loadLibrary("hello");
	}

	

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		button1 = (Button) findViewById(R.id.button1);
		button1.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View arg0) {
				String str = Hello();
				Toast.makeText(MainActivity.this, str, Toast.LENGTH_LONG)
						.show();

			}
		});
	}
	  
	public native String Hello();   // 定义native方法,c实现
}

5. Application.mk   文件内容,在hello.c目录下

APP_ABI := armeabi x86
#APP_ABI := armeabi armeabi-v7a x86
#  添加所有支持架构
#APP_ABI := all

3.添加本地支持,自动生成 jni 目录、添加代码提示,每次运行,自动部署

3.1. eclipse配置ndk

2. 给项目添加NDK本地支持(自动给项目生成jni目录,.mk文件)

 

3.  给JNI添加源码 (添加以后写代码有提示)

 代码提示如下:

 如果一直项目报错,不能编译,难么直接右键把错误删除即可

4.实现Java 字符串Stirng 加密功能(传递字符串)

4.1. hello.c代码实现

 // 工具方法:把java的string 转化为 c语言的 char*
char*   Jstring2CStr(JNIEnv*   env,   jstring   jstr)
{
// jstring --> char *
	 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");
	 // 转换 为char*
	 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);  //
 // jstring --> char *
	 return rtn;
}


// 加密
JNIEXPORT jstring JNICALL Java_com_example_hava_MainActivity_arrString  // 返回值+ 全类名
	 (JNIEnv * env, jobject obj,jstring jstr){

	char* cstr = Jstring2CStr(env, jstr);  // char*  在 Jstring2CStr 中malloc了
	int i=0;
	for (i = 0; i < strlen(cstr); i++) {
            *(cstr+i)= *(cstr+i)+1;
	}
// 把 char* 转化为 jstring
	return (*env)->NewStringUTF(env, cstr);
}
  // 解密
JNIEXPORT jstring JNICALL Java_com_example_hava_MainActivity_decodearrString  // 返回值+ 全类名
	 (JNIEnv * env, jobject obj,jstring jstr){

	char* cstr = Jstring2CStr(env, jstr);  // char*  在 Jstring2CStr 中malloc了
	int i=0;
	for (i = 0; i < strlen(cstr); i++) {
            *(cstr+i)= *(cstr+i)-1;
	}
	return (*env)->NewStringUTF(env, cstr);
}

   native代码实现:

    public native String arrString(String arr);
	public native String decodearrString(String arr)

4.1传递数组,(数组无效果,值没有变)

 JAVA:

    public native void arrayEncode(int[] arr);

     for(int i=0;i<3;i++){
                 Log.e("denganzhi1", "数组元素是:"+a[i]);
             }

 C 代码:

JNIEXPORT void JNICALL Java_com_example_hava_MainActivity_arrayEncode
  (JNIEnv * env, jobject obj, jintArray jintarr){

	//拿到整型数组的长度以及第0个元素的地址
		 //jsize       (*GetArrayLength)(JNIEnv*, jarray);
		int length = (*env)->GetArrayLength(env, jintarr);
		 //jint*       (*GetIntArrayElements)(JNIEnv*, jintArray, jboolean*);
		 // 获取整形数组首数据指针, 这些到 jni.h中去找
		int* arrp = (*env)->GetIntArrayElements(env, jintarr, 0);
		LOGI("arrayEncode--%d",length);
		int i;
		for(i = 0;i < length; i++){
		//	LOGD("debug !!!");
			*(arrp + i) += 10;
		}

		LOGI("arrp--%d",*(arrp));
}

5. c 调用 java

5.1.Android控制台日志打印

#include <stdio.h>
#include <stdlib.h>
#include <jni.h>
#include <string.h>


#include <android/log.h>
#define LOG_TAG "System.out"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEFAULT, LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
/*  ...替换__VA_ARGS__,LOG_TAG过滤标识  */
 
 

JNIEXPORT jstring JNICALL Java_com_example_hava_MainActivity_Hello  // 返回值+ 全类名
	 (JNIEnv * env, jobject obj){
	char* cstr = "hello from a iiiii";
    // env 是二级指针,NewStringUTF 到  jni.h中去找  对应函数
	jstring jstr = (*env)->NewStringUTF(env, cstr);
	LOGI("hello ... jni");  // c语言打印log输出控制台
    return jstr;
}

5.2.C通过放射调用Java

通过放射,通过全类名获取类的字节码,获取类方法签名,然后调用类方法

#include <stdio.h>
#include <stdlib.h>
#include <jni.h>
#include <string.h>


#include <android/log.h>
#define LOG_TAG "System.out"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEFAULT, LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
/*  ...替换__VA_ARGS__,LOG_TAG过滤标识  */


JNIEXPORT jstring JNICALL Java_com_example_hava_MainActivity_Hello  // 返回值+ 全类名
	 (JNIEnv * env, jobject obj){
	char* cstr = "hello from a iiiii";
    // env 是二级指针,NewStringUTF 到  jni.h中去找  对应函数
	jstring jstr = (*env)->NewStringUTF(env, cstr);
	LOGI("hello ... jni");


	 //jclass   (*FindClass)(JNIEnv*, const char*);
			// 包名换斜杠
			jclass clazz = (*env)->FindClass(env, "com/example/hava/MainActivity");  // 找到字节码
			//jmethodID   (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
			// 第三个参数是方法名 Sinarate
			// 最后一个参数是show方法签名:javap -s 全类名 即可 在bin\classes\目录下,执行命令
			// 如果是系统的方法  javap -s java.util.Date
			// show 方法签名
			jmethodID methodID = (*env)->GetMethodID(env, clazz, "sayHelloJava", "(Ljava/lang/String;)V");
			//void        (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
			// CallObjectMethod():有返回值的
			// obj:方法的对象 ,c代码中是gbk,android是utf-u,转化为u8
			(*env)->CallVoidMethod(env, obj, methodID, (*env)->NewStringUTF(env, "是时候再黑一波小志了"));

    return jstr;
}

 Java代码:

package com.example.hava;




public class MainActivity extends Activity {
	
	
	public void sayHelloJava(String s){
		Toast.makeText(MainActivity.this, s, Toast.LENGTH_LONG)
		.show();
	}

}

如何使用别人的so库,把别人的App破解,找到so库,找到jni类,直接调用jni方法即可

6. Java调用c++

6.1.源码分析:jni.h中:

#if defined(__cplusplus)
typedef _JNIEnv JNIEnv;
typedef _JavaVM JavaVM;
#else
typedef const struct JNINativeInterface* JNIEnv;
typedef const struct JNIInvokeInterface* JavaVM;
#endif

如何是c++,使用的是JNIEnv 结构体,结构体源码
struct _JNIEnv {
    /* do not rename this; it does not seem to be entirely opaque */
    const struct JNINativeInterface* functions;

#if defined(__cplusplus)

    jstring NewStringUTF(const char* bytes)
    { return functions->NewStringUTF(this, bytes); }
}

_JNIEnv 结构体中和 C中 JNINativeInterface ,对比 C++ 中 函数封装了C函数已经传递了 this, 那么c++ 调用函数的时候不用传递this

6.2. 开发流程

1.Java代码:

package com.example.oo;

import android.app.Activity;
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 show(View view){
		String str=Hello();
		
		Toast.makeText(this, str, 1).show();
	}
	public native String Hello();
}

 在src目录下javah com.example.oo.MainActivity  生产com.example.oo.MainActivity.h头文件拷贝到jni目录下

2.  hello2.cpp代码实现

#include <stdio.h>
#include <stdlib.h>
#include <jni.h>

#include "com_example_oo_MainActivity.h"

jstring Java_com_example_oo_MainActivity_Hello
  (JNIEnv * env, jobject obj){
	char* cstr = "hello from c++";
	// 如果是 c++ env 就是指针,不用传递对象
	jstring jstr = (env)->NewStringUTF(cstr);
	return jstr;
}

3.  配置文件Application.mk

APP_ABI := armeabi x86
#APP_ABI := armeabi armeabi-v7a x86
#  添加所有支持架构
#APP_ABI := all

3.  配置文件Android.mk

 LOCAL_PATH := $(call my-dir)

    include $(CLEAR_VARS)
	#编译生成的文件的类库叫什么名字
    LOCAL_MODULE    := hello
    #要编译的c++文件
    LOCAL_SRC_FILES := hello2.cpp

    include $(BUILD_SHARED_LIBRARY)

4.  在jni目录下  ndk-build, 生产 so文件

5. 运行项目

总结:

 java 调用c++ 
1. 把c文件后缀名换成cpp
2. Android.mk文件中的hello.c也要换成hello.cpp
3. c++的使用的环境变量结构体中,访问了c使用的结构体的函数指针,函数名全部都是一样的,只是参数去掉了结构体指针
4. 访问函数指针时,把env前面的*号去掉,因为此时env已经是一级指针
5. clean,清除之前编译的残留文件
6. 把声明函数的h文件放入jni文件夹中,include该h文件

发布了60 篇原创文章 · 获赞 57 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/dreams_deng/article/details/104445081