我们中的许多人都会遇到类似的问题,但即使经过最相关的链接reference link1和reference link2,我也无法解决。
问题:
创建自定义插件(Cordova)以便在ionic2中使用它 项目
期望:此插件可以与IOS和Android的原生功能进行互动。确切地说,我正在尝试使用cordova访问Ionic项目中的原生SDK(Aruba内部定位SDK)的功能。
第1步根据reference link 1
初始创建的插件第2步:创建了Ionic 2项目(使用this基本步骤创建)
第3步插件中的JavaScript文件无法在Ionic2中引用和访问。
在谷歌搜索之后,我找到this discussion,由于以下原因,它被告知在插件中创建界面。
从' ../../../ plugins / xxx / *导入{myPluginName}。js'
无效,因为插件不是离子本机包的一部分。
如果你有自定义插件,你可以做一些事情。
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项目没有 typings 文件夹本身。
在示例Github Project中,使用 typings 文件夹引用cordova包。 TypeScript File in project使用index.t.js
引用Cordova
用于引用的导入应该像
declare var cordova: Cordova;
疑惑:
Cordova
插件并在离子typings
文件夹编辑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
答案 0 :(得分:10)
经过多次试验和错误后,我找到了解决方案。
我正在详细说明以下细节,以便将来参考尝试类似内容的任何人!
代码问题如下(我的插件名称为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
file <button ion-button (click)="showToast('Yo Man! Its working ', 'center')">Default</button>
答案 1 :(得分: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;
来使用您的插件
来自文件:/[custom plugin name]/js/custom_plugin.js
。
回答编辑问题1,这里有一些ANDROID PART的详细信息: 在Android插件项目中(曾经使用离子CLI添加和构建至少一次平台android),在android studio(2.2.2)中查看&#34; [我的项目] \平台下的构建项目机器人&#34;:
在层次结构中,MainActivity文件在以下位置自动生成: &#34; android \ java \ com \ ionicframework。[我的项目名称+一个大数字] \ MainActivity&#34;:
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);
}
}
我的自定义插件(此处不再详述)&#34; 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>