本地内容主要简介如下:
experimental-plugin
插件编译本篇文章大纲.png
操作系统为
操作系统.png
Android环境为:
Android环境.png
NDK环境
NDK环境.png
模拟器为
模拟器.png
具体流程如下:
首先在Android Studio创建一个Android项目,包名为gebilaolitou.ndkdemo
然后创建一个class
为NDKTools
代码如下:
package gebilaolitou.ndkdemo;
public class NDKTools {
public static native String getStringFromNDK();
}
MainActivity对应的xml中的textview
添加id
如下:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="gebilaolitou.ndkdemo.MainActivity">
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
然后修改MainActivity,在里面调用NDKTools
的getStringFromNDK()
方法。
package gebilaolitou.ndkdemo;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String text = NDKTools.getStringFromNDK();
Log.i("gebilaolitou","text="+text);
((TextView)findViewById(R.id.tv)).setText(text);
}
}
在Android Studio中点击Build
中的Make Project
或者Rebuild Project
进行编译来获取中间文件。如下图
编译.png
编译完成后,我们就可以获取class文件如下图
classes文件.png
点击Android Studio下面的Terminal,然后跳到NDKDemo/app/build/intermediates/classes/debug
下(其中NDKDemo为是项目的根目录),在Terminal执行pwd
确认目录。
在NDKDemo/app/build/intermediates/classes/debug
下执行下面的命令javah -jni gebilaolitou.ndkdemo.NDKTools
。如果没有问题,则会在NDKDemo/app/build/intermediates/classes/debug
下面生成gebilaolitou_ndkdemo_NDKTools.h
文件。如下图
头文件.png
其内容如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class gebilaolitou_ndkdemo_NDKTools */
#ifndef _Included_gebilaolitou_ndkdemo_NDKTools
#define _Included_gebilaolitou_ndkdemo_NDKTools
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: gebilaolitou_ndkdemo_NDKTools
* Method: getStringFromNDK
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_gebilaolitou_ndkdemo_NDKTools_getStringFromNDK
(JNIEnv *, jclass);
#ifdef __cplusplus
}
#endif
#endif
如下图
头文件内容.png
在工程main目录下创建一个名字为jni目录,然后将刚才的.h文件剪切过来。在jni目录下新建一个c文件。命名为ndkdemotest.c
。此时项目目录如下:
jnipng
将ndkdemotest.c协商如下内容
#include "gebilaolitou_ndkdemo_NDKTools.h"
JNIEXPORT jstring JNICALL Java_gebilaolitou_ndkdemo_NDKTools_getStringFromNDK
(JNIEnv *env, jobject obj){
return (*env)->NewStringUTF(env,"Hellow World,这是隔壁老李头的NDK的第一行代码");
}
ndkdemotest.png
内容不多,就是两部分,第一部分就是 添加gebilaolitou_ndkdemo_NDKTools.h
头文件,然后就是具体实现Java_gebilaolitou_ndkdemo_NDKTools_getStringFromNDK
函数
同样在jni目录下,添加一个Android.mk文件,其目录结构如下:
添加Android.mk文件.png
同样在Android.mk文件里面编写如下内容
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := ndkdemotest-jni
LOCAL_SRC_FILES := ndkdemotest.c
include $(BUILD_SHARED_LIBRARY)
Android.mk内容.png
关于Android.mk语言后面会单独写一篇文章进行讲解,这里重点说上面代码的内容
LOCAL_PATH := $(call my-dir)
:每个Android.mk文件必须以定义开始。它用于在开发tree中查找源文件。宏my-dir
则由Build System 提供。返回包含Android.mk目录路径。include $(CLEAR_VARS)
:CLEAR_VARS
变量由Build System提供。并指向一个指定的GNU Makefile,由它负责清理很多LOCAL_xxx。例如LOCAL_MODULE,LOCAL_SRC_FILES,LOCAL_STATIC_LIBRARIES等等。但不是清理LOCAL_PATH。这个清理是必须的,因为所有的编译控制文件由同一个GNU Make解析和执行,其变量是全局的。所以清理后才能便面相互影响。LOCAL_MODULE := ndkdemotest-jni
:LOCAL_MODULE模块必须定义,以表示Android.mk中的每一个模块。名字必须唯一且不包含空格。Build System 会自动添加适当的前缀和后缀。例如,demo,要生成动态库,则生成libdemo.so。但请注意:如果模块名字被定义为libabd,则生成libabc.so。不再添加前缀。LOCAL_SRC_FILES := ndkdemotest.c
:这行代码表示将要打包的C/C++源码。不必列出头文件,build System 会自动帮我们找出依赖文件。缺省的C++ 源码的扩展名为.cpp。include $(BUILD_SHARED_LIBRARY)
:BUILD_SHARED_LIBRARY
是Build System提供的一个变量,指向一个GUN Makefile Script。它负责收集自从上次调用include $(CLEAR_VARS)
后的所有LOCAL_xxxxinx。并决定编译什么类型 BUILD_STATIC_LIBRARY
:编译为静态库BUILD_SHARED_LIBRARY
:编译为动态库BUILD_EXECUTABLE
:编译为Native C 可执行程序BUILD_PREBUILT
:该模块已经预先编译PS:这里不编写Android.mk会提示如下问题:
Error:Execution failed for task ':app:compileDebugNdk'.
> Error: Flag android.useDeprecatedNdk is no longer supported and will be removed in the next version of Android Studio. Please switch to a supported build system.
Consider using CMake or ndk-build integration. For more information, go to:
https://d.android.com/r/studio-ui/add-native-code.html#ndkCompile
To get started, you can use the sample ndk-build script the Android
plugin generated for you at:
/Users/gebilaolitou/AndroidStudioProjects/JNIDemo/app/build/intermediates/ndk/debug/Android.mk
Alternatively, you can use the experimental plugin:
https://developer.android.com/r/tools/experimental-plugin.html
To continue using the deprecated NDK compile for another 60 days, set
android.deprecatedNdkCompileLease=1523001628930 in gradle.properties
全是英文,简单的翻译下如下:
错误:执行app:compileDebugNdk任务失败 错误:不再支持android.useDeprecatedNdk标志,并且将会在未来的Android Studio版本中删除这个标志。请切换到CMake构建系统或者ndk-build中集成。更多的信息请参考
https://d.android.com/r/studio-ui/add-native-code.html#ndkCompile
。您可以使用Android的示例ndk-build脚本在以下位置生成的插件:/Users/gebilaolitou/AndroidStudioProjects/JNIDemo/app/build/intermediates/ndk/debug/Android.mk
。另外,你也可以使用实验性插件https://developer.android.com/r/tools/experimental-plugin.html 如果你还想继续再使用已经被弃用的NDK编译60天,你需要再gradle.properties中设置android.deprecatedNdkCompileLease=1523001628930
因为以上原因,我所以我们需要设置Android.mk
首先检查local.properties
文件中是否有NDK路径,如果有没有NDK路径,则添加NDK路径,比如我的如下:
ndk.dir=/Users/debilaolitouLibrary/Android/sdk/ndk-bundle
sdk.dir=/Users/debilaolitouLibrary/Library/Android/sdk
其次修改app module目录下的build.gradle中的内容,如下:
apply plugin: 'com.android.application'
android {
compileSdkVersion 26
defaultConfig {
applicationId "gebilaolitou.ndkdemo"
minSdkVersion 19
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
ndk{
moduleName "ndkdemotest-jni"
abiFilters "armeabi", "armeabi-v7a", "x86"
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
externalNativeBuild {
ndkBuild {
path 'src/main/jni/Android.mk'
}
}
sourceSets.main {
jni.srcDirs = []
jniLibs.srcDirs = ['src/main/jniLibs']
}
}
}
这样就有了so文件(此时还没生成so文件)
最后在NDKTools
类中添加静态初始化代码,如下:
public class NDKTools {
static {
System.loadLibrary("ndkdemotest-jni");
}
public static native String getStringFromNDK();
}
最后run一下即可,如下图
显示.png
有的同学在运行的时候,会报如下错误:
Error:Execution failed for task ':app:compileDebugNdk'.
> Error: Your project contains C++ files but it is not using a supported native build system.
Consider using CMake or ndk-build integration. For more information, go to:
https://d.android.com/r/studio-ui/add-native-code.html
Alternatively, you can use the experimental plugin:
https://developer.android.com/r/tools/experimental-plugin.html
首先把检查你项目中gradle.properties文件后面加上一句
Android.useDeprecatedNdk=true
大家可能会有疑问,那so去哪里了,我们平时使用第三方的sdk的so的时候,会要粘贴复制到项目里面,而我们上所述整个过程,并没有出现.so这个文件,那么这个.so去哪里了?
其实Android Studio自动帮我们把so放到apk里面,如果我们想找也能找到,如下图:
so文件的位置.png
上面这套方式是传统的Android Studio的模式,那有没有更简单的方式,是有的,那下面我们就继续来看下
SDK Tools.png
上面看到第三个 CMake
我本地没有,所以我要进行安装
Include C++ Support
复选框。在向导的 Configure your new project 部分,选中 Include C++ Support 复选框。 如下图
勾选.png
这里有个坑,就是有好多同学说我没有这个
Include C++ Support
复选框,这是因为Android Studio设计的的"bug",你把这个对话框进行拉大,就出现了,因为一般的Android 项目用不到,所以在设计这个的时候,如果不特意的拉大,就选择性的"隐藏"了,太JB坑了。
然后一直下一步,直到Customize C++ Support
部分
Customize C++ Support
的自定义项目如下:
模式.png
里面有个三个项目
CMake
设置。-fexceptions
标志添加到模块级build.gradle
文件的cppFlags
中,Gradle会将其传递到CMake。-frtti
标志添加到模块级build.gradle
文件的cppFlags中,Gradle会将其传递到CMake。最后点击 Finish
。
在Android Studio 完成新项目的创建后,请从IDE左侧打开Project 矿口并选择Android 视图。如下图所示,Android Studio 将添加cpp
和External Build Files 组
:
Android模式.png
该图为开发者的原生源文件和外部构建脚本的Android 视图组。
PS:(此视图无法反应磁盘上的实际文件层次结构,而是将相似文件分到一组中,简化项目导航)。如果为Project
模式则如下:
Project模式.png
那我们简单介绍下这两个多出来的文件夹:
native-lib.cpp
,并将其置于应用模块src/main/cpp/
目录中。这个示例代码提供了一个简单的C++函数stringFromJNI()
,此函数可以返回字符串“Hello from C++” build.gradle
文件指示Gradle构建应用一样,CMake和ndk-build需要一个构建脚本来了解如何构原生库。对于新项目,Android Studio 会创建一个CMake 构建脚本CMakeLists.txt
,并将其置于模块根目录中。我们来直接 run一下这个项目,看下结果
结果1.png
native-lib.cpp
这时候我们修改下native-lib.cpp
,native-lib.cpp
内容如下:
native-lib.cpp内容.png
再直接run一下项目,看下结果。如下:
结果2.png
我们看到对应的文字已经修改了
我们看打了,我们什么都没做,就自动实现了C++的实现,它的背后原理是什么那?我们大家就思考一下?
它既然可以跑起来,一定有一个入口,那这个入口在哪里那?
~~~~~~~~~~~~~~~~~~~~~~~~~~分隔符~~~~~~~~~~~~~~~~~~~~
先和大家说下我是怎么想象的,首先我们在点击Android Studio中的run
按钮的时候,它是执行Gradle来进行打包的,所以说关于CMake的是怎么植入进去的,一定在项目的build.gradle
,有相应的入口。
通过上面的思想,我们能举一反三得到什么?对的,就是类似于这种操作,一般都是在
build.gradle
里面实现的,因为在目前Android Studio就是通过Gradle是实现的
那我们就来看下它的build.gradle
里面的代码,如下:
apply plugin: 'com.android.application'
android {
compileSdkVersion 26
defaultConfig {
applicationId "gebilaolitou.cmakendkdemo"
minSdkVersion 23
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
cppFlags ""
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:26.1.0'
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
}
和我们平时搭建的项目差不多,就是多出来一块内容,externalNativeBuild。那这里我们重点说下externalNativeBuild
我们在build.gradle
里面看到,有两个地方用到了externalNativeBuild
,一个是在defaultConfig
里面,是一个是在defaultConfig
外面。
defaultConfig
外面的externalNativeBuild
里面的cmake
指明了CMakeList.txt
的路径(在本项目下,和是build.gradle
在同一个目录里面)。defaultConfig
里面的externalNativeBuild
里面的cmake
主要填写的是CMake
的命令参数。即由arguments中的参数最后转化成一个可执行的CMake的命令,可以在defaultConfig外面的 externalNativeBuild - cmake,指明了 CMakeList.txt 的路径;
defaultConfig 里面的 externalNativeBuild - cmake,主要填写 CMake 的命令参数。即由 arguments 中的参数最后转化成一个可执行的 CMake 的命令,可以在 app/externalNativeBuild/cmake/debug/{abi}/cmake_build_command.txt
中查到。如下
路径位置如下图:
路径.png
内容如下:
arguments :
-H/Users/gebilaolitou/Desktop/codeLib/CMakeNDKDemo/app
-B/Users/gebilaolitou/Desktop/codeLib/CMakeNDKDemo/app/.externalNativeBuild/cmake/debug/x86
-DANDROID_ABI=x86
-DANDROID_PLATFORM=android-23
-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=/Users/gebilaolitou/Desktop/codeLib/CMakeNDKDemo/app/build/intermediates/cmake/debug/obj/x86
-DCMAKE_BUILD_TYPE=Debug
-DANDROID_NDK=/Users/gebilaolitou/Library/Android/sdk/ndk-bundle
-DCMAKE_CXX_FLAGS=
-DCMAKE_TOOLCHAIN_FILE=/Users/gebilaolitou/Library/Android/sdk/ndk-bundle/build/cmake/android.toolchain.cmake
-DCMAKE_MAKE_PROGRAM=/Users/gebilaolitou/Library/Android/sdk/cmake/3.6.4111459/bin/ninja
-GAndroid Gradle - Ninja
jvmArgs :
更多的可以填写的命令参数和含义可以参见Android NDK-CMake文档
ok上面既然提到了CMakeLists.txt
,那我们就来看下CMakeLists.txt
CMakeLists.txt
这个文件主要定义了哪些文件需要编译,以及和其他库的关系等,那让我们来看下我们项目中的CMakeLists.txt
的内容
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.4.1)
# 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).
src/main/cpp/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} )
上面很多是注释,我们除去注释来个"精简干练版"的如下:
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} )
CMakeLists.txt
我们看到这里主要是分为四个部分,下面我们就依次来看下
动态的
还是静态的
这其实是一个最基础的CMakeLists.txt
,其实CMakeLists.txt
里面可以非常强大,比如自定义命令、查找文件、头文件包含、设置变量等等。这里推荐CMake的官网文档,不过是英文的,不好阅读,大家可以参考中文的CMake手册
上面分析完毕CMakeLists.txt
,我们就大致的知道了CMake整体的构建流程,那我们就来看下
CMakeLists.txt
native-lib.cpp
编译到共享的对象库中,并命名为 libnative-lib.so
,Gradle 随后会将其打包到APK中MainActivity
会使用System.loadLibrary()
加载原生库。应用就是可以使用库的原生函数stringFromJNI()
。PS:这里注意一点就是:Instant Run
与使用原生的项目不兼容
如果想看Gradle是否将原生库打包到APK中,可以使用Analyze APK
来检测。
我们在做日常需求的时候,往往会遇到一个问题,即在已有的项目中,添加C库,这样就不能通过上面的
创建
流程,来使用CMake。那怎么办?
其实没关系的,CMake也提供这样的功能的,现在我们就回到上面的第一个demo中,删除和NDK的有关的所有代码,删除后其目录如下:
新目录.png
即在main
目录下新建一个目录,我们就叫cpp
好了。然后在该目录下创建一个C++ Source File(右键点击您刚刚创建的目录,然后选择 New > C/C++ Source File)。我们将其命名为native-lib。
创建后,目录如下:
创建源文件.png
因为目前这个项目没有CMake的构建脚本,所以咱们需要自行创建一个并包含适当的CMake命令。CMake构建脚本是一个纯文本的文件,而且这个名字必须是是CMakeLists.txt
要常创建一个可以用作CMake构建脚本的纯文本文件,请按以下步骤操作:
New
——> File
。
PS:这个位置不是不固定的,位置可以随意,但是配置构建脚本时,需要将这个位置写入构建脚本
CMakeLists.txt
作为文件并点击OK
创建后,目录如下:
CMakeLists.txt.png
这块上面讲解了过了,就不详细说明了,内容如下:
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( # Defines the name of the path variable that stores the
# location of the NDK library.
log-lib
# Specifies the name of the NDK library that
# CMake needs to locate.
log )
target_link_libraries( # Specifies the target library.
native-lib
# Links the log library to the target library.
${log-lib} )
要将Gradle关联到原生库,需要提供一个指向CMake或ndk-build 脚本文件的路径。在构建应用时,Gradle会以依赖项的形式运行CMake或ndk-build,并将共享的库打包到APK中。Gradle还是用构建脚本来了解将那些文件添加到Android 项目中。
如果原生文件还没有构建脚本,需要创建CMake构建脚本
关于 关联到原生库有两种方式,一种是通过Android Studio,一种是手动,其实其背后的东西是一致的,我们就一一来说明
Project
窗格 并选择 Android
视图Link C++ Project with Gradle
。如下图OK
。Link C++ Project with Gradle.png
要手动配置Gradle 以关联到原生库,需要将externalNativeBuild{}
块添加到模块级 build.gradle
文件中,并使用cmake {}
对其进行配置
代码如下:
apply plugin: 'com.android.application'
android {
compileSdkVersion 26
defaultConfig {
applicationId "gebilaolitou.ndkdemo"
minSdkVersion 19
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
externalNativeBuild {
cmake {
path 'CMakeLists.txt'
}
}
}
这块很简单,内容如下:
#include <jni.h>
#include <string>
extern "C"
JNIEXPORT jstring
JNICALL
Java_gebilaolitou_ndkdemo_NDKTools_getStringFromNDK(
JNIEnv *env, jobject /* this */) {
std::string hello = "(*^__^*) 嘻嘻……~Hello from C++ 隔壁老李头";
return env->NewStringUTF(hello.c_str());
}
然后在NDKTools.java
添加引用,如下:
package gebilaolitou.ndkdemo;
public class NDKTools {
static {
System.loadLibrary("native-lib");
}
public static native String getStringFromNDK();
}
然后直接运行,即可,结果如下:
结果3.png
我们在使用NDK开发有件比较麻烦的事情,就是编写Android.mk和Application.mk,儿Android Studio的插件gradle-experimental就是用来解决这个问题的。所以使用gradle-experimental插件可以不用再编写.mk文件情况下进行NDK开发。
gradle-experimental是Android Studio的一个实验性的项目,是基于gradle的一个插件,主要用来自动化NDK的配置实现,无需自己编写Android.mk和Android.mk,对于调试NDK项目也更加友好,不过现在已经 不支持 ,详细请看Experimental Plugin User Guide
Note to experimental Android plugin users: The experimental plugin will no longer be supported after version 0.11.0 (released October 25, 2017). That's because the experimental plugin is designed around a Software Component Model that Gradle announced they will no longer support (read their blog post here). Gradle has backported many features from the component model, which are now available with Android plugin 3.0.0, such as variant-aware dependency resolution, and api and implementation dependency configurations. Gradle is working on backporting built-in support for compiling C/C++ code, and the Android plugin will integrate that support when it becomes available. Until then, you can either keep using experimental plugin 0.11.0 with Android Studio 3.0 or later, or migrate to Android Studio's support for using external native build tools.
简单翻译下如下:
对使用
experimental
Android插件的用户请注意:自2017年10月25日发布的0.11.0后,我们将不再支持experimental
插件了。因为Gradle不再支持这个依靠软件组件模型设计experimental
插件了(通过他们的博客)。在Gradle Android插件的3.0.0版本,现在已经支持组建模型中的许多功能。例如variant-aware dependency resolution
和api and implementation dependency configurations
。Gradle现在支持编译C/C++代码的内置支持,并且Android插件再可用时集成该支持。在此之间,您可以继续使用Android Studio3.0或者更高版本的experimental
插件,或者使用Android Studio支持的外部原生构建工具。