精华内容
下载资源
问答
  • 首先,声明一下本文不代表所有嵌入式软件的发布流程都一样,该文主要是针对汽车电子行业。 软件发布 新版本软件的发布是公司非常重要的事情,一般只有...但是,很负责的告诉大家,汽车行业中软件发布就是这么复杂...

    首先,声明一下本文不代表所有嵌入式软件的发布流程都一样,该文主要是针对汽车电子行业。

    软件发布

    新版本软件的发布是公司非常重要的事情,一般只有团队Leader才有权限发布软件。

    幸运的是,我们团队的leader去国外,我才能够获得这来之不易的机会,虽说中途出了一点小意外,但总体比较顺利。

    有些读者可能不理解软件发布有什么复杂的,认为我小题大做。但是,很负责的告诉大家,汽车行业中软件发布就是这么复杂,这都是为了保证软件的可靠与安全。

    接下来,介绍一下我所经历的软件发布流程:

    在发布新版本软件之前,团队leader需要将该版本测出的所有bug全部fix掉。如果有些bug短时间内无法解决,团队leader需要与OEM协商,请求下个版本解决。

    在此之后,团队leader需要在发布版本前三天将代码冻结。之后,各工程师针对不同车型的软件进行回归测试,而Safety工程师对软件做Safety测试,确保该版本的软件未发生功能回退,且安全性满足要求。

    上述测试结束并且报告上传后,团队leader需要将最新版软件pull到本地编译。编译结束后,leader需要在git上给软件打tag,打tag类似于人的身份证号码,这标志该版本的软件已经生成,任何人都无法改动。

    在此之后,leader需要将带有bootloader的hex文件、ulp文件、最新版的CAN matrix和CANoe工程打包发送给SVE(软件测试工程师)、SVE(系统测试工程师)和SQO(软件质量工程师)。

    你以为这就结束了吗?No, 还有很多事情没有做呢。

    软件发出后,团队leader需要在SQO(软件质量工程师)面前将最新版本的代码pull到本地编译。然后,对比发出的hex文件与本地编译的hex文件,SQO确保无误后,你才能走接下来的流程。

    这个过程是公司强制执行的,哪怕发出的hex文件是刚刚编译的,你仍然要在SQO面前重新做一遍。虽说这个过程浪费时间,但其实是为了保证发送给客户的软件版本是完全正确的,也算是一种防呆措施。

    之后,需要测试CPU负载和内存占用率,公司对于这两项指标也有标准。接下来,leader需要整理改版本软件解决多少bug,拒绝多少bug,更改多少需求等。

    做完之后,leader需要与SQO一起检查从上个版本到这个版本之间上传代码的所有历史记录,一条一条的过。这个过程非常耗费时间,通常不同版本之间相隔两个月,有上百次的代码上传。

    确保代码没问题后,SQO需要检查代码的QAC报告,即代码静态检查报告,确保所有的代码符合命名规范。

    之后,SQO会针对编译器的warning询问leader为何没有消除掉。如果你是学生,那么你可能对warning没概念。如果你是程序员,相信都做过消除编译器warning的工作。毕竟,有些warning真的是未发现的bug。

    最后,SQO会检查所有的回归测试、Safety测试报告是否正确,是否每一条bug都对应有测试用例。

    如果上述工作都做的非常好,那么软件发布这个事情一天能够结束。但如果做的不仔细,那么发布一个版本有可能需要两到三天。

    总结

    由于行业原因,汽车行业发布软件比较慢。但这也是为了保证软件的安全和可靠性,将用户的生命安全放在第一位,无论多么麻烦都是合理的。

    **题外话:**这周写的文章太过细化,对部分读者可能没什么用。我也是写完之后才发现这个问题。以后会在写文章时注意这个问题,为大家带来优质文章,望大家这次莫见怪。

    ps: 欢迎关注我的公众号[酷酷的coder],分享转行后的所学所思,希望我的经历能够给你帮助。
    在这里插入图片描述

    展开全文
  • 说说基本数据类型和复杂数据类型特点是什么? 基本数据类型 变量名在栈上存储的是具体的数值 使用“==”是判断值是否相等 复杂数据类型 变量名栈上存储的是对象的[内存地址],内容存储在堆上面 使用“==”是判断...

    说说基本数据类型和复杂数据类型特点是什么?

    • 基本数据类型
      • 变量名在栈上存储的是具体的数值
      • 使用“==”是判断值是否相等
    • 复杂数据类型
      • 变量名栈上存储的是对象的[内存地址],内容存储在堆上面
      • 使用“==”是判断地址是否相同

    转载于:https://www.cnblogs.com/zcsmile/p/10962062.html

    展开全文
  • PS一句:最终还是选择CSDN来整理发表这几年的知识...以上第一部分就是JNI开发常见的基本结构模板,实际开发代码量和文件和目录结构都会比这复杂,这只是一个雏形用来领悟重点。 第二部分 概述 如果你已经...

    PS一句:最终还是选择CSDN来整理发表这几年的知识点,该文章平行迁移到CSDN。因为CSDN也支持MarkDown语法了,牛逼啊!

    【工匠若水 http://blog.csdn.net/yanbober】 阅读前一篇《NDK-JNI实战教程(二) JNI官方中文资料》 http://blog.csdn.net/yanbober/article/details/45310365

    这里写图片描述

    第一部分

    概述

    学习JNI NDK你需要有java与C或者C++基础。因为NDK几乎就是java与C或者C++的混合编程互调,JNI在其中只是扮演了一个不同语种间对接握手调运的规则而已。就像C语言嵌入调运执行汇编程序一样,需要一种规则来约束沟通。这个例子是我在闲时继续使用Android Studio撸的,不难,适合入门。不要一下子被这么几个文件吓着了。重点是为了通过这个例子引出来几个Android NDK开发的重要基础模板知识点。所以内在代码逻辑看上去可能十分僵硬不合理,代码风格可能也不是十分规范,还请多多指点交流,然后撸的更多。

    需要知识点:C语言基础,C语言动态参数宏,Java基础,JNI基本概念

    代码及工程文件介绍

    点我进入完整工程代码示例下载页面

    这个例子是一个简单的场景模拟实现;我们通过在app java层传入一个name到c库中,c库通过app传入的name经过保密的自定义加密算法(本代码没实现,只是模拟)处理生成一个客户化定制的key反馈给app层使用。这样至于通过name得到key的具体加密机制被编译成了so文件,很难被破解。而如果使用java则很容易被破解。

    这是这篇文章要介绍的代码工程的几个主要文件夹文件分布情况:

    JNI

    浅析:正常NDK工程目录结构,其中jni目录下只是多包涵了两个文件夹而已。在这里在jni根目录下的两个文件就是jni核心文件,起到C与Java的互联互通作用;utils目录是我自己加入的一个常用工具目录,里面放置一些通用代码,譬如这里的android_log_print.h用来打印log;local_logic_c目录是我放置的用C语言实现的加密逻辑代码,其中包含实现和头文件。你的jni目录结构也可以随意组织,符合自己习惯效率就行。在这里需要注意的一点是Android JNI下面c代码使用printf打印是不显示的,所以才需要像我加入的宏,使用android提供的log打印函数,不过在编译时请记得加入log依赖的官方lib。

    io.github.yanbober.ndkapplication包中MainActivity主Activity代码:

    package io.github.yanbober.ndkapplication;
    
    import android.os.Bundle;
    import android.support.v7.app.ActionBarActivity;
    import android.widget.TextView;
    
    public class MainActivity extends ActionBarActivity {
        private TextView mTextView;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            mTextView = (TextView) this.findViewById(R.id.test);
    
            NdkJniUtils jni = new NdkJniUtils();
            //传入name="vip"到jni代码模拟拿到加密后的key
            mTextView.setText(jni.generateKey("vip"));
        }
    }
    

    浅析:这就是App的传统界面了,一个UI传入name=”vip”,调运native方法取到转换好的key显示在TextView里,没啥技术难度。

    io.github.yanbober.ndkapplication包中NdkJniUtils类代码:

    
    package io.github.yanbober.ndkapplication;
    
    public class NdkJniUtils {
        public native String generateKey(String name);
    
        static {
            System.loadLibrary("YanboberJniLibName");
        }
    }
    

    浅析:这个类就是定义本地native方法,编译以后通过javah生成这个文件的h头文件,如下文。其中static块作用就不说了吧。System.loadLibrary(“YanboberJniLibName”);就是加载你编译生成的库文件,注意库生成在lib目下默认会添加lib前缀,形如:libXxx.so,我们在load函数里传入的名字只需要Xxx就行。

    jni根目录下通过系列教程一中javah生成的头文件io_github_yanbober_ndkapplication_NdkJniUtils.h内容:

    
    /* DO NOT EDIT THIS FILE - it is machine generated */
    #include <jni.h>
    
    #ifndef _Included_io_github_yanbober_ndkapplication_NdkJniUtils
    #define _Included_io_github_yanbober_ndkapplication_NdkJniUtils
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    JNIEXPORT jstring JNICALL Java_io_github_yanbober_ndkapplication_NdkJniUtils_generateKey(JNIEnv *, jobject, jstring);
    
    #ifdef __cplusplus
    }
    #endif
    #endif
    

    浅析:通过javah生成的头文件,不明白的参考系列教程一中。

    jni根目录下通过系列教程一中类似test生成的jni接口c文件jni_interface.c内容:

    
    #include <jni.h>
    #include <string.h>
    #include "io_github_yanbober_ndkapplication_NdkJniUtils.h"
    #include "./utils/android_log_print.h"
    #include "./local_logic_c/easy_encrypt.h"
    
    JNIEXPORT jstring JNICALL Java_io_github_yanbober_ndkapplication_NdkJniUtils_generateKey
      (JNIEnv *env, jobject obj, jstring name){
         //声明局部量
         char key[KEY_SIZE] = {0};
         memset(key, 0, sizeof(key));
    
         char temp[KEY_NAME_SIZE] = {0};
    
         //将java传入的name转换为本地utf的char*
         const char* pName = (*env)->GetStringUTFChars(env, name, NULL);
    
         if (NULL != pName) {
            strcpy(temp, pName);
            strcpy(key, generateKeyRAS(temp));
    
            //java的name对象不需要再使用,通知虚拟机回收name
            (*env)->ReleaseStringUTFChars(env, name, pName);
         }
    
         return (*env)->NewStringUTF(env, key);
      } 
    

    浅析:jni”接口封装实现”文件,我就叫这名吧,可能好理解些,别把jni想的太高大上。这里面就是实现h文件声明的函数。一些基本参数可以查阅系列教程二文档,复制关键字在教程二里搜索查阅即可。主要流程就是通过GetStringUTFChars拿到java传入的String的name转换后的char* utf-8指针;把name通过generateKeyRAS传入C语言实现的加密逻辑代码中处理,同时通过ReleaseStringUTFChars告诉虚拟机不需要持有name的引用,以便Java释放String的name;完事将C语言处理生成的key通过NewStringUTF转换返回给java层使用。

    jni目录下utils子目录下的log打印工具宏android_log_print.h文件内容:

    
    /*
     * 作者:工匠若水
     * 说明:Android JNI Log打印宏定义文件
     */
    
    #ifndef _ANDROID_LOG_PRINT_H_
    #define _ANDROID_LOG_PRINT_H_
    
    #include <android/log.h>
    
    #define IS_DEBUG
    
    #ifdef IS_DEBUG
    
    #define LOG_TAG ("CUSTOMER_NDK_JNI")
    
    #define LOGV(...) ((void)__android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
    
    #define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG  , LOG_TAG, __VA_ARGS__))
    
    #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO   , LOG_TAG, __VA_ARGS__))
    
    #define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN   , LOG_TAG, __VA_ARGS__))
    
    #define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR  , LOG_TAG, __VA_ARGS__))
    
    #else
    
    #define LOGV(LOG_TAG, ...) NULL
    
    #define LOGD(LOG_TAG, ...) NULL
    
    #define LOGI(LOG_TAG, ...) NULL
    
    #define LOGW(LOG_TAG, ...) NULL
    
    #define LOGE(LOG_TAG, ...) NULL
    
    #endif
    
    #endif
    

    浅析:这个文件是我自己写JNI时每次直接使用的文件,就是一个工具文件一样。目的是因为Android的JNI使用printf函数打印的东西是没法显示,这里这么转化其实对应的就是java层打印Log的函数Log.d(), Log.i(), Log.w(),Log.e(), Log.f()。原因是因为Android的java层和C++ framework层都提供了Log函数,但是JNI环境下打印稍有不同,使用的是__android_log_print并且用NDK环境编译和android源码framework环境编译选择链接Android.mk库也不同。所以你会发现Google NDK官方sample代码中也是类似处理的,这里只是简单封装的更实用而已。需要一点C语言知识理解。如果你喜欢再往深里折腾,那我再提一点吧,那就是自己去android系统源码的system/core/include/cutils/log.h去看看吧,如果是在完整源码编译环境下,只要include

    jni目录下local_logic_c子目录中本地C语言实现的逻辑目录下的接口头文件easy_encrypt.h内容:

    
    #ifndef _EASY_ENCRYPT_H_
    #define _EASY_ENCRYPT_H_
    /*
     * 作者:晏博(工匠若水)
     *
     * 功能:通过name获取加密后的key
     * 类型:测试代码
     */
    #define KEY_NAME_SIZE  (6)
    #define KEY_SIZE  (129)
    
    char* generateKeyRAS(char* name);
    
    #endif /* _EASY_ENCRYPT_H_ */
    

    浅析:这就是标准的C语言模块了,这是逻辑的h文件,不解释。

    jni目录下local_logic_c子目录中本地C语言实现的逻辑目录下的接口逻辑实现文件easy_encrypt.c内容:

    
    #include <string.h>
    #include "easy_encrypt.h"
    #include "./../utils/android_log_print.h"
    
    /*
     * 功能:通过传入name生成加密后唯一的key值
     *
     * name 传入小于KEY_NAME_SIZE的字符串
     * return 通过name生成的验证key值
     */
    char* generateKeyRAS(char* name)
    {
        //判断形参是否有效
        if (NULL == name || strlen(name) > KEY_NAME_SIZE) {
            LOGD("function generateKey must have a ok name!\n");
            return NULL;
        }
    
        //声明局部变量
        int index = 0;
        int loop = 0;
        char temp[KEY_SIZE] = {"\0"};
        //清空数组内存
        memset(temp, 0, sizeof(temp));
        //将传进来的name拷贝到零时空间
        strcpy(temp, name);
        //进行通过name转化生成key的逻辑,这里是模拟测试,实际算法比这复杂
        for (index=0; index<KEY_SIZE-1; index++)
        {
            temp[index] = 93;
            LOGD("---------------temp[%d]=%c", index, temp[index]);
        }
    
        return temp;
    }
    

    浅析:这就是标准的C语言模块了,这是逻辑的c文件,模拟实现了加密算法而已。

    build.gradle文件中android.defaultConfig中新加如下代码(其他使用AS编译设置参见本系列教程一):

    ndk{
        moduleName "YanboberJniLibName"
        ldLibs "log", "z", "m"  //添加依赖库文件,因为有log打印等
        abiFilters "armeabi", "armeabi-v7a", "x86"
    }

    浅析:不解释。

    编译代码运行在LogCat中可以看见主要的几条Log如下:

    JNI

    浅析:这里你会看到在运行app时:

    • 尝试加载so文件 Trying to load lib /data/app-lib/io.github.yanbober.ndkapplication-2/libYanboberJniLibName.so 0xa6a4e120
    • 加载了so文件 Added shared lib /data/app-lib/io.github.yanbober.ndkapplication-2/libYanboberJniLibName.so 0xa6a4e120
    • 先不解释这句话 No JNI_OnLoad found in /data/app-lib/io.github.yanbober.ndkapplication-2/libYanboberJniLibName.so 0xa6a4e120, skipping init

    上面说“先不解释这句话”的No JNI_OnLoad found……skipping init其实透露出了一个新的知识点,下文会介绍的。

    运行程序结果如下:

    JNI

    浅析:传入name加密后得到的key显示。

    点我进入完整工程代码示例下载页面

    总结

    以上第一部分就是JNI开发常见的基本结构模板,实际开发代码量和文件和目录结构都会比这复杂,这只是一个雏形用来领悟重点。

    第二部分

    概述

    如果你已经大致理解掌握了第一部分内容,那基本OK了。接下来要扯蛋的就是第一部分遗留的历史问题和其他提升技能。

    首先,不知道还记不记得第一部分编译代码运行在LogCat中可以看见主要的几条Log。“No JNI_OnLoad found……skipping init”这句话是不是还是依旧耿耿于怀呢?那么接下来咱们放大招来kill它。

    从Load这个蛋疼的词说起

    Android OS加载JNI Lib的方法有两种:

    • 通过JNI_OnLoad。
    • 如果JNI Lib实现中没有定义JNI_OnLoad,则dvm调用dvmResolveNativeMethod进行动态解析。

    PS:咱们上面第一部分就是dvm调用dvmResolveNativeMethod进行动态解析,所以log打印No JNI_OnLoad found。

    从网上查到的深入解析(此解析模块代码引用自网络)

    JNI_OnLoad机制分析

    System.loadLibrary调用流程如下所示:

    System.loadLibrary->Runtime.loadLibrary->(Java)nativeLoad->(C: java_lang_Runtime.cpp)Dalvik_java_lang_Runtime_nativeLoad->dvmLoadNativeCode->(dalvik/vm/Native.cpp)

    接着如下:

    • dlopen(pathName, RTLD_LAZY) (把.so mmap到进程空间,并把func等相关信息填充到soinfo中)
    • dlsym(handle, “JNI_OnLoad”)
    • JNI_OnLoad->RegisterNatives->dvmRegisterJNIMethod(ClassObject* clazz, const char* methodName, const char* signature, void* fnPtr)->dvmUseJNIBridge(method, fnPtr)->(method->nativeFunc = func)

    JNI函数在进程空间中的起始地址被保存在ClassObject->directMethods中。

    
    struct ClassObject : Object {  
        /* static, private, and <init> methods */  
        int             directMethodCount;  
        Method*         directMethods;  
    
        /* virtual methods defined in this class; invoked through vtable */  
        int             virtualMethodCount;  
        Method*         virtualMethods;  
    }
    

    此ClassObject通过gDvm.jniGlobalRefTable或gDvm.jniWeakGlobalRefLock获取。

    dvmResolveNativeMethod延迟解析机制

    如果JNI Lib中没有JNI_OnLoad,即在执行System.loadLibrary时,无法把此JNI Lib实现的函数在进程中的地址增加到ClassObject->directMethods。则直到需要调用的时候才会解析这些javah风格的函数 。这样的函数dvmResolveNativeMethod(dalvik/vm/Native.cpp)来进行解析,其执行流程如下所示:

    void dvmResolveNativeMethod(const u4* args, JValue* pResult, const Method* method, Thread* self)->(Resolve a native method and invoke it.)

    接着如下:

    • void* func = lookupSharedLibMethod(method)(根据signature在所有已经打开的.so中寻找此函数实现)dvmHashForeach(gDvm.nativeLibs, findMethodInLib,(void*) method)->findMethodInLib(void* vlib, void* vmethod)->dlsym(pLib->handle, mangleCM)
    • dvmUseJNIBridge((Method*) method, func)
    • (*method->nativeFunc)(args, pResult, method, self);(调用执行)

    说完蛋疼Load基础后该准么办?

    答案其实就是推荐Android OS加载JNI Lib的方法的通过JNI_OnLoad。因为通过它你可以干许多自定义的事,譬如实现自己的本地注册等。因为在上面的解析中已经看到了JNI_OnLoad->RegisterNatives->…这两个关键方法。具体细节咱们现在再说说。

    先来看JNI_OnLoad函数

    JNI_OnLoad()函数主要的用途有两点:

    • 通知VM此C组件使用的JNI版本。如果你的.so文件没有提供JNI_OnLoad()函数,VM会默认该.so使用最老的JNI 1.1版本。而新版的JNI做了许多扩充,如果需要使用JNI的新版功能,例如JNI 1.4的java.nio.ByteBuffer, 就必须藉由JNI_OnLoad()函数来告知VM。
    • 因为VM执行到System.loadLibrary()函数时,会立即先调运JNI_OnLoad(),所以C组件的开发者可以由JNI_OnLoad()来进行C组件内的初期值之设定(Initialization)。

    既然有JNI_OnLoad(),那就有相呼应的函数,那就是JNI_OnUnload(),当VM释放JNI组件时会呼叫它,因此在该方法中进行善后清理,资源释放的动作最为合适。

    再来看RegisterNatives函数

    在上面第一部分时我们看见通过javah命令生成的io_github_yanbober_ndkapplication_NdkJniUtils.h里函数的名字好长,看着就蛋疼。你肯定也想过怎么这么长,而且当有时候项目需求原因导致类名变了的时候,函数名必须一个一个的改,更加蛋疼。我第一次接触时那时候自己经验不足,就遇上了这个蛋疼问题。泪奔啊!

    既然这样那就有解决办法的,那就是RegisterNatives大招。接下来来看下这个大招:

    App的Java程序寻找c本地方法的过程一般是依赖VM去寻找*.so里的本地函数,如果需要连续调运很多次,每次都要寻找一遍,会多花许多时间。因此为了解决这个问题我们可以自行将本地函数向VM进行登记,然后让VM自行调registerNativeMethods()函数。

    VM自行调registerNativeMethods()函数的作用主要有两点:  

    • 更加有效率去找到C语言的函数  
    • 可以在执行期间进行抽换,因为自定义的JNINativeMethod类型的methods[]数组是一个名称-函数指针对照表,在程序执行时,可以多次调运registerNativeMethods()函数来更换本地函数指针,从而达到弹性抽换本地函数的效果。

    上面提到的JNINativeMethod结构是c/c++方法和Java方法之间映射关系的关键结构,该结构定义在jni.h中,具体定义如下:

    typedef struct {   
        const char* name;//java方法名称   
        const char* signature; //java方法签名  
        void*       fnPtr;//c/c++的函数指针  
    } JNINativeMethod; 

    所谓自定义的JNINativeMethod类型的methods[]数组自然也就类似长下面这样了:

    static JNINativeMethod methods[] = {  
            {"generateKey", "(Ljava/lang/String;)Ljava/lang/String;", (void*)generateKey},  
    }; 

    以上也就是所谓的动态注册JNI了。

    好了,该补脑的也差不多了,很空洞很枯燥,空虚寂寞冷啊;接下来进入实战吧,通过对第一部分代码的改变来轻松理解这部分扯淡的内容。

    代码实例分析

    点我进入完整工程代码示例下载页面

    我们对第一部分的jni根目录下的c代码修改如下:

    
    #include <jni.h>
    #include <string.h>
    #include <assert.h>
    #include "io_github_yanbober_ndkapplication_NdkJniUtils.h"
    #include "./utils/android_log_print.h"
    #include "./local_logic_c/easy_encrypt.h"
    
    JNIEXPORT jstring JNICALL native_generate_key(JNIEnv *env, jobject obj, jstring name)
    {
         //声明局部量
         char key[KEY_SIZE] = {0};
         memset(key, 0, sizeof(key));
    
         char temp[KEY_NAME_SIZE] = {0};
    
         //将java传入的name转换为本地utf的char*
         const char* pName = (*env)->GetStringUTFChars(env, name, NULL);
    
         if (NULL != pName)
         {
            strcpy(temp, pName);
            strcpy(key, generateKeyRAS(temp));
    
            //java的name对象不需要再使用,通知虚拟机回收name
            (*env)->ReleaseStringUTFChars(env, name, pName);
         }
    
         return (*env)->NewStringUTF(env, key);
      }
    
    //参数映射表
    static JNINativeMethod methods[] = {
        {"nativeGenerateKey", "(Ljava/lang/String;)Ljava/lang/String;", (void*)native_generate_key},
        //这里可以有很多其他映射函数
    };
    
    //自定义函数,为某一个类注册本地方法,调运JNI注册方法
    static int registerNativeMethods(JNIEnv* env , const char* className , JNINativeMethod* gMethods, int numMethods)
    {
        jclass clazz;
        clazz = (*env)->FindClass(env, className);
        if (clazz == NULL)
        {
            return JNI_FALSE;
        }
        //JNI函数,参见系列教程2
        if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0)
        {
            return JNI_FALSE;
        }
    
        return JNI_TRUE;
    }
    
    //自定义函数
    static int registerNatives(JNIEnv* env)
    {
        const char* kClassName = "io/github/yanbober/ndkapplication/NdkJniUtils";//指定要注册的类
        return registerNativeMethods(env, kClassName, methods,  sizeof(methods) / sizeof(methods[0]));
    }
    
    JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
    {
        LOGD("customer---------------------------JNI_OnLoad-----into.\n");
        JNIEnv* env = NULL;
        jint result = -1;
    
        if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK)
        {
            return -1;
        }
        assert(env != NULL);
    
        //动态注册,自定义函数
        if (!registerNatives(env))
        {
            return -1;
        }
    
        return JNI_VERSION_1_4;
    }
    

    相应的h头文件修改如下:

    
    /* DO NOT EDIT THIS FILE - it is machine generated */
    #include <jni.h>
    
    #ifndef _Included_io_github_yanbober_ndkapplication_NdkJniUtils
    #define _Included_io_github_yanbober_ndkapplication_NdkJniUtils
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    JNIEXPORT jstring JNICALL native_generate_key(JNIEnv *env, jobject obj, jstring name);
    
    #ifdef __cplusplus
    }
    #endif
    #endif
    

    对应的java文件中native方法名字换为映射表中的nativeGenerateKey即可。

    以上代码不做详细解释,代码中有注释,同时可以参考该系列第二篇博客。

    点我进入完整工程代码示例下载页面

    总结

    至此一个比Hello World稍微复杂一丁点儿的例子就分析的差不多了。整个JNI的基本雏形也就差不多这样子。下一篇会从其他角度来啃。T_T!!!

    【工匠若水 http://blog.csdn.net/yanbober】 继续阅读《待续新章。。。》

    这里写图片描述

    展开全文
  • 说说代码

    2015-10-30 21:38:39
    众所周知在我们公司的面试流程比较复杂——其中有一个是Homework,即给候选人一份作业,三天之内完成代码,然后会对其Review。然后的环节便是到Office结对编程。...
        

    众所周知在我们公司的面试流程比较复杂——其中有一个是Homework,即给候选人一份作业,三天之内完成代码,然后会对其Review。然后的环节便是到Office结对编程。今天有幸成为一名年轻的面试官,经历了今天两小时的学习后,发现自己在代码方面还是“很弱”。

    无关的编程经验

    只要我有更多时间,我就会写一封更短的信给你。

    从小学算起我的编程年限应该也有十几年了吧,笑~~。只是我过去的多年编程经验对于我现在的工作来说,是多年的无关经验(详见《REWORK》——多年的无关经验)。

    高中的时候学习了点游戏编程,也因此学了点C++的皮毛,除了学会面向对象,其他都忘光了。随后在学习Linux内核,当时代码里就各种struct。比起之前学过的Logo和QBASIC简直是特别大的进步,然当时觉得struct与面向对象两者间没啥太大区别。在那个年少的时候,便天真的以为程序语言间的区别不是很大。

    大学的时候主要营业范围是各种硬件,也没有发现写出好的代码是特别重要的一件事。也试了试Lisp,尝试过设计模式,然后失败了,GoF写DP的时候一定花了特别长的时间,所以这本书很短。期间出于生活压力(没有钱买硬件),便开始兼职各种Web前端开发。

    在有了所谓的GNU/Linux系统编译经验、写过各种杂七杂八的硬件代码,如Ada、汇编,要保证代码工作是一件很简单的事,从某个项目中引入部分代码,再从某个Demo中引入更多的代码,东拼西凑一下就能工作了。

    多年的无关经验只让我写出能工作的代码——在别人看来就是很烂的代码。于是,虽然有着看上去很长的编程经验,但是却比不上实习的时候6个月学到的东西。

    只是因为,我们不知道: 我们不知道。

    代码整洁

    过去,我有过在不同的场合吐槽别人的代码写得烂。而我写的仅仅是比别人好一点而已——而不是好很多。

    然而这是一件很难的事,人们对于同一件事物未来的考虑都是不一样的。同样的代码在相同的情景下,不同的人会有不同的设计模式。同样的代码在不同的情景下,同样的人会有不同的设计模式。在这里,我们没有办法讨论设计模式,也不需要讨论。

    我们所需要做的是,确保我们的代码易读、易测试,看上去这样就够了,然而这也是挺复杂的一件事:

    1. 确保我们的变量名、函数名是易读的

    2. 没有复杂的逻辑判断

    3. 没有多层嵌套

    4. 减少复杂函数的出现

    然后,你要去测试它。这样你就知道需要什么,实际上要做到这些也不是一些难事。

    只是首先,我们要知道我们要自己需要这些。

    别人的代码很烂?

    什么是很烂的代码? 应该会有几种境界吧。

    1. 不能工作,不能读懂

    2. 不能工作,能读懂

    3. 能工作,很难读懂

    4. 能工作,能读懂,但是没有意图

    5. 能工作,能理解意图,但是读不懂

    如果我们能读懂,能理解意图,那么我们还说他烂,可能是因为他并不整洁。这就回到了上面的问题,模式是一种因人而异的东西。

    我们在做Code Review的时候,总会尝试问对方说: “这样做的意图是”。

    对于代码来说也是如此,如果我们能理解意图的话,那么我们要理解代码相对也比较容易。如果对方是没有意图,那么代码是没救的。

    自我救赎

    于是,回到开始的地方,现在的我还是只能写出整洁的代码。

    展开全文
  • 虽然一般情况下默认的清单文件只包含两个条目,但是复杂的清单文件可以包含更多的条目。 清单文件的条目被分成几个部分。每个节都有两个条目节名及其值。 清单文件中除了包含相关的版本信息外号,好包含作者签名,...
  • 说说异步

    2018-12-11 12:26:06
    首先,因为JavaScript语言是单线程的*(目标为浏览器端,出生即为单线程)*,所以就需要异步,否则JavaScript脚本智能自上而下执行,如果在上部存在一些极其复杂的代码需要解析很长的时间的话,下面的代码就会遭到阻塞...
  • 说说微服务

    千次阅读 2016-09-09 17:38:14
    2. 底层复杂性扩散,各业务层需要缓存,分表分库等底层技术 3. 基础库耦合,库的版本维护与业务线之间代码的耦合 4. SQL质量得不到保障,业务相互影响 5. 数据库耦合,各业务系统耦合了多张表,无法垂直拆分
  • 说说MVP模式

    2019-09-24 01:05:09
    初看MVP模式时被它复杂的包含,继承,接口搞晕,View中有Presenter,Presenter中又有View,View又要抽象出IView,View又调用Presenter的方法,Presenter又调用IView的方法.花了点时间算是搞明白了,这里说说自己的理解. ...
  • 说说搜索

    2012-08-05 23:34:00
    也说高圆圆老了,看她的装束的确是如此,那公车上的一出戏和老板办公室的一抹眼泪都能看出这女子已经过了青葱玉立,过了盛华玲珑,无论是肢体还是神态,都多了岁月留下的复杂,再也简单不起来了。但她还是太美了,...
  • 随便说说

    2010-09-01 19:29:48
    这两天给系统加了一个基于hessian的远程服务,本来很简单的一个东西,却因为种种原因,发布过程颇为坎坷,确实系统业务和流程非常庞大复杂,我刚开始来,也确实对系统不熟悉,但是不管怎样,我应该反思。    ...
  • 说说你还使用过哪些视频后期软件?说说优缺点?以下软件几乎都只有PC/工作站版本。(没有手机APP版本。)都是商业软件,授权费用高昂!...功能复杂,上手容易,但是学精学习曲线比较长!授权费用昂贵:(参考价...
  • QQ空间说说爬虫

    2018-08-20 02:28:00
    这次采用的主要模块是selenium,这是一个模拟浏览器的模块,一开始我不想用这个模块写的,但是后面分析的时候,发现QQ空间的数据加密有点复杂,也没有找到好用的接口,正好又有在学习这个模块,然后就直接用这个模块...
  • 说说Mochiweb

    千次阅读 2009-03-10 10:32:00
    Mochiweb是什么,有多么多么好,……这些就别跟着白话了。想说的是对Mochiweb的感受。...Yaws有一堆现成的工具,功能也很强,但它的用法复杂死板,要以学习半个Erlang的工夫去掌握它。Yaws大致相当于

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,713
精华内容 1,085
关键字:

复杂说说