Android Studio 3.0 利用cmake搭建jni环境(很详细哦)

2019-04-01 22:59:22

原文链接  Android Studio 3.0 利用cmake搭建jni环境(很详细哦)

我用的Android Studio是3.0的版本,然后想搭建一下jni的环境。这里把自己遇到的问题和注意点都记录下。 

首先是需要在android studio里面安装最基本的环境。 

打开Default Preference里面查看SDK Tool选项。 


CMake、LLDB、NDK这三个勾选上去,然后安装。 

安装好了以后,在File-》Project Structure的SDK Location里面应该可以看到Android NDK Location的地址。 

如下图所示。 


新建一个Android Studio项目,注意到我这里新建项目的时候并没有什么include C++ 的勾选项目,as也不会在项目生成后帮忙生成cmakelist.txt, 我以为这个as版本的问题,后面也能够顺利搭建就行了。 


首先新建一个JNITest类,代码如下:


package jnifive.preqel.com.jnifive;

public class JNITest {


    static {

        System.loadLibrary("jnilib");

    }


    public native String getString();

}


这里的jnilib代表的就是你的jni库名。这时候编译器会提示有错误,不用管。 

然后根据javah工具来生成c语言头文件。 

首先配置好你的jdk环境变量,即在termianla终端里面可以正常使用javac和javah。 

到JNITest同级目录下面用javac 生成class文件。 

具体操作:用终端进入到JNITest.java所在目录,执行 javac JNITest.java 


然后在终端里面用命令行退到java文件目录下面,用javah生成c++头文件。 

终端输入:javah -jni jni.example.com.testjni6.JNITest 

注意:请用cd命令退到工程文件目录的java同级目录下面执行,否则会提示找不到文件 

成功后会生成 



然后 

右击app目录,新建Folder-》Jni Folder 建立jni目录 



点击下一步后看见android studio在工程下面帮我们生成了一个cpp的文件夹,并没有看到所谓的jni文件夹。晕了?没关系,其实是一样的,把Android studio的视图转化为Project,找到app目录,然后在main下面找到jni目录。如图所示 



然后把刚才的jni_example_com_testjni6_JNITest.h文件拷贝进来。 

在这个jni目录下面创建一个新的c/c++ source file。 

名字就叫main.cpp好了。里面代码如下:


#include "jni_example_com_testjni6_JNITest.h"

#include <string>

extern "C"

JNIEXPORT jstring JNICALL

JNICALL Java_jni_example_com_testjni6_JNITest_getString(

        JNIEnv* env,

        jobject /* this */) {

    std::string hello = "Hello from C++";

    return env->NewStringUTF(hello.c_str());

}


注意这里的包名都要对应上。 

这时候build一下工程你会发现 



这时候不用配什么android.useDeprecatedNdk=true ,这个东西在3.0以后好像是被取消了,配了只会走错路。(表示折腾了很久。。。) 

正确的做法是直接用cmakelist.txt配置环境。在工程app/java/目录(并非一定要这个地方,但是最后在build.gradle里面应正确关联)下面直接新建一个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.

             jnilib


             # Sets the library as a shared library.

             SHARED


             # Provides a relative path to your source file(s).

             ../jni/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.

                       jnilib


                       # Links the target library to the log library

                       # included in the NDK.

                       ${log-lib} )


带#号全是注释,所以这里有几个重要的属性要配的。 

add_library 下面首先指明库的名称,这里是jnilib,Provides a relative path to your source file(s),这里要用相对路径指到cpp文件所在的路径。cmakelist的配置,这个配置一定要配对,否则会抱什么 

CMake Error: CMake can not determine linker language for target: 的错误。


然后在app的build.gradle下面新增一些语句。 

在defaultConfig下面新增


   ndk {

            abiFilters 'armeabi', 'armeabi-v7a','x86'

        }


    externalNativeBuild {

            cmake {

                cppFlags ""

                //生成多个版本的so文件

                abiFilters 'arm64-v8a','armeabi-v7a','x86','x86_64'

            }

        }


这里表示支持的abi版本,如果设备的abi版本不在这个列表中是安装不了apk的。 

然后在同一文件的android下面加入下面的代码


    externalNativeBuild {

        cmake {

            path "src/main/java/CMakeLists.txt"  // 设置所要编写的c源码位置,以及编译后so文件的名字

        }

    }



path 就是CMakeLists.txt的相对路径。 

好了。 

最后在MainActivity里面调用一下这个JNITest的getString()函数就行了,关键代码如下:


   @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        TextView tv = (TextView) findViewById(R.id.textview);

        JNITest ndkTest = new JNITest();

        try {

            tv.setText(ndkTest.getString());

        }catch (Exception e){

            e.printStackTrace();

        }

    }


 

如有需要,请点击下面链接: 

项目github源码地址



  • 2018-12-10 00:57:37

    Android沉浸式状态栏(透明状态栏)最佳实现

    在Android4.4之前,我们的应用没法改变手机的状态栏颜色,当我们打开应用时,会出现上图中左侧的画面,在屏幕的顶部有一条黑色的状态栏,和应用的风格非常不协调;为了提供更好的界面交互,google在Android4.4以后提供了设置沉浸式状态栏的方法,对于沉浸式状态栏这个名字存在争议,我们不做讨论,实际的效果其实就是透明的状态栏,然后在状态栏的位置显示我们自定义的颜色,通常为应用的actionbar的颜色,或者是将应用的整体的一张图片也占据到状态栏中,如下图所示:

  • 2018-12-11 10:20:40

    Android下载图片到相册

    调用以上系统自带的方法会把bitmap对象保存到系统图库中,但是这种方法无法指定保存的路径和名称,上述方法的title、description参数只是插入数据库中的字段,真实的图片名称系统会自动分配。 或者

  • 2018-12-11 15:45:00

    Laravel中七个非常有用但很少人知道的Carbon方法

    在编写PHP应用时经常需要处理日期和时间,Carbon继承自 PHP DateTime 类的 API 扩展,它使得处理日期和时间更加简单,这篇文章主要给大家分享了Laravel中七个非常有用但很少人知道的Carbon方法,需要的朋友可以参考下。

  • 2018-12-13 11:41:23

    Android drawable微技巧,你所不知道的drawable的那些细节

    好像有挺久时间没更新博客了,最近我为了准备下一个系列的博客,也是花了很长的时间研读源码。很遗憾的是,下一个系列的博客我可能还要再过一段时间才能写出来,那么为了不至于让大家等太久,今天就给大家更新一篇单篇的文章,讲一讲Android drawable方面的微技巧。

  • 2018-12-13 17:14:41

    Android安全开发之浅谈密钥硬编码

    在阿里聚安全的漏洞扫描器中和人工APP安全审计中,经常发现有开发者将密钥硬编码在Java代码、文件中,这样做会引起很大风险。信息安全的基础在于密码学,而常用的密码学算法都是公开的,加密内容的保密依靠的是密钥的保密,密钥如果泄露,对于对称密码算法,根据用到的密钥算法和加密后的密文,很容易得到加密前的明文;对于非对称密码算法或者签名算法,根据密钥和要加密的明文,很容易获得计算出签名值,从而伪造签名。

  • 2018-12-13 17:17:02

    轻松实现动态获取Android手机CPU架构类型

    .so文件是unix的动态连接库,是二进制文件,作用相当于windows下的.dll文件。 他使用了C/C++代码编写的可以操作硬件比java更高级的 底层代码,执行速度和效率比其他语言要高。 在Android中调用动态库文件(*.so)都是通过jni的方式。