1. 程式人生 > >Cordova外掛開發(2)-Android外掛安裝包製作詳解

Cordova外掛開發(2)-Android外掛安裝包製作詳解

本篇文章講述的是如何製作自己的Cordova外掛安裝包(Android),具體內容包括以下三個方面:

1,安裝和使用plugman;

2,開發自己的Cordova外掛安裝包;

3,外掛安裝包的安裝與解除安裝;

本篇文章以自定義加密壓縮檔案外掛的實踐過程為例,本人不會IOS,倒是擼過兩年Android,所以這裡只講述Android平臺的實現過程。

一,準備工作

需已搭建好Codova環境;

二,具體實現

1,安裝plugman

plugman是Cordova官方推薦使用的建立外掛工具,執行以下命令即可安裝:

npm install -g plugman

2,使用plugman建立外掛模板

2.1 建立外掛

plugman建立外掛方式如下:

plugman create --name 外掛名 --plugin_id 外掛id --plugin_version 外掛版本號
執行以下命令,即可建立一個外掛名為fileUtil,外掛id為fxp-plugin-fileUtil,版本號為1.0.0的外掛:
plugman create --name fileUtil --plugin_id fxp-plugin-fileUtil --plugin_version 1.0.0

2.2 給外掛新增Android平臺

cd fileUtil
plugman platform add --platform_name android
到了這一步,外掛模板就建立完成了,新建立的外掛模板目錄結構如下:


以上目錄中,一共有plugin.xml、fileUtil.java、fileUtil.js三個檔案。接下來我們就可以在此外掛模板基礎上開發自己的外掛安裝包了。當然,也可以不安裝使用plugman,直接手動建立以上檔案目錄。

3,開發自己的Cordova外掛安裝包

其實外掛安裝包檔案目錄結構完全不用拘泥於以上,只要在plugin.xml檔案中配置得當,目錄結構可以隨意。但出於規範,我們還是參照以上結構比較好。下面是我寫的加密壓縮檔案外掛目錄結構:


以上目錄中,plugin.xml是核心配置檔案,src->android->code目錄下是安卓程式碼,src->android->libs目錄下是安卓用到的jar包,www 目錄下的是js介面檔案。下面開始講解具體實現。

3.0  編輯package.json檔案

package.json檔案用於安裝和釋出,在以前的cordova版本中,沒有此檔案也可以正常本地安裝自己的外掛,但最近不行了,必須得有。直接上程式碼:

{
  "name": "fxp-plugin-fileUtil",
  "version": "1.0.0",
  "description": "create and zip file, used to export data to zip",
  "cordova": {
    "id": "fxp-plugin-fileUtil",
    "platforms": [
      "android"
    ]
  },
  "repository": {
    "type": "",
    "url": ""
  },
  "keywords": [
    "cordova",
    "zip",
    "file",
    "phonegap",
    "ecosystem:cordova",
    "cordova-android"
  ],
  "author": "",
  "license": "Apache 2.0",
  "bugs": {
    "url": ""
  },
  "homepage": ""
}

3.1  編輯plugin.xml檔案

plugin.xml是核心配置檔案,不廢話,直接上程式碼,程式碼已詳細註釋配置說明及需要注意的問題:

<?xml version='1.0' encoding='utf-8'?>
<plugin xmlns:android="http://schemas.android.com/apk/res/android" id="fxp-plugin-fileUtil"
    version="1.0.0" xmlns="http://apache.org/cordova/ns/plugins/1.0">
    <name>fileUtil</name>
    <!-- 一個plugin可以有多個module,類似於Android Studio中project和module的概念-->
    <!-- js-module name可隨意命名,src值為此module對應js檔案的相對路徑 -->
    <!-- 【外掛id.js-module的name值】對應安裝後zipUtil.js中cordova.define方法第一個引數,以及其在cordova_plugin.js中的id值 -->
    <js-module name="zipUtil" src="www/fileUtil.js">
        <!-- target值即為js呼叫外掛方法時所用的物件名,可隨意命名;對應安裝後cordova_plugin.js中clobbers值 -->
        <clobbers target="fxp.plugins.zipUtil" />
    </js-module>
    <!-- 可新增多個平臺,name值為平臺名-->
    <platform name="android">
        <config-file parent="/*" target="res/xml/config.xml">
            <!-- 註冊外掛module,feature的name值為需要註冊的js-module的name值 -->
            <feature name="zipUtil">
                <!-- name值隨意,value值為【將外掛id中"-"替換為"."後的字串】.【feature的name值】-->
                <param name="android-package" value="fxp.plugin.fileUtil.zipUtil" />
            </feature>
        </config-file>
        <config-file parent="/*" target="AndroidManifest.xml">
            <!-- 此處新增所需許可權 -->
            <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
            <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
        </config-file>
        <!-- 將外掛檔案放到指定目錄:src為在外掛安裝包中的相對路徑,target-dir為在外掛安裝後的相對路徑 -->
        <!-- 將java類檔案放到【src/包名】目錄,注意:此處所寫包名須與java類中包名一致 -->
        <source-file src="src/android/code/zipUtil.java" target-dir="src/fxp.plugin.fileUtil" />
        <source-file src="src/android/code/CompressUtil.java" target-dir="src/fxp.plugin.fileUtil" />
        <source-file src="src/android/code/DateUtils.java" target-dir="src/fxp.plugin.fileUtil" />
        <source-file src="src/android/code/FileService.java" target-dir="src/fxp.plugin.fileUtil" />
        <source-file src="src/android/code/FileUtils.java" target-dir="src/fxp.plugin.fileUtil" />
        <source-file src="src/android/code/StringUtil.java" target-dir="src/fxp.plugin.fileUtil" />

        <!-- Cordova官方提供了以下兩種庫依賴方式 -->
        <!-- 1,首選framework標籤。此方法引入的庫可被多個外掛使用而不產生衝突;但此方法引入非官方jar包會失敗,故此demo不用此方式 -->
        <!--<framework src="com.android.support:support-v4:24.1.1+" />-->

        <!-- 2,其次lib-file標籤。但如果有其他外掛也添加了此依賴,則可能產生衝突;此demo依賴的是非官方庫,只有用此標籤了 -->
        <lib-file src="src/android/libs/zip4j_1.3.1.jar" />
        <lib-file src="src/android/libs/commons-lang3-3.1.jar" />
        <lib-file src="src/android/libs/commons-codec-1.7.jar" />

        <!-- 經驗證,使用source-file標籤將依賴包放到libs目錄,然後新增依賴關係也是可行的。如下: -->
        <!-- 將依賴包檔案放到【libs】目錄 -->
        <!-- 外掛安裝後執行cordova run 命令或在Android Studio中Sync操作即可自動新增依賴關係;如果沒有自動新增,可以手動新增 -->
        <!--<source-file src="src/android/libs/commons-codec-1.7.jar" target-dir="libs" />-->
        <!--<source-file src="src/android/libs/commons-lang3-3.1.jar" target-dir="libs" />-->
        <!--<source-file src="src/android/libs/zip4j_1.3.1.jar" target-dir="libs" />-->

    </platform>
</plugin>
plugin.xml中配置項其實是很多的,由於此demo中只需要以上配置,所以就沒有體現其他配置項了。想要了解更多的話,可以留言討論,我會盡快回復。當然,也可以直接看官方文件(純英文):http://cordova.apache.org/docs/en/latest/plugin_ref/spec.html

3.2  編輯fileUtil.js(js介面實現)

依舊直接上程式碼,程式碼已詳細註釋:

/**
* cordova.define 的第一個引數為【外掛id.js-module的name值】,對應安裝後cordova_plugins.js裡面定義的id
* exec方法,引數說明:
* 引數1:成功回撥function
* 引數2:失敗回撥function
* 引數3:feature name,與config.xml中註冊的一致
* 引數4:呼叫java類時的action
* 引數5:要傳遞的引數,json陣列格式
* 下面提供三種實現方式
*/
//cordova.define("fxp-plugin-fileUtil.zipUtil", function(require, exports, module) {
var exec = require('cordova/exec');

            exports.exportDatasToEncryptedZip=function(content, successCallback, errorCallback){
                exec(successCallback,errorCallback,"zipUtil","exportDatasToEncryptedZip",[content]);
            };
            exports.writeFileToDir=function (content, successCallback, errorCallback) {
                cordova.exec(successCallback, errorCallback, "zipUtil", "writeFileToDir", [content]);
            };
            exports.zipEncryptedFolders= function (content, successCallback, errorCallback) {
                cordova.exec(successCallback, errorCallback, "zipUtil", "zipEncryptedFolders", [content]);
            }

/*        module.exports = {
            exportDatasToEncryptedZip: function(content, successCallback, errorCallback){
                cordova.exec(successCallback,errorCallback,"zipUtil","exportDatasToEncryptedZip",[content]);
            }
            ,
            writeFileToDir: function (content, successCallback, errorCallback) {
                cordova.exec(successCallback, errorCallback, "zipUtil", "writeFileToDir", [content]);
            },
            zipEncryptedFolders: function (content, successCallback, errorCallback) {
                cordova.exec(successCallback, errorCallback, "zipUtil", "zipEncryptedFolders", [content]);
            }
        }*/

/*                var exec = require('cordova/exec');
                var FXP = function(){};
                FXP.prototype.exportDatasToEncryptedZip=function(content,success, error) {
                        cordova.exec(success,error,"zipUtil","exportDatasToEncryptedZip",[content]);
                };
                FXP.prototype.writeFileToDir=function(content,success, error) {
                        cordova.exec(success, error, "zipUtil", "writeFileToDir", [content]);
                };
                FXP.prototype.zipEncryptedFolders=function(content,success, error) {
                        cordova.exec(success, error, "zipUtil", "zipEncryptedFolders", [content]);
                };
                var fxp = new FXP();
                module.exports = fxp;*/

//});

3.3  編輯zipUtil.java(安卓原生介面實現)
package fxp.plugin.fileUtil;

import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.CallbackContext;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.util.Log;
import android.widget.Toast;

/**
 * This class echoes a string called from JavaScript.
 */
public class zipUtil extends CordovaPlugin {

    /**
     * @param action          The action to execute.
     * @param args            The exec() arguments, wrapped with some Cordova helpers.
     * @param callbackContext The callback context used when calling back into JavaScript.
     * @return
     * @throws JSONException
     */
    @Override
    public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
        if (action.equals("exportDatasToEncryptedZip")) {
            exportDatas(args,callbackContext);
            return true;
        } else if (action.equals("writeFileToDir")) {
            String message = args.getString(0);
            //TODO

            return true;
        } else if (action.equals("zipEncryptedFolders")) {
            String message = args.getString(0);
            //TODO

            return true;
        }else {
            //TODO
        }
        return super.execute(action, args, callbackContext);
    }

    private void exportDatas(JSONArray args,CallbackContext callbackContext) throws JSONException{
        JSONObject jsonArgs = new JSONObject(args.getString(0));
        String fileContent = (String) jsonArgs.get("fileContent");
        String fileName = (String) jsonArgs.get("fileName");
        String zipPath = (String) jsonArgs.get("zipPath");
        String zipName = (String) jsonArgs.get("zipName");
        Boolean isCreateDir = (Boolean) jsonArgs.get("isCreateDir");
        String zipPwd = (String) jsonArgs.get("zipPwd");
        String exportPath = FileService.getInstance(cordova.getActivity()).exportDatasToEncryptedZip(fileContent, fileName, zipPath, zipName, isCreateDir, zipPwd);
        if (exportPath != null && exportPath != "") {
            Log.e("本地匯出成功-exportPath", exportPath);
            callbackContext.success(exportPath);
        } else {
            Log.e("本地匯出失敗-exportPath", exportPath);
            callbackContext.error(exportPath);
        }
    }

}

關於此類中execute方法執行緒和args取值問題,我在另一篇博文中有詳細講述,請參看:Cordova外掛開發(1)-Android外掛開發詳解

主要就是以上三個檔案了,業務程式碼可根據實際需求新增。檔案加密壓縮外掛呼叫方式如下:

        var args = {
            fileContent:"鵬超帥才不是單身狗鵬超帥才不是單身狗鵬超帥才不是單身狗",
            fileName:"fxp_export.json",
            zipPath:"/storage/emulated/0/FXP/export/",
            zipName:"EncryptedZip.zip",
            isCreateDir:true,
            zipPwd:"123456"
        }
        fxp.plugins.zipUtil.exportDatasToEncryptedZip(args,function(success){
            alert("本地匯出成功-exportPath:" + success);
        },function (error){
            alert("本地匯出失敗-exportPath:" + error);
        });

下面也貼一點功能實現程式碼吧。這種業務需求,一般習慣寫個服務類統一代理,然後單例模式呼叫。如下:

package fxp.plugin.fileUtil;

import android.content.Context;

import java.util.ArrayList;
import java.util.List;

/**
 * File
 * $desc:檔案操作服務類
 *
 * @author fxp
 *         Created at 2017/4/17.
 */
public class FileService {

    private final String TAG = "FileService";

    private static FileService instance;

    private Context context;

    public static FileService getInstance(Context context) {
        if (null == instance) {
            instance = new FileService(context);
        }
        return instance;
    }

    private FileService(Context context) {
        this.context = context;
    }

    /**
     * create by fxp 2017-4-18 將字串匯出到加密壓縮包
     *
     * @param fileContent:文字內容
     * @param fileName:檔名
     * @param exportPath:壓縮包存放的位置,為Null則存放在第一個帶壓縮檔案/資料夾所在路徑
     * @param zipName:壓縮包名稱
     * @param isCreateDir:是否需要建立子資料夾
     * @param zipPwd:加密密碼
     * @return 壓縮檔案的路徑,失敗則為NULL。返回值示例: /storage/emulated/0/FXP/export/20170418115248_done.zip
     */
    public String exportDatasToEncryptedZip(String fileContent, String fileName, String exportPath, String zipName, boolean isCreateDir, String zipPwd) {
        String filePath = "";
        //臨時資料夾路徑。tempPath示例: /storage/emulated/0/FXP/temp/
        String tempPath = FileUtils.getTmpPath();
        String exportTime = DateUtils.formatDate("yyyyMMddHHmmss", DateUtils.getNow());
        //臨時檔案路徑
        String dirPath = tempPath + exportTime + "/data/";

        //將資料寫入檔案
        boolean isWriteSuccess = writeFileToDir(dirPath, fileName, fileContent);
        if (isWriteSuccess) {
            List<String> folders = new ArrayList<String>();
            //將需要匯出的資料夾路徑新增到連結串列
            folders.add(tempPath + exportTime);
            //加密壓縮資料夾
            filePath = zipEncryptedFolders(folders, exportPath, zipName, isCreateDir, zipPwd);
        }

        //刪除臨時資料夾及檔案
        FileUtils.deleteDirOrFile(tempPath);

        return filePath;
    }

    /**
     * create by fxp 2017-4-17 將文字內容寫入到指定目錄下檔案
     *
     * @param dirPath:檔案目錄
     * @param fileName:檔名稱
     * @param fileContent:文字內容
     * @return 文字寫入操作是否成功
     */
    public boolean writeFileToDir(String dirPath, String fileName, String fileContent) {
        return FileUtils.writeFileToDir(dirPath, fileName, fileContent);
    }

    /**
     * create by fxp 2017-4-17 將1~N個檔案或資料夾加密壓縮到特定目錄下
     *
     * @param folders:1~N個檔案或資料夾的連結串列
     * @param exportPath:壓縮包存放的位置,為Null則存放在第一個帶壓縮檔案/資料夾所在路徑
     * @param zipName:壓縮包名稱
     * @param isCreateDir:是否需要建立子資料夾
     * @param pwd:加密密碼
     * @return 壓縮檔案的路徑,失敗則為NULL。返回值示例: /storage/emulated/0/FXP/export/20170418115248_done.zip
     */
    public String zipEncryptedFolders(List<String> folders, String exportPath, String zipName, boolean isCreateDir, String pwd) {
        return CompressUtil.zipEncryptedFolders(folders, exportPath, zipName, isCreateDir, pwd);
    }
}

該下班了,其他的就懶得貼了。迴歸正題,外掛安裝包開發完成。

4,安裝自己的外掛

本篇文章的重點是講述外掛安裝包製作過程,所以這裡先只講本地安裝,後面我會專門整理出一篇文章講述npm、git等安裝方式。

進入cordova工程目錄執行以下命令:

cordova plugin add 外掛安裝包相對路徑
例如,
sudo cordova plugin add ../fileUtil

Installing "fxp-plugin-fileUtil" for android

ANDROID_HOME=/Users/fxp/Library/Android/sdk

JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_92.jdk/Contents/Home

Subproject Path: CordovaLib

Incremental java compilation is an incubating feature.

:clean

:CordovaLib:clean

BUILD SUCCESSFUL

Total time: 7.386 secs

Installing "fxp-plugin-fileUtil" for ios

注意:本地安裝是根據相對路徑,不是外掛id。我這裡是把fileUtil資料夾(外掛安裝包)放在cordova工程同一目錄。

5,解除安裝外掛

解除安裝方式並無不同,在cordova工程目錄下執行以下命令:

cordova plugin remove 外掛id
例如,
sudo cordova plugin remove fxp-plugin-fileUtil

Password:

Uninstalling fxp-plugin-fileUtil from android

Uninstalling fxp-plugin-fileUtil from ios

Removing "fxp-plugin-fileUtil"

三,寫在後面

1,本篇文章意在講述外掛安裝包製作過程,歡迎交流討論;

2,本篇文章只講解了製作本地安裝包以及本地安裝,後面有空會再整理一篇文章講述基於npm、git方式;

3,此demo為需求驗證所寫,並非實際專案最終所用,不存在侵權問題;

4,已手擼外掛實現了檔案加密、檔案解密、檔案壓縮、檔案解壓、檔案MD5值校驗和其他常見檔案操作,但只擼了Android,IOS不會,期待IOS大神實現IOS版,願交換完整程式碼;

5,不足之處,歡迎批評指正;