前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >一个人单挑滴滴Android开发团队?

一个人单挑滴滴Android开发团队?

作者头像
用户1907613
发布于 2019-07-08 03:09:30
发布于 2019-07-08 03:09:30
1.8K00
代码可运行
举报
文章被收录于专栏:Android群英传Android群英传
运行总次数:0
代码可运行

本文是一位朋友的投稿,他花了很大精力来实现了一个滴滴客户端的完整功能,非常具有学习的价值,推荐给大家~

作者: 谭妥 | 来源:公众号: 谭妥

https://blog.csdn.net/weixin_37734988/article/details/92796055

前言

这是我自己做的一个仿滴滴打车的Android出行项目,主要针对滴滴等出行平台一直饱受质疑的“人车不符”问题,以及当前越发火热的国际化和出海战略,给出行项目增加了下面几个功能:

  1. RFID识别验证功能:在司机证件或者车内识别硬件里嵌入RFID识别芯片,乘客使用手机读取到芯片信息,并且通过网络(okhttp3)发送到出行平台数据库进行验证(我用NDK加了一个C语言的MD5加密算法对识别到的信息进行了加密)。如果不是合规的“人”或“车”,则不能完成订单并向平台或监管单位汇报当前位置。(为了方便读者测试,可以使用手机读取任何一个加密或非加密RFID芯片,比如银行卡、公交卡等,我在代码中的验证前阶段把芯片信息都换成我自己的司机信息,确保读者测试时可以收到服务器的回复。
  2. 海外版功能:点击切换当前语言。
  3. 司机证件号码识别功能:读取司机证件上的证件号码,也可以用来与出行平台数据库的信息进行核实比对。

项目源码地址:https://github.com/18601949127

项目代码都是一行一行自己敲的,在多部手机上调试过确保各项功能能够顺畅运行。GitHub的源码中保留了所有的手机CPU指令集架构,保证在所有手机上能够运行成功。觉得包太大的同学可以自己把不需要的 .so 指令集删掉,主要是做识别的 OpenCV4Android.so 包比较大,其次是百度地图的包。

地图我使用的是百度地图LBS 版本5.3,海外的话考虑到信息数据多少、性能、包大小、数据源等多方面因素推荐使用mapbox。感兴趣的读者可以看Trinea 的这篇文章:https://blog.csdn.net/weixin_37734988/article/details/92852349

文章发布以后得到《滴滴国际化项目 Android 端演进》作者滴滴公司技术专家Trinea @trinea 的提点, 告诉我要特别根据海外版的应用场景认真分析国外几款 Map 服务的各项优劣,比较Google地图、 mapbox 、Nutiteq 等,非常感谢。我后面会单独写几篇关于 mapbox使用的文章并且分享出来

读者如果想到滴滴出行或者其他平台比较实用的功能可以留言或者微信给我(微信:18601949127),我会抽时间把好的 idea 或者功能继续添加到项目里。

开发环境

1.Android端:Android Studio 版本3.4, 百度地图LBS 版本5.3 , OpenCV4Android 版本3.2

2.服务器端:Apache + PHP + MySQL 用的是我自己租的腾讯云主机做服务器,我会一直开放出这个项目的接口,接受并处理读者发来的测试请求。

主界面概览

界面最上面TitleBar 的位置是主要的功能区,除了中间的醒目logo,两侧分布主要功能选项,最左边的SlidingMenu提供侧滑菜单,给乘客个人信息和软件设置提供入口,右边的证件标志按钮用于导向司机证件号码识别功能,再右边的英语标志按钮是国际化语言切换,最右边的无线标志是RFID识别认证功能的入口。

主界面的中间部分是地图区域,可以在上边选择不同交通工具,用于展示乘客所在位置,附近车辆或者POI热点,以及路径规划。

主界面的下方可以提供上划菜单,主要用于上车和目的地地址关键字输入,以及安全提示信息或者广告的入口。

项目文件结构

首先介绍一下项目文件结构,方便读者阅读代码:

包名:com.tantuo.didicar

  • Activity 文件夹:有的Activity 相对独立,并不属于某个功能模块,可以放到这个文件夹。
  • adapter 文件夹:相对复杂一点的adapter会从类文件中取出单独保存到 adapter文件夹,比如左侧侧滑菜单中 recycler view的adapter。简单一点的adapter还是会保存在调用的类中。
  • Bean 文件夹:存放Entity 实体类,比如司机的相关信息会包装成一个DriverBean,每个司机都是一个类对象,使用Gson 传递很方便,用的时候get,set 就可以。
  • DriverLicenseNFC 文件夹:RFID识别验证模块,乘客使用这个功能模块验证司机身份或者车辆信息。
  • DriverLicenseRecognition 文件夹:司机证件号码OCR识别的功能模块。
  • splash 文件夹:app 初始化和引导界面。
  • TabFragment:主界面上方的滑动主题条用来切换交通工具或者服务项目(Tab),不同的交通工具或者服务项目代码都保存在TabFragment 文件夹里。
  • utils 文件夹:用来保存项目用到的各种工具类,比如DriverRouteOverlay 用来在地图上渲染规划出来的驾车路线,MD5JniUtils 用NDK调用MD5加密算法,保护RFID芯片信息,NfcUtils 用来管理手机的NFC功能,POIOverlay 用来在地图上渲染周围兴趣点(POI)。

把工具类从Activity 或者 Fragment 中extract 出来放到统一的utils 文件夹,会让你的代码更清晰,可读性更强。

引导界面

先看下真机上的效果:

引导界面最初的logo动画是用我自己用SVG矢量动画做的,路径规划描述在 drawable 的splash_logo.xml 文件里:

还需要资源文件里的animator文件夹下的didilogoanimator.xml 对路径进行动画描述:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<objectAnimator
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="3000"
    android:propertyName="trimPathEnd"
    android:repeatCount="0"
    android:valueFrom="0"
    android:valueTo="1"
    android:valueType="floatType"/>

这几秒的时间里可以在下图的位置添加一些初始化代码,比如网络请求,得到后续Activity的素材,地理位置等等。

出行界面

通过滑动地图界面上方的主题可以切换不同的项目界面。滑动主题条是一个VIewPager的 Indicator,每一个主题对应一个下面的服务项目,放在各自独立的VIewPager里。每个服务项目有各自独立的上划菜单,作为此服务对应的地址关键字输入或者相关信息入口。

出行界面的UI结构:

注意:乘客的位置信息、当前经纬度、当前街道名字、楼宇名字都是在MainAcitivity做为静态成员变量定义的,原因是在别的Acitivity或者类中,这些变量需要经常使用,直接调用 MainActivity.CurrentLocation就可以了,后面用到的所有当前位置,都是在MainActivity中 MyLocationListener 类得到的。

上车地址和目的地址的路线规划

不同交通工具(快车,出租车,单车,公交车等等)对应的服务项目都嵌在TItleBar下边的 VIewpager里,一个服务项目对应一个独立的Fragmen文件,由其顶部的的VIewpagerIndicator滑动切换。服务项目的主要代码在com.tantuo.didicar 包下 TabFragment 文件夹里。

底部上滑动菜单

buttonsheet是在布局文件中加入android.support.v4.widget.NestedScrollView类的 bottom_sheet_behavior

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
app:layout_behavior="@string/bottom_sheet_behavior"

左侧侧滑菜单

左侧侧滑菜单可以作为个人信息、安全提示、设置信息的入口

司机证件的号码OCR识别功能

证件号码识别功能的主要代码在com.tantuo.didicar 包下的 DriverLicenseRecognition 模块。

还是先看下真机效果:

点击进入司机证件号码识别功能以后,可以选择对证件拍照,为了方便演示,这里是从手机相册选择刚刚拍的照片。同时为了方便读者测试这个功能,我把照片保存在了开发包的asset文件夹里面,这样读者下载我保存在GIthub上https://github.com/18601949127 的版本,点击选择司机证件以后调用的是我保存在assets 文件夹里的司机证件照片,也就是下面图片里的 getDriverLicenseFromMySample() 方法,可以立刻进行测试。想继续从手机相册读取的读者可以执行LicenseMainActivity 下的 LicenseMainActivity 方法。

这里用我以前在国外读书时候的证件作为例子:

首先:要从照片中找到司机证件区域,也就是上证件边缘红色的区域

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
    * 找到图像中的证件区域
    * 在RGB色彩空间求取驾驶员证件的图像梯度,之后在此图像上做二值化,从而通过轮廓(contour)发现与面积大小过滤得到证件区域
    * author:Tantuo 86-1860194917
    * @param fileUri
    * @return
    */
    public Mat findLicenseContour(Uri fileUri) {
        //首先使用openCV 的 Imgcodecs类得到相机获取的证件图片
        Mat src = Imgcodecs.imread(fileUri.getPath());
        if (src.empty()) {
            return null;
        }
        //得到证件照片的x梯度和y梯度
        Mat grad_x = new Mat();
        Mat grad_y = new Mat();
        Mat abs_grad_x = new Mat();
        Mat abs_grad_y = new Mat();
        //注意求梯度的时候我们使用的是Scharr算法,sofia算法容易收到图像细节的干扰
        //所谓梯度运算就是对图像中的像素点进行就导数运算,从而得到相邻两个像素点的差异值 by:Tantuo
        Imgproc.Scharr(src, grad_x, CvType.CV_32F, 1, 0);
        Imgproc.Scharr(src, grad_y, CvType.CV_32F, 0, 1);
        //openCV中有32位浮点数的CvType用于保存可能是负值的像素数据值
        Core.convertScaleAbs(grad_x, abs_grad_x);
        Core.convertScaleAbs(grad_y, abs_grad_y);
        //openCV中使用release()释放Mat类图像,使用recycle()释放BitMap类图像
        grad_x.release();
        grad_y.release();
        //使用openCV的Core.addWeighted方法将x梯度和y梯度合并成一个梯度图像
        Mat grad = new Mat();
        Core.addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 0, grad);
        abs_grad_x.release();
        abs_grad_y.release();
        //得到梯度图像以后将其二值化,以便更清晰地找到轮廓边缘
        //Imgproc.cvtColor方法将梯度图像转换成binary gray灰度图像
        Mat binary = new Mat();
        Imgproc.cvtColor(grad, binary, Imgproc.COLOR_BGR2GRAY);
        //手动阈值化,threshould阈值定为40
        Imgproc.threshold(binary, binary, 40, 255, Imgproc.THRESH_BINARY);
        grad.release();
        //下面对二值图像进行形态学(morphology excution)的去噪声操作,先得到大小为 3*3像素的结构元素
        Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(3, 3));
        //然后对结构元素进行 Morph_open开操作。腐蚀:去除噪声-膨胀:覆盖去除的噪声点
        Imgproc.morphologyEx(binary, binary, Imgproc.MORPH_OPEN, kernel);
        //接下来使用openCV的Imgproc.findContours()方法,在图像中寻找驾驶员证件的轮廓 contour roi
        List<MatOfPoint> contours = new ArrayList<>();
        Mat hierarchy = new Mat();
        Imgproc.findContours(binary, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE, new Point(0, 0));
        int hw = binary.cols() / 2;
        int hh = binary.rows() / 2;
        Rect roi = new Rect();
        for (int i = 0; i < contours.size(); i++) {
            Rect rect = Imgproc.boundingRect(contours.get(i));
            //如果发现某一个 roi兴趣区域的轮廓宽度超过图片的一半,即可以认为这个轮廓是驾驶员证件的轮廓 contour
            if (rect.width > hw) {
                roi.x = rect.x;
                roi.y = rect.y;
                roi.width = rect.width;
                roi.height = rect.height;
                break;
            }
        }
        //没找到就返回
        if (roi.width == 0 || roi.height == 0) {
            return null;
        }
        //找到证件轮廓区域就将其拷贝到card图片中
        Mat card = new Mat();
        src.submat(roi).copyTo(card);
        //拷贝完成以后记得释放资源0
        src.release();
        binary.release();
        return card;
    }

第一步先调用Imgproc.Scharr()方法对司机证件的原始照片进行Scharr梯度运算,所谓梯度运算就是对图像中的像素点进行导数运算,从而得到相邻两个像素点的差异值,像素差异大的地方就是图像内轮廓contour,第二步在此图像上做二值化Binarization,调用 Imgproc.morphologyEx()方法,通过轮廓(contour)发现与面积大小过滤得到证件区域。

边缘发现以后调用Imgproc.cvtColor()方法得到下面的证件区域:

2.识别到证件区域以后我们注意到证件左上角有一个比较醒目的矩形,我们用它作为reference识别到照片下方包含数字的号码区域。在程序中这个过程调用下面的 findCardNumBlock(Mat card) 方法。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public Mat findCardNumBlock(Mat card) {
        //首先初始化HSV色彩空间(H:hue色相,S:saturation饱和度,V:value明度,亮度)
        Mat hsv = new Mat();
        Mat binary = new Mat();
        //从RGB色彩空间转换到hsv色彩空间,使用openCV的 Imgproc函数:Imgproc.COLOR_BGR2HSV
        Imgproc.cvtColor(card, hsv, Imgproc.COLOR_BGR2HSV);
        //inRange函数将hsv彩色图片的根阈值进行过滤,用来过滤掉对识别左上角标志区域帮助不大的颜色
        //并且把滤出的图像保存到 binary里面
        // Scalar()是具有三个参数的结构体,三个参数代表 hsv的色相,饱和度,亮度值
        Core.inRange(hsv, new Scalar(30, 40, 45), new Scalar(180, 255, 255), binary);
        //以上会得到一个驾驶员证件的二值化图像,但是噪声比较多
        //下面对二值话图像进行形态学的开操作(morphology excution),去除小的 5*5大小的结构元素(噪声)
        Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(5, 5));
        Imgproc.morphologyEx(binary, binary, Imgproc.MORPH_OPEN, kernel);//去除噪声
        //获取证件标志的轮廓(contours)
        List<MatOfPoint> contours = new ArrayList<>();
        Mat hierarchy = new Mat();
        Imgproc.findContours(binary, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE, new Point(0, 0));
        int offsetx = binary.cols() / 3;
        int offsety = binary.rows() / 3;
        Rect numberROI = new Rect();
        //对每个ROI寻找外接矩形 contourArea
        for (int i = 0; i < contours.size(); i++) {
            Rect roi = Imgproc.boundingRect(contours.get(i));
            //对于识别出来的矩形区域如果太小(面积小于200像素)则忽略
            if (Imgproc.contourArea(contours.get(i)) < 200) {
                continue;
            }
            //找到标志区域以后,以标志区域为基准,证件号码的位置在标志x坐标 *2 左右,宽度大概在 binary.cols() - roi.x - 100像素
            //证件号码的高度大概是证件标志(基准)的0.7倍 height*0.7 ;
            //如果找到的左上角标志物的轮廓长宽都小于证件的三分之一,则以此标志物作为标准定为号码区域
            if (roi.x < offsetx && roi.y < offsety) {
                numberROI.x = 3* roi.x + 120;
                numberROI.y = roi.y + 4 * roi.height - 65;
                numberROI.width = binary.cols() - roi.x - 390;
                numberROI.height = (int) (roi.height * 0.9);
                break;
            }
        }
        //如果没有找到就返回null
        if (numberROI.width == 0 || numberROI.height == 0) {
            return null;
        }
        //得到证件号码的区域以后就可以截取下来保存到 textimage
        Mat textImage = new Mat();
        card.submat(numberROI).copyTo(textImage);
        //拷贝完成以后记得释放release mat资源
        card.release();
        hsv.release();
        binary.release();
        return textImage;
    }

同样还是先把图像从RGB色彩空间转换成HSV色彩空间,调用OpenCV的 Imgproc类生成一个 Imgproc.COLOR_BGR2HSV 对象。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Imgproc.cvtColor(card, hsv, Imgproc.COLOR_BGR2HSV);

之后调用下面的Core.inRange()方法得到一个hsv颜色在new Scalar(30, 40, 45)范围内的区域,也就是左上角的reference 矩形。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Core.inRange(hsv, new Scalar(30, 40, 45), new Scalar(180, 255, 255), binary);

下一步还是形态学操作去噪声。噪声就是二值化图像里面识别出来的一个个小的黑点,形态学的开操作(morphology excution)会把图像中这些小小的黑点用旁边的大区域颜色覆盖掉,目的是为了让处理后的图像更加容易被机器识别。比如下面的代码调用OpenCV的Imgproc.morphologyEx()方法可以把大小为 5*5的结构元素(噪声)用周边像素弥补掉

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
        //下面对二值化图像进行形态学的开操作(morphology excution),去除小的 5*5大小的结构元素(噪声)
        Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(5, 5));
        Imgproc.morphologyEx(binary, binary, Imgproc.MORPH_OPEN, kernel);//去除噪声
噪声处理以后开始寻找证件区域内的号码区域 Contour做轮廓发现操作:
        //对每个ROI寻找外接矩形 contourArea
        for (int i = 0; i < contours.size(); i++) {
            Rect roi = Imgproc.boundingRect(contours.get(i));
            //对于识别出来的矩形区域如果太小(面积小于200像素)则忽略
            if (Imgproc.contourArea(contours.get(i)) < 200) {
                continue;
            }
            //找到标志区域以后,以标志区域为基准,证件号码的位置在标志x坐标 *2 左右,宽度大概在 binary.cols() - roi.x - 100像素
            //证件号码的高度大概是证件标志(基准)的0.7倍 height*0.7 ;
            //如果找到的左上角标志物的轮廓长宽都小于证件的三分之一,则以此标志物作为标准定为号码区域
            if (roi.x < offsetx && roi.y < offsety) {
                numberROI.x = 3* roi.x + 120;
                numberROI.y = roi.y + 4 * roi.height - 65;
                numberROI.width = binary.cols() - roi.x - 390;
                numberROI.height = (int) (roi.height * 0.9);
                break;
            }
        }

得到证件号码的区域以后就可以截取下来保存到 textimage

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    Mat textImage = new Mat();
    card.submat(numberROI).copyTo(textImage);
    //拷贝完成以后记得释放release mat资源
    card.release();
    hsv.release();
    binary.release();
    return textImage;

完成以上工作以后可以识别到证件号码区域的矩形轮廓:

识别出了证件中的号码区域,后面就调用 DigitImageProcessor 类对这些数字进行识别,这个过程需要我单独在另外一篇文章介绍,下面仅仅对几个重要方法的功能作介绍:

  • splitNumberBlock(Mat textImage)方法使用二值化方法识别数字区域里的字符轮廓。
  • getSplitLinePos(Mat mtexts) 方法用来对图像中有两个数字粘结起来的情况做分离。
  • extractFeatureData(Mat txtImage) 方法的作用是证件卡号识别的特征提取,获取卡号每个数字的黑色像素点特征,作为每个号码的特征和识别的重要依据。
  • dumpFeature(float[] fv, int textNum) 方法将生成的特征文本文件保存在手机。
  • readFeatureVector(File f) 用来读取保存的特征向量。
  • recognitionChar(Mat charImage) 用来根据特征向量对证件号码进行识别。

RFID识别验证功能

RIFD识别验证功能的主要代码在com.tantuo.didicar 包下的DriverLIcenseNFC模块里:

还是先看下真机效果:

点击右上角的RFID验证入口以后,会提示乘客使用手机背面像刷公交卡那样感应RFID硬件,比如嵌入芯片的司机证件、固定在车上识别器等。

注意:某个Activity 要想能够在当前栈顶接收RFID芯片号码,必须在 Manifest.xml 文件中设置intent-filter 拦截TAGDISCOVERED的Action,这样这个Activity 才能捕获RFID标签信息。并且设置LunchMode 为SingleTop,确保再次捕获RFID标签信息(TAGDISCOVERED)的时候,始终由处于栈顶的这个Activity 来处理,而不是把他压入栈,调取新的DriverRFIDMainActivity作栈顶。有疑惑的同学可以看下 Activity 启动模式和栈管理的相关文章。

链接:彻底明白Activity启动模式-SingleTop、SingleTask、SingleInstance具体使用场景 https://blog.csdn.net/weixin_37734988/article/details/93508139

考虑到用户的手机可能有多个APP或者Activity 可以拦截RFID或者 NFC 芯片信息,所以需要给处于当前栈顶的 DriverRFIDMainActivity 设置前台分发系 enableForegroundDispatch ,可以确保检测到RFID标签时,此活动拥有最高的捕获优先权,而不是由Android Activity 调度机制调出新的有拦截权限的活动

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Override
    protected void onResume() {
        super.onResume();
        // 前台分发系统,用于确保检测RFID标签时拥有最高的捕获优先权.
        NfcUtils.mNfcAdapter.enableForegroundDispatch(this, NfcUtils.mPendingIntent, NfcUtils.mIntentFilter, NfcUtils.mTechList);
    }
    @Override
    protected void onPause() {
        super.onPause();
        //关闭前台调度系统
        NfcUtils.mNfcAdapter.disableForegroundDispatch(this);
    }

手机读取芯片ID这个功能的代码我单独放到NfcUtils工具类里,在utils 文件夹下。

手机读取到芯片信息,会调用NDK编译成C语言的MD5加密算法so 文件(文章最后会讲),连同当时的地理位置经纬度一起发送给平台服务器(我用的 OkHttp3 ),与数据库中注册司机的信息进行比对,并将验证结果和司机信息发送给乘客:

服务器端用的是我自己的腾讯云主机 + Apache + PHP+ MySQL , 会一直开放出这个项目的网络接口并持续维护,方便读者测试这个功能。读者只要在验证环节使用手机读取任何一个嵌有RFID加密芯片比如学生证、银行卡、公交卡,程序在发送数据请求之前(下图代码中第二行高亮的部分)都会把读取到的ID信息换成作者本人的,再发送给平台服务器服务器做验证,这样读者测试时使用手机读取任何RFID信息都会接收到从服务器发回来的司机信息。实际项目中把这一行注释掉即可。

服务器端收到乘客发送过来的验证请求以后,会对比平台司机数据库进行核实,并把核实结果和对应司机、车辆信息发回给乘客。下面就是平台服务器端注册司机的注册信息数据库,我用Navicat 做了部分截图,第一行红色部分就是平台验证的结果,也就是作者本人的信息:

服务器端还会对乘客发送过来的数据进行整理和分析,也可以将“人车不符”数据和位置信息发送给合规部门。

下图是“人车不符”情况发生的地区热力图:

还可以根据乘客的叫车时间,筛选出高峰时段的用车需求热力图,给司机调度部门提供数据支持。

对服务器端的打车数据进行分析,还可以生成非常漂亮的24小时动态热力图、星云图、蝌蚪迁移图,感兴趣的读者可以研究下Python 、Pandas 、MatplotLib,可以快捷地处理服务器端数据,生成可视化图表。

使用NDK调用MD5加密算法

前面提到项目中会把ID号码使用C语言的MD5算法进行加密,关键代码在下图中的cpp 文件夹。图中 NDK Components 组件提供了一整套编译C语言动态库(.so )和打包的工具,可以把*.so 动态库打包到apk中。下面的MD5.h 和 MD5.cpp 文件分别是C语言写的算法类头文件和源文件。头文件用来声明源文件要用到的变量、类型、宏定义,源文件则用来描述方法和具体实现,里面会有一个#include"MD5.h"把头文件导入进来。两者的关系有点像书的目录和内容的关系,目录是对章节和内容进行简单表示,真正的实现实在书里面的。

上图中MD5 C语言文件下面还有一个native-lib.cpp 文件,是NDK 在 Android studio 里帮助我们生成的。它可以认为是Java方法调用C语言方法的桥梁。下面的图可以看到 native-lib 是如何帮助 MD5JniUtils 类的 getMd5 () 方法调用 C语言加密方法的,JNIEXPORT 和 JNICALL 两个宏用来标识函数用途是调用.so 库,就好像 C++可以调用 .dll 动态链接库一样,后面紧跟的是函数名,命名规则很重要:Java_ + 包名 + 调用这个加密算法的Java工具类名 + Java调用方法 ,后面的变量参数是Java中String类型对应的JNI jstring类型,下面在方法体中,就可以使用对传入的加密前字符串进行加密的C语言运算了,并把加密完成的 jstring类型结果返回给java层。

结语

整个项目就大概介绍完了,欢迎读者转载和提问,我看到会尽快回复,如果读者能想到更加实用的功能,我会更新GitHub上的源码加上新的功能。我也会尽快抽时间把百度地图换成海外版地图MapBox ,并且把实现步骤详细写在另外一篇文章。

项目源码地址:https://github.com/18601949127

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-07-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 群英传 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Android OpenCV 4.6.0 颜色追踪
转换方法可以参考:Android ImageProxy 转 OpenCV Mat对象
zinyan.com
2022/12/07
1.9K2
计算机视觉 OpenCV Android | 基本特征检测 之 轮廓分析
调用该API会返回一个Rect对象实例,它是OpenCV关于矩形的数据结构, 从中可以得到外界矩形(边界框)的宽高, 然后就可以计算出轮廓的横纵比了。
凌川江雪
2019/03/04
1.5K0
计算机视觉 OpenCV Android | 基本特征检测 之 轮廓分析
图像中二维码的检测和定位
所谓开操作是指先腐蚀后膨胀的操作。在之前的文章二值图像分析:案例实战(文本分离+硬币计数)曾经介绍过开操作的用途。
fengzhizi715
2018/08/24
3.6K0
图像中二维码的检测和定位
Android OpenCV(三十一):图像形态学
形态学梯度能够描述目标的边界,根据图像腐蚀和膨胀与原图之间的关系计算得到,形态学梯度可以分为基本梯度、内部梯度和外部梯度。基本梯度是原图像膨胀后图像和腐蚀后图像间的差值图像,内部梯度图像是原图像和腐蚀后图像间的差值图像,外部梯度是膨胀后图像和原图像间的差值图像。
Vaccae
2021/04/21
6190
Android OpenCV(三十一):图像形态学
Android OpenCV(三十七):轮廓外接多边形
该方法用于求取输入二维点集合的最小外接矩形。返回值为RotateRect对象。RotateRect类型和Rect类型虽然都是表示矩形,但是在表示方式上有一定的区别。通过查看成员变量可以很明显的看到差异。Rect是通过左上角的坐标来定位,默认横平竖直,然后通过宽高确定大小。而RotateRect则是通过center确定位置,angle结合宽高,计算各顶点的坐标,从而确定矩形。
Vaccae
2021/07/07
1.4K0
Android OpenCV(三十七):轮廓外接多边形
【从零学习OpenCV 4】图像矩的计算与应用
矩是描述图像特征的算子,被广泛用于图像检索和识别、图像匹配、图像重建、图像压缩以及运动图像序列分析等领域。本节中将介绍几何矩与Hu矩的计算方法以及应用Hu矩实现图像轮廓的匹配。
小白学视觉
2020/02/20
1.7K0
【从零学习OpenCV 4】图像矩的计算与应用
基于opencv库,tess-two,Zxing在Android实现人工智能身份证号识别
在科技未达到如此高的境界时,我们应该先关注一下身边的人工智能:Android端身份证识别。为什么我会提到这个名词呢?随着移动终端(智能手机及平板电脑)的普及,几乎人人都有一部手机已经毋庸置疑了。进而也引发了移动应用爆发似的浪潮,各类APP应接不暇:金融类的、办公类的、电商类的等等。这些应用大多会涉及到身份证的实名认证。如果让用户手动输入信息,显然不符合时代背景,必然会流失一大批“粉丝”。
Android技术干货分享
2019/03/26
2.4K0
基于opencv库,tess-two,Zxing在Android实现人工智能身份证号识别
OpenCV二维码检测与定位
在如今流行扫描的年代,应用程序实现二维码扫描检测与识别已经是应用程序的标配、特别是在移动端、如果你的应用程序不能自动发现检测二维码,自动定位二维码你都不好意思跟别人打招呼,二维码识别与解析基于ZXin
OpenCV学堂
2018/04/04
4.8K0
OpenCV二维码检测与定位
Android通过OpenCV和TesserartOCR实时进行识别
最近一系列的文章都是用Android利用OpenCV NDK的方法通过摄像头实时获取图像进行图像处理,在上一篇《Android使用Tesseract-ocr进行文字识别》我们学习了一下TesserartOCR的图像识别功能,这一章主要介绍怎么样通过图像的处理再加上我们OCR的识别获取的想要的东西。
Vaccae
2019/07/30
4K0
Android通过OpenCV和TesserartOCR实时进行识别
一起用Python做个车牌自动识别系统,好玩又实用!
前段时间,用PyQt5写了两篇文章,关于Python自制一款炫酷音乐播放器、自定义桌面动画挂件。有粉丝问我,为什么要用PyQt5?之前没接触过PyQt5,能不能多分享一些这方面的开发案例?
全栈程序员站长
2022/07/01
1.3K0
一起用Python做个车牌自动识别系统,好玩又实用!
Android OpenCV(三十八):凸包检测
凸包(Convex Hull)是一个计算几何(图形学)中的概念。在一个实数向量空间V中,对于给定集合X,所有包含X的凸集的交集S被称为X的凸包。X的凸包可以用X内所有点(X1,...Xn)的凸组合来构造.在二维欧几里得空间中,凸包可想象为一条刚好包着所有点的橡皮圈。用不严谨的话来讲,给定二维平面上的点集,凸包就是将最外层的点连接起来构成的凸多边形,它能包含点集中所有的点。
Vaccae
2021/07/07
1.3K0
Android OpenCV(三十八):凸包检测
OpenCV从入门到精通:安装、配置、依赖、基本语法与常用方法详解
本文旨在为计算机视觉初学者提供一份详尽的OpenCV入门指南。从OpenCV的安装配置、依赖项安装,到基本语法和常用方法的解析,我们力求以通俗易懂的方式,配合丰富的代码示例,帮助读者快速掌握OpenCV的核心概念和技术,并为后续深入学习打下坚实的基础。无论您是Python爱好者还是C++开发者,都能从中受益。最后,欢迎大家加我的微信一起交流学习!
默 语
2025/05/12
2390
Java使用OpenCV进行答题卡识别
https://github.com/opencv/opencv/releases
码客说
2022/09/09
3K1
Java使用OpenCV进行答题卡识别
不废话,看我20行代码搞定色块提取与定位…….
这个问题是谁问我的我已经不记得了,刚开始的时候他发了这张图像给我,让我给他看一下,我当时告诉他转换一下色彩空间提取就好啦,后来我记得他在微信上有问了我一次,今天我整理文件看到这张图又想起了,感觉他问了我好几次我都没回复挺不意思的,但是我实在不知道他是谁了,微信上消息太多,早已经把他淹没了,加之我记忆力退化严重,思来想去只好写篇文章告诉他,我回答了!如果看完感觉对你也有用,点在看支持即可!
OpenCV学堂
2020/03/09
1.3K0
附代码 | OpenCV实现银行卡号识别,字符识别算法你知多少?
随着计算机视觉在我们生活中的应用越来越广泛,大量的字符识别和提取应用逐渐变得越来越受欢迎,同时也便利了我们的生活。像我们生活中的凭借身份码取快递、超市扫码支付的机器等等。
AI科技大本营
2020/05/29
2.2K0
附代码 | OpenCV实现银行卡号识别,字符识别算法你知多少?
OpenCV实现人脸对齐
OpenCV实现人脸对齐 一:人脸对齐介绍 在人脸识别中有一个重要的预处理步骤-人脸对齐,该操作可以大幅度提高人脸识别的准确率与稳定性,但是早期的OpenCV版本不支持人脸Landmark检测,因此一
OpenCV学堂
2018/04/24
4.3K2
OpenCV实现人脸对齐
OpenCV 轮廓检测
http://blog.sina.com.cn/s/blog_8fc98fe501017ypb.html
流川疯
2022/11/29
9460
OpenCV 轮廓检测
Android OpenCV(四十三):图像分割(Grabcut)
图像分割就是把图像分成若干个特定的、具有独特性质的区域并提出感兴趣目标的技术和过程。它是由图像处理到图像分析的关键步骤。现有的图像分割方法主要分以下几类:基于阈值的分割方法、基于区域的分割方法、基于边缘的分割方法以及基于特定理论的分割方法等。从数学角度来看,图像分割是将数字图像划分成互不相交的区域的过程。图像分割的过程也是一个标记过程,即把属于同一区域的像素赋予相同的编号。
Vaccae
2021/07/30
1.2K0
Android OpenCV(四十三):图像分割(Grabcut)
opencv识别多条形码数字_opencv测试代码
首先接到这个图像识别的小工程需要先确定这个工程的最初输入,和最后输出,输入就是普通的RGB图像,输出是数据库文件。
全栈程序员站长
2022/11/16
5750
Android OpenCV(四十二):图像分割(分水岭法)
图像分割就是把图像分成若干个特定的、具有独特性质的区域并提出感兴趣目标的技术和过程。它是由图像处理到图像分析的关键步骤。现有的图像分割方法主要分以下几类:基于阈值的分割方法、基于区域的分割方法、基于边缘的分割方法以及基于特定理论的分割方法等。从数学角度来看,图像分割是将数字图像划分成互不相交的区域的过程。图像分割的过程也是一个标记过程,即把属于同一区域的像素赋予相同的编号。
Vaccae
2021/07/07
1K0
Android OpenCV(四十二):图像分割(分水岭法)
推荐阅读
相关推荐
Android OpenCV 4.6.0 颜色追踪
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验