RAKUS Developers Blog | ラクス エンジニアブログ

株式会社ラクスのITエンジニアによる技術ブログです。

【Android】0から理解する Cordova Plugin

はじめに

こんにちは、@rs_tukkiです。

12月にラクスのAdvent CalenderでCordovaについての記事を寄稿しましたが、今回もCordovaについての話をさせていただきます。

Cordovaとは?

さて、まずはCordovaについて軽く触れておきます。
Cordova(Apache Cordova)とは、オープンソースの開発フレームワークです。

平たく言えばSpring Framework等と似たようなものです。
Cordovaの特徴は、同じソースコードで、AndroidiOS、ブラウザの3つのプラットフォーム向けにアプリのビルドができることです。
いわゆる「クロスプラットフォーム」というやつです。
また、基本的にHTML/CSS/Javascriptで動作するため、バックエンドの知識に明るくないエンジニアでも単純なアプリケーションの開発であればできてしまうのもメリットです。

Cordova Pluginを自分で作ってみる

先ほど「(Cordovaは)基本的にHTML/CSS/Javascriptで動作する」と書きました。
基本的に、というのは文字通り基本的な動作しか実現できないという意味で、AndroidiOS特有の機能、例えばカメラなんかにアクセスすることはそのままではできません。
そこでどうするかというと、JavaScriptからJavaやSwiftのコードを呼び出して使えるようにするわけです。
こういったネイティブ特有の機能を呼び出すためのコード群を「Cordova Plugin」といいます。

Cordova自体はかなり成熟したフレームワークですから、主要なネイティブ機能はApache公式がプラグインを整備していたりしますし、
Githubを漁ってみればそれこそCordovaユーザが独自に開発したプラグインが山のように見つかります。

ただし、そういった第三者の作成したプラグインはあまりメンテナンスされていないものが多く、最終更新が3年前、4年前といったものもザラです。
使いたい機能のためのCordovaプラグインを見つけた!と思ったらろくにアップデートもされておらずエラーになって使用できない……というのはよくある話。

そのようなプラグインはフォークして自分でメンテナンスしてもいいですが、折角なので0から自分でプラグインを作成するやり方を学んでみましょう。

Cordovaプロジェクトの作成

Cordova Pluginを作成する前に、まずは土台となるCordovaプロジェクトを作成します。
npm installでCordovaをインストールしたら、cordova create [プロジェクト名]でテンプレートを作成してくれます。便利。

$ sudo npm install -g cordova
Password:

+ cordova@11.0.0
added 129 packages from 59 contributors, removed 36 packages, updated 125 packages and moved 2 packages in 15.838s

$ cordova create testApp
Creating a new cordova project.
$

Cordovaプロジェクトの作成直後は、以下のようなツリー構造になっています。

$ tree
.
├── config.xml
├── package.json
└── www
    ├── css
    │   └── index.css
    ├── img
    │   └── logo.png
    ├── index.html
    └── js
        └── index.js

4 directories, 6 files

wwwディレクトリがCordovaで画面表示や画面遷移を実現するための核となる部分です。

これだけでも最低限アプリは動くようになっていますが、今回はAndroid向けのCordovaプラグインを実装して、
「今開いているアプリがdebugビルドかどうか」を判定できるようにしてみましょう。

先に結論から言ってしまうと、プラグインの実装後は以下のようなツリー構造になります。

$ tree
.
├── config.xml
├── package.json
├── plugin
│   └── cordova-plugin-checkIsDebug
│       ├── package.json
│       ├── plugin.xml
│       ├── src
│       │   └── android
│       │       └── CheckIsDebugPlugin.java
│       └── www
│           └── checkIsDebugPlugin.js
└── www
    ├── css
    │   └── index.css
    ├── img
    │   └── logo.png
    ├── index.html
    └── js
        └── index.js

10 directories, 11 files
Pluginの定義(plugin.xml / package.json)

まずは、Cordovaプラグインを利用するために必要な設定を記載していきます。
まずはファイル全体をぺたり。

<?xml version="1.0" encoding="UTF-8"?>
<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
    id="cordova-plugin-check-is-debug"
    version="1.0.0">
    <name>Check is Debug Plugin</name>
    <description>check whether cordova app is debugMode</description>
    <author>rs_tukki</author>
    <license>MIT License</license>
    <engines>
        <engine name="cordova" version=">=7.0.0"/>
    </engines>
    <js-module src="www/checkIsDebugPlugin.js" name="pluginBridging">
        <clobbers target="checkIsDebug" />
    </js-module>
    <platform name="android">
        <config-file target="res/xml/config.xml" parent="/*">
            <feature name="CheckIsDebug">
                <param name="android-package" value="com.rs.tukki.testapp.plugin.checkisdebug.CheckIsDebugPlugin"/>
            </feature>
        </config-file>
        <source-file src="src/android/CheckIsDebugPlugin.java" target-dir="com/rs/tukki/testapp/plugin/checkisdebug/" />
    </platform>
</plugin>

Plugin.xmlではこのプラグインの名前やバージョン、どのようにそのプラグインを呼び出すかといった情報を定義しています。
特に重要なのは<engines><js-module><platform>の各ディレクティブです。

では一つづつ解説していきます。

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

<engines>ディレクティブでは、このプラグインを使用するのに要求されるCordovaのバージョンを指定できます。
省略しても問題ない項目ではありますが、あまりにも古いCordovaを使用していると使えないケースもありますので、ひとまず7.0辺りを指定しておくとよいでしょう。

    <js-module src="www/checkIsDebugPlugin.js" name="pluginBridging">
        <clobbers target="checkIsDebug" />
    </js-module>

<js-module>ディレクティブでは、Plugin内で外部から参照させるためのjsファイルを指定できます。
今回のPluginではCordovaプロジェクト本体とネイティブコードの橋渡しとして、www/ディレクトリ内にcheckIsDebugPlugin.jsを追加していますので、これをsrc属性に指定します。
name属性は開発者が参照することはないので、任意の名前をつければOKです。

重要なのは内部のcolobbers要素で、ここのtarget属性で指定した値を使ってCordovaプロジェクトから関数として呼び出すことになります。

    <platform name="android">
        <config-file target="res/xml/config.xml" parent="/*">
            <feature name="CheckIsDebug">
                <param name="android-package" value="com.rs.tukki.testapp.plugin.checkisdebug.CheckIsDebugPlugin"/>
            </feature>
        </config-file>
        <source-file src="src/android/CheckIsDebugPlugin.java" target-dir="com/rs/tukki/testapp/plugin/checkisdebug/" />
    </platform>

<platform>では、各プラットフォームごとにどのような設定やネイティブコードが必要かを定義しています。
今回はAndroidに絞って解説します。

最初の<config-file>ディレクティブでは、「このプラグインはこのような形でこのクラスを呼び出します」という情報をconfig.xmlに追記するための設定を行なっています。
<feature>要素のname属性が特に重要で、この値はcordova.execというネイティブコードを呼び出すための関数で第三引数に指定する必要があります。(後述)
<param>要素のvalue属性には呼び出す対象となるクラスをパッケージ名を含めて記載してください。

<source-file>要素には、Android向けにプロジェクトを構築する際、ネイティブコードをプロジェクトに含めるために追加する必要がある項目です。
この記載がないとプロジェクト内にネイティブコードが含まれず、当然呼び出しに失敗します。
src属性はこのplugin.xmlからの相対パスjavaファイルを指定します。
target-dir属性は構築されたプロジェクトでのファイルの配置先の指定に使います。

ここまで記載できればネイティブコードを呼び出す最低限のPlugin.xmlとしてはOKです。
(実際はネイティブコードを一切呼び出さないCordovaプラグインもあったりしますが...それはまた次の機会に)

続いてpackage.jsonを作成します。
ここでは依存性の注入や最初に呼ばれるスクリプトの設定などが行えます...が、 プラグインOSSとして公開する必要がなく、最低限の実装だけ行いたい場合は必須の2項目だけ記載しておけばよいでしょう。

{
  "version": "1.0.0",
  "name": "cordova-plugin-check-is-debug"
}

そのほかの設定項目は以下を参考にしてみてください。

qiita.com

ネイティブコードの実装(CheckIsDebugPlugin.java)

さて、次はプラグインの本体となるJavaクラスを実装していきます。
JavaでCordovaプラグインを実装する場合、CordovaPluginクラスを継承したクラスを作成し、
executeメソッドをOverrideして処理を記載していきます。

今回はdebugビルドかどうかを取得したいので、javaコード上で判定処理を組み込み、返却用のJSONオブジェクトに格納します。

package com.rs.tukki.testapp.plugin.checkisdebug;

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

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import io.cordova.hellocordova.BuildConfig;

public class CheckIsDebugPlugin extends CordovaPlugin {

    @Override
    public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
        JSONObject result = new JSONObject();
        if (BuildConfig.DEBUG) {
            result.put("isDebug", true);
        } else {
            result.put("isDebug", false);
        }
        callbackContext.success(result);
        return true;
    }
}

最終的に、executeはbool値を戻り値として返します。
trueなら成功時のcallbackが、falseなら失敗時のcallbackが呼ばれます。
この前に、callbackContextのsuccessメソッドもしくはerrorメソッドにパラメータを格納しておけば、callback内でその値を参照することができます。

ネイティブコードを呼び出す(checkIsDebugPlugin.js)

さて、次はこのネイティブコードをフロント側から呼び出せるようにします。
ネイティブコードは直接Cordovaプロジェクト側から呼び出す方法もなくはないですが、基本的には一旦プラグイン内のjsコードを挟むことになります。

const CheckIsDebugPlugin = function () {
};

CheckIsDebugPlugin.prototype.isDebug = function (success, fail) {
    cordova.exec(success, fail, "CheckIsDebug", "isDebug", []);
};

const checkIsDebug = new CheckIsDebugPlugin();
module.exports = checkIsDebug;

cordova.execはネイティブコードを呼び出すためのメソッドです。
第一引数と第二引数はそれぞれ成功時、失敗時のコールバック、
第三引数はplugin.xmlfeature要素で指定した値、
第四引数はネイティブコード内で処理を判別するための文字列、
第五引数は任意のパラメータを取ります。
この関数をexportsすることで、Plugin.xmlclobbers要素で指定したtarget名で呼び出すことができます。

すなわち、実際に呼び出す際は以下のような形になります。
成功した場合のコールバック処理でははdebugビルドかどうかを表示するようにしておきましょう。

    window.checkIsDebug.isDebug(result => {
        console.log(result.isDebug);
        document.getElementById("isDebug").textContent = "debugMode is " + result.isDebug;
    }, err => {
        console.log(err);
        document.getElementById("isDebug").textContent = "error!:" + err;
    });

動作確認

さて、これでPluginを導入したCordovaアプリのサンプルが作成できましたので、
早速実機で動作確認をしてみましょう。

アプリの動作確認は、以下の順序で行います。
なお、Androidでビルドを行う場合はSDKにパスを通す必要があるので忘れずに行っておきましょう。

  1. cordova plugin add [フォルダパス]で作成したCordova Pluginを登録する
  2. cordova platform add [プラットフォーム名]で各プラットフォーム向けにビルドの準備をする
  3. cordova run [プラットフォーム名]でアプリをビルドし、実行する

release向けにビルドを行う場合はもう少し詳細に設定が必要ですが、debug版での動作確認はひとまずこれだけできれば十分です。

$ cordova plugin add plugin/cordova-plugin-check-is-debug
Installing "cordova-plugin-check-is-debug" for android
Adding cordova-plugin-check-is-debug to package.json

$ cordova platform add android
Using cordova-fetch for cordova-android@^9.0.0
Adding android project...
Creating Cordova project for the Android platform:
    Path: platforms/android
    Package: io.cordova.hellocordova
    Name: HelloCordova
    Activity: MainActivity
    Android target: android-29
Subproject Path: CordovaLib
Subproject Path: app
Android project created with cordova-android@9.1.0
Discovered plugin "cordova-plugin-whitelist". Adding it to the project
Installing "cordova-plugin-whitelist" for android
Adding cordova-plugin-whitelist to package.json

$ cordova run android
Checking Java JDK and Android SDK versions
ANDROID_SDK_ROOT=/Users/your_name/Library/Android/SDK (recommended setting)
ANDROID_HOME=/Users/your_name/Library/Android/SDK (DEPRECATED)
Using Android SDK: /Users/your_name/Library/Android/SDK
Starting a Gradle Daemon (subsequent builds will be faster)

Deprecated Gradle features were used in this build, making it incompatible with Gradle 8.0.

You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.

See https://docs.gradle.org/7.3/userguide/command_line_interface.html#sec:command_line_warnings

BUILD SUCCESSFUL in 7s
1 actionable task: 1 executed
Subproject Path: CordovaLib
Subproject Path: app
Downloading https://services.gradle.org/distributions/gradle-6.5-all.zip
.............10%..............20%..............30%..............40%..............50%..............60%..............70%..............80%..............90%..............100%
Starting a Gradle Daemon (subsequent builds will be faster)

Deprecated Gradle features were used in this build, making it incompatible with Gradle 7.0.
Use '--warning-mode all' to show the individual deprecation warnings.
See https://docs.gradle.org/6.5/userguide/command_line_interface.html#sec:command_line_warnings

BUILD SUCCESSFUL in 1m 57s
40 actionable tasks: 40 executed
Built the following apk(s): 
    /Users/your_name/workspace/testApp/platforms/android/app/build/outputs/apk/debug/app-debug.apk
Checking Java JDK and Android SDK versions
ANDROID_SDK_ROOT=/Users/your_name/Library/Android/SDK (recommended setting)
ANDROID_HOME=/Users/your_name/Library/Android/SDK (DEPRECATED)
Using Android SDK: /Users/your_name/Library/Android/SDK
Deploying to device 0B161FDD4001VQ
Using apk: /Users/your_name/workspace/testApp/platforms/android/app/build/outputs/apk/debug/app-debug.apk
Package name: io.cordova.hellocordova
INSTALL SUCCESS
LAUNCH SUCCESS

コマンドを実行してアプリを起動すると、
デフォルトのTOPページに新しく追加したDEBUGMODE IS TRUEの文字が確認できるかと思います。

おわりに

今回は、Cordovaプロジェクトの新規作成から、Android向けのCordovaプラグインを作成して動作確認するところまでを実装してみました。(iOSはまた機会があれば……)
仕組みさえ理解してしまえばKotlinやSwiftのネイティブアプリと遜色ないアプリを作れますし、なによりフロントエンドの知識で画面制御を実現できるのが圧倒的に便利ですので、まず何かアプリを作ってみたい!という方はCordovaを試してみてはいかがでしょうか。

参考

Apache Cordova

Cordovaを使って、Androidの実機実行するまで - Qiita

MacでCordovaのインストール・実行・ビルドまで (iOS, Android, Web) | 株式会社TKS2

Cordova PluginのJavaScript部分の実装 - Qiita

package.jsonの中身を理解する - Qiita

BuildConfig クラスでアプリの動作を切り替える | まくまくAndroidノート

Cordova Pluginの基本事項 - Qiita

Cordovaプラグインを開発してみよう! – モナカプレス


◆TECH PLAY
techplay.jp

◆connpass
rakus.connpass.com

Copyright © RAKUS Co., Ltd. All rights reserved.