給Ionic寫一個cordova(PhoneGap)外掛

funnyZpC發表於2018-05-09

    給Ionic寫一個cordova(PhoneGap)外掛

  之前由javaWeb轉html5開發,由於面臨新技術,遂在適應的過程中極為掙扎,不過還好~,這個過程也極為短暫;現如今面臨一些較為複雜的需求還會有一絲絲頭痛,卻沒有一開始那麼強烈了。。。

  在正式寫下文之前,我先感謝公司大boss:王總,感謝他讓我進入了一個有挑戰性的技術公司 並在這個過程中一直鼓勵我不斷汲取新技術,同時也指正了我在開發中的一些不太好的習慣,十分感謝!

  再~,感謝在開發中給予我太多幫助的杜勇以及孫金~,不論是需求討論還是具體開發階段都會給予一些十分有用的思路以及難點解答,尤其是面臨技術瓶頸的時候~,再次感謝他們,十分感謝!

  對於新手,建議準備好相應的IDE及環境:webstrom、google chrome、eclipse(或者 idea),android SDK ; webstrom 用於配合頁面js以及外掛開發,eclipse用於app外掛除錯。

  就拿最近一個需求來說吧,需求:未防止第三方破解app,客戶找了一個安全公司做個評估,其中一個安全問題是安卓apk的包經過修改後依然可以安裝執行(ios由於安全機制存在不存在這個問題),專案組內部討論出一個比較好的解決方案是使用者登陸前驗證app包的hashcode值,並與後臺互動驗證當前發行版app的hashcode的有效性,以杜絕破解。

  這個需求的難點在於需要訪問手機的記憶體讀取安裝包檔案,如果是普通的需求就可以一個html、一個JS(controller)外加上路由配置就 so easy~

  首先一個規範的cordova外掛是這樣子的(這裡我寫的一個外掛的名字是 cordova-plugin-integrity-checking):

 

  外掛的主目錄下面有兩個資料夾(src和www)以及四個檔案(LICENSE、package.json、plugin.xml、README.md),外掛內部的排列是根據cordova規範來的,這裡不多解釋,請看上圖:

src:是放置安卓,ios,wp8等原生代目的地方,一般為了區分各個平臺的程式碼都會先建一個資料夾(以上是android),資料夾下面是程式碼檔案。

www:這裡放置的是js呼叫原生程式碼的api,裡面有呼叫方法和返回物件(可無)。

LICENSE:是一些開源說明,一般宣告的開源協議有GNU、BSD、Apache等~ 

package.json:這裡面用一個json宣告瞭當前外掛的檔案結構,當然這個不是給開發者用的,是為了將此外掛新增到專案中時打cordova命令用的,請不要忽略~

plugin.xml:這個檔案裡面是以xml的形式定義了包的路徑以及api(js)對應原生的呼叫方法…,以及外掛需要的許可權宣告(比如相機許可權、位置許可權、聯絡人許可權等~),打apk及ipa包後此檔案會被融合~

README.md:這裡是一些使用說明、注意事項等~,一般你將開發的外掛共享在github上的時候會需要這個,如涉及版權及專案安全此檔案可忽略~

  好了,我們們開始了~,首先按以上造型建檔案和資料夾,我能說這是抄麼-_-|||  

  完畢,先寫個原生的android程式碼吧(反正我們不會寫oc d=====( ̄▽ ̄*)b), 一下樣例是CordovaApkValidte.java =>

定義一個包名稱(雖然檔案最終都是集中放置的,但請想想大熱天一個人穿著褲衩在溜街多辣眼睛啊(● ̄(エ) ̄●)),命名如下,寫java的童鞋大概都知道,這裡就不解釋啦~:

package com.funnyZpC.integrityChecking.plugin;

然後就是cordova規範固定的寫法,繼承CordovaPlugin重寫execute方法,你可以改,結果當然是不能用<( ̄ˇ ̄)/,exceute方法的形參圖上已經說明,這兒就不必綴訴啦~

    /**
     * Apk integrity checking
     * @author funnyZpC
     */
    public class CordovaApkValidate extends CordovaPlugin {
        /**
         * action:方法的動作,根據動作走相應的處理邏輯
         * args:js呼叫方法時傳的引數,均以json的形式讀入(這裡未使用)
         * callbackContext:方法返回的物件,物件裡面包好兩個變數success和error,js的回撥函式會用到
         * 
         */
        @Override
        public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {

以下是程式碼的主體部分,這只是一個很簡單的外掛,邏輯說明已經寫進去了,自行閱讀哈~

            /**
             * 思路:根據cordova物件獲取包的路徑
             *             然例項化MessageDigest物件的SHA-1演算法
             *             再講當前包載入到輸入流
             *             再按位元組陣列讀取輸入流獲取大integer的值
             *             後將大integer轉換成16進位制的hashcode的表示
             *             後再將16進位制扔給callbackContext物件返回
             */
            String apkPath=cordova.getActivity().getApplicationContext().getPackageCodePath();
              MessageDigest msgDigest = null;
            if (action.equals("getSHA1")) {
                try {
                            msgDigest = MessageDigest.getInstance("SHA-1");
                            byte[] bytes = new byte[1024];
                            int byteCount;
                            FileInputStream fis = new FileInputStream(new File(apkPath));
                            while ((byteCount = fis.read(bytes)) > 0){
                                msgDigest.update(bytes, 0, byteCount);
                            }
                            BigInteger bi = new BigInteger(1, msgDigest.digest());
                            callbackContext.success(bi.toString(16));
                            fis.close();
                } catch (Exception e) {
                    callbackContext.error("ERROR MESSAGE:"+e);
                    return false;
                }
            }
            return true;

在webstrom裡面盲寫沒有引入包沒有程式碼提示,bug率高,建議大家引入android SDK和cordova包在eclipse或idea裡面寫較nice~

雄關漫道真如鐵,而今我們們只邁出了一步~,再接再厲哈(*^__^*)

下是js所呼叫的api:

 1 cordova.define("cordova-plugin-integrity-checking.apkValidatePlugin", function(require, exports, module) {
 2 /*
 3 var exec = require(`cordova/exec`);
 4 
 5 exports.isDeviceRooted = function(success, error) {
 6     exec(success, error, "RootDetection", "isDeviceRooted", []);
 7 };
 8 */
 9 var exec = function (command, success, fail) {
10   cordova.exec(success, fail, "ApkValidatePlugin", command, []);//引數(回撥成功,回撥錯誤,別名,action名稱,引數)
11 };
12 var apkValidate={};
13 apkValidate.getSHA1 = function (success, fail) {
14          return exec(`getSHA1`, success, fail);
15 };
16 
17 module.exports = apkValidate;
18 
19 });

 一個完整的api包含api的id,以及一個回撥,如第一行,這個api內部有一個核心(程式碼第10行),裡面包含了一些呼叫的引數,需要說明的是第三個引數是一個別名(可隨意寫),這個名字需要對應到之後要說的plugin.xml裡面的包的別名,第四個引數是action的名稱,也就是剛剛在java檔案裡面寫的action的名稱(一定要對應啊~),最後一個是傳入的引數,別忘了這也是與CordovaApkValidate.java裡面對應的,最後17行共享出來的是一個變數,方便打點呼叫,彷彿快成了~o( ̄▽ ̄)d,別激動,這個會在最後的使用會詳細講解,現在安卓原生的邏輯已經寫好了,api也已經寫好,如何將兩者結合起來,that is a trouble,but ,It`s not trouble.不懂不懂(O_O)? ,下面就是嘿~

 1 <?xml version=`1.0` encoding=`utf-8`?>
 2 <plugin id="cordova-plugin-integrity-checking" version="1.0.0" xmlns="http://apache.org/cordova/ns/plugins/1.0"
 3         xmlns:android="http://schemas.android.com/apk/res/android">
 4     <name>Integrity checking</name>
 5     <author>@funnyZpC</author>
 6     <description>Cordova Plugin for integrity checking</description>
 7     <keywords>Cordova,Integrity,Checking,Ecosystem:Cordova,Cordova-android</keywords>
 8     <license>MIT</license>
 9     <repo>https://github.com/funnyZpC/cordova-plugin-integrity-checking</repo>
10     <issue>https://github.com/funnyZpC/cordova-plugin-integrity-checking/issues</issue>
11     <engines>
12         <engine name="cordova" version=">=3.0.0"/>
13     </engines>
14     <js-module  src="www/apkValidatePlugin.js">
15         <clobbers target="Cordova"/>
16     </js-module>
17     <!-- android -->
18     <platform name="android">
19         <config-file parent="/*" target="res/xml/config.xml">
20             <feature name="ApkValidatePlugin">
21                 <param name="android-package" value="com.funnyZpC.integrityChecking.plugin.CordovaApkValidate"/>
22                 <param name="onload" value="true"/>
23             </feature>
24         </config-file>
25         <config-file parent="/*" target="AndroidManifest.xml"></config-file>
26         <source-file src="src/android/CordovaApkValidate.java" target-dir="src/com/funnyZpC/integrityChecking/plugin"/>
27     </platform>
28 </plugin>

以上,第一行需要明確定義外掛的id,這個建議與外部的外掛名一致(第一張圖中的資料夾的名稱),14行中的路徑需要參照api檔案所在的相對路徑填寫,20行中定義的別名與api檔案中的定義的呼叫別名一致,21行中的value值一定是上面java檔案中最上面定義的package名+類名(這是個坑,我以前經常性寫錯,心傷~~~~(>_<)~~~~),最後需要注意的是26行中的src的值是java檔案的相對路徑,還有target-dir也是相對路徑(竊不要以為後面是包名,包也是資料夾((* ̄^ ̄)),這些都不要寫錯,其他隨意哈~<(* ̄▽ ̄*)/,另外,如果開發的是一個比較複雜的外掛,比如中間需要呼叫記憶體卡讀寫許可權,你需要再定義一個config-file(與其他的config-file同級),具體如下(name的值是官方定義的,自行google)

1        <config-file target="AndroidManifest.xml" parent="/*">
2             <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
3         </config-file>

View Code

嗯~,貌似外掛已近九成了~,下面該為命令準備一個package.json檔案方便將外掛新增到專案中:

 

請注意上圖5~10行,其他地方隨意哈~

 

 

                

以上兩張圖中,第一張圖是在專案目錄下打 “cordova plugin list“命令列出當前專案所用的所有的cordova外掛,第二張圖是在當前專案下將外掛新增到專案中,只要不出現fail字樣即外掛新增成功,如果外掛中需要新增變數,請在 命令後面 新增 “–veriable”(後面的英文單詞是變數名)。。。。。。,新增成功,webstrom會自動重新整理,這時候請看這裡,看這裡:

 

當前外掛呼叫的方式是: 

Cordova.apkValidate.getSHA1(function (successCallback) {

  //success logic~~~
},function (errorCallback) {

  //error logic~~~
}
)

 

 

你的專案有一個專門放置plugin的plugins的目錄,目錄下面有兩個檔案android.json和fetch.json,這兩個檔案裡面都有外掛的申明,以上三張圖中第二和第三張,如沒有請檢查!,好了寫了仨小時多該結束了(^o^)/,順便放兩張圖(構建平臺後的檔案),讀者自行思索,看有沒有發現什麼哈(∩_∩)

 

 

 

 

 

我的部落格即將搬運同步至騰訊雲+社群,邀請大家一同入駐:https://cloud.tencent.com/developer/support-plan?invite_code=y2lervmuyy9z

 

相關文章