Android之NDK开发入门_android ndk 开发 入门-程序员宅基地

技术标签: NDK开发  JNI开发  Android NDK开发  Android引入第三方so库  

注意:本文操作环境为mac,Android Studio版本3.5

前言

NDK全称Native Development Kit,是Android的一个工具开发包,能够快速开发C,C++的动态库,并自动将so和应用打包成APK。而NDK的使用场景就是通过NDK在Android中使用JNI,那么JNI又是啥呢?JNI全称是Java Native Interface,即Java的本地接口,JNI可以使得Java与C,C++语言进行交互。这么一来,通过NDK和JNI,就可以很方便的在Android的开发环境中使用c,c++的开源库。

一、安装和配置NDK

1.安装NDK

可通过Android Studio下载和官网下载,下面为Android Studio下载

  1. 打开Android Studio,点击Android Studio->Preferences,搜索SDK,然后在SDK Tools中勾选LLDB,NDK,Cmke进行下载。其中LLDB是调试本地代码的工具,可调试C++代码在这里插入图片描述
  2. 打开File->Project Structure,然后在SDK Location中配置NDK路径,点击右下角就会出现我们刚刚下载的NDK路径,点击Default NDK路径即可,如果是在官网中下载的,可以根据自己下载的路径进行配置
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vGku4a3U-1589177307895)(/Users/jaceyuan/md/博客/Android:NDK开发/2.png)]2.配置NDK环境变量

2.配置NDK环境

  1. 启动终端,进入当前用户的home目录

    cd ~(注意中间的空格)
    
  2. 创建.bash_profile(假如之前已经创建好了,执行这个命令行不会对原本的文件内容造成影响)

    终端输入:touch .bash_profile
    
  3. 查看、编辑.bash_profile

    如果忘记了NDK的目录,可以通过Android Studio的File->Project Structure中的NDK location查看

    export NDK_ROOT=/Users/{
          你的用户名}/Library/Android/sdk/ndk-bundle
    export PATH=$PATH:$NDK_ROOT
    
  4. 保存然后关闭.bash_profile文件

  5. 更新刚配置的环境变量

    终端输入: source .bash_profile
    
  6. 重新打开终端,检查是否配置成功(如果不成功,记得先关闭当前终端然后打开)

    终端输入: ndk-build
    

    如果出现下列结果的即为成功

    Android NDK: Could not find application project directory !
    Android NDK: Please define the NDK_PROJECT_PATH variable to point to it.
    

二、CMake的方式编译生成so库

1. Android Studio自动生成的示例

1.1 新建Native C++工程

在Android Studio新建一个Native C++的工程,然后填写项目名,并选择Toolchain Default使用默认的C++标准。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Eyt77dA2-1589177307901)(/Users/jaceyuan/md/博客/Android:NDK开发/3.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-15bP6O2A-1589177307903)(/Users/jaceyuan/md/博客/Android:NDK开发/4.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u7w2KQw5-1589177307904)(/Users/jaceyuan/md/博客/Android:NDK开发/5.png)]

1.2 分析AS创建和添加的文件

当点击Finish后,Android Studio会自动添加NDK开发相关的文件。cpp是AS帮我们自动生成的,里面有两个文件:

  • CMakeLists.text:构建脚本
  • Native-lib.cpp:示例C++源文件

另外还对MainActivity,build.gradle进行了一些改动。下面将分析这四个重要的文件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-elYNgbq4-1589177307905)(/Users/jaceyuan/md/博客/Android:NDK开发/6.png)]

  1. CMakeLists.txt

    cmake_minimum_required(VERSION 3.4.1)
    # 这里会把 native-lib.cpp转换成共享库,并命名为 native-lib
    add_library( # 库的名字
            native-lib
    
            # 设置成共享库
            SHARED
    
            # 库的源文件(由于native-lib.app和CMakeLists.txt同处于一个包,因此可以直接写文件名
            # 不然的话需要写成src/main/cpp/native-lib.cpp)
            native-lib.cpp)
    
    # 如果需要使用第三方库,可以使用 find-library来找到
    find_library( # so库的变量路径名字,在关联的时候使用
            log-lib
    
            # 你需要关联的so名字
            log)
    
    # 通过link将源文件的库和第三方库添加进来
    target_link_libraries( 
            # 源文件库的名字
            native-lib
    
            # 添加第三方库的变量名
            ${
          log-lib})
    

    需要注意的是第三库是路径变量名,因此需要用${}方式引用

  2. Native-lib.cpp

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-khciEW8X-1589177307906)(/Users/jaceyuan/md/博客/Android:NDK开发/7.png)]

    一看就是C++的代码,这个方法的作用其实就是返回一个字符串“Hello from C++”。需要重点注意的是这个方法的命名格式,包名,类名,方法名其实就是在Java代码中定义这个native方法stringFromJNI所在的包名和类名。

  3. MainActivity

    public class MainActivity extends AppCompatActivity {
          
    
        //加载so库
        static {
          
            System.loadLibrary("native-lib");
        }
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
          
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            
            TextView tv = findViewById(R.id.sample_text);
            //直接调用native方法
            tv.setText(stringFromJNI());
        }
    
        //native方法
        public native String stringFromJNI();
    }
    

    在这里我们验证了native-lib.cpp里面方法的命名格式,在MainActivity确实有一个stringFromJNI的方法。在这里我们首先需要加载so库,so库的名称就是我们在CmakeList.txt定义的库的名字。然后通过定义的native方法就可以调用C++层的Java_com_example_ndkdemo_MainActivity_stringFromJNI方法。

  4. build.gradle

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ibQ34NaX-1589177307906)(/Users/jaceyuan/md/博客/Android:NDK开发/8.png)]

    看到这,你可以先运行下,看看AS自动生成的JNI例子是否运行成功了。想必当你看到成功运行后是不是已经热血沸腾,迫不及待的想自己尝试下。并且一个so库中不可能只有一个方法,因此接下来就让我们照猫画虎的添加自己编写的c++文件。

2. 自己编写的so库

  1. 创建Java对应的加载类。在这里我们不准备在MainActivity中加载.so库,而是新建了一个JNI工具类来完成加载.so库和声明native方法的任务。然后将MainActivity中的native方法复制过来,并且新建了一个helloFromJNI的方法。另外为了在新项目中使用该so库,我们将so库的名字更改为hello,下面也会在CMakeList.txt中更改so库的名称。(可以发现下面的native方法是红色的,这是因为我们还没有在C++层中实现这两个方法)

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6HCgDEtR-1589177307907)(/Users/jaceyuan/md/博客/Android:NDK开发/9.png)]

  2. 添加需要的C/C++文件。我们直接在cpp中新建一个就行,cpp->右键->new->c/c++ source File。然后就可以命名一个c/c++文件了,并且勾选create an associated header,表示在创建才C/C++文件的同时会创建对应的头文件。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CBQA13iV-1589177307907)(/Users/jaceyuan/md/博客/Android:NDK开发/10.png)]

    (1) 编写头文件hello.h,你可以将头文件看成Java的接口,在这里我们需要声明方法。

    #ifndef NDKDEMO_HELLO_H
    #define NDKDEMO_HELLO_H
    
    //声明接口
    extern const char* helloWorld();
    #endif //NDKDEMO_HELLO_H
    

    (2) 然后在hello.cpp中实现这个头文件,可以发现在这里我们只是简单的返回了一个hello world的字符串

    #include "hello.h"
    extern const char* helloWorld(){
          
        return "Hello World";
    }
    
  3. 在native-lib中引入hello.h头文件。这个操作跟Java中的导包有点类似,并且我们新建了一个在前面JniUtil中声明的native方法,注意的是由于我们将加载so库和声明native方法都放到了JniUtil中,因此我们需要更改之前stringFromJNI的包名和方法名。

    #include <jni.h>
    #include <string>
    #include "hello.h"
    
    extern "C" JNIEXPORT jstring JNICALL
    Java_com_example_ndkdemo_util_JniUtil_stringFromJNI(
            JNIEnv *env,
            jobject /* this */) {
          
        std::string hello = "Hello from C++";
        return env->NewStringUTF(hello.c_str());
    }
    
    
    extern "C" JNIEXPORT jstring JNICALL
    Java_com_example_ndkdemo_util_JniUtil_helloFromJNI(
            JNIEnv *env,
            jobject /* this */) {
          
        std::string helloStr = helloWorld();
        return env->NewStringUTF(helloStr.c_str());
    }
    
  4. CMakeList.txt中加入hello.cpp的路径添加

    这里需要注意的是,如果是多次使用add_library,则会生成多个so库。在这里我们只是将多个本地文件编译到一个so库中,因此只需要在原本的add_library中添加hello的相对路径。并且为了方便在新项目中使用该so库,在这里我将之前native-lib的名字改成了hello。因此生成so库的时候也会生成libhello.so文件(生成so库的时候会自动加上lib的前缀)

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T28Ek8Pz-1589177307908)(/Users/jaceyuan/md/博客/Android:NDK开发/11.png)]

  5. 在MainActivity中使用调用JniUtil中的native方法

    public class MainActivity extends AppCompatActivity {
          
    
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
          
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            TextView tv = findViewById(R.id.sample_text);
            //直接调用native方法
            tv.setText(JniUtil.helloFromJNI());
        }
    }
    
  6. 运行项目

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XAPnknng-1589177307909)(/Users/jaceyuan/md/博客/Android:NDK开发/12.png)]

  7. 查看so库。在app->intermediates->cmake中就会生成对应类型的so库,因为生成so库的时候会自动加上lib的前缀。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ni0VTIMh-1589177307909)(/Users/jaceyuan/md/博客/Android:NDK开发/13.png)]

三、使用CMake引入第三方so库

通常情况下,引入第三方.so库会有两种场景:

  • JNI规范的so。比如返回的是JNI直接支持的类型,比如说上述NdkDemo中的native-lib.cpp中的两个方法。
  • 只提供.so库和头文件。第三方共享.so库一般情况下只提供.so文件和头文件,就是没有将C++文件直接暴露给JAVA层,也没有编写JNI方法的C++文件,比如上述的hello.cpp,这个C++文件中的方法并不是JNI直接支持的类型。

在实际开发中,更常见的是第二种场景。两种场景的引入方法不同,第一种可以直接引入第三方so库,而第二种需要引入自己的so库,然后将自己的so库与第三方so库和头文件进行相关联。接下来我们就来分析这两种引入方式。

1. 引入JNI规范的so

  1. 新建一个普通的Android项目。引入JNI规范的.so库并不需要Native C++类型的项目。

  2. 在main中新建一个jniLibs。我们在app->src->main中新建立一个jniLibs,然后将上面生成的libhello.so文件拷贝过来,这里我们直接将上面cmake->debug->obj中的四个文件夹都拷贝过来

    在这里插入图片描述

  3. 新建一个JniUtil类。注意包名和类名都要跟引入so库中的暴露的JNI方法中的一致,接着就是加载hello这个so库,然后声明native方法,这个native方法就是hello.so库中暴露的JNI方法。其实你会发现这个JniUtil中的代码跟上述的NdkDemo中的是一样的。

    在这里插入图片描述

  4. 在MainActivity中使用JniUtil中的native方法

    public class MainActivity extends AppCompatActivity {
          
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
          
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            TextView tv = findViewById(R.id.tv);
            //调用native方法
            tv.setText(JniUtil.helloFromJNI() + " & "+JniUtil.stringFromJNI());
        }
    }
    
  5. 运行项目

    在这里插入图片描述

可以发现引入JNI规范的.so库是很简单的,因为我们知道hello.so库中接口方法的命名方式,不用CMake,不用编写C++文件,直接在JniUtil中声明native方法,然后运行即可。

2. 引入第三方so库和头文件

这里我们使用场景就是,在Java层中要调用hello.so库中hello.cpp中的helloWorld方法

  1. 新建一个Native C++工程

    因为引入这种类型的so库,我们需要创建自己的so文件,然后在自己的so文件里再调用第三方so,最后在Java层中调用自己的so,因此需要进行NDK开发。而上面我们已经分析了新建Native C++工程AS帮我们建立和修改的文件,因此如果你想在原有的项目中进行NDK开发的话,其实就是自己手动增加和修改这些文件即可。

  2. 新增文件夹,用来存放要导入的第三方so库以及头文件。主要是在cpp文件中新建include文件和在main中新建jniLibs,然后将第三方的头文件放在include中,第三方so库放入jniLibs中。

    在这里插入图片描述

  3. 配置CMakeLists.txt。我们需要关联第三方头文件到native-lib,并配置好第三方so库以及头文件导入的路径。这里需要注意的是set_target_properties这里配置的so库目录,你可以利用message打印,so库的路径是否正确,CMAKE_SOURCE_DIR代表着CMakeLists.txt的路径,由于我的CMakeLists.txt在cpp中,因此需要加上/…进行回退到上一级的main目录,然后配置libhello.so的相对路径。

    cmake_minimum_required(VERSION 3.4.1)
    
    # 利用这个打印路径
    message("******************************************************************")
    message("CMAKE_SOURCE_DIR=${CMAKE_SOURCE_DIR}")
    message("******************************************************************")
    # 这里会把 native-lib.cpp转换成共享库,并命名为 native-lib
    add_library( # 库的名字
            native-lib
    
            # 设置成共享库
            SHARED
    
            # 库的源文件(由于native-lib.app和CMakeLists.txt同处于一个包,因此可以直接写文件名
            # 不然的话需要写成src/main/cpp/native-lib.cpp)
    
            native-lib.cpp)
    
    # 如果需要使用第三方库,可以使用 find-library来找到
    find_library( # so库的变量路径名字,在关联的时候使用
            log-lib
    
            # 你需要关联的so名字
            log)
    
    #将native-lib关联到第三方库头文件
    #由于我的inclue目录与CMakeList都在cpp目录,因此可以直接写include,否则需要写相对目录
    include_directories(include)
    #导入第三库,不同到第三方库需要分开导入,因为有4个so库需要导入,因此需要4add_library(hello SHARED IMPORTED)
    #设置导入第三库名称,目标位置
    set_target_properties(hello
            PROPERTIES IMPORTED_LOCATION
            ${
          CMAKE_SOURCE_DIR}/../jnilibs/${
          ANDROID_ABI}/libhello.so)
    
    # 通过link将源文件的库和第三方库添加进来
    target_link_libraries(
            # 源文件库的名字
            native-lib
            #第三方库的名称
            hello
    
            # 添加第三方库的变量名
            ${
          log-lib})
    
  4. 新建JniUtil用于加载so库和声明native方法。在引入JNI规范的so库时,我们特别强调了该类要与hello.so库中的JniUtil包名,类名要一致。而在这里并不需要,因为在这里我们并不是引入hello.so库,而是引入自己的so库(native-lib),我们只是为了方便管理,然后取JniUtil。

    在这里插入图片描述

  5. 在native-lib.cpp中引入第三方头文件(hello.h)。在这里我们引入了hello.h的头文件,然后实现了对外的JNI方法,在该方法中我们引用了第三方库中的hello.cpp中的helloWorld方法,而这也就是我们引入第三方so库和头文件的最终目的。

    注意:JNI方法的包名,类名,方法名与上面的JniUtil一致

    #include <jni.h>
    #include <string>
    #include "hello.h"
    
    
    extern "C" JNIEXPORT jstring JNICALL
    Java_com_example_jnidemo_util_JniUtil_helloFromJNI(
            JNIEnv *env,
            jobject /* this */) {
          
        std::string helloStr = helloWorld();
        return env->NewStringUTF(helloStr.c_str());
    }
    
    
  6. 在MainActivity中引用JniUtil中的native方法

    在这里插入图片描述

  7. 运行项目。你就能发现神奇的HelloWorld

四、踩坑

  1. 在引入JNI规范的.so库时一定要记得包名,类名要和引入的so库中的一致,不然运行时会报No implementation found for java.lang.String com.example…之类的错误,然后闪退

  2. 在引入第三so库的时候,如果你将so库放在src/main/jniLibs时,可以不在项目的build.gradle中配置so库路径,因为AS默认加载so库的路径就是src/main/jniLibs。但是如果放在其他地方的时候,或者不取名jniLibs时,比如我们放在了src/main/jniLib,这时候就得在build.gradle中配置,如下
    在这里插入图片描述

  3. 引入第二种so库和头文件中配置CMakeList.txt的时候,我们会通过set_target_properties来设置目标so库的路径,网上大部分的教程配置的路径都是:${CMAKE_SOURCE_DIR}/jnilibs/${ANDROID_ABI}/so库完整名字.so,但其实是要看具体情况的,如果你编译或运行的时候出现了类似下面这种的错误,那么大概率是由于so库的路径配置错误导致的。

     'F:/Android/JNIDemo/app/src/main/cpp/jnilibs/armeabi-v7a/libhello.so', needed by 'F:/Android/JNIDemo/app/build/intermediates/cmake/debug/obj/armeabi-v7a/libnative-lib.so', missing and no known rule to make it
    

    这时候我们可以利用message来打印${CMAKE_SOURCE_DIR}/jnilibs/${ANDROID_ABI}/so库完整名字.so这个路径,然后对比一下你引入第三方so库的位置,就可以进行判断是否路径配置错误。

    在这里插入图片描述

    添加打印信息后我们进行编译,如果编译错误的话,应该能够在build中看到打印的message信息,如果看不到的话,可以查看app->.cxx->cmake->debug->随便一个机型->build_output.txt中的打印信息,然后对比你引入第三方库的位置。因为我的jnilibs是放在main层,所以这个路径明显是错误的,因此需要在${CMAKE_SOURCE_DIR}加上/…进行回退一个目录,即最终的目录应该为${CMAKE_SOURCE_DIR}/../jnilibs/${ANDROID_ABI}/libhello.so

    在这里插入图片描述
    在这里插入图片描述

    如果你确定你的路径配置没有错误,那么你也可以看看报错的so库位置,也有可能是因为你运行的环境缺少了相关的机型,比如在虚拟机中运行需要x86的环境,而你引入so库的时候没有将x86的so库导进来,或者是说你的手机运行需要arm64-v8a或armeabi-v7a的环境,但是你没有引入对应的环境,也有可能missing and no known rule to make it的错误,所以最好是将所有机型的so库文件拷贝过来。

总结

自己是NDK开发和C++的小白,所以整个过程下来感觉收获很多。从安装到使用NDK开发,一路上下来也踩了不少的坑,所以想记录这整个过程,如果有错误的还请大家多多包涵,同时也欢迎大家指出错误。

参考博客:

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_41979349/article/details/106053194

智能推荐

华为鸿蒙系统概念图,华为P50Pro概念图:没有麒麟芯片,鸿蒙系统和7镜头也可以很豪横...-程序员宅基地

文章浏览阅读133次。余承东正式对外宣布,手机芯片没了,也就是说华为Mate40Pro很可能是最后一款麒麟芯片手机,明年发布的华为P50Pro很可能就要搭载其他制造商芯片了。如果没有麒麟芯片,那么华为旗舰机还豪横得起来吗?外媒最近发布了一组华为P50Pro概念图,就算麒麟芯片无法供应,但鸿蒙系统和7镜头也可以很豪横。还新增了手写笔,让这款华为旗舰机功能更加丰富。今年发布的华为P40Pro搭载了麒麟990 5G芯片,配备..._华为p50pro图

vue处理多个异步请求_promise处理多个相互依赖的异步请求(实例讲解)-程序员宅基地

文章浏览阅读1.1k次。promise处理多个相互依赖的异步请求(实例讲解)发布时间:2020-09-05 10:41:05来源:脚本之家阅读:143在项目中,经常会遇到多个相互依赖的异步请求。如有a,b,c三个ajax请求,b需要依赖a返回的数据,c又需要a和b请求返回的数据。如果采用请求嵌套请求的方式自然是不可取的。导致代码难以维护,如何请求很多。会出现很多问题。Promise就是解决多个异步请求的问题。 Promi..._vue有1个场景(a,b,c请求,a,b返回结果是c的参数,如何实现)

thinkphp5在Model模型里使用hasOne和belongto关联表查询-程序员宅基地

文章浏览阅读377次,点赞4次,收藏3次。默认情况下,Thinkphp5.0使用的是user_id作为外键关联,如果不是的话则需要在关联定义的时候指定,例如:user表对应的的Model模型:Userphp//1.hasOne 一对一//2.hasMany 一对多//hasOne('关联模型名','外键名','主键名',['模型别名定义'],'join类型');?user_login表对应的Model模型:UserLoginphp。

浅测贝锐蒲公英A20-1226千兆AP(单台家用)_贝锐蒲公英 r3000a 功耗-程序员宅基地

文章浏览阅读340次。测试了2个位置,一个连接到了5G,另一个连接到了2.4G且只有286Mbps,2.4G和5G信号强度同样均比蒲公英X3A和红米AC2100要弱,尤其是5G,差别的原因可能与是否为承重墙有关。5G信号可穿普通墙,但穿不了承重墙。p.oray.com页面左侧设备管理下的AP管理页面提供批量管理页面,可添加设备(右侧蓝色按钮,支持批量导入),添加群组,查看AP设备信息和网络信息,并可以批量对AP进行导入配置(需先创建模板)、重启、升级等操作。802.11kvr漫游,更为稳定,不易掉线,无配对切换AP,未测试。_贝锐蒲公英 r3000a 功耗

穷举法求解鸡兔同笼问题_鸡兔同笼python穷举法-程序员宅基地

文章浏览阅读5.5k次,点赞2次,收藏4次。#include#includeusing namespace std;int main(){ //int heads = 23, feet = 60; int heads, feet; cout << "输入头,足的数量" << endl; cin >> heads >> feet; for(int i=0;i<35;i++) for (int j = 0;j<35; j_鸡兔同笼python穷举法

graphpad如何检测方差齐_方差分析中两两多重比较方法的含义及如何正确选择-程序员宅基地

文章浏览阅读4.5k次。欢迎订阅SPSS训练营微信号以SPSS方差分析为例,十多种多重比较方法可选,上图为英文视图,下图为中文翻译视图,请对照学习。简单介绍一下常用的方法它们的含义,以及如何正确恰当选择使用这些方法。LSDLSD:最小显著差异法,实际上是 t 检验的改进,检验统计量为T,在变异和自由度的计算上利用了整个样本信息,而不仅仅是比较两组的信息。它的敏感度最高,在比较时仍然存在放大α水准(一类错误)的问..._graphpad多重比较

随便推点

Bmob后端云+ImageLoader框架实现图文列表,flutterui构建工具_bmob flutter-程序员宅基地

文章浏览阅读275次。=====================================================================================1.了解什么是Bmob后端云对于很多Android/ios/wp个人移动开发者来说,开发一个具有网络功能的应用不是一件容易的事,不仅需要购买/租赁服务器,还必须掌握一门诸如Java/.net/php这类的服务器开发语言,每开发一款移动应用程序,就必须开发维护对应的服务器程序。这一切对于移动开发者来说,都是一个冗长的噩梦。如何让移动开_bmob flutter

HISI_3516_vi_sample_snap_info_s-程序员宅基地

文章浏览阅读1.2k次。主要的解释还是看这些大牛的,我这里只是想自己整理一遍:海思3518E开发笔记2.5——海思VI(video input)模块详解_Spark!的博客-程序员宅基地_海思wdr目录海思video input模块架构介绍海思video input模块功能介绍结构体说明函数调用关系流程分析step 1: mipi configurestep 2: configure sensor and ISP(include WDR mode)step 3: run isp threadstep 4 : config &am._sample_snap_info_s

Unable to load class ‘org.gradle.api.internal.plugins.DefaultConvention‘_unable to load class 'org.gradle.initialization.bu-程序员宅基地

文章浏览阅读1w次,点赞4次,收藏8次。错误Unable to load class 'org.gradle.api.internal.plugins.DefaultConvention'.原因IDE与Gradle不兼容。解决办法https://services.gradle.org/distributions,检查是否有对应的版本。修改gradle-wrapper.properties,把链接复制过去:错误:https\://services.gradle.org/distributions/gradle-5.6.4-._unable to load class 'org.gradle.initialization.buildcompletionlistener'.

【presto】使用python执行presto任务_python presto-程序员宅基地

文章浏览阅读1.6k次。前言目前发现有两个驱动包,分别是:pyhive : https://github.com/dropbox/PyHivepresto-python-client : https://github.com/prestodb/presto-python-client这里项目使用的是presto-python-client,毕竟是官方的。而且我这里访问的Presto集群是需要用户名密码进行https认证。环境准备python2.7presto-python-client安装 pip inst_python presto

深度学习硬件指南(号称最全)_深度学习 硬件选择-程序员宅基地

文章浏览阅读1.7k次。from 机器之心 : http://www.almosthuman.cn/2016/02/04/bqrzz/深度学习计算密集,所以你需要一个快速多核CPU,对吧?还是说买一个快速CPU可能是种浪费?搭建一个深度学习系统时,最糟糕的事情之一就是把钱浪费在并非必需的硬件上。本文中,我将一步步带你了解一个高性能经济系统所需的硬件。研究并行化深度学习过程中,我搭建了一个GPU集群,为此_深度学习 硬件选择

斐波那契数列前20项_短线交易的秘诀——斐波那契数列,数学界的“完美”公式!...-程序员宅基地

文章浏览阅读940次。斐波拉契数列一直被认为是大自然中的神奇异数。它的相邻两项之商趋近黄金分割0.618,与之相关的0.191、0.382和0.500等数字,构成了股市中市场时间和空间计算的重要节点。金融市场的时间和价格服从斐波拉契数列,有时准确率达到十分惊人的程度。斐波拉契数列在股市中的应用:“炒股者都懂数学”,这是华尔街操盘手的一句名言。在许多人眼里,周期是一种玄乎的东西,特别是股市中的一些“神棍”例如李大霄的拙劣...