本文介绍学习和开发移动端音频视频开发必备技能的NDK开发以及基于Linux Pthread 实现生产者和消费者模式的笔记。从Android Stuido下NDK开发环境和基础设置开始。
NDK版本:r23
Android Stuido版本 4.4
开发语言: c++ Kotlin
新建Android 工程,模版选择Native c++
填写包名等基本信息
选择基本C++ standard为工具链默认,这里也用不到c++新标准
完成后就会新建项目,等待Gradle完成sync就可以继续了。
不同于纯SDK项目,NDK项目多了cpp的Soure目录,其中有cpp文件和CMakeLists.txt,可以看出android studio是通过CMake来实现编译C++代码的。这个CMake文件可以用来加入新的cpp source文件,引用三方头文件,链接各种三方c库
cmake_minimum_required(VERSION 3.10.2)
# Declares and names the project.
project("pthread")
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
add_library( # Sets the name of the library.
native-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
native-lib.cpp )
# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.
target_link_libraries( # Specifies the target library.
native-lib
# Links the target library to the log library
# included in the NDK.
${log-lib} )
之所以能编译,可以看下build.gradle中多了如下代码
# defaultconfig中:
externalNativeBuild {
cmake {
cppFlags ''
}
}
## android {}
externalNativeBuild {
cmake {
path file('src/main/cpp/CMakeLists.txt')
version '3.10.2'
}
}
Kotlin作为一个JVM下运行的语言,也具备有Java一样和C++互操作的能力,也是遵循JNI规范的,具体为另外说明Kotlin和c++的交互,这里简单的看下
// 类似Java 的natvie 关键字,kotilin是用external表示外部的原生方法,就是c++方法
external fun nativeThread()
// companion object 修饰为伴生对象,伴生对象在类中只能存在一个,类似于java中的静态方法 Java 中使用类访问静态成员,静态方法。
companion object {
// Used to load the 'native-lib' library on application startup.
init {
// 加载c库,so文件,系统会根据架构区加载,这的名字就是cmake中的add_library的名字,如果是引用一句存在的c库,就是libxxx.so
System.loadLibrary("native-lib")
}
}
看下c++文件,按照JNI规范来实现
// 必须extern “C” 否则c++编译产生的符号会不一样造成不能调用或者被其他c库链接
// 可以看出jni规范,这里函数的命名就是包名+类名+方法名,不过就是点换成下划下线,JNIEnv jobct thiz作用另问说明和jvm相关
extern "C"
JNIEXPORT void JNICALL
Java_com_example_pthread_MainActivity_nativeThread(JNIEnv *env, jobject thiz) {
}
pthread是符合可移植操作系统接口的线程规范,Linux自然是实现了,android上c++的多线程方案本质就是基于pthread实现。
表示一个线程,可以通过pthread_create创建
int pthread_create(pthread_t* __pthread_ptr, pthread_attr_t const* __attr, void* (*__start_routine)(void*), void*);
参数2 pthread_attr_t是设置线程的一些属性的,这里不详细说明,可以查询相关文档,没有可以NULL
参数3 是线程真正的实现方法,其实类似Java Runable接口,是一个
void fun(vod test)的函数
参数4 用来传递参数,void* 多个参数可以定义结构体,使用指针的方式传参
pthread_exit(): 用来退出线程,同时每个线程退出必须return,否则会出错
互斥锁,当线程需要竞争访问一个资源时候,需要锁定,可以通过pthread_mutex_init 创建,通过pthread_mutex_lock锁,pthread_mutex_unlock解锁,两个线程访问同一个对象是先获得pthread_mutex_t的继续执行,其他等待释放
条件量,可以通过进行等待,当满足某个条件或者收到某个条件时候放开。pthread_cond_init 创建,pthread_cond_signal发信号,pthread_cond_wait等待并放开互斥锁
思路:两条线程,一个生产者,一个消费者,队列采用stl中的queue对象,pthread_mutex_t pthread_cond_t 实现等待和放开
#include <jni.h>
#include <string>
#include <android/log.h>
#include "pthread.h"
#include "queue"
#include "unistd.h"
// 定义Log
#define LOG_TAG "FromNDK"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
// 生产者进程
pthread_t produceThread;
// 消费者进程
pthread_t consumeThread;
// 条件信号
pthread_cond_t cond;
// 队列
std::queue<int> queue;
// 互斥
pthread_mutex_t mutex;
// 生产者实现
void* producer(void* args) {
LOGI("start producer thread");
while (1) {
// 加锁。因为queue公用
pthread_mutex_lock(&mutex);
for(int i=0;i<10;i++) {
LOGI("start producer produce %d",i);
//推入
queue.push(i);
}
// 放开
pthread_mutex_unlock(&mutex);
// 模拟休眠和慢
usleep(1000* 10 * 1000);
// 开始又开始生产了,通知消费者
pthread_cond_signal(&cond);
}
return 0;
}
// 消费者实现
void *consume(void * args) {
while (1) {
// 加锁。因为queue公用
pthread_mutex_lock(&mutex);
// 队列里又消息,则消费
if(queue.size() > 0) {
LOGI("now consume %d",queue.front());
queue.pop();
} else {
// 队列空了,等条件并放开互斥
pthread_cond_wait(&cond,&mutex);
}
// 放开互斥锁
pthread_mutex_unlock(&mutex);
}
return 0;
}
// JNI给到Kotlin调用
extern "C"
JNIEXPORT void JNICALL
Java_com_example_pthread_MainActivity_nativeThread(JNIEnv *env, jobject thiz) {
// 初始化
pthread_mutex_init(&mutex,NULL);
pthread_cond_init(&cond,NULL);
for(int i=0;i<10;i++) {
queue.push(i);
}
pthread_create(&produceThread,NULL,producer,NULL);
pthread_create(&consumeThread,NULL,consume,NULL);
}
本文为Lokie.Wang原创文章,转载无需和我联系,但请注明来自lokie博客http://lokie.wang