前言
期待了几个月的Android Studio 2.2 版本稳定版昨天终于发布,迫不及待的更新尝试。这次更新内容颇多。我主要关注NDK开发,所以期待这一版本带来的c/c++支持的增强。本文介绍一下这两天折腾新版AS开发和调试NDK的一些经验

一、NDK的支持
Android Studio 2.2 的NDK开发支持 Cmake和ndk-build两种方式。相比与以前的gradle去配置ndk编译目录什么的简直是方便多了。对于老的通过Android.mk文件编译的NDK项目,直接一条配置整个项目就可以被AS支持了。
1.Cmake方式使用AS开发调试NDK
新版的Android Studio支持使用Cmake构建c/c++工程。相比与上一版要通过gradle来配置c/c++工程简单了多,而且也便于老项目的迁移了,下面先来一个简单的例子
1).新建一个空的工程
默认建一个空的工程,只包含一个MainActivity
2).创建jni目录和cpp文件
在左侧Project栏选择app,右键-->New-->Folder-->JNI Folder ,app目录下会多出一个cpp(貌似上一版还是jni目录)的目录。

在cpp目录右键New-->C/C++ Source File ,新建两个文件jni_lib.cpp/h

3).配置jni工程
进入文件管理器,在jni目录下创建CmakeLists.txt文件
# Sets the minimum version of CMake required to build the native
# library. You should either keep the default value or only pass a
# value of 3.4.0 or lower.
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 it for you.
# Gradle automatically packages shared libraries with your APK.
add_library( # Sets the name of the library.
native-lib
# 这个是jni编译生产的so库的名字
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
# Associated headers in the same location as their source
# file are automatically included.
# 要编译的c/c++文件列表 文件路径想对于cmake文件路径
./jni_lib.cpp )
# Searches for a specified prebuilt library and stores the path as a
# variable. Because system libraries are included 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
# 依赖的系统so库
# 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 the
# 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} )
Project栏app右键,Link C++ Project With Gradle 选择CMakeLists.txt文件。或者也可以直接在gradle中加入如下配置即可
externalNativeBuild{
cmake{
path file("src/main/jni/CMakeLists.txt")
}
}
选择Gralde同步之后就完成了jni工程的创建
4).写第一个jni函数
我们可以完成我们的第一个jni函数
jni_lib.h
#ifndef DEMOCMAKE_JNI_LIB_H
#define DEMOCMAKE_JNI_LIB_H
#include "jni.h"
jstring GetStrFromJNI(JNIEnv* env,jobject callObj);
#endif //DEMOCMAKE_JNI_LIB_H
jni_lib.cpp
#include "jni_lib.h"
jstring GetStrFromJNI(JNIEnv *env, jobject callObj)
{
return env->NewStringUTF("String From Jni With c++");
}
光有这些是不够的,java虚拟机是无法直接找到GetStrFromJNI这个函数的,需要通过调用JNI_OnLoad函数,实现JNI函数和java native声明的对接。关于jni的知识可以多Google一下学习。
so库的加载和native函数的声明
MainActivity.java
public class MainActivity extends AppCompatActivity {
static {
/*
加载动态库,动态库加载的时候 JNI_OnLoad函数会被调用
在JNI——OnLoad函数中,Java虚拟机通过函数表的形式将JNI函数和java类中native函数对应起来
*/
System.loadLibrary("native-lib");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView textView= (TextView) findViewById(R.id.text);
textView.setText(GetStrFromJNI());
}
/*
Jni 函数的声明
当调用到此函数时,java虚拟机会通过JNI_OnLoad里注册的函数表找到对应的函数去执行
*/
private native String GetStrFromJNI();
}
JNI_OnLoad的实现
jni_lib.cpp
//
// Created by kang on 9/23/16.
//
#include "jni_lib.h"
#define JNI_AN_MainActivity "com/kang/demondk/MainActivity"
#define METHOD_NUM 1
JNINativeMethod g_nativeMethod[METHOD_NUM]={
{"GetStrFromJNI","()Ljava/lang/String;",(void*)GetStrFromJNI}
};
/*
* 被虚拟机自动调用
*/
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
JNIEnv *env;
if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK)
return JNI_ERR;
jclass jClass = env->FindClass(JNI_AN_MainActivity);
env->RegisterNatives(jClass,g_nativeMethod,METHOD_NUM);
env->DeleteLocalRef(jClass);
return JNI_VERSION_1_6;
}
void JNI_OnUnload(JavaVM* vm, void* reserved) {
JNIEnv *env;
int nJNIVersionOK = vm->GetEnv((void **)&env, JNI_VERSION_1_6) ;
jclass jClass = env->FindClass(JNI_AN_MainActivity);
env->UnregisterNatives(jClass);
env->DeleteLocalRef(jClass);
}
jstring GetStrFromJNI(JNIEnv *env, jobject callObj) {
return env->NewStringUTF("String From Jni With c++");
}
2.ndk-build方式使用AS开发调试NDK
ndk-build方式与cmake方式类似,只需要将cmake文件改写为Android.mk和Appliction.mk文件。在CMakeLists.txt加载的位置将CMakeLists.txt替换为Android.mk即可
Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
APP_ABI := all
LOCAL_MODULE := native-lib
LOCAL_CPPFLAGS := -O0 -D_UNICODE -DUNICODE -DUSE_DUMP -Wno-error=format-security
LOCAL_CPP_EXTENSION := .cpp
LOCAL_LDLIBS := -lm -llog -lz
LOCAL_SHORT_COMMANDS := true
INC_DIRS = -I$(LOCAL_PATH)/jni
LOCAL_CPPFLAGS += $(INC_DIRS)
LOCAL_SRC_FILES := \
jni_lib.cpp \ \
LOCAL_SHARED_LIBRARIES += libandroid_runtime
include $(BUILD_SHARED_LIBRARY)
Appliction.mk
APP_ABI := all
NDK_TOOLCHAIN_VERSION := clang
APP_SHORT_COMMANDS := true
APP_STL := stlport_static
APP_CPPFLAGS := -std=gnu++11 -D__STDC_LIMIT_MACROS
在gradle的配置中cmake的配置替换为
// externalNativeBuild{
// cmake{
// path file("src/main/jni/CMakeLists.txt")
// }
// }
externalNativeBuild{
ndkBuild{
path file("src/main/jni/Android.mk")
}
}
4.一些简单的配置
二、体验改进和存在的问题
1.编译
配置好CmakeLists.txt或者Android.mk之后,编译的时候,Android Stuido 会自动进行NDK的编译。我测试了mac和ubuntu都没有问题,在windows下似乎有些问题。发现,当Android.mk项目中有依赖的静态库的时候,编译处理有些问题,无法通过编译。windows党自行测试。
2.调试
我升级新版Androd Studio的直接原因就是,终于可以方便的调试底层代码了。但是经过两天的使用还是发现有些问题的。
- 调试带有NDK项目的工程的时候,Android Studio会同时启动两个调试器,一个针对NDK的lldb调试器,和Java调试器。
- 默认NDK的调试是开的,因为打开NDK调试,启动调试的时候还是挺慢的,不需要的时候,可以将工程设置里的Debuger从Auto 改为Java
- 另为需要注意的问题是,如果NDK代码为了项目的Model中,必须在如下位置Symbol Directories,增加NDK所在项目的根路径,否则LLDB调试器会报找不到符号文件错误,是无法进行调试的

- 在Ubuntu测试的时候还发现一个见鬼的问题,有时候配置好一个项目,程序死活不会停在c/c++的断点出。刚开始感觉很莫名其妙,配置两个一模一样的工程,一个可以调试NDK,一个不可以。最后经过反复测试,才发现一个问题:当NDK代码位于Model中的时候,这个项目的APP的目录深度一定不能大于NDK所在Model的路径深度!
后话
Android Studio 2.2的发布,尽管还有许多问题,在这两天的使用中还是发现有很多不稳定的地方。在编辑和调试大的项目时还是经常容易出现异常。但是毕竟对于Androd Studio 进行NDK开发来说还是很大的进步。本人才疏学浅,近一年一直学习和使用NDK,发现网上资料甚少,写次博文全当交流学习。欢迎有共同爱好和需求的朋友交流讨论,共同学习
本文Sample代码
网友评论
The currently selected Native debugger doesn't support breakpoints of type 'Java Line Breakpoints'. As a result, these breakpoints will not be hit.
The debugger selection can be modified in the run configuration dialog.
新建工程没问题,老工程调不了,不晓得啥原因。
Please set them or make sure they are set and tested correctly in the CMake files:
log-lib
linked by target "native-lib" in directory /Users/dongye/mogujie_android/TestIn/app
-- Configuring incomplete, errors occurred!
See also "/Users/dongye/mogujie_android/TestIn/app/CMakeFiles/CMakeOutput.log".
请问一下这是什么问题呢?
不是说2.2已经完美支持NDK了吗
“
当NDK代码位于Model中的时候,这个项目的APP的目录深度一定不能大于NDK所在Model的路径深度!
”
ps。2.2出来后,打算放弃adt了,没想到断点有时候断到 有时候又不行。。。。
还有就是设置编译之后,第二次编译android工程时,不理会cmake源码,只读取.so 构建 新apk?在win平台