NDK:ndk-build命令构建so包

本文详细记录了从C/C++源码到SO包的编译全流程,并将其应用于Android项目中。涵盖JNI编程、mk脚本编写及ndk-build构建流程,适合初学者快速上手。

本文记录了自己从写C/C++源码到so的全过程,并应用到项目中。本篇文章不会深入NDK编程,但是我会把涉及到的内容简单说下,在开始之前,先把整个架构简单说下,对于理解下面的东西要容易点。
整个过程如下:
java->JNI->C/C++
从这个关系可以看出来,JNI就是native和java的桥梁,流程如下:

  • 1.java代码。
  • 2.实现功能的源码(C/C++)
  • 3.实现JNI代码。
  • 4.编写.mk脚本。
  • 5.采用ndk构建命令构建

我记录了自己编译so包的整个过程,如何写构建脚本,如何构建。当然你需要有一定的C/C++基础,需要了解一下JNI,还需要了解.mk文件编写。我会用一个加减乘除为例来讲述整个过程,写此文有两个目的,其一,让自己理清整个流程,其二,希望能能给有需要的一点启发。

准备工作

首当其冲就是NDK环境的配置,自己百度一下环境配置,很容易的。

ndk-build 构建方式

  • 1.ndk-build 的配置工作
    配置路径网上有很多教程,或者直接从Android Studio下载,可以参考一步步编译Android so包
  • 2.mk文件的预备知识
    本文主要从Android.mk和Application.mk两个文件讲,因为我参考的项目中提供了这两个文件,方便我们直接构建。下面两个文件的内容
  • Android.mk具体内容
LOCAL_PATH := $(call my-dir)//表示当前目录下

include $(CLEAR_VARS)//

LOCAL_MODULE := p7zipUtil//so包的名称

LOCAL_SRC_FILES := Calculator.cpp//源文件,要是有多个文件,通过/链接

LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog

include $(BUILD_SHARED_LIBRARY) //构建共享库,当然还有其他几种方式,如可执行的库,Android需要的就是共享库
  • Application.mk具体内容
# The ARMv7 is significanly faster due to the use of the hardware FPU
APP_ABI := armeabi # armeabi-v7a//设置CPU架构
#APP_PLATFORM := android-8//设置Android版本

开始编译

按照上面的构建流程来,实现加减乘除功能
1.编写 Java代码:
说实话这个没什么好讲的,注意两点:

  • 1.如何加载so包
    通过一个static块实现加载,路径写的时候,写的是模块名,而不是文件名字。生成的so文件的名字一定是lib+模块名+.so组成,但是在System.loadLibrary(“模块名”)。
  • 2.如何写函数
    这个函数与普通的函数多个native关键字。

详见代码

public class Zip7Utils {
	static {
        System.loadLibrary("p7zipUtil");
    }
    public static native int add(int var0, int var1);//加法

    public static native int minus(int var0, int var1);//减法

    public static native int multiple(int var0, int var1);//乘法

    public static native int divide(int var0, int var1);//除法
}

2.生成JNI代码
通过javac -encoding utf8 -h .\jni path这个命令生产**.h**文件,这个命令执行时,注意切换目录
如下:

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

#ifndef _Included_com_haochen_a7ziputile_Zip7Utils
#define _Included_com_haochen_a7ziputile_Zip7Utils
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_haochen_a7ziputile_Zip7Utils
 * Method:    add
 * Signature: (FF)F
 */
JNIEXPORT jfloat JNICALL Java_com_haochen_a7ziputile_Zip7Utils_add
  (JNIEnv *, jclass, jfloat, jfloat);

/*
 * Class:     com_haochen_a7ziputile_Zip7Utils
 * Method:    minus
 * Signature: (FF)F
 */
JNIEXPORT jfloat JNICALL Java_com_haochen_a7ziputile_Zip7Utils_minus
  (JNIEnv *, jclass, jfloat, jfloat);

/*
 * Class:     com_haochen_a7ziputile_Zip7Utils
 * Method:    multiple
 * Signature: (FF)F
 */
JNIEXPORT jfloat JNICALL Java_com_haochen_a7ziputile_Zip7Utils_multiple
  (JNIEnv *, jclass, jfloat, jfloat);

/*
 * Class:     com_haochen_a7ziputile_Zip7Utils
 * Method:    divide
 * Signature: (FF)F
 */
JNIEXPORT jfloat JNICALL Java_com_haochen_a7ziputile_Zip7Utils_divide
  (JNIEnv *, jclass, jfloat, jfloat);

#ifdef __cplusplus
}
#endif
#endif

可能对于初学者来说,为啥要生成.h文件呢,其实这就是JNI了,需要编写native代码时引入。可能看到一脸懵,什么jfloat,什么jclass,我们先了解一下这些JNI基础知识。

  • JNI基本类型
    java中以基本类型,同样JNI也有,只是和java中略有区别。
java基本类型JNI基本类型
Booleanjboolean
bytejbyte
charjchar
shortjshort
intjint
longjlong
floatjfloat
doublejdouble

看完两者的对比关系,有没发现一个规律,只需要在java基础类型前面加上一个j,就变成了JNI基础类型。再回头看之前生成的代码是不是有点眉目了。

除了基础类型之外,我们还要了解一下引用类型:
在这里插入图片描述

3.编码C++文件
生成.h文件之后,我们才真正开始编写c++文件,实现具体功能如下:

#include <jni.h>
#include "com_haochen_a7ziputile_Zip7Utils.h"

JNIEXPORT jfloat JNICALL Java_com_haochen_a7ziputile_Zip7Utils_add(JNIEnv *env, jclass clazz, jfloat add1, jfloat add2) {
    return add1 + add2;
}

JNIEXPORT jfloat JNICALL Java_com_haochen_a7ziputile_Zip7Utils_minus(JNIEnv *env, jclass clazz, jfloat m1, jfloat m2) {
    return m1 - m2;
}

JNIEXPORT jfloat JNICALL Java_com_haochen_a7ziputile_Zip7Utils_multiple(JNIEnv *env, jclass clazz, jfloat p1, jfloat p2) {
    return p1 * p2;
}

JNIEXPORT jfloat JNICALL Java_com_haochen_a7ziputile_Zip7Utils_divide(JNIEnv *env, jclass clazz, jfloat d1, jfloat d2) {
    if (d2 == 0) {
        return -1;
    } else {
        return d1 / d2;
    }
}

到此我们已经将C++ 代码写完,是不是很简单,我们将要进入构建脚本编写阶段。

4.编写构建脚本
在main文件夹下新建一个jni文件夹,切记名字一定要是jni,否则报错,把上面两个文件放入进去。
我的项目工程的文件的目录如下:
在这里插入图片描述
Android.mk内容:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE    := p7zipUtil
LOCAL_SRC_FILES := Calculator.cpp//源文件,要是有多个文件,通过/链接
LOCAL_LDLIBS :=  -L$(SYSROOT)/usr/lib -llog
include $(BUILD_SHARED_LIBRARY)//一定要是这个

Application.mk内容:

# The ARMv7 is significanly faster due to the use of the hardware FPU
APP_ABI := armeabi # armeabi-v7a
#APP_PLATFORM := android-8

到此构建脚本算是写完了。

5.用构建命令构建
从控制台切换到jni目录下,也就是包含Android.mk和Application.mk的目录,执行ndk-build命令,成功后可以在libs文件夹下找到。

进入命令行界面,切换到jni目录下,键入命令ndk-build,如下图:
命令行
若是代码没有问题,则会出现上面的结果。构建成功后会生成两个目录:
目录
libs目录下,就是你构建好的so文件,直接可以被apk代码所使用的。
obj这个是中间目录,对于我们来说,不用关心。

总结

编译第三方源码的感悟,我一开始就在想,如何使用源码?源码如何被Android使用?如何编译源码?如何编译源码这篇文章能给你一个大致的了解,但是如何使用源码?主要还是依靠开源项目的文档,会提供如何使用源码的教程
源码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

技术人Howzit

钱不钱的无所谓,这是一种鼓励!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值