Tips : Processing でAndroidアプリ化するときのポイント

Processingで実際にAndroidアプリをリリースするにあたって、細かいところで色々手を入れたので書き留めておきます。

手元の環境はProcessing 2.1、AndroidのビルドにはApache Ant 1.8.4を使っています。 ProcessingのAndroidアプリ化については、公式Wikiに詳細があるので参考になります。

Exportの前に

実際はexportしたあとに変更できるので、あまり気にしなくてもいいのですが、size(w,h)であれば記載は必要ないので消しておいていいです。 アプリ表示の際のwidthとheightは下記コードが生成されるので、export後も調整できます。

    public int sketchWidth() {
        return displayWidth;
    }
    public int sketchHeight() {
        return displayHeight;
    }

あとは、Orientationを固定するには下記を記載しておくことで固定できます。

  orientation(PORTRAIT);  // the hamburger way
  orientation(LANDSCAPE);  // the hot dog way

Android ProjectのExport

どういうアプリにするかによって変わりますが、Androidアプリとしてリリースすることを考えると、 例えばディスプレイの明るさやタイムアウトを制御したくなったり、Settingsでアプリ要素の色や動きのパラメータを変えて楽しめるようにするなど、やりたいことがでてきます。 AndroidのProjectとしてExportすることでAndroidの開発手法が使えるようになるので、できることに幅が出せます。

ということで、ProcessingのIDEにはapkを作る機能もついていますが、ここではAndroid ProjectのExportをします。
ただし、コードマネジメント上pdeのコードとAndroidプロジェクトの管理が分裂するので、その点考慮が必要です。

exportしたAndroid Projectはそのままビルド可能なはずですが、手元(Android Build Tool 19.0.0)では下記のエラーに遭遇したので回避方法もメモしておきます。

   [dx] UNEXPECTED TOP-LEVEL EXCEPTION:
   [dx] java.nio.BufferOverflowException
   [dx]     at java.nio.Buffer.nextPutIndex(Buffer.java:499)
   [dx]     at java.nio.HeapByteBuffer.putShort(HeapByteBuffer.java:296)
   [dx]     at com.android.dex.Dex$Section.writeShort(Dex.java:818)
   [dx]     at com.android.dex.Dex$Section.writeTypeList(Dex.java:870)
   [dx]     at com.android.dx.merge.DexMerger$3.write(DexMerger.java:437)
   [dx]     at com.android.dx.merge.DexMerger$3.write(DexMerger.java:423)
   [dx]     at com.android.dx.merge.DexMerger$IdMerger.mergeUnsorted(DexMerger.java:317)
   [dx]     at com.android.dx.merge.DexMerger.mergeTypeLists(DexMerger.java:423)
   [dx]     at com.android.dx.merge.DexMerger.mergeDexes(DexMerger.java:163)
   [dx]     at com.android.dx.merge.DexMerger.merge(DexMerger.java:187)
   [dx]     at com.android.dx.command.dexer.Main.mergeLibraryDexBuffers(Main.java:439)
   [dx]     at com.android.dx.command.dexer.Main.runMonoDex(Main.java:287)
   [dx]     at com.android.dx.command.dexer.Main.run(Main.java:230)
   [dx]     at com.android.dx.command.dexer.Main.main(Main.java:199)
   [dx]     at com.android.dx.command.Main.main(Main.java:103)

BUILD FAILED
C:\Users\Jaap\android-sdks\tools\ant\build.xml:892: The following error occurred while executing this line:
C:\Users\Jaap\android-sdks\tools\ant\build.xml:894: The following error occurred while executing this line:
C:\Users\Jaap\android-sdks\tools\ant\build.xml:906: The following error occurred while executing this line:
C:\Users\Jaap\android-sdks\tools\ant\build.xml:284: null returned: 2

回避方法そのものは、こちらのStackOverflowにあるとおりAndroidManifest.xmlの‘android:targetSdkVersion‘とproject.propertiesのtarget=android-<value>を一致させるだけです。

Activityライフサイクルの利用

exportされたAndroid Projectのコードを見ると、javaのコードはPAppletクラスを継承したクラスにProcessingのコードがそのまま書かれていることがわかると思います。

public class dtclock extends PApplet {
    public void setup(){
        ...
    }

    public void draw(){
        ...
    }
}

試してはいないですが、libs以下に配置されているprocessing-core.jarさえあれば、PAppletクラスを継承することで、 自分で作成したAndroid ProjectにProcessingのViewを組み込むこともできると思います。

このPAppletクラスはActivityを継承しているため、Activityでできることはすべてできます。
例えば、ここでは自分のアプリにScreenLockの変更をActivityのライフサイクルに沿って行う処理を入れてみます。

import android.os.PowerManager;
import android.os.PowerManager.WakeLock;

public class dtclock extends PApplet {
    WakeLock lock;
    @Override
    public void onResume(){
        super.onResume();
        PowerManager pm = (PowerManager)getSystemService(POWER_SERVICE);
        lock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK,"My tag");
        lock.acquire();
    }

    @Override
    public void onPause(){
        super.onPause();
        lock.release();
    }

    public void setup(){
        ...
    }

    public void draw(){
        ...
    }
}

まず、WakeLockを利用するために、PowerManagerPowerManager.WakeLockをimportします。 そしてActivityのライフサイクルメソッドをOverrideします。ここでは、onResume()onPause()をオーバーライドし、WakeLockの変更処理を記載しています。

Processingの提供するキーハンドル機構

上記した公式Wikiにあるように、PAppletでキーハンドルもできます。

    public void keyPressed() {
        // doing other things here, and then:

        if (key == CODED) {
            if (keyCode == android.view.KeyEvent.KEYCODE_BACK ||
                keyCode == android.view.KeyEvent.KEYCODE_HOME) {
                println("back or home");
                // do something

            } else if (keyCode == android.view.KeyEvent.KEYCODE_MENU){
                println("menu");
                // do something
            }
        }
    }

手元で試している限りでは、Activityのライフサイクルに関わるBackとHomeのうち、Homeがちゃんと動きません。 Homeを押すとActivityはonPause()されますが、このメソッドにKEYCODE_HOMEが通知されませんでした。

なので、特にアプリのライフサイクルに対応して制御する必要のあるコードは、ここに書かないほうが良いかと思います。

まとめ

ざっと自分が触ったところを上げてきましたが、AndroidアプリとしてメンテしていくのであればAndroid Projectにexportして以降はその中でコードマネジメントしていくのが良いかと思います。 実際PApplet以下でProcessingでコーディングできますし。ただ、動作確認がIDEより面倒なのでその辺はバランスをとってScriptingをIDEでやってAndroid Projectにリリースするなど、やり方はあると思います。

後は、Web APIと連携するようなケースではAsyncTaskを使ったり、ローディングアイコンを使ったりすることでProcessingによるドローイングとは関係ないアプリとしての機能部分は、Android Frameworkを有効に利用できると思います。