Android NDK 开发总结
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android NDK 开发总结相关的知识,希望对你有一定的参考价值。
一.安装配置环境
1.安装android Studio,下载路径https://developer.android.com/studio/index.html?hl=zh-cn。我下载的是Windows 64位集成Android SDK版本https://dl.google.com/dl/android/studio/install/2.3.2.0/android-studio-bundle-162.3934792-windows.exe?hl=zh-cn,不用单独安装Android SDK。因为公司支持访问Google,所以下载很方便。
2.安装完毕,下载CMake,LLDB和NDK工具
3.如果要使用Git版本控制,需要单独下载安装包,官网地址https://git-scm.com/,安装好后在Android Studio里设置路径
然后可以指定下载路径获取代码,比如kotlin的代码仓库
二.分析新建NDK工程
首先创建一个新工程,选择Include C++Support
打开工程,里面有一个C++输出字符串的例子
路径在D:\\Project\\MyApplication\\app\\src\\main\\cpp\\native-lib.cpp,代码如下
#include <jni.h> #include <string> extern "C" JNIEXPORT jstring JNICALL Java_com_example_myapplication_MainActivity_stringFromJNI( JNIEnv *env, jobject /* this */) { std::string hello = "Hello from C++"; return env->NewStringUTF(hello.c_str()); }
调用的文件是MainActivity.java,路径是D:\\Project\\MyApplication\\app\\src\\main\\java\\com\\example\\myapplication\\MainActivity.java,内容如下
package com.example.myapplication; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.widget.TextView; public class MainActivity extends AppCompatActivity { // Used to load the ‘native-lib‘ library on application startup. static { System.loadLibrary("native-lib"); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Example of a call to a native method TextView tv = (TextView) findViewById(R.id.sample_text); tv.setText(stringFromJNI()); } /** * A native method that is implemented by the ‘native-lib‘ native library, * which is packaged with this application. */ public native String stringFromJNI(); }
按Shift+F9,调试运行,选择真机
在手机端输出如下
默认对这个C++代码是用CMake编译的,CMakeLists.txt的路径在D:\\Project\\MyApplication\\app\\CMakeLists.txt,内容精简如下:
cmake_minimum_required(VERSION 3.4.1) 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). src/main/cpp/native-lib.cpp ) 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 ) target_link_libraries( # Specifies the target library. native-lib # Links the target library to the log library # included in the NDK. ${log-lib} )
其中,#代表行注释。大概意思是设置要编译的库名称是native-lib,源代码路径是src/main/cpp/native-lib.cpp
在模块的gradle文件(注意不是项目的gradle文件,前者的路径是D:\\Project\\MyApplication\\app\\build.gradle,后者路径是D:\\Project\\MyApplication\\build.gradle)里默认配置使用cmake文件编译
三.按照CMake+gradle的方式编写NDK程序
接下来我尝试自己模仿用NDK写一个C++的例子
编写一个包含native方法的java类,如TestNative.java,路径在D:\\Project\\MyApplication\\app\\src\\main\\java\\com\\example\\myapplication\\TestNative.java
代码如下:
package com.example.myapplication; public class TestNative { static { System.loadLibrary("NativeLib"); } public static native String GetStr(); }
其中,NativeLib指定的是这个类要加载的库的名称,稍后我会生成这个库,方法一定要用native修饰,而且为了不定义对象,把这个方法声明为static类型,这样可以直接用TestNative.GetStr()这样的方式访问。
接下来需要生成对应的库,首先要编写C++文件,有两种方式,一种是使用Javah,生成头文件,对应头文件件编写源代码;另一种是根据jni生成函数的格式手动写一个cpp即可,不用头文件,先在实现第一种:
输入CMD使用控制台或者在Android Studio下方的Terminal终端,先cd找到当前TestNative.java文件的位置
D:\\Project\\MyApplication>cd D:\\Project\\MyApplication\\app\\src\\main\\java\\com\\example\\myapplication D:\\Project\\MyApplication\\app\\src\\main\\java\\com\\example\\myapplication>
然后输入javac TestNative.java编译生成class文件
D:\\Project\\MyApplication\\app\\src\\main\\java\\com\\example\\myapplication>javac TestNative.java D:\\Project\\MyApplication\\app\\src\\main\\java\\com\\example\\myapplication>
在D:\\Project\\MyApplication\\app\\src\\main\\java\\com\\example\\myapplication下会生成一个TestNative.class文件
然后cd到最上层包的路径上,这点非常重要,是我反复尝试带包名的类执行java或javah的方法
执行javah,参数是带包名的类
D:\\Project\\MyApplication\\app\\src\\main\\java\\com\\example\\myapplication>cd D:\\Project\\MyApplication\\app\\src\\main\\java D:\\Project\\MyApplication\\app\\src\\main\\java>javah com.example.myapplication.TestNative D:\\Project\\MyApplication\\app\\src\\main\\java>
会在D:\\Project\\MyApplication\\app\\src\\main\\java路径下生成com_example_myapplication_TestNative.h文件,内容如下
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class com_example_myapplication_TestNative */ #ifndef _Included_com_example_myapplication_TestNative #define _Included_com_example_myapplication_TestNative #ifdef __cplusplus extern "C" { #endif /* * Class: com_example_myapplication_TestNative * Method: GetStr * Signature: ()Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_com_example_myapplication_TestNative_GetStr (JNIEnv *, jclass); #ifdef __cplusplus } #endif #endif
按照这个声明编写一个cpp,如如TestNative.cpp,路径是在D:\\Project\\MyApplication\\app\\src\\main\\cpp\\TestNative.cpp,包含该头文件,实现即可
#include <string>
#include "../java/com_example_myapplication_TestNative.h"
extern "C" {
JNIEXPORT jstring JNICALL Java_com_example_myapplication_TestNative_GetStr
(JNIEnv * env, jclass) {
std::string str = "My First Native Test";
return env->NewStringUTF(str.c_str());
}
}
第二种就是根据这样的声明特征,直接写一个TestNative.cpp,注意函数命名方式一定要和用Javah生成的头文件的方法一致,仔细观察记住就行了。这样不用使用javac和javah手动生成class文件和头文件,但要包含jni.h头文件,代码如下
#include <string>
#include <jni.h>
extern "C" {
JNIEXPORT jstring JNICALL Java_com_example_myapplication_TestNative_GetStr
(JNIEnv * env, jclass) {
std::string str = "My First Native Test";
return env->NewStringUTF(str.c_str());
}
}
然后修改CMakeLists.txt文件,如下
cmake_minimum_required(VERSION 3.4.1) add_library( NativeLib SHARED src/main/cpp/TestNative.cpp ) find_library( log-lib log ) target_link_libraries( NativeLib ${log-lib} )
修改调用JNI的文件,如下
package com.example.myapplication; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.widget.TextView; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Example of a call to a native method TextView tv = (TextView) findViewById(R.id.sample_text); tv.setText(TestNative.GetStr()); } }
运行程序,在手机输出
用CMake方式,会在路径D:\\Project\\MyApplication\\app\\build\\intermediates\\cmake\\debug\\obj下生成各种平台版本的so文件,如armeabi中的libNativeLib.so,注意生成库的名称是在设定库名称前加了lib
三.按照ndk-build+gradle的方式编写NDK程序
按照ndk-build的方式编译,需要生成.mk文件
在之前建立工程的基础上
1.首先,要在D:\\Project\\MyApplication\\app\\src\\main下新建jni文件夹,在里面新建Android.mk文件,内容如下
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE = NativeLib LOCAL_SRC_FILES := ../cpp/TestNative.cpp LOCAL_LDLIBS := -landroid -llog -latomic include $(BUILD_SHARED_LIBRARY)
大体意思是指定目标库的名称,源代码的路径,可以对比CMakeLists.txt。
2.接在,在Android.mk的同级目录下,建立Application.mk文件,内容如下
APP_STL := c++_static
即说明使用C++的版本,具体可以参考https://developer.android.com/ndk/guides/cpp-support.html#runtimes
注意在使用C++时要新建Application.mk文件,写上C++版本,直接写在Android.mk不行,否则会提示STL的库找不到
3.修改gradle的编译方式为ndkBuild,内容如下
用ndk-build的方式,会在路径D:\\Project\\MyApplication\\app\\build\\intermediates\\ndkBuild\\debug\\obj\\local\\下生成各种平台版本的so文件
四.设置指定平台版本的库
1.清理掉D:\\Project\\MyApplication\\app\\build下的文件夹
2.在模块的build.gradle文件中添加如下:
就只会生成libNativeLib.so了。
以上是关于Android NDK 开发总结的主要内容,如果未能解决你的问题,请参考以下文章
我的OpenGL学习进阶之旅NDK开发中find_library查找的系统动态库在哪里?