一天掌握Android JNI本地编程 快速入门

一、JNI(Java Native Interface)

       1、什么是JNI:
              JNI(Java Native Interface):java本地开发接口

              JNI是一个协议,这个协议用来沟通java代码和外部的本地代码(c/c++)

              外部的c/c++代码也可以调用java代码
       2、为什么使用JNI
              效率上 C/C++是本地语言,比java更高效
              代码移植,如果之前用C语言开发过模块,可以复用已经存在的c代码
              java反编译比C语言容易,一般加密算法都是用C语言编写,不容易被反编译
       3、Java基本数据类型与C语言基本数据类型的对应
              
       3、引用类型对应
              
       4、堆内存和栈内存的概念
              栈内存:系统自动分配和释放,
                      保存全局、静态、局部变量,
                      在站上分配内存叫静态分配,
                      大小一般是固定的
              堆内存:程序员手动分配(malloc/new)和释放(free/java不用手动释放,由GC回收),
                      在堆上分配内存叫动态分配,
                      一般硬件内存有多大堆内存就有多大
二、交叉编译
       1、交叉编译的概念
          交叉编译即在一个平台,编译出另一个平台能够执行的二进制代码
          主流平台有: Windows、 Mac os、 Linux
          主流处理器: x86、 arm、 mips
       2、交叉编译的原理
          即在一个平台上,模拟其他平台的特性
          编译的流程: 源代码-->编译-->链接-->可执行程序
       3、交叉编译的工具链
          多个工具的集合,一个工具使用完后接着调用下一个工具
       4、常见的交叉编译工具
          NDK(Native Development Kit): 开发JNI必备工具,就是模拟其他平台特性类编译代码的工具
          CDT(C/C++ Development Tools): 是Eclipse开发C语言的一个插件,高亮显示C语言的语法
          Cygwin: 一个Windows平台的Unix模拟器(可以参考之前博客Cygwin简介及使用
       5、NDK的目录结构(可以在Google官网下载NDK开发工具,需要FQ)
          docs: 帮助文档
          build/tools:linux的批处理文件
          platforms:编译c代码需要使用的头文件和类库
          prebuilt:预编译使用的二进制可执行文件
          sample:jni的使用例子
          source:ndk的源码
          toolchains:工具链
          ndk-build.cmd:编译打包c代码的一个指令,需要配置系统环境变量
三、JNI的第一个例子
          好了,准备知识已经完毕,下面开始我们的一个JNI例子。
        1、新建一个Android项目,在根目录下创建 jni文件夹,用于存放 C源码。
        2、在java代码中,创建一个本地方法 getStringFromC 本地方法没有方法体。
  1. private native String getStringFromC();

            3、在jni中创建一个C文件,定义一个函数实现本地方法,函数名必须用使用 本地方法的全类名,点改为下划线。

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <jni.h>
 4 //方法名必须为本地方法的全类名点改为下划线,穿入的两个参数必须这样写,  5 //第一个参数为Java虚拟机的内存地址的二级指针,用于本地方法与java虚拟机在内存中交互  6 //第二个参数为一个java对象,即是哪个对象调用了这个 c方法  7 jstring Java_com_mwp_jnihelloworld_MainActivity_getStringFromC(JNIEnv* env,  8  jobject obj){  9 //定义一个C语言字符串 10 char* cstr = "hello form c"; 11 //返回值是java字符串,所以要将C语言的字符串转换成java的字符串 12 //在jni.h 中定义了字符串转换函数的函数指针 13 //jstring (*NewStringUTF)(JNIEnv*, const char*); 14 //第一种方法:很少用 15 jstring jstr1 = (*(*env)).NewStringUTF(env, cstr); 16 //第二种方法,推荐 17 jstring jstr2 = (*env) -> NewStringUTF(env, cstr); 18 return jstr2; 19 }

        4、在jni中创建 Android.mk文件,用于配置 本地方法

  1.  LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)#编译生成的文件的类库叫什么名字LOCAL_MODULE    := hello#要编译的c文件LOCAL_SRC_FILES := Hello.cinclude $(BUILD_SHARED_LIBRARY)

             5、在jni目录下执行 ndk-build.cmd指令,编译c文件

         6、在java代码中加载编译后生成的so类库,调用本地方法,将项目部署到虚拟机上之后就会发现toast弹出的C代码定义的字符串,第一个例子执行成功了。
static{//加载打包完毕的 so类库System.loadLibrary("hello");}

         7、jni打包的C语言类库默认仅支持 arm架构,需要在jni目录下创建 Android.mk 文件添加如下代码可以支持x86架构

  1. APP_ABI := armeabi armeabi-v7a x86


    四、JNI常见错误

         1、findLibrary returned null:
                CPU平台不匹配 或者 在加载类库时,类库名字写错了
         2、本地方法找不到:
                忘记加载类库了 或者 C代码中方法名写错了
   
五、javah工具与javap工具
         1、javah:  生成本地方法头文件
            需要在C/C++模块下才能生效
            在JDK1.7中,在src目录下执行javah 全类名
            在JDK1.6中,在bin/classes目录下执行
         2、javap:  打印方法签名
            在C语言中调用java的方法需要用到反射,C语言的反射需要一个方法签名,使用javap能够生成方法签名,很熟练的话也可以自己写方法签名
            在bin/classes目录下执行 javap -s 全类名
            
六、使用本地方法加密字符串的一个小例子
      C语言字符串与Java中的字符串类型不同,所以需要进行字符串类型转换。
      一个重要的思想:C语言计算字符串的长度不方便,但是java很方便,只需要调用一个length()方法就可以,所以像这种需求,那个语言有优势就用哪个语言算,算完当做参数传递给另一种语言就ok。
                      混合语言编程这应该是一种非常有用的思想。
     Java非常容易被反编译,所以加密都是用 c语言写的
#include <jni.h>
#include <string.h>
//将java字符串转换为c语言字符串(工具方法)
char*   Jstring2CStr(JNIEnv*   env,   jstring   jstr)
{char* rtn = NULL; jclass clsstring = (*env)->FindClass(env,"java/lang/String"); jstring strencode = (*env)->NewStringUTF(env,"GB2312"); jmethodID mid = (*env)->GetMethodID(env,clsstring, "getBytes", "(Ljava/lang/String;)[B"); jbyteArray barr= (jbyteArray)(*env)->CallObjectMethod(env,jstr,mid,strencode); // String .getByte("GB2312"); jsize alen = (*env)->GetArrayLength(env,barr); jbyte* ba = (*env)->GetByteArrayElements(env,barr,JNI_FALSE); if(alen > 0) { rtn = (char*)malloc(alen+1); //"\0"  memcpy(rtn,ba,alen); rtn[alen]=0; } (*env)->ReleaseByteArrayElements(env,barr,ba,0); // return rtn; } JNIEXPORT jstring JNICALL Java_com_mwp_encodeanddecode_MainActivity_encode (JNIEnv * env, jobject obj, jstring text, jint length){ char* cstr = Jstring2CStr(env, text); int i; for(i = 0;i<length;i++){ *(cstr+i) += 1; //加密算法,将字符串每个字符加1  } return (*env)->NewStringUTF(env,cstr); } JNIEXPORT jstring JNICALL Java_com_mwp_encodeanddecode_MainActivity_decode (JNIEnv * env, jobject obj, jstring text, jint length){ char* cstr = Jstring2CStr(env, text); int i; for(i = 0;i<length;i++){ *(cstr+i) -= 1; } return (*env)->NewStringUTF(env, cstr); }

 

七、JNI操作一个数组(引用传递)
          传递数组其实是传递一个堆内存的数组首地址的引用过去,所以实际操作的是同一块内存,
          当调用完方法,不需要返回值,实际上参数内容已经改变,
          Android中很多操作硬件的方法都是这种C语言的传引用的思路
 1 public class MainActivity extends Activity {
 2  3 static{  4 System.loadLibrary("encode");  5  }  6 int[] array = {1,2,3,4,5};  7  @Override  8 protected void onCreate(Bundle savedInstanceState) {  9 super.onCreate(savedInstanceState); 10  setContentView(R.layout.activity_main); 11  } 12 13 public void click(View v){ 14  encodeArray(array); 15 //不需要返回值,实际操作的是同一块内存,内容已经发生了改变 16 for (int i : array) { 17  System.out.println(i); 18  } 19  } 20 21 //传递数组其实是传递一个堆内存的数组首地址的引用过去,所以实际操作的是同一块内存, 22 //当调用完方法,不需要返回值,实际上参数内容已经改变, 23 //Android中很多操作硬件的方法都是这种C语言的传引用的思路,要非常熟练 24 private native void encodeArray(int[] arr); 25 }

 

 1 #include <jni.h>
 2 /*
 3  * Class:     com_mwp_jniarray_MainActivity
 4  * Method: encodeArray  5  * Signature: ([I)V  6 */  7 JNIEXPORT void JNICALL Java_com_mwp_jniarray_MainActivity_encodeArray  8 (JNIEnv * env, jobject obj, jintArray arr){  9 //拿到整型数组的长度以及第0个元素的地址 10 //jsize (*GetArrayLength)(JNIEnv*, jarray); 11 int length = (*env)->GetArrayLength(env, arr); 12 // jint* (*GetIntArrayElements)(JNIEnv*, jintArray, jboolean*); 13 int* arrp = (*env)->GetIntArrayElements(env, arr, 0); 14 int i; 15 for(i = 0;i<length;i++){ 16 *(arrp + i) += 10; //将数组中的每个元素加10 17  } 18 }

 

八、偷用美图秀秀的C语言本地类库加深JNI的理解
    项目中不需要有c代码,只需要有一个编译过后的类库供Java调用就可以了。
    将美图秀秀的apk文件解压缩,将lib目录下C类库导入自己的项目,
    反编译美图秀秀的apk文件,将其本地方法类 JNI.java复制到自己的项目
    根据本地方法名和参数猜函数的作用及如何使用,
    下例调用了美图的一个LOMO美化效果
 1 public class MainActivity extends Activity {
 2  3 static{  4 //加载美图秀秀的类库  5 System.loadLibrary("mtimage-jni");  6  }  7 private ImageView iv;  8 private Bitmap bitmap;  9  @Override 10 protected void onCreate(Bundle savedInstanceState) { 11 super.onCreate(savedInstanceState); 12  setContentView(R.layout.activity_main); 13 14 iv = (ImageView) findViewById(R.id.iv); 15 16 bitmap = BitmapFactory.decodeFile("sdcard/aneiyi.jpg"); 17  iv.setImageBitmap(bitmap); 18  } 19 20 public void click(View v){ 21 22 int width = bitmap.getWidth(); 23 int height = bitmap.getHeight(); 24 25 //用于保存所有像素信息的数组 26 int[] pixels = new int[width*height]; 27 //获取图片的像素颜色信息,保存至pixels 28 bitmap.getPixels(pixels, 0, width, 0, 0, width, height); 29 30 JNI jni = new JNI(); 31 //调用美图秀秀本地库中的美图方法,靠猜 32 //arg0:保存了所有像素颜色信息的数组 33 //arg1:图片的宽 34 //arg2:图片的高 35 //此方法是通过改变pixels的像素颜色值来实现美化效果,传递一个数组参数是不需要返回值的 36  jni.StyleLomoB(pixels, width, height); 37 38 Bitmap bmNew = Bitmap.createBitmap(pixels, width, height, bitmap.getConfig()); 39  iv.setImageBitmap(bmNew); 40  } 41 }

 

九、在C语言中调用java方法(反射)
        1、有时需要在C语言中调用java的方法,如刷新UI显示加载资源进度
           在本地方法C语言代码中打印 Android的Logcat日志输出,Google已经帮我们封装好了方法,只需要调用一下就可以
           如果要输出中文的话,必须将C语言的文件编码改成 utf-8,否则乱码
           在C语言中调用java的方法需要用到反射,C语言的反射需要一个方法签名,使用javap能够生成方法签名,很熟练的话也可以自己写方法签名
           在bin/classes目录下执行 javap -s 全类名
 1 public class MainActivity extends Activity {
 2 static{  3 System.loadLibrary("hello");  4  }  5  6  @Override  7 protected void onCreate(Bundle savedInstanceState) {  8 super.onCreate(savedInstanceState);  9  setContentView(R.layout.activity_main); 10  } 11 12 public void click(View v){ 13  cLog(); 14  } 15 16 public native void cLog(); 17 18 public void show(String message){ 19 Builder builder = new Builder(this); 20 builder.setTitle("标题"); 21  builder.setMessage(message); 22  builder.show(); 23  } 24 25 }
#include <jni.h>
#include <android/log.h>
#define LOG_TAG "System.out"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
JNIEXPORT void JNICALL Java_com_mwp_ccalljava2_MainActivity_cLog (JNIEnv * env, jobject obj){ //打印log输出 LOGD("我是C语言打印的debug日志"); LOGI("我是C语言打印的info日志"); //通过反射来调用java的方法,需要知道方法签名,使用javap得到方法签名 //在bin/classes目录下执行 javap -s 全类名 //1、得到类的字节码对象 //jclass (*FindClass)(JNIEnv*, const char*); jclass clazz = (*env)->FindClass(env, "com/mwp/ccalljava2/MainActivity"); //jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*); jmethodID methodID = (*env)->GetMethodID(env, clazz, "show", "(Ljava/lang/String;)V"); //void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...); (*env)->CallVoidMethod(env,obj,methodID, (*env)->NewStringUTF(env, "这是弹窗的内容")); }
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_LDLIBS += -llog
LOCAL_MODULE    := hello
LOCAL_SRC_FILES := log.c
include $(BUILD_SHARED_LIBRARY)


十、模拟监测压力传感器

        传感器的原理是使用敏感电阻如(光敏电阻,热敏电阻)等监测电流电压的变化
        Android程序只需要处理传感器传递的数据,并将其显示在界面上就可以。
        下面模拟一个压力传感器来练习JNI编程
 1 public class MainActivity extends Activity {
 2 static{  3 System.loadLibrary("monitor");  4  }  5 private MyProgressBar mpb;  6  @Override  7 protected void onCreate(Bundle savedInstanceState) {  8 super.onCreate(savedInstanceState);  9  setContentView(R.layout.activity_main); 10 11 mpb = (MyProgressBar) findViewById(R.id.mpb); 12 mpb.setMax(100); 13  } 14 15 public void start(View v){ 16 new Thread(){ 17 public void run() { 18  startMonitor(); 19  }; 20  }.start(); 21  } 22 23 public void stop(View v){ 24  stopMonitor(); 25  } 26 27 public native void startMonitor(); 28 public native void stopMonitor(); 29 30 //供本地方法调用刷新UI 31 public void show(int pressure){ 32  mpb.setPressure(pressure); 33  } 34 }
#include <jni.h>
#include <stdio.h>
#include <stdlib.h>
//模拟压力传感其传递数据
int getPressure(){ return rand()%101; } //用于控制循环的开关 int monitor; JNIEXPORT void JNICALL Java_com_mwp_monitor_MainActivity_startMonitor (JNIEnv * env, jobject obj){ monitor = 1; int pressure; jclass clazz; jmethodID methodid; while(monitor){ //本地方法获取传感器数据 pressure= getPressure(); //使用反射调用java方法刷新界面显示 //jclass (*FindClass)(JNIEnv*, const char*); clazz= (*env)->FindClass(env, "com/mwp/monitor/MainActivity"); //jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*); methodid= (*env)->GetMethodID(env, clazz, "show","(I)V"); // void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...); (*env)->CallVoidMethod(env, obj, methodid, pressure); sleep(1); } } JNIEXPORT void JNICALL Java_com_mwp_monitor_MainActivity_stopMonitor (JNIEnv * env, jobject obj){ //结束循环 monitor = 0; }

 

十一、使用C++代码实现本地方法
         1、把c文件后缀名换成cpp
         2、Android.mk文件中的hello.c也要换成hello.cpp
         3、c++的使用的环境变量结构体中,访问了c使用的结构体的函数指针,函数名全部都是一样的,只是参数去掉了结构体指针
         4、访问函数指针时,把env前面的*号去掉,因为此时env已经是一级指针
         5、clean,清除之前编译的残留文件
         6、把声明函数的h文件放入jni文件夹中,include该h文
#include <jni.h>
#include "com_mwp_cplusplus_MainActivity.h"
JNIEXPORT jstring JNICALL Java_com_mwp_cplusplus_MainActivity_helloC(JNIEnv * env, jobject obj){ char* cstr = "hello from c"; //return (*env)->NewStringUTF(env, cstr); return env->NewStringUTF(cstr); }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/283239.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[转]CentOS 7忘记root密码解决办法

转自&#xff1a;http://www.linuxidc.com/Linux/2016-08/134034.htm 亲测可用&#xff01; CentOS 7 root密码的重置方式和CentOS 6完全不一样&#xff0c;CentOS 7与之前的版本6变化还是比较大的&#xff0c;以进入单用户模式修改root密码为例。 1.重启开机按esc 2.按e 3.编…

美链BEC合约漏洞技术分析

这两天币圈链圈被美链BEC智能合约的漏洞导致代币价值几乎归零的事件刷遍朋友圈。这篇文章就来分析下BEC智能合约的漏洞 <!-- more --> 漏洞攻击交易 我们先来还原下攻击交易&#xff0c;这个交易可以在这个链接查询到。我截图给大家看一下&#xff1a; 攻击者向两个账号转…

vue 公众号扫描_vue编写微信公众号打开相机功能

vue编写微信公众号打开相机功能&#xff0c;什么都不多说直接上代码页面布局代码class"previewer-demo-img":key"index":src"item.src"width"100"click"previewImg(index)">1.微信config初始化前端代码initWxConfig() {l…

SQL Server-聚焦NOT IN VS NOT EXISTS VS LEFT JOIN...IS NULL性能分析(十八)

前言 本节我们来综合比较NOT IN VS NOT EXISTS VS LEFT JOIN...IS NULL的性能&#xff0c;简短的内容&#xff0c;深入的理解&#xff0c;Always to review the basics。 NOT IN、NOT EXISTS、LEFT JOIN...IS NULL性能分析 我们首先创建测试表 USE TSQL2012 GOCREATE SCHEMA [c…

global using 的另类用法

前言global using 指令在 C# 10 中被引入&#xff0c;意味着 using 将应用于编译中的所有文件&#xff08;通常是一个项目&#xff09;。比如&#xff1a;global using System.Text;则在同一项目的其他位置&#xff0c;可以直接使用 System.Text 下的所有类型而无需再次声明 us…

利用 Node.js 实现 SAP Hana 数据库编程接口

为什么80%的码农都做不了架构师&#xff1f;>>> 自 SAP HANA SP 11 之后&#xff0c;可以使用 Node.js 作为 Hana 的编程接口。SAP 将 Application server 简称为 XS。现在 XS 已经演化为 Advanced 版本。为了区别&#xff0c;早期的 XS 被称为 XS Classical。 从下…

WPF 实现自绘验证码

WPF 实现自绘验证码控件名&#xff1a;VerifyCode作者&#xff1a;WPFDevelopersOrg原文链接&#xff1a; https://github.com/WPFDevelopersOrg/WPFDevelopers框架使用大于等于.NET40&#xff1b;Visual Studio 2022;项目使用 MIT 开源许可协议&#xff1b;如何通过DrawingV…

css中的单位换算_CSS单位px、em、rem及它们之间的换算关系

作者:WangMin格言:努力做好自己喜欢的每一件事国内的设计师大都喜欢用px&#xff0c;而国外的网站大都喜欢用em和rem&#xff0c;那么三者的区别与优势是什么&#xff1f;接下来我们就来学习一下吧&#xff01;单位px、em、rem分别表示什么&#xff1f;1、 px(Pixel) 相对于显示…

【MAC】Ncnn 编译so文件方案

【MAC】Ncnn 编译so文件方案 1、下载ncnn github地址是&#xff1a;https://github.com/Tencent/ncnn 指定目录&#xff1a;在终端或者git管理工具 输入&#xff1a;git clone https://github.com/Tencent/ncnn.git 2、编译Ncnn 2.1 Mac平台 安装cmake、wget&#xff08;根据实…

SSM学习注意杂记

2019独角兽企业重金招聘Python工程师标准>>> 1.spring导包时一定要版本对应&#xff0c;最好不要导不同版本的包&#xff0c;还有mybatis的包&#xff0c;springmvc的包&#xff0c;三个框架的包都需配套&#xff0c;要不然会出现一些想象不到的错误。 2.mybatis写映…

《ASP.NET Core 6框架揭秘》实例演示[15]:针对控制台的日志输出

针对控制台的ILogger实现类型为ConsoleLogger&#xff0c;对应的ILoggerProvider实现类型为ConsoleLoggerProvider&#xff0c;这两个类型都定义在 NuGet包“Microsoft.Extensions.Logging.Console”中。ConsoleLogger要将一条日志输出到控制台上&#xff0c;首选要解决的是格式…

《HeadFirst Python》第一章学习笔记

对于Python初学者来说&#xff0c;舍得强烈推荐从《HeadFirst Python》开始读起&#xff0c;这本书当真做到了深入浅出&#xff0c;HeadFirst系列&#xff0c;本身亦是品质的保证。这本书舍得已在《Python起步&#xff1a;写给零编程基础的童鞋》一文中提供了下载。为了方便大家…

Oracle-13:Oracle中的表分区

------------吾亦无他,唯手熟尔&#xff0c;谦卑若愚&#xff0c;好学若饥------------- 本篇博客记录了表分区 表分区的含义&#xff1a; 典型的拿空间换时间的案例&#xff01; 表分区对一张表进行分区&#xff0c;分区之后表中的数据存在相对应的分区内&#xff08;可以是不…

js控制图像等比例缩放

<!DOCTYPE html> <html> <head><title>图片内部放大效果</title> <meta charset"utf-8"> <style type"text/css">#imgborder{ width: 200px;height: 160px;border: 3px solid #000; overflow: hidden;position:…

mysql一张表最多多少索引_MySQL一个索引最多有多少个列?真实的测试例子

MySQL一个索引最多有多少个列&#xff1f;真实的测试例子更新时间&#xff1a;2009年07月01日 22:22:21 作者&#xff1a;MySQL一个索引最多有多少个列&#xff1f;下面是具体的实现代码。最多16列。create table test (f1 int,f2 int,f3 int,f4 int,f5 int,f6 int,f7 int,f8…

.NET Core 使用 LibreOffice 实现 Office 预览(Docker 部署)

前些年做云盘产品的时候&#xff0c;一个很核心的功能就是 Office 文件预览&#xff0c;当时还没有使用 .NET Core ,程序部署在 Windows Server 服务器上&#xff0c;文件预览的方案采用了微软的 OWA 。目前在做的零代码产品中的表单附件控件&#xff0c;同样面临着 Office 文件…

[开源精品] C#.NET im 聊天通讯架构设计 -- FreeIM 支持集群、职责分明、高性能

&#x1f4bb; FreeIM 是什么&#xff1f;FreeIM 使用 websocket 协议实现简易、高性能&#xff08;单机支持5万连接&#xff09;、集群即时通讯组件&#xff0c;支持点对点通讯、群聊通讯、上线下线事件消息等众多实用性功能。 ImCore 已正式改名为 FreeIM。使用场景&#xff…

用websploit获取管理员后台地址

1, use web/dir_scanner 2, set TARGET http://www.****.com 3, run SOURCE: https://sourceforge.net/projects/websploit/ WebSploit Advanced MITM Framework[]Autopwn – Used From Metasploit For Scan and Exploit Target Service[]wmap – Scan,Crawler Target Used Fro…

《ASP.NET Core 6框架揭秘》实例演示[16]:内存缓存与分布式缓存的使用

.NET提供了两个独立的缓存框架&#xff0c;一个是针对本地内存的缓存&#xff0c;另一个是针对分布式存储的缓存。前者可以在不经过序列化的情况下直接将对象存储在应用程序进程的内存中&#xff0c;后者则需要将对象序列化成字节数组并存储到一个独立的“中心数据库”。对于分…

人工智能教程007:创建一个卷积神经网络(2)

2019独角兽企业重金招聘Python工程师标准>>> 我们如何对图像应用卷积 当我们在图像上应用卷积时&#xff0c;我们在两个维度上执行卷积——水平和竖直方向。我们混合两桶信息&#xff1a;第一桶是输入的图像&#xff0c;由三个矩阵构成——RGB三通道&#xff0c;其中…