javascript - example - ionic create cordova plugin
Creación de plugins de cordova personalizada para el proyecto ionic2 (3)
Muchos de nosotros habríamos tenido problemas similares, pero incluso después de seguir los enlaces más relevantes, enlace de referencia1 y enlace de referencia2 , no puedo resolverlo.
Problema:
Cree un complemento personalizado (Cordova) para usar esto en el proyecto ionic2.
Expectativa : este complemento podrá interactuar con las funciones nativas de iOS y Android. Para ser precisos, intento acceder a las características de un SDK nativo (SDK de posicionamiento interno de Aruba) usando cordova en el proyecto Ionic.
Paso 1 Plugin creado inicialmente según el enlace de referencia 1
Paso 2: creó el proyecto Ionic 2 (creado con estos pasos básicos)
Paso 3 El archivo JavaScript en el complemento no fue capaz de referirse y acceder en Ionic2.
Después de buscar en Google, encontré esta discusión , donde se dice que cree una interfaz en el plugin por el siguiente motivo.
import {myPluginName} de ''../../../plugins/xxx/*.js''
No funcionará porque el complemento no es parte del paquete nativo iónico.
Si tiene un complemento personalizado, puede hacer algunas cosas.
1) Haga un PR para agregarlo a iónico nativo
2) Use la API de complemento sin formato. Puede usar la API del complemento sin procesar sin que forme parte de Ionic Native. El complemento está en el objeto ventana, por lo que apuntarías a la API normalmente
window.plugin.myPlugin.myMethod ()
De acuerdo con el proyecto de ejemplo GITHUB de esta manera, la interfaz debería implementarse
interface CordovaPlugins {
ZPLPrinter: ZPLPrinter;
}
interface ZPLPrinter {
print(
ipaddress: string,
bclabels: any,
printSuccess: (ip: string, labels: string[]) => void,
printError: (message: string) => void): void;
}
Ahora creé una interfaz similar en mi complemento que es la siguiente en la carpeta www de los complementos
interface CordovaPlugins {
Communicator: Communicator;
}
interface Communicator {
getInfo(successCallback: any, errorCallback: any);
}
Esta interfaz idealmente se dirigiría a este método en el archivo 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", []);
};
Ahora estoy atascado, ya que mi proyecto Ionic no está teniendo la carpeta de los típings .
En la muestra Github Project , los paquetes cordova se envían utilizando la carpeta typings . El archivo TypeScript en el proyecto se refiere a Cordova
utilizando index.t.js
La importación utilizada para referirse debería funcionar como
declare var cordova: Cordova;
Dudas:
- ¿Estoy en la dirección correcta del proceso?
- ¿Es esta la manera de crear el complemento de
Cordova
y usar en iónica - Por qué no puedo obtener la carpeta de typings en Ionic2
EDIT 1:
Después de simplemente agregar el complemento sin siquiera referirme al proyecto Ionic, traté de ejecutarlo en un dispositivo Android. Pero me dio el siguiente error.
Error principal es esto
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
¿Por qué este error estaría causando? Registros detallados han dado a continuación
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
En cuanto a tipings, ya no se usa. Todas o la mayoría de las declaraciones de mecanografía se mueven a npm en sí y las instala como npm install @types/package_name
. https://www.npmjs.com/~types https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/README.md Si necesita la carpeta de typings puede probar
npm install typings
también puede referance tipo declaraciones a través de
// <reference path="" />
en mecanografiado
Su complemento debe verse así:
En: / [nombre del complemento personalizado] /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;
En: / [nombre del complemento personalizado] / src / [android] || [ios], las clases con código nativo.
Y en: / [nombre del complemento personalizado] /plugin.xml (este es un ejemplo, la configuración debe ajustarse a su caso):
<?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>
luego agregas tu complemento con la CLI: ionic plugin add [folder of your plugin]
En su proyecto Ionic, en las clases (directivas angulares2) en las que desea usar su complemento, escriba antes de la sección @Component: declare var CustomPlugin: any;
. Luego, en esa clase, puede usar su complemento al CustomPlugin
a CustomPlugin
que se exporta con module.exports = CustomPlugin;
del archivo: /[custom plugin name]/js/custom_plugin.js
.
PARA RESPONDER A LA EDICIÓN 1 DE LA PREGUNTA, AQUÍ ALGUNOS DETALLES DE LA PARTE ANDROID : En el proyecto de complemento de Android (una vez que Android se ha agregado y construido al menos una vez, con CLI iónico), en Android Studio (2.2.2), cuando mira en el proyecto de construcción en "[mi proyecto] / plataformas / android":
En la jerarquía, el archivo MainActivity se autogenera en: "android / java / com / ionicframework. [Mi nombre de proyecto + un gran número] / 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);
}
}
Para mi complemento personalizado (sin entrar en detalles aquí) en "android / java [paquete del complemento personalizado]:
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();
}
}
}
Y finalmente android / manifiestats / manifiestats.xml se ve así:
<?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>
Después de muchas pruebas y errores, encontré la solución.
Estoy anotando a continuación los detalles para futuras referencias a cualquiera que esté intentando cosas similares.
Los problemas con el código eran los siguientes (el nombre de mi complemento es Inject
)
-
Inject/www/Inject.js
no tenía una función esencial para instalar el complemento - Cualquier nombre de método mencionado en
Inject.js
debe ser el mismo hastaInject/src/Inject.java
ya que hay opciones en el método deexecute
para referir el nombre del método diferente en función de las etiquetas recibidas
Pasos:
- Use plugman para crear el esqueleto del plugin Reference Link
- En lugar de usar nombres como
cordova-plugin-am-i-late
, usecordova.plugin.Inject
. Me enfrenté a problemas de compilación / tiempo de ejecución usando-
símbolo - Agregar la plataforma de
plugman platform add --platform_name android
- Verifique plugin.xml comparando la misma referencia
- Incluye permisos en
plugin.xml
si es necesario - Ahora verifica Inject.js sus llamadas a métodos esenciales que faltan. Actualmente solo tiene el siguiente código.
var exec = require(''cordova/exec'');
exports.coolMethod = function(arg0, success, error) {
exec(success, error, "Inject", "coolMethod", [arg0]);
};
- Pero tenemos que incluir a continuación también para que se instale y funcione
function Inject(){
}
Inject.install = function () {
if (!window.plugins) {
window.plugins = {};
}
window.plugins.Inject = new Inject();
return window.plugins.Inject;
};
cordova.addConstructor(Inject.install);
- Por ejemplo, me estoy orientando para mostrar un mensaje de Toast para el cual a continuación está mi archivo completo
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);
- Ahora
Inject.java
implementar nuestroInject.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.");
}
}
}
- Para dejar esto en claro, estoy publicando mi último
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>
- Ahora crea un proyecto Ionic2 de muestra y agrega la plataforma Android / IOS
- Agregue el complemento creado usando el
cordova plugin add
command - Navegue al proyecto iónico en el símbolo del sistema de Nodejs y proporcione este comando (consulte Plugin folder base after
add
)cordova plugin add D:/PluginTrial/Inject
- El complemento agregado debe estar poblado en la carpeta
Ionic2Project/plugins
- Llame a esta función usando el objeto
window
. A continuación está mihome.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);
});
}
}
- Ahora refiera este método
home.html
en el archivohome.html
<button ion-button (click)="showToast(''Yo Man! Its working '', ''center'')">Default</button>
- Eso es. ¡Deberías poder probar el plugin exitosamente! Happy Coding
Referencia: referencia uno , referencia dos , referencia tres