Skip to content

Instantly share code, notes, and snippets.

@bsorrentino
Forked from mlynch/cordova-plugin-guide.md
Last active September 11, 2023 13:43
Show Gist options
  • Star 14 You must be signed in to star a gist
  • Fork 11 You must be signed in to fork a gist
  • Save bsorrentino/68b4589c31cbe96311e597eba73b776c to your computer and use it in GitHub Desktop.
Save bsorrentino/68b4589c31cbe96311e597eba73b776c to your computer and use it in GitHub Desktop.
Cordova Plugin Developer Guide

Cordova Plugin Development Guide for Android

Cordova Plugins are the magic that enable our mobile web app content to access the full power of Native SDKs underneath, but through clean JavaScript APIs that work the same across all platforms we target.

This simple guide, walks through the what, when, why, and how of Cordova plugin development for Android.

Introduction

Before start, understand their

What is a Cordova Plugin?

A Cordova plugin consists of some JavaScript code and Native code that communicates with each other to pass data between the web and native worlds.

A plugin is how we extend the functionality of the browser environment our Cordova apps run in to add the full power of the underlying native SDKs.

When do you need a Cordova Plugin?

Cordova apps run primarily in a web context which makes it easy to rapidly build apps just like we build web apps. However, the web doesn't always have all the features we need at a native level, so we need a way to write native code and communicate with it.

Getting Started

Install Cordova CLI (Command Line Interface)

npm install -g cordova

Create Plugin

Plugman

First you have to initialize your plugin using a scaffolder, plugman . To install plugman run with this shell command:

npm install -g plugman

Create Plugin Project

plugman create --name <name> --plugin_id <id> --plugin_version <version> --variable description="<description>"

Example:

plugman create --name PluginCustom --plugin_id "cordova-plugin-custom" --plugin_version 0.0.1 --variable description="Awesome PLugin"

It will generate in the current directory the folders: www, src and file plugin.xml.

To add a supported platfrom and the sample files (e.g. the java files for android) for it, run:

plugman platform add --platform_name <platform_name>

To create package.json, in order to have the possibility to publish on a npm repo, run:

plugman createpackagejson .
Plugin Javascript API

In the www folder will contain a javascript file <plugin name>.js (in our example will be PluginCustom.js)

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

exports.coolMethod = function(arg0, success, error) {
    exec(success, error, "PluginCustom", "coolMethod", [arg0]);
};

This is the file where we have to write the javascript api (used in your Cordova App) that allow access to the native code. In this case you can call from javaScript the coolMethod that will be linked to the platform native code.

Plugin Manifest

In the Plugin Manifest will be defined how the Javascript API will be made available to hosted app.

<?xml version='1.0' encoding='utf-8'?>
<plugin id="cordova-plugin-custom" version="0.0.1" xmlns="http://apache.org/cordova/ns/plugins/1.0" xmlns:android="http://schemas.android.com/apk/res/android">
    <name>PluginCustom</name>
    <DESCRIPTION>Awesome Plugin</DESCRIPTION>
    <js-module name="PluginCustom" src="www/PluginCustom.js">
    <!-- API will be available through Object 'PluginCustom' -->
        <clobbers target="cordova.plugins.PluginCustom" />
    </js-module>
</plugin>
Add dependency to plugin

If you want add a new dependency we have to insert a new framework tag in the plugin.xml file within the <platform name:"android">

<framework src="yourDependency" />

Example:

<platform name="android">
  ....
  <framework src="com.github.nkzawa:socket.io-client:0.3.0" />
</platform>
Customize Gradle configuration

If you want customize Gradle configuration concerning dependencies, you can create a file named build-extras.gradle and append it as framework within plugin.xml

<framework src="build-extras.gradle" custom="true" type="gradleReference" />

In the build-extras.gradle file you can configure, according Gradle syntax, every dependency.

Example:

configurations {
  all*.exclude group: 'org.json', module: 'json'
 }

Create Test App

1. Create a Cordova application

cordova create <PATH> [ID [NAME [CONFIG]]] [options]

Create a Cordova project

PATH ......................... Where to create the project
ID ........................... Reverse-domain-style package name - used in <widget id>
NAME ......................... Human readable name
CONFIG ....................... json string whose key/values will be included in
                                [PATH]/.cordova/config.json

Options

--template=<PATH|NPM PACKAGE|GIT URL> ... use a custom template located locally, in NPM, or GitHub.
--copy-from|src=<PATH> .................. deprecated, use --template instead.
--link-to=<PATH> ........................ symlink to custom www assets without creating a copy.

Example
cordova create myapp com.mycompany.myteam.myapp MyApp

2. Add platforms

cordova platform add android --save

3. Install and Link your Plugin

cordova plugin add --link ~/path/to/plugin

Note:

With the --link flag, Cordova creates a symbolic link to our plugin so we can update the plugin locally and rebuild without having to copy anything.

to remove the plugin, use the symbolic name of the plugin instead:

cordova plugin rm my-cordova-plugin

4. Testing process

When our plugin is linked, we can make modifications to the native code in our plugin and rebuild our app immediately. If we make modifications to the JavaScript portion of our plugin, we need to reinstall the plugin (currently is the only way to update Cordova stuff within web app).

cordova plugin rm my-cordova-plugin
cordova plugin add --link ~/path/to/plugin

5. Building our plugin

Now that the scaffolding is in place to build and test the plugin, follow the JavaScript and then platform-specific guides below to get coding.

Best Practices

Ideally, we should try to have our native plugin code do as little work as possible by focusing on sending data from the native layer up to the JavaScript layer and processing it there. This approach maximizes our ability to create cross-platform code, and minimizes the amount of native code to maintain. Of course, you may decide to perform more work at the native layer for performance reasons, but this is increasingly rare.

Example

Here is a simple example of a Cordova plugin JS:

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

var PLUGIN_NAME = 'MyCordovaPlugin';

var MyCordovaPlugin = {
  echo: function(phrase, cb) {
    exec(cb, null, PLUGIN_NAME, 'echo', [phrase]);
  }
};

module.exports = MyCordovaPlugin;

This is, largely, just standard JavaScript. The Cordova magic happens with the exec call, which takes a few params:

exec(callback, errorCallback, pluginName, actionName, argumentArray)

  • callback is called when the plugin successfully returns, and any arguments from the native plugin are passed into it
  • errorCallback is called when the plugin encounters an error. We've omitted this above
  • pluginName is the plugin class name on the native side.
  • actionName is the action we will perform on the native side.
  • argumentArray is an array of arguments to pass to the native side

With this call, we can send and receive data from our native plugin, and this is really 95% of what you need on the JavaScript side to interact with Cordova.

Android Plugin Development

This assumes you've downloaded and installed the Android Studio.

Make sure to add the android platform to your app: cordova platform add android. Open Android Studio, then Import a project and choose your app's platforms/android folder.

Back in our plugin repo, we can edit the code in src/android/ and as we modify the files in here, they will update in Android Studio and we can rebuild the app and re-run it.

If we make code modifications to our plugin's www/plugin.js file, we will need to remove the plugin and re-install it otherwise the changes won't take effect.

Handling Actions

Unlike iOS, Android handles actions by sending them through a single method and then doing a String compare on the name of the action:

MyCordovaPlugin.java

public class MyCordovaPlugin extends CordovaPlugin {
  private static final String TAG = "MyCordovaPlugin";

  public void initialize(CordovaInterface cordova, CordovaWebView webView) {
    super.initialize(cordova, webView);

    Log.d(TAG, "Initializing MyCordovaPlugin");
  }

  public boolean execute(String action, JSONArray arg, final CallbackContext callbackContext) throws JSONException {
    if(action.equals("echo")) {
      String phrase = arg.getString(0);
      // Echo back the first argument
      Log.d(TAG, phrase);
    } else if(action.equals("getDate")) {
      // An example of returning data back to the web layer
      final PluginResult result = new PluginResult(PluginResult.Status.OK, (new Date()).toString());
      callbackContext.sendPluginResult(result);
    }
    return true;
  }
}

Useful Tips

Adding Variables

Cordova plugins support preference variables that can control the settings that go into the AndroidManifest (Android) and info plist (iOS). We can also specify preferences for all platforms or only for specific ones.

To add support for preferences in our plugin.xml, use <preference:

<preference name="URL_SCHEME" />

<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
    id="my-plugin"
    version="1.0.8">

    <preference name="MY_SETTING" />

    <platform name="android">
      <preference name="MY_ANDROID_SETTING" default=" " />
      ...
    </platform>
</plugin>

To specify variables on plugin install, add each with a --variable flag:

cordova plugin add --link ~/path/to/plugin --variable MY_SETTING=bar --variable MY_ANDROID_SETTING=foo --variable MY_IOS_SETTING=baz

Adding 3rd party libraries

There are two options for including 3rd party libraries in Cordova plugins and/or projects that rely on them. The first is to include the code directly in the plugin itself. The second is to import the library into the app that relies on it.

Cordova plugins aren't built on their own (meaning they are built only in a real app project), so the second one is probably preferable.

Troubleshooting

Cordova plugins can get into an inconsistent state if modifications are made to things like the name of the plugin, and other lower-level plugin settings.

1. Renaming a plugin

If you need to rename a plugin, first remove it from the app with cordova plugin rm my-plugin. Rename the plugin in the plugin's plugin.xml and reinstall it.

If Cordova complains that the plugin is already installed, or there are duplicate symbols, modify the app/plugins/android.json and remove any offending lines that reference the old plugin.

@sikaili99
Copy link

Great, how can I develop a plugin that has User interface?

@parasappafluke
Copy link

I'm getting below error when i do "platform add --platform_name ios", what could be the reason.

(node:6218) UnhandledPromiseRejectionWarning: Error: platform: true not yet supported
at Object.add (/usr/local/lib/node_modules/plugman/node_modules/cordova-lib/src/plugman/platform.js:51:35)
at Object.module.exports [as platform] (/usr/local/lib/node_modules/plugman/node_modules/cordova-lib/src/plugman/platform_operation.js:23:38)
at Object.platform (/usr/local/lib/node_modules/plugman/node_modules/cordova-lib/src/plugman/plugman.js:153:17)
at Object. (/usr/local/lib/node_modules/plugman/main.js:87:42)
at Module._compile (internal/modules/cjs/loader.js:701:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:712:10)
at Module.load (internal/modules/cjs/loader.js:600:32)
at tryModuleLoad (internal/modules/cjs/loader.js:539:12)
at Function.Module._load (internal/modules/cjs/loader.js:531:3)
at Function.Module.runMain (internal/modules/cjs/loader.js:754:12)
(node:6218) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:6218) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

@noahzarro
Copy link

You are a generous god

@bsorrentino
Copy link
Author

You are a generous god

Glad that this could help someone ( first one has been me )

@noahzarro
Copy link

I just forked your gist and corrected a formatting issue and added the explanation on how to add a platform plugman platform add --platform_name android. If you want you can update yours with it:
https://gist.github.com/noahzarro/0cfe1fdfd15e6d5a9c1ab927fb828f15

@b1tw0rker
Copy link

b1tw0rker commented Feb 8, 2020

There is a bug in MyCordovaPlugin.java

The correct method must be:

  public boolean execute(String action, JSONArray arg, final CallbackContext callbackContext) throws JSONException {
    if(action.equals("echo")) {
      String phrase = arg.getString(0);
      // Echo back the first argument
      Log.d(TAG, phrase);
    } else if(action.equals("getDate")) {
      // An example of returning data back to the web layer
      final PluginResult result = new PluginResult(PluginResult.Status.OK, (new Date()).toString());
      callbackContext.sendPluginResult(result);
    }
    return true;
  }

So use arg insteat of args

@noahzarro
Copy link

Hi, why do you mean? Maybe I missed something, but the argument name should not matter right? Or do you just mean it for clarity, because there can be more than one arguments?

@bsorrentino
Copy link
Author

bsorrentino commented Mar 5, 2020 via email

@dherediat97
Copy link

@bsorrentino thanks for answer, and sorry i found the solution using npm in local or in a remote server(for that i deleted the comment), that link i saw few time

@webmozart
Copy link

webmozart commented May 4, 2022

Can anyone give tips how I'd sanely develop the Android code of the plugin in an IDE (IntelliJ in my case)? For example, I would like to build a Cordova plugin that relies on AndroidX, but no auto completion or automated code checks are working, so it's a pretty big PITA. I'm sure this has been solved?

@Sukumaran001
Copy link

i have an error for cordova is already defined

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment