簡體   English   中英

為ionic2項目創建自定義cordova插件

[英]Custom cordova plugin creation for ionic2 project

我們中的許多人都會經歷類似的問題,但即使經過以下大多數相關鏈接引用link1參考link2 ,我也無法解決。

問題:

創建一個自定義插件(Cordova)以便在ionic2項目中使用它。

期望 :此插件將能夠與IOS和Android的本機功能進行交互。 確切地說,我正在嘗試使用cordova訪問Ionic項目的本機SDK(Aruba內部定位SDK)的功能。

步驟1根據參考鏈接1初始創建插件

第2步創建Ionic 2項目(使用基本步驟創建)

步驟3插件中的JavaScript文件無法在Ionic2中引用和訪問。

谷歌搜索后,我發現了這個討論 ,由於以下原因,它被告知在插件中創建界面。

從'../../../plugins/xxx/*.js'導入{myPluginName}

因為插件不是離子本機包的一部分而無法工作。

如果你有自定義插件,你可以做一些事情。

1)制作PR以將其添加到離子原生物中

2)使用原始插件API。 您可以使用原始插件API,而不必將其作為Ionic Native的一部分。 該插件位於窗口對象上,因此您可以正常定位api

window.plugin.myPlugin.myMethod()

根據GITHUB Example項目,這種方式應該實現接口

interface CordovaPlugins {
  ZPLPrinter: ZPLPrinter;
}

interface ZPLPrinter {

  print(
    ipaddress: string,
    bclabels: any,
    printSuccess: (ip: string, labels: string[]) => void,
    printError: (message: string) => void): void;

}

現在我在我的插件中創建了一個類似的界面,插件的www文件夾中有以下內容

interface CordovaPlugins {
  Communicator: Communicator;
}

interface Communicator {

  getInfo(successCallback: any, errorCallback: any);

}

理想情況下,此接口將在JS文件中定位此方法

Device.prototype.getInfo = function(successCallback, errorCallback) {
    console.log("device.js: getInfo function called");
    argscheck.checkArgs('fF', 'Device.getInfo', arguments);
    exec(successCallback, errorCallback, "Device", "getDeviceInfo", []);
};

現在我卡住了,因為我的Ionic項目本身並沒有打字文件夾。

在示例Github項目中 ,cordova包使用typings文件夾引用。 項目中的TypeScript文件使用index.t.js引用Cordova

用來引用的導入應該是這樣的

declare var cordova: Cordova;

釋疑:

  1. 我是在這個過程的正確方向
  2. 這是創建Cordova插件並在離子中使用的方法
  3. 為什么我不能夠獲得typings文件夾中Ionic2

編輯1:

剛剛添加插件后,甚至沒有參考Ionic項目,我嘗試在Android設備上運行。 但它給了我以下錯誤。

主要錯誤是這個

java.lang.RuntimeException: Unable to start activity ComponentInfo{com.ionicframework.cutepuppypics234138/com.ionicframework.cutepuppypics234138.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'void org.apache.cordova.CordovaPlugin.privateInitialize(java.lang.String, org.apache.cordova.CordovaInterface, org.apache.cordova.CordovaWebView, org.apache.cordova.CordovaPreferences)' on a null object reference

為什么會導致此錯誤? 詳細日志如下

12-08 16:10:49.079 20555-20555/? E/ApkAssets: Error while loading asset assets/natives_blob_64.bin: java.io.FileNotFoundException: assets/natives_blob_64.bin
12-08 16:10:49.079 20555-20555/? E/ApkAssets: Error while loading asset assets/snapshot_blob_64.bin: java.io.FileNotFoundException: assets/snapshot_blob_64.bin
12-08 16:10:49.682 20555-20555/? E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.ionicframework.cutepuppypics234138, PID: 20555
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.ionicframework.cutepuppypics234138/com.ionicframework.cutepuppypics234138.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'void org.apache.cordova.CordovaPlugin.privateInitialize(java.lang.String, org.apache.cordova.CordovaInterface, org.apache.cordova.CordovaWebView, org.apache.cordova.CordovaPreferences)' on a null object reference
   at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2339)
   at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2413)
   at android.app.ActivityThread.access$800(ActivityThread.java:155)
   at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1317)
   at android.os.Handler.dispatchMessage(Handler.java:102)
   at android.os.Looper.loop(Looper.java:135)
   at android.app.ActivityThread.main(ActivityThread.java:5343)
   at java.lang.reflect.Method.invoke(Native Method)
   at java.lang.reflect.Method.invoke(Method.java:372)
   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:905)
   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:700)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void org.apache.cordova.CordovaPlugin.privateInitialize(java.lang.String, org.apache.cordova.CordovaInterface, org.apache.cordova.CordovaWebView, org.apache.cordova.CordovaPreferences)' on a null object reference
   at org.apache.cordova.PluginManager.getPlugin(PluginManager.java:171)
   at org.apache.cordova.PluginManager.startupPlugins(PluginManager.java:97)
   at org.apache.cordova.PluginManager.init(PluginManager.java:86)
   at org.apache.cordova.CordovaWebViewImpl.init(CordovaWebViewImpl.java:115)
   at org.apache.cordova.CordovaActivity.init(CordovaActivity.java:149)
   at org.apache.cordova.CordovaActivity.loadUrl(CordovaActivity.java:224)
   at com.ionicframework.cutepuppypics234138.MainActivity.onCreate(MainActivity.java:39)
   at android.app.Activity.performCreate(Activity.java:6010)
   at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1129)
   at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2292)
   at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2413) 
   at android.app.ActivityThread.access$800(ActivityThread.java:155) 
   at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1317) 
   at android.os.Handler.dispatchMessage(Handler.java:102) 
   at android.os.Looper.loop(Looper.java:135) 
   at android.app.ActivityThread.main(ActivityThread.java:5343) 
   at java.lang.reflect.Method.invoke(Native Method) 
   at java.lang.reflect.Method.invoke(Method.java:372) 
   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:905) 
   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:700) 
12-08 16:10:49.879 20656-20656/? E/SubDex: SubDex Config : .dex 2
12-08 16:10:50.285 20656-20656/? E/project: extsdcard==/storage/emulated/0/Android/data/com.cleanmaster.mguard/files
12-08 16:10:50.303 20656-20656/? E/project: extsdcard==/storage/emulated/0/Android/data/com.cleanmaster.mguard/files

經過多次試驗和錯誤后,我找到了解決方案。

我正在記下以下細節,以便將來參考嘗試類似內容的任何人!

代碼問題如下(我的插件名稱為Inject

  • Inject\\www\\Inject.js沒有安裝插件的基本功能
  • Inject.js提到的任何方法名稱應該相同,直到Inject\\src\\Inject.java因為execute方法中有選項可以根據收到的標記引用不同的方法名稱

腳步:

  1. 使用plugman創建插件參考鏈接的骨架
  2. 而不是使用像cordova-plugin-am-i-late這樣的名字,請使用cordova.plugin.Inject 我使用-符號編譯/運行時問題
  3. 添加有針對性的平台plugman platform add --platform_name android
  4. 驗證plugin.xml比較相同的引用
  5. 如果需要,在plugin.xml包含權限
  6. 現在驗證Inject.js缺少必要的方法調用。 目前它只有以下代碼。

var exec = require('cordova/exec');
exports.coolMethod = function(arg0, success, error) {
exec(success, error, "Inject", "coolMethod", [arg0]);
};
  1. 但是我們還需要在下面包含它以使其安裝和工作

function Inject(){
}
Inject.install = function () {
  if (!window.plugins) {
    window.plugins = {};
  }

  window.plugins.Inject = new Inject();
  return window.plugins.Inject;
};

cordova.addConstructor(Inject.install);
  1. 例如,我的目標是顯示Toast消息,下面是我完整的Inject.js文件

function Inject(){
}

Inject.prototype.coolMethod = function (options, successCallback, errorCallback) {
  cordova.exec(successCallback, errorCallback, "Inject", "coolMethod", []);
};

Inject.install = function () {
  if (!window.plugins) {
    window.plugins = {};
  }

  window.plugins.Inject = new Inject();
  return window.plugins.Inject;
};

cordova.addConstructor(Inject.install);
  1. 現在讓我們實現我們的Inject.java

public class Inject extends CordovaPlugin {

private static final int GRAVITY_CENTER = Gravity.CENTER_VERTICAL|Gravity.CENTER_HORIZONTAL;
private static final String TAG = "InjectCordovaPlugin";
String messageReceived;

    @Override
    public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
        Log.v(TAG, "execute , action =" + action);
        if (action.equals("coolMethod")) {
            String message = args.getString(0);
            Log.v(TAG, "coolMethod called with message =" + message);
            this.coolMethod(message, callbackContext);
            return true;
        }

        return false;
    }
    private void coolMethod(String message, CallbackContext callbackContext) {
    Log.v(TAG, "Inject's coolMethod called ,message="+message);
    messageReceived = message;
        if (message != null && message.length() > 0) {
            cordova.getActivity().runOnUiThread(new Runnable() {
            public void run() {

            final android.widget.Toast toast = android.widget.Toast.makeText(
              cordova.getActivity().getWindow().getContext(),
              messageReceived,
              android.widget.Toast.LENGTH_LONG 
                );
                toast.setGravity(GRAVITY_CENTER, 0, 0);
                toast.show();
            }
            });
            callbackContext.success(message);
        } else {
            callbackContext.error("Expected one non-empty string argument.");
        }
    }
}
  1. 為了明確這一點,我正在推出我的最終plugin.xml

<?xml version='1.0' encoding='utf-8'?>
<plugin id="cordova.plugin.Inject" 
    version="1" 
    xmlns="http://apache.org/cordova/ns/plugins/1.0" 
    xmlns:android="http://schemas.android.com/apk/res/android">
<name>Inject</name>
<js-module name="Inject" src="www/Inject.js">
    <clobbers target="window.plugins.Inject"/>
</js-module>
<platform name="android">
<config-file parent="/*" target="res/xml/config.xml">
<feature name="Inject">
<param name="android-package" 
        value="cordova.plugin.Inject.Inject" />
        </feature>
</config-file>
<config-file parent="/*" target="AndroidManifest.xml">
</config-file>
<source-file src="src/android/Inject.java" 
target-dir="src/cordova.plugin.Inject/Inject" />
</platform>
</plugin>
  1. 現在創建一個示例Ionic2項目並添加Android / IOS平台
  2. 使用cordova plugin add命令添加創建的插件
  3. 在Nodejs命令提示符下導航到ionic項目並給出此命令( add后參考插件文件夾庫) cordova plugin add D:\\PluginTrial\\Inject
  4. 添加的插件應填充在Ionic2Project\\plugins文件夾下
  5. 使用window對象調用此函數。 以下是我的home.ts

import { Component } from '@angular/core';

import { NavController, Platform } from 'ionic-angular';

declare var window: any;
@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})
export class HomePage {

  constructor(public navCtrl: NavController, private platform: Platform) {

  }
    showToast(message, position) {
        this.platform.ready().then(() => {
            window.plugins.Inject.coolMethod(message, "short", position);
        });
    }
}
  1. 現在在home.html文件中引用此showToast方法

<button ion-button (click)="showToast('Yo Man! Its working ', 'center')">Default</button>
  1. 而已。 您應該能夠成功測試插件! 快樂的編碼

參考: 參考文獻1參考文獻2參考文獻3

你的插件需要看起來像這樣:

在:/ [自定義插件名稱] /js/custom_plugin.js

var CustomPlugin = function(){};

CustomPlugin.someFunction = function(){
    console.log("someFunction starts");

    return new Promise(function(resolve,reject){
    cordova.exec(
        resolve,
        reject,
        [PLUGIN_NAME],
        [ACTION_ON_NATIVE_SIDE],
        []
    );

    });
    console.log("someFunction stops");
}

.... more functions

module.exports = CustomPlugin;

在:/ [自定義插件名稱] / src / [android] || [ios],具有本機代碼的類。

並在:/ [自定義插件名稱] /plugin.xml(這是一個示例,必須根據您的情況調整設置):

<?xml version="1.0" encoding="UTF-8"?>
<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
    id="[CustomPlugin]"
    version="1.0.0">
    <name>CustomPlugin</name>
    <description>...</description>
    <license>...</license>
    <author>...</author>

    <engines>
        <engine name="cordova" version=">=6.0.0" />
    </engines>



    <js-module src="www/js/custom_plugin.js" name="CustomPlugin">
        <clobbers target="CustomPlugin" />
    </js-module>


    <platform name="ios">
        <config-file target="config.xml" parent="/*">
            <preference name="orientation" value="portrait"/>
            <feature name="CustomPlugin">
                <param name="ios-package" value="CustomPlugin" />
                <param name="onload" value="true"/>
            </feature>
        </config-file>

        <header-file src="src/ios/CustomPlugin.h" />
        <source-file src="src/ios/CustomPlugin.m" />
        <!--framework src="QuartzCore.framework" /> 
        <framework src="AssetsLibrary.framework" />
        <framework src="CoreGraphics.framework" />
        <framework src="MobileCoreServices.framework" /-->
    </platform>

    <platform name="android">
        <config-file target="res/xml/config.xml" parent="widget">
            <preference name="orientation" value="portrait"/>
            <feature name="CustomPlugin" >
                <param name="android-package" value="[package name].CustomPlugin"/>
                <param name="onload" value="true"/>
            </feature>
        </config-file>

        <config-file target="AndroidManifest.xml" parent="/*">
            <supports-screens android:anyDensity="true" android:largeScreens="true" android:normalScreens="true" android:resizeable="true" android:smallScreens="true" android:xlargeScreens="true" />
            <uses-permission android:name="..." />
            <uses-feature android:name="..." />
        </config-file>

        <source-file src="src/android/CustomPlugin.java" target-dir="[package folder directory organization like: com.android.something]" />
        <source-file ... />
        <source-file src="src/android/custom_plugin.xml" target-dir="res/layout" />

    </platform>

</plugin>

然后你用CLI添加你的插件: ionic plugin add [folder of your plugin]

在你的Ionic項目中,在你想要使用你的插件的類(angular2指令)中,在@Component部分之前寫: declare var CustomPlugin: any; 然后在該類中,您可以通過CustomPlugin使用module.exports = CustomPlugin;導出的module.exports = CustomPlugin;來使用您的插件module.exports = CustomPlugin; 來自文件: /[custom plugin name]/js/custom_plugin.js

回答第1題的問題,這里有ANDROID PART的一些細節 :在android插件項目中(曾經使用離子CLI添加和構建至少一次平台android),在android studio(2.2.2)中,當看在“[my project] \\ platforms \\ android”下的構建項目中:

在層次結構中,MainActivity文件是自動生成的:“android \\ java \\ com \\ ionicframework。[我的項目名稱+一個大數字] \\ MainActivity”:

  package com.ionicframework.[my project name + a large number];

import android.os.Bundle;
import org.apache.cordova.*;

public class MainActivity extends CordovaActivity
{
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

        // enable Cordova apps to be started in the background
        Bundle extras = getIntent().getExtras();
        if (extras != null && extras.getBoolean("cdvStartInBackground", false)) {
            moveTaskToBack(true);
        }

        // Set by <content src="index.html" /> in config.xml
        loadUrl(launchUrl);
    }
}

對於我的自定義插件(這里沒有詳細介紹)“android \\ java [自定義插件包]:

package [package of the custom plugin];

import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.PluginResult;

// + other imports needed

public class CustomPlugin extends CordovaPlugin  {

    private static CallbackContext customDeferredCallback;

    public boolean execute(String action, final JSONArray args, final CallbackContext callbackContext) throws JSONException {
//all the thing corresponding to your case need to end if with either:
//   if OK: callbackContext.success(); return true;  ;
//   if not OK: callbackContext.error(error.getMessage()); return false;
//   if JAVA returns a result to JS do: actionBindListener(callbackContext);



}

    private boolean actionBindListener(final CallbackContext callbackContext){
    cordova.getThreadPool().execute(new Runnable() {
            public void run(){
                try{
                    customDeferredCallback= callbackContext;
                }catch(Exception e){e.printStackTrace();}
            }
        });
        return true;
    }


    //in your program when you get the result you want to send back to javascript call this function
    public static void sendResultToJS(res){
        JSONObject eventData = new JSONObject();
        try {
            eventData.put("CUSTOM_KEY", res);
        } catch (JSONException e) {
            e.printStackTrace();
        }
        PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, eventData);
        pluginResult.setKeepCallback(true);
        try{
                customDeferredCallback.sendPluginResult(pluginResult);
        } catch(NullPointerException e){
            e.printStackTrace();
        }
    }
}

最后android \\ manifests \\ manifests.xml看起來像這樣:

<?xml version='1.0' encoding='utf-8'?>
<manifest android:hardwareAccelerated="true" android:versionCode="1" android:versionName="0.0.1" package="com.ionicframework.[project name + large number]" xmlns:android="http://schemas.android.com/apk/res/android">
    <supports-screens android:anyDensity="true" android:largeScreens="true" android:normalScreens="true" android:resizeable="true" android:smallScreens="true" android:xlargeScreens="true" />
    <uses-permission android:name="android.permission.INTERNET" />
    <application android:hardwareAccelerated="true" android:icon="@mipmap/icon" android:label="@string/app_name" android:supportsRtl="true">
        <activity android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale" android:label="@string/activity_name" android:launchMode="singleTop" android:name="MainActivity" android:theme="@android:style/Theme.DeviceDefault.NoActionBar" android:windowSoftInputMode="adjustResize">
            <intent-filter android:label="@string/launcher_name">
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:exported="true" android:name="com.adobe.phonegap.push.PushHandlerActivity" />
        <receiver android:name="com.adobe.phonegap.push.BackgroundActionButtonHandler" />
        <receiver android:exported="true" android:name="com.google.android.gms.gcm.GcmReceiver" android:permission="com.google.android.c2dm.permission.SEND">
            <intent-filter>
                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
                <category android:name="${applicationId}" />
            </intent-filter>
        </receiver>
        <service android:exported="false" android:name="com.adobe.phonegap.push.GCMIntentService">
            <intent-filter>
                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
            </intent-filter>
        </service>
        <service android:exported="false" android:name="com.adobe.phonegap.push.PushInstanceIDListenerService">
            <intent-filter>
                <action android:name="com.google.android.gms.iid.InstanceID" />
            </intent-filter>
        </service>
        <service android:exported="false" android:name="com.adobe.phonegap.push.RegistrationIntentService" />
    </application>
    <uses-sdk android:minSdkVersion="16" android:targetSdkVersion="24" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
    <uses-permission android:name="${applicationId}.permission.C2D_MESSAGE" />
    <permission android:name="${applicationId}.permission.C2D_MESSAGE" android:protectionLevel="signature" />
</manifest>

就打字而言,它已不再使用。 所有或大多數打字稿聲明都會移動到npm本身,然后將它們安裝為npm install @types/package_name https://www.npmjs.com/~types https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/README.md如果您需要typings文件夾,您可以嘗試

npm install typings

你也可以通過referance type declararions

// <reference path="" />

在打字稿中

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM