一我的單挑滴滴Android開發團隊?

本文是一位朋友的投稿,他花了很大精力來實現了一個滴滴客戶端的完整功能,很是具備學習的價值,推薦給你們~

做者: 譚妥java

本文較長,建議收藏食用;現建立了一個Android開發水友圈,圈內會不定時更新一些Android中高級的進階資料,歡迎你們帶着技術問題來討論,共同成長進步!(包含資深UI工程師,Android底層開發工程師,Android架構師,原生性能優化及混合優化,flutter專精);但願有技術的大佬加入,水圈內解決的問題越多得到的權利越大!

前言

這是我本身作的一個仿滴滴打車的Android出行項目,主要針對滴滴等出行平臺一直飽受質疑的「人車不符」問題,以及當前愈加火熱的國際化和出海戰略,給出行項目增長了下面幾個功能:android

  1. RFID識別驗證功能:在司機證件或者車內識別硬件裏嵌入RFID識別芯片,乘客使用手機讀取到芯片信息,而且經過網絡(okhttp3)發送到出行平臺數據庫進行驗證(我用NDK加了一個C語言的MD5加密算法對識別到的信息進行了加密)。若是不是合規的「人」或「車」,則不能完成訂單並向平臺或監管單位彙報當前位置。(爲了方便讀者測試,可使用手機讀取任何一個加密或非加密RFID芯片,好比銀行卡、公交卡等,我在代碼中的驗證前階段把芯片信息都換成我本身的司機信息,確保讀者測試時能夠收到服務器的回覆。
  2. 海外版功能:點擊切換當前語言。
  3. 司機證件號碼識別功能:讀取司機證件上的證件號碼,也能夠用來與出行平臺數據庫的信息進行覈實比對。

項目源碼地址:https://github.com/18601949127git

項目代碼都是一行一行本身敲的,在多部手機上調試過確保各項功能可以順暢運行。GitHub的源碼中保留了全部的手機CPU指令集架構,保證在全部手機上可以運行成功。以爲包太大的同窗能夠本身把不須要的 .so 指令集刪掉,主要是作識別的 OpenCV4Android.so 包比較大,其次是百度地圖的包。github

地圖我使用的是百度地圖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 對路徑進行動畫描述:

<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

app:layout_behavior="@string/bottom_sheet_behavior"

左側側滑菜單

左側側滑菜單能夠做爲我的信息、安全提示、設置信息的入口

司機證件的號碼OCR識別功能

證件號碼識別功能的主要代碼在com.tantuo.didicar 包下的 DriverLicenseRecognition 模塊。

仍是先看下真機效果:

點擊進入司機證件號碼識別功能之後,能夠選擇對證件拍照,爲了方便演示,這裏是從手機相冊選擇剛剛拍的照片。同時爲了方便讀者測試這個功能,我把照片保存在了開發包的asset文件夾裏面,這樣讀者下載我保存在GIthub上https://github.com/18601949127 的版本,點擊選擇司機證件之後調用的是我保存在assets 文件夾裏的司機證件照片,也就是下面圖片裏的 getDriverLicenseFromMySample() 方法,能夠馬上進行測試。想繼續從手機相冊讀取的讀者能夠執行LicenseMainActivity 下的 LicenseMainActivity 方法。

這裏用我之前在國外讀書時候的證件做爲例子:

首先:要從照片中找到司機證件區域,也就是上證件邊緣紅色的區域

/**

\* 找到圖像中的證件區域
\* 在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) 方法。

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 對象。

Imgproc.cvtColor(card, hsv, Imgproc.COLOR_BGR2HSV);

以後調用下面的Core.inRange()方法獲得一個hsv顏色在new Scalar(30, 40, 45)範圍內的區域,也就是左上角的reference 矩形。

Core.inRange(hsv, new Scalar(30, 40, 45), new Scalar(180, 255, 255), binary);

下一步仍是形態學操做去噪聲。噪聲就是二值化圖像裏面識別出來的一個個小的黑點,形態學的開操做(morphology excution)會把圖像中這些小小的黑點用旁邊的大區域顏色覆蓋掉,目的是爲了讓處理後的圖像更加容易被機器識別。好比下面的代碼調用OpenCV的Imgproc.morphologyEx()方法能夠把大小爲 5*5的結構元素(噪聲)用周邊像素彌補掉

//下面對二值化圖像進行形態學的開操做(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

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 調度機制調出新的有攔截權限的活動。

@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層。

須要更多視頻及文檔資料的私信我,更多進階及面試資料的請進圈領取,歡迎你們理性討論,有疑問的歡迎留言討論。

相關文章
相關標籤/搜索