[英]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;
釋疑:
Cordova
插件並在離子中使用的方法 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
方法中有選項可以根據收到的標記引用不同的方法名稱 腳步:
cordova-plugin-am-i-late
這樣的名字,請使用cordova.plugin.Inject
。 我使用-
符號編譯/運行時問題 plugman platform add --platform_name android
plugin.xml
包含權限 var exec = require('cordova/exec');
exports.coolMethod = function(arg0, success, error) {
exec(success, error, "Inject", "coolMethod", [arg0]);
};
function Inject(){
}
Inject.install = function () {
if (!window.plugins) {
window.plugins = {};
}
window.plugins.Inject = new Inject();
return window.plugins.Inject;
};
cordova.addConstructor(Inject.install);
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);
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.");
}
}
}
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>
cordova plugin add
命令添加創建的插件 add
后參考插件文件夾庫) cordova plugin add D:\\PluginTrial\\Inject
Ionic2Project\\plugins
文件夾下 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);
});
}
}
home.html
文件中引用此showToast方法 <button ion-button (click)="showToast('Yo Man! Its working ', 'center')">Default</button>
你的插件需要看起來像這樣:
在:/ [自定義插件名稱] /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.