1. 程式人生 > >cordova 自定義外掛之完整流程

cordova 自定義外掛之完整流程

一.前期開發環境

1.android studio和xcode開發環境

2.安裝node.js 

3.安裝plugman

4.命令工具環境(可以安裝git也可以使用系統自帶的工具,window的dos,ios的終端)

5.subline text3 主要用於編輯外掛的的檔案(當然也可以用notepad 看個人喜好 也可以用一些其它編輯工具)

二.建立工程

1.安裝cordova和plugman

使用npm命令:npm install -g cordova 

                         npm install -g plugman

如果是mac上有可能安裝失敗,這是因為讀寫許可權問題,在命令前面加上sudo就好。

cordova安裝的是最新版本。

2.建立android或ios的cordova專案

 雖然這一步沒必要寫 ,網上也有很多教程 ,但是為了整個流程的完整性 ,還是寫出來

命令步驟如下:

在這之前有一點細碎的事,個人習慣一定要把工程放在合適的位置

例如在window 的DOS 環境下,cd到相應的磁碟目錄在使用下面的命令(ios則直接在Desktop目錄下就可以了 也就是桌面上)

    2.1 建立Hello工程

          cordova create hello  com.moke.hello  Hello 

          第一個hello是建立的工程資料夾名 中間的com.moke.hello就不用多說了,Hello,工程名

    2.2 新增開發環境平臺

         首先得切換命令目錄到工程目錄,例如本例子就需要 cd hello到工程目錄中,然後執行 以下命令:  

         android環境:cordova platform add android ,ios環境:cordova platform add ios 

        會生出如下目錄(ios為例): 

         2.3 新增外掛

          這一步可做可不做 ,但是命令還是要寫出來。 cordova plugin add  ********* ,星號部分為外掛的地址 或者 自定義外掛的目錄地址

          可以新增也可以刪除 cordova plugin rm ******** ,星號為外掛名 如果不知道外掛名 可以用命令檢視當前工程的外掛列表 cordova plugin list

       2.4 最後就是編譯 

          cordova build ios或者android

三 自定義外掛

     有時候github或者npm上的外掛不能滿足專案需求時 就需要自己自定義外掛

     3.1建立外掛基本結構

     先cmd到桌面或者任意磁碟目錄執行以下命令:

    輸入 plugman create --name com.moke.TestPlugin --plugin_id com.moke.testPlugin --plugin_version 0.0.1

  生成一個名字為com.moke.TestPlugin的外掛資料夾以及目錄為下:

   

         3.2 外掛目錄配置

         plugin.xml 是配置相關android ios  wp等平臺的資訊,這是一個非常重要的配置檔案,在js中能否呼叫到外掛中的本地方法就需要看這個檔案中是否配置正確。

         src資料夾:裡面放置的是各個平臺的類檔案以及一些lib庫,但是需要注意在src目錄下 要新建例如 命名為ios的資料夾 這個資料夾就放置的ios的.h 和.m的檔案以及ios環境下外掛所需要的第三方庫等,同等,放置android的java檔案需要建立命名為android的資料夾。

         www資料夾:這個資料夾裡面有一個js檔案,是在3.1中建立外掛檔案時自動生成的。這個js的主要作用就是連結原生代碼的介面 通過訪問該js介面來呼叫src資料夾中不同平臺的本地方法        

         3.3 外掛配置詳細

             3.3.1 以一個簡單demo為例子來表示相應配置資訊 在這建立了相應android平臺的hello.java 和ios平臺的HVPhello.h HVPhello.m檔案 主要實現了字串的顯示

          hello.java檔案:

package com.moke.testPlugin;

import org.apache.cordova.*;
import org.json.JSONArray;
import org.json.JSONException;

public class Hello extends CordovaPlugin {

    @Override
    public boolean execute(String action, JSONArray data, CallbackContext callbackContext) throws JSONException {

        if (action.equals("greet")) {

            String name = data.getString(0);
            String message = "Hello, " + name;
            callbackContext.success(message);

            return true;

        } else {
            
            return false;

        }
    }
}

            HVPhello.h HVPhello.m檔案

#import <Cordova/CDV.h>

@interface HWPHello : CDVPlugin

- (void) greet:(CDVInvokedUrlCommand*)command;

@end


#import "HWPHello.h"

@implementation HWPHello

- (void)greet:(CDVInvokedUrlCommand*)command
{

    NSString* name = [[command arguments] objectAtIndex:0];
    NSString* msg = [NSString stringWithFormat: @"Hello, %@", name];

    CDVPluginResult* result = [CDVPluginResult
                               resultWithStatus:CDVCommandStatus_OK
                               messageAsString:msg];

    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
}

@end

   備註:在本地外掛檔案中  

       java callbackContext 和 ios中的 sendPluginResult:result  都代表了將相應結果回撥到你的html中的js回撥方法中。

   3.3.2 plugin.xml配置

<?xml version='1.0' encoding='utf-8'?>
<plugin id="com.moke.testPlugin" version="0.0.1" xmlns="http://apache.org/cordova/ns/plugins/1.0">
    <name>TestPlugin</name>
    <description>Cordova hello Plugin</description>
    <license>Apache 2.0</license>
    <keywords>cordova,hello</keywords>

    <!--www資料夾下js的配置 主要作用是用於能夠找到該js檔案 其中clobbers中的target是用於你的h5應用呼叫js中的方法的變數 可以自定義該tag-->
    <js-module name="com.moke.TestPlugin" src="www/com.moke.TestPlugin.js">
        <clobbers target="TestPlugin" />
    </js-module>

    <!--android平臺的配置-->
    <platform name="android">
		
		<!--config-file 是把你android平臺的java檔案配置進去 feature中的name可以自定義-->
        <config-file target="res/xml/config.xml" parent="/*">
            <feature name="TestPlugin" >
                <param name="android-package" value="com.moke.testPlugin.Hello"/>
                <param name="onload" value="true" />
            </feature>
        </config-file>

         <!--source-file 其中 src中的目錄是你外掛java檔案的目錄 target-dir是把你外掛的類檔案在cordova工程中的目錄,記住com/moke/testPlugin一定要是你自己的檔案的包名-->
        <source-file src="src/android/Hello.java" target-dir="src/com/moke/testPlugin" />
    </platform>

     <!--ios平臺的配置-->
     <platform name="ios">
        <config-file target="config.xml" parent="/*">
            <feature name="TestPlugin">
                <param name="ios-package" value="HWPHello"/>
                <param name="onload" value="true" /> 
            </feature>
        </config-file>

        <!-- Plugin source code -->
        <header-file src="src/ios/HWPHello.h" />
        <source-file src="src/ios/HWPHello.m" />
    </platform>
</plugin>

        備註:在執行plugman命令生成的plugin檔案 在最開始是隻有<js-module> 和 <name>標籤的 

                   所以平臺相關配置還是需要自己配置,具體配置的註解在程式碼中我已經標明。
      

        3.3.3 www資料夾下的js配置:

var exec = require('cordova/exec');

<!-- msg 是傳遞的資料 success error 是回撥方法 其中 TestPlugin是plugin中配置的js中clobbers標籤中的target-->
<!--greet 是java檔案中excute中的action值,和.h中定義的greet方法, 記住 要保持一致。-->
exports.greet = function(msg, success, error) {
    exec(success, error, "TestPlugin", "greet", [msg]);
};

      備註:js是執行plugman自動生成的,但是其中的exports 和exec中的相應引數需要做一些改變,程式碼中做了註釋

      4.外掛原生代碼的建立

          外掛原生代碼邏輯和方法的編碼  也就是java檔案 和 .h .m 檔案的建立,可以在android studio  或者 xcode 中 載入相應平臺的cordova工程專案 ,在專案工程中做外掛原生代碼的編碼,然後把寫好的檔案再copy到外掛檔案中的src中相應資料夾下即可。

      5.新增外掛。

      先cmd到相應cordova目錄 然後執行 cordova plugin add  xx/xx/xx/com.moke.TestPlugin   。

      這時就可以看到在cordova工程中的外掛目錄中新增好的 com.moke.TestPlugin外掛

     6.外掛呼叫

      在你的cordova工程中的 html頁面用呼叫js方法 並且在js的function中寫入

     var success = function(e){
          alert(e);
      }
      var error = function(e){
          alert(e);
      }
      TestPlugin.greet("Geek",success,error);

      四最後一點外掛相應的配置

          如果我們寫的外掛需要使用第三方庫那麼 第三方庫的配置怎麼辦?

          我們以微信支付為例子 : 只做個簡單的例子配置展現。

         plugin.xml配置

<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
           id="moke-plugin-wepay"
      version="1.3.0">
    <name>WeChatPay</name>
    <description>Cordova wepay Plugin</description>
    <license>Apache 2.0</license>
    <keywords>cordova,pay,wechat</keywords>
    <preference name="WECHATAPPID" />
    <engines>
      <engine name="cordova-android" version=">=4.0.0" />
    </engines>
	
 

    <platform name="ios">
        <config-file target="config.xml" parent="/*">
            <feature name="WeChatPay">
                <param name="ios-package" value="CDVWeChatPayPlugin"/>
                <param name="onload" value="true" /> 
            </feature>
            <preference name="WECHATAPPID" value="$WECHATAPPID"/>
        </config-file>

        <config-file target="*-Info.plist" parent="LSApplicationQueriesSchemes">
            <array>
                <string>weixin</string>
                <string>wechat</string>
            </array>
        </config-file>

        <config-file target="*-Info.plist" parent="NSAppTransportSecurity">
            <dict>
                <key>NSAllowsArbitraryLoads</key>
                <true/>
            </dict>
        </config-file>
        

        <config-file target="*-Info.plist" parent="CFBundleURLTypes">
            <array>
                <dict>
                    <key>CFBundleURLName</key>
                    <string>weixin</string>
                    <key>CFBundleURLSchemes</key>
                    <array>
                        <string>$WECHATAPPID</string>
                    </array>
                </dict>
            </array>
        </config-file>

        <js-module src="www/Wechatpay.js" name="WeChatPay">
        <clobbers target="WeChatPay" />
        </js-module>

        <!-- Plugin source code -->
        <header-file src="src/ios/CDVWeChatPayPlugin.h" />
        <source-file src="src/ios/CDVWeChatPayPlugin.m" />

        <!-- Wechat Official -->
        <header-file src="src/ios/libs/OpenSDK1.7.4/WXApi.h" />
        <header-file src="src/ios/libs/OpenSDK1.7.4/WXApiObject.h" />
        <source-file src="src/ios/libs/OpenSDK1.7.4/libWeChatSDK.a" framework="true" />

        <!-- Other required frameworks -->
        <framework src="libz.tbd" />
        <framework src="libsqlite3.0.tbd" />
        <framework src="CoreTelephony.framework" />
        <framework src="SystemConfiguration.framework" />
        <framework src="Security.framework" />
        <framework src="CFNetwork.framework" />
        <framework src="libstdc++.6.tbd" />
    </platform>

	
    <platform name="android">

    <js-module src="www/Wechatpay.js" name="WeChatPay">
        <clobbers target="WeChatPay" />
    </js-module>


	    <hook type="after_plugin_add" src="scripts/android-install.js" />
        <hook type="after_plugin_install" src="scripts/android-install.js" />
        <hook type="before_plugin_rm" src="scripts/android-install.js" />
        <hook type="before_plugin_uninstall" src="scripts/android-install.js" />
		
        <config-file target="res/xml/config.xml" parent="/*">
            <feature name="WeChatPay" >
                <param name="android-package" value="org.apache.cordova.pay.WeChatPayPlugin"/>
                <param name="onload" value="true" />
            </feature>
        </config-file>

        <source-file src="src/android/WeChatPayPlugin.java" target-dir="src/org/apache/cordova/pay" />
		
		<source-file src="src/android/Utils.java" target-dir="src/org/apache/cordova/pay" />
        <source-file src="src/android/libammsdk.jar" target-dir="libs" />
        	<info>
               This plugin is only applicable for versions of cordova-android greater than 4.0. If you have a previous platform version, you do *not* need this plugin since the whitelist will be built in.
        </info>
		<config-file target="AndroidManifest.xml" parent="/*">
            <uses-permission android:name="android.permission.INTERNET"/>
            <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
            <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
            <uses-permission android:name="android.permission.READ_PHONE_STATE" />
            <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
        </config-file>
		
		
		<config-file target="AndroidManifest.xml" parent="/manifest/application">
         <activity
                android:name=".wxapi.WXPayEntryActivity"
                android:label="@string/launcher_name"
                android:exported="true"
                android:launchMode="singleTop">
                <intent-filter>
                    <action android:name="android.intent.action.VIEW"/>
                    <category android:name="android.intent.category.DEFAULT"/>
                </intent-filter>
            </activity>
        </config-file>
    </platform>
	


</plugin>

     android-install.js

module.exports = function (context) {
    var path        = context.requireCordovaModule('path'),
        fs          = context.requireCordovaModule('fs'),
        shell       = context.requireCordovaModule('shelljs'),
        projectRoot = context.opts.projectRoot,
        plugins     = context.opts.plugins || [];

    // The plugins array will be empty during platform add
    if (plugins.length > 0 && plugins.indexOf('cordova-plugin-wechat') === -1) {
        return ;
    }

    var ConfigParser = null;
    try {
        ConfigParser = context.requireCordovaModule('cordova-common').ConfigParser;
    } catch(e) {
        // fallback
        ConfigParser = context.requireCordovaModule('cordova-lib/src/configparser/ConfigParser');
    }

    var config      = new ConfigParser(path.join(context.opts.projectRoot, "config.xml")),
        packageName = config.android_packageName() || config.packageName();

    // replace dash (-) with underscore (_)
    packageName = packageName.replace(/-/g , "_");
    
    console.info("Running android-install.Hook: " + context.hook + ", Package: " + packageName + ", Path: " + projectRoot + ".");

    if (!packageName) {
        console.error("Package name could not be found!");
        return ;
    }

    // android platform available?
    if (context.opts.cordova.platforms.indexOf("android") === -1) {
        console.info("Android platform has not been added.");
        return ;
    }

    var targetDir  = path.join(projectRoot, "platforms", "android", "src", packageName.replace(/\./g, path.sep), "wxapi");
        targetFiles = ["WXPayEntryActivity.java"];

    if (['after_plugin_add', 'after_plugin_install'].indexOf(context.hook) === -1) {
        // remove it?
        targetFiles.forEach(function (f) {
            try {
                fs.unlinkSync(path.join(targetDir, f));
            } catch (err) {}
        });
    } else {
        // create directory
        shell.mkdir('-p', targetDir);

        // sync the content
        targetFiles.forEach(function (f) {
            fs.readFile(path.join(context.opts.plugin.dir, 'src', 'android', f), {encoding: 'utf-8'}, function (err, data) {
                if (err) {
                    throw err;
                }

                data = data.replace(/^package __PACKAGE_NAME__;/m, 'package ' + packageName + '.wxapi;');
                fs.writeFileSync(path.join(targetDir, f), data);
            });
        });
    }
};


微信目錄結構:

         

           最後 該微信支付例子是從github上的一個開源專案做了相應的改動,由此感謝github上貢獻的各位同行。

          由於是第一次寫部落格,如果文章中不足和錯誤,希望大家能夠指正,不勝感激。