android ndk开发(2)——基础编程

0x00 基础知识

  • jni 是java native interface,java调用底层C代码所用到的技术
  • ndk 是Android方便jni编程所提供的工具集合
  • 在windows 环境下,需要安装cygwin工具,方便对c代码进行编译 配置ndk运行环境
    打开cygwin64\etc\profile文件,编辑其中的环境变量,添加ndk位置变量:
    1
    2
    3
    4
    5
    if [ ${CYGWIN_NOWINPATH-addwinpath} = "addwinpath" ] ; then
    PATH="/usr/local/bin:/cygdrive/h/software/android_ndk_develop/windows/android-ndk-r10e:/usr/bin${PATH:+:${PATH}}"
    else
    PATH="/usr/local/bin:/cygdrive/h/software/android_ndk_develop/windows/android-ndk-r10e:/usr/bin"
    fi
  • 安装cygwin选择163的源*,安装bash和make即可。需要更新也是用setup文件。
    参考链接:
    http://pielot.org/2010/12/using-cygwin-with-the-android-ndk-on-windows/
    http://mirrors.163.com/.help/cygwin.html
    http://blog.csdn.net/hu_shengyang/article/details/7828998

0x01 开始编程

1. 创建工程 java部分

  • 新建一个空的Android App程序,添加几个按钮,用于测试调用ndk。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    <Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="JAVA与C交互 integer"
    android:id="@+id/button2"
    android:layout_alignTop="@+id/button"
    android:layout_toRightOf="@+id/button"
    android:layout_toEndOf="@+id/button"
    android:layout_marginLeft="60dp"
    android:layout_marginStart="60dp"
    android:onClick="clickInteger" /> //调用clickInteger函数

    <Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="JAVA与C交互 string"
    android:id="@+id/button3"
    android:layout_below="@+id/button"
    android:layout_alignLeft="@+id/button"
    android:layout_alignStart="@+id/button"
    android:layout_marginTop="72dp"
    android:onClick="clickString" /> //调用clickString函数
    ```
    - 新建DataProvider类,里面用native关键字声明需要调用C的函数。

    public class DataProvider {
    /**
    *

    • @param a integer a

    • @param b integer b

    • @return a + b

    • /
      public native int add(int a , int b);

      /**

    • @param message

    • @return “hello” + message

    • /
      public native String sayHello(String message);

}

1
2

- 测试C代码调用
public void clickInteger(View view)
{
    Toast.makeText(this,dataProvider.add(1, 2) + "" ,Toast.LENGTH_LONG).show();
}

public void clickString(View view)
{
    Toast.makeText(this,dataProvider.sayHello("angel~~"),Toast.LENGTH_LONG).show();
}
1
2
3
4

### 2. 编写C代码

- 使用javah 帮助生成C头文件
cd app\build\intermediates\classes\debug
javah com.example.huanqi.myapplication.DataProvider 
1
2

便可生成对应的头文件`com_example_huanqi_myapplication_DataProvider.h`。
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_huanqi_myapplication_DataProvider */

#ifndef _Included_com_example_huanqi_myapplication_DataProvider
#define _Included_com_example_huanqi_myapplication_DataProvider
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_huanqi_myapplication_DataProvider
 * Method:    add
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL Java_com_example_huanqi_myapplication_DataProvider_add
  (JNIEnv *, jobject, jint, jint);

/*
 * Class:     com_example_huanqi_myapplication_DataProvider
 * Method:    sayHello
 * Signature: (Ljava/lang/String;)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_example_huanqi_myapplication_DataProvider_sayHello
  (JNIEnv *, jobject, jstring);

#ifdef __cplusplus
}
#endif
#endif
1
2
3
4
5
- 使用jni技术,C代码编程   

这里用到了`jni.h` 头文件进行基础编程和`android/log.h`文件进行打印日志。这里的C代码需要通过jni中数据转换为java可识别的类型。

**_data_test.c_**
//
// Created by HuAnqi on 2015-09-25.
//


#include <string.h>
#include "com_example_huanqi_myapplication_DataProvider.h"


#include "until.h"

#include "log.h"

/*
 * Class:     com_example_huanqi_myapplication_DataProvider
 * Method:    add
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL Java_com_example_huanqi_myapplication_DataProvider_add
        (JNIEnv * env , jobject object , jint x , jint y)
{

    LOGD("x = %d",x);
    LOGD("y = %d",y);

    return x + y ;

}

/*
 * Class:     com_example_huanqi_myapplication_DataProvider
 * Method:    sayHello
 * Signature: (Ljava/lang/String;)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_example_huanqi_myapplication_DataProvider_sayHello
        (JNIEnv * env, jobject object, jstring message)
{
    /*
     * java String  is not supported  in C language
     * we need to translate the String type
     */

    char *msg = Jstring2CStr(env, message);
    LOGD("message = %s" , msg);
    strcat(msg , "Hello world");
    LOGD("new message = %s", msg);
    return (*env) -> NewStringUTF(env,msg);

}
1
**_log.h_** 公用log打印函数
#include <android/log.h>

#define LOG_TAG "System.out.c"  // 定义log的标签
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)  //宏定义日志打印,等级为 info
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__) //宏定义日志打印,等级为 debug
1
**_until.h_** 公用数据转换函数
//
// Created by HuAnqi on 2015-09-25.
//
#include <stdio.h>
#include <jni.h>
#ifndef MYAPPLICATION_UNTIL_H
#define MYAPPLICATION_UNTIL_H

#endif //MYAPPLICATION_UNTIL_H
#ifdef __cplusplus
extern "C" {
#endif
    JNIEXPORT char*   Jstring2CStr(JNIEnv*   env,   jstring   jstr);
#ifdef __cplusplus
}
#endif
1
**_until.c_**
//
// Created by HuAnqi on 2015-09-25.
//

#include "until.h"
#include <malloc.h>

/**
 * 返回值 char* 这个代表char数组的首地址
 *  Jstring2CStr 把java中的jstring的类型转化成一个c语言中的char 字符串
 */
char*   Jstring2CStr(JNIEnv*   env,   jstring   jstr)
{
    char*   rtn   =   NULL;
    jclass   clsstring   =   (*env)->FindClass(env,"java/lang/String"); //String
    jstring   strencode   =   (*env)->NewStringUTF(env,"GB2312");  // 得到一个java字符串 "GB2312"
    jmethodID   mid   =   (*env)->GetMethodID(env,clsstring,   "getBytes",   "(Ljava/lang/String;)[B"); //[ String.getBytes("gb2312");
    jbyteArray   barr=   (jbyteArray)(*env)->CallObjectMethod(env,jstr,mid,strencode); // String .getByte("GB2312");
    jsize   alen   =   (*env)->GetArrayLength(env,barr); // byte数组的长度
    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);  //Release memory
    return rtn;
}
1
2
3
4
### 3. 编译C代码

- 编写Android.mk 交叉编译
**_Android.mk_**
#交叉编译 linux  makefile的语法子集
LOCAL_PATH := $(call my-dir)   #get "Android.mk" local path
include $(CLEAR_VARS)          #init varibles

LOCAL_MODULE    := datatest    # output filename   libHello.so
LOCAL_SRC_FILES := data_test.c until.c   # the file to compiler

# add dependency libs
LOCAL_LDLIBS    += -llog   # add liblog.so

include $(BUILD_SHARED_LIBRARY)
1
将C代码编译为可以在arm平台上运行的库文件。这里需要使用cygwin,cd到jni目录中,输入`ndk-build`命令即可。
$ ndk-build
[armeabi] Compile thumb  : datatest <= data_test.c
[armeabi] SharedLibrary  : libdatatest.so
[armeabi] Install        : libdatatest.so => libs/armeabi/libdatatest.so
1
2
3
- 将so文件放在指定的工程目录下

android studio 对于ndk的支持还只是实验阶段,所以需要在`gradle.properties`文件中添加:
android.useDeprecatedNdk=true
1
2
3
4
5
6
7
8
然后将so文件放在`src/main/jniLibs/armeabi`目录下,即可被项目找到。

0x02 运行调试
---------

- 用Genymotion调试运行

在运行时,记得在使用的地方加载运行库:
static {
       //load modules  Hello
       System.loadLibrary("datatest");
   }
1
2
3
4
5
6

然后点击项目运行,可以测试运行得到: ![](https://i.imgur.com/55JYfDw.png)

- adb shell查看调试

配置好sdk路径之后,可以使用adb对设备进行调试,权限是root。
adb devices

List of devices attached
192.168.56.101:5555     device

adb shell
root@vbox86p:/ # ls
acct
cache
...
1
2
3
4
5
6
7
8
9
10
11
12
13
 
- 编辑log窗口,查看日志

像eclipse一样,在输出的日志中,进行过滤操作。这里配置标签为`System.out.c`


![](https://i.imgur.com/34wDiIF.png)

0x03 git版本控制
------------

- 先按照之前初始化的步骤将ssh key添加到github中。 [https://www.angelwhu.com/blog/?p=346](https://www.angelwhu.com/blog/?p=346)
- 在根目录下,在项目文件夹下,根目录初始化
git init
git add README.md
git commit -m “first commit”
git remote add origin git@github.com:angelwhu/android_ndk.git
git push -u origin master

```

可能需要登录github,先创建android ndk仓库。

  • 接着配置Android Studio Git 插件

http://blog.csdn.net/hello0370/article/details/41899207

文章作者: angelwhu
文章链接: https://www.angelwhu.com/paper/2015/10/13/android-ndk-development-2-basic-programming/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 angelwhu_blog