当前位置: 首页 > news >正文

Android NDK学习笔记

前言

Android系统中的应用程序都是用Java开发的。Android NDK使我们能够在android上使用C/C++开发的原生代码。有两个理由使用NDK: 一是合理的重用现有的代码;二是在程序中某些关键的部分提高执行效率。
这里先讲几个符号的约定:
<project> - 你的Android应用程序工程的目录
<ndk> - 你的ndk安装的目录

捷径

这里先扯一句题外话 -- 如果你不需要使用NDK开发,只是需要使用第三方用NDK开发的库,那么你只需要这样做:
把第三方提供的libxxx.so放到你的<project>/libs/armeabi/下, 然后在程序随便什么地方中加入
static {
    System.loadLibray("xxx");
}
就可以使用该库了。

安装NDK

NDK的安装很简单:
1. 首先要将SDK升级至最新,然后下载ndk(可能要翻墙,恩恩)将它解压到某个目录<ndk>下。
2. 运行: <ndk>/build/host-setup.sh
3. 如果成功的话就OK了,如果失败的话检查一下你是不是下载了正确的ndk版本(例如你的操作系统是linux而下载了windows版的ndk).

Java部分

现在我们用一个很简单的例子来说明NDK的使用。我们在eclipse中新建一个android工程,其中:
Project Name:jnitest
Build Target: Android 1.6
Application Name: JNI Test
Package Name: org.eshock.jnitest
Create Activity: JNITest

JNITest.java:

package org.eshock.jnitest;

import android.app.Activity;
import android.os.Bundle;

public class JNITest extends Activity {
    public native int plus (int x, int y);
    
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        int x = plus(1, 2);
        android.util.Log.d("jni", String.valueOf(x));
    }
    
    static {
        System.loadLibrary("mylib");
    }
}

我们只是演示NDK,所以就不要界面了。在这个程序中,我们调用一个c语言编写的plus(int, int)函数来计算1+2的值,然后在log中打印它。这个库的名称叫做mylib。
要使用一个c语言的函数,需要在java中声明:
public native int plus(int x, int y);
这样java编译器就知道这个函数是外部库中实现的。

C部分


接下来我们使用C语言实现这个plus函数。
创建<project>/jni/mylib.c:

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

JNIEXPORT jint JNICALL
Java_org_eshock_jnitest_JNITest_plus( JNIEnv* env,
                                                  jobject thiz,
                                                  jint x,
                                                  jint y )
{
    return x + y;
}

这 里我们看到,mylib.c里的plus函数比java里面的plus函数的签名要复杂了很多,主要是plus前面增加了包名,函数中多了两个参数,以及 所有的int变成了jint。关于c中类型与jni中类型的对应关系,可以参见jni的相关文档,例如core Java II中的最后一章。如果你不想去查阅文档,有一个方法:
首先随便在某个目录下创建一个文件JNITest.java:

public class JNITest {
    public native int plus (int x, int y);
}

然后在命令行下执行:
javac JNITest.java && javah JNITest 
将生成一个文件JNITest.h:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class JNITest */

#ifndef _Included_JNITest
#define _Included_JNITest
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     JNITest
 * Method:    plus
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL Java_JNITest_plus
  (JNIEnv *, jobject, jint, jint);

#ifdef __cplusplus
}
#endif
#endif

这里自动生成了java中native函数对应的签名,你可以在c代码中使用它。(记得修改包名)。

Android.mk和Application.mk

Android.mk是一个makefile,用来告诉NDK需要编译哪些文件,生成哪些模块。我们创建<jni>/Android.mk文件:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := mylib
LOCAL_SRC_FILES := mylib.c

include $(BUILD_SHARED_LIBRARY)

其中LOCAL_PATH表示c源代码文件的位置;LOCAL_MODULE表示生成的共享库的名称;LOCAL_SRC_FILES代表c代码的文件。不需要把头文件列在里面;头文件的依赖关系是ndk自动计算的。

Application.mk的作用是告诉Android SDK需要哪些库文件。有了它,NDK就可以把库放在正确的位置。我们创建<project>/Application.mk:

APP_PROJECT_PATH := $(call my-dir)
APP_MODULES      := mylib

APP_PROJECT_PATH代表android工程所在目录,在本例中它被放在<project>中; APP_MODULES表示工程需要的库,如果有多个以空格分开。

编译共享库

由于ndk规定Application.mk必须放在<ndk>/apps/project_name,我们在<ndk>下创建一个符号连接:
ln -s <project> <ndk>/apps/jnitest
这样,我们就可以开始编译共享库了:
在<ndk>目录下运行:
make APP=jnitest

Android NDK: Building for application 'jnitest'
Compile thumb  : mylib <= apps/jnitest/jni/mylib.c
SharedLibrary  : libmylib.so
Install        : libmylib.so => apps/jnitest/libs/armeabi

可以看到编译成功,并在<project>/libs/armeabi/下生成了libmylib.so。

测试

在运行之前,首先确认一下你的工程里面各个文件都齐全,应该类似于这样:


然后测试,正常的话应该能看到log中有输出3.

from:http://eshock.blogbus.com/logs/61459223.html

相关文章:

  • android常用控件(三)- ProgressBar、ListView
  • 【Android2D游戏开发之二】剖析游戏开发用view还是sarfaceView
  • android中 自定义两个View进行画图,如何让其各自刷新重绘
  • ActivityGroup实现Tab分页标签
  • 自定义Menu(TabMenu)
  • Android上图像处理-倒影,圆角,渐变
  • Activity的任务堆栈学习笔记(一)
  • activity的任务堆栈学习笔记二
  • Android 简史
  • android textview 中超出屏幕宽度的字符 省略号显示
  • 【原创·视频·教程】大话优酷、酷六类视频网站Android端软件开发
  • Android线程相关
  • 横竖屏切换时候activity的生命周期 android:configChanges
  • 以Dialog形式显示Activity
  • Android面试题
  • 002-读书笔记-JavaScript高级程序设计 在HTML中使用JavaScript
  • Angular js 常用指令ng-if、ng-class、ng-option、ng-value、ng-click是如何使用的?
  • C++回声服务器_9-epoll边缘触发模式版本服务器
  • cookie和session
  • CSS 专业技巧
  • docker容器内的网络抓包
  • ECMAScript 6 学习之路 ( 四 ) String 字符串扩展
  • JavaScript创建对象的四种方式
  • PHP 的 SAPI 是个什么东西
  • Redis 中的布隆过滤器
  • 阿里云应用高可用服务公测发布
  • 搭建gitbook 和 访问权限认证
  • 订阅Forge Viewer所有的事件
  • 对话 CTO〡听神策数据 CTO 曹犟描绘数据分析行业的无限可能
  • 回顾 Swift 多平台移植进度 #2
  • 极限编程 (Extreme Programming) - 发布计划 (Release Planning)
  • ------- 计算机网络基础
  • 记一次和乔布斯合作最难忘的经历
  • 蓝海存储开关机注意事项总结
  • 微信如何实现自动跳转到用其他浏览器打开指定页面下载APP
  • 详解移动APP与web APP的区别
  • 一道闭包题引发的思考
  • AI又要和人类“对打”,Deepmind宣布《星战Ⅱ》即将开始 ...
  • UI设计初学者应该如何入门?
  • ###51单片机学习(2)-----如何通过C语言运用延时函数设计LED流水灯
  • #NOIP 2014# day.2 T2 寻找道路
  • (分布式缓存)Redis持久化
  • (亲测有效)解决windows11无法使用1500000波特率的问题
  • (十)【Jmeter】线程(Threads(Users))之jp@gc - Stepping Thread Group (deprecated)
  • (收藏)Git和Repo扫盲——如何取得Android源代码
  • (一)eclipse Dynamic web project 工程目录以及文件路径问题
  • (已解决)什么是vue导航守卫
  • (转)Google的Objective-C编码规范
  • (最优化理论与方法)第二章最优化所需基础知识-第三节:重要凸集举例
  • .Net 8.0 新的变化
  • .Net Framework 4.x 程序到底运行在哪个 CLR 版本之上
  • .net 桌面开发 运行一阵子就自动关闭_聊城旋转门家用价格大约是多少,全自动旋转门,期待合作...
  • .NET开源的一个小而快并且功能强大的 Windows 动态桌面软件 - DreamScene2
  • .NET平台开源项目速览(15)文档数据库RavenDB-介绍与初体验
  • /bin/rm: 参数列表过长"的解决办法