titanium_mobile

その1と、その2、そして その3 で、ようやくTitanium Mobile での Android Moduleの開発準備が整いました。

今回その続きです。既存のActivityをModuleとして移殖しちゃう時ハマった事です。

 


リソースと国際化

既存のアプリをモジュールとして再利用しようとした時とかにぶつかる壁が、リソースファイルの扱い。

javacのコンパイルがコケます。

java.io.FileNotFoundException: /Users/MYNAME/MYPROJECT/modules/testmodule/build/generated/json/org/appcelerator/titanium/bindings/testmodule.json (No such file or directory)
     [java]     at org.apache.tools.ant.taskdefs.ExecuteJava.execute(ExecuteJava.java:194)
java.io.FileNotFoundException appcelerator/titanium/bindings/ json  (No such file or directory)

FAQなのでググればたくさんでてきます。

R.java生成してくれないので、リソースはTitaniumのTiRHelperを使う必要があります。

String someText = getResources().getString(TiRHelper.getResource("string.sometext"));

参考サイト

Native android java module for titanium doesn’t generate r.java – Community Questions & Answers

Android R.* references in appcelerator module – Community Questions & Answers – Appcelerator Deve

 


AndroidManifest.xmlのカスタマイズ

モジュールから既存のActivityをまんま起動したいけど、まずはAndroidManifest.xmlはどーすればって話です。

最終的なAndroidManifest.xmlはTitanium Mobile が自動で生成するので、そちらを修正してもダメです。

また、あらかじめAndroidManifest.xmlを書いておく方法もありますが、モジュール単体のテストとかどうすれば。

結論としては、モジュールのテスト用としは、モジュールディレクトリ直下の timodule.xml、タイタニウム側は tiapp.xml に、それぞれこんな風に書けば、設定ファイルで生成されるAndroidManifestをカスタマイズ出来ます。

<android xmlns:android="http://schemas.android.com/apk/res/android">
    <manifest>
        <application android:debuggable="true">
            <activity android:name="com.tdtsh.testmodule.TestActivity"/>
        </application>
    </manifest>
</android>

参考サイト

java – Setting up an Android Activity within Titanium – Stack Overflow

Maintaining a Custom AndroidManifest.xml – Documentation & Guides – Appcelerator Wiki

 


Activityの起動

アクティティを起動して結果を受け取りたい場合は、こんな感じです。

判ってしまえばどうって事ないですが、兎に角情報が少なくて難儀しました。

public class TestModule extends KrollModule
    implements TiActivityResultHandler
{
...

@Kroll.method
public void openActivity()
{
    Activity activity = getTiContext().getTiApp().getCurrentActivity();
    TiActivitySupport support = (TiActivitySupport) activity;
    requestCode = support.getUniqueResultCode();

    Intent intent = new Intent(activity, TestActivity.class);  
    intent.putExtra("some_arg", some_arg);
    support.launchActivityForResult(intent, requestCode, this);
}

※余談

起動する事だけが目的であれば、openActivity() の中身はコレで充分ですが、戻り値とか欲しい場合は、onActivityResultどーかくの?ってなります。

Intent intent = new Intent(TiApplication.getInstance().getRootActivity(), TestActivity.class);  
TiApplication.getInstance().getRootActivity().startActivityForResult(intent, ACT_SKINCHECK);

因みにAndroid Module側でActivityのコールバックinterfaceはこうかきます。

@Override
    public void onResult(Activity activity, int thisRequestCode, int resultCode, Intent data)
    {
        this.triggerComplete();
    }

    @Override
    public void onError(Activity activity, int requestCode, Exception e)
    {
        this.triggerError(-2);
    }

Titanium側のイベントを着火するにはこんな感じです。

@Kroll.method
    public void triggerComplete() 
    {
        KrollDict event = new KrollDict();
        event.put("arg", "something");
        fireEvent("skinCheckComplete", event);     
    }

参考サイト

java – Appcelerator custom module receiving callback for onActivityResult – Stack Overflow

http://stackoverflow.com/questions/5215071/appcelerator-custom-module-receiving-callback-for-onactivityresult

 


android.os.NetworkOnMainThreadException org.apache.http.client.HttpClient

モジュールから起動したActivity で 同期通信をすると、NetworkOnMainThreadExceptionで処理がコケます。

これはTitaniumに限った話では無くてハニカム以上のAndroidの仕様でメインUIスレッドで同期通信が出来ないんですけど、既存のアプリで動作していたコードがTitanium Mobile ではダメ、というのに遭遇しました。スレッドの動作が既存アプリとTitaniumモジュールとして動作した場合とで違った、という事なんでしょうか。

そもそもなんで既存アプリは動作いていたのかよく判りませんし、設計としてイケてないしで、仕方ないのでAsync Taskを使って本来あるべき姿にActivtyをリライトする羽目になりました。

ま、コレに関してはTitaniumは悪くないんですけど。

参考サイト

【Android】メインスレッド(要は画面処理)でhttpリクエスト投げようとしたら怒られた。 〜NetworkOnMainThreadException〜 – 記すに足らず。

AsyncTaskの使い方考察 – プログラマーの脳みそ

 


jarが重複

コレも判ってしまえばどうってことないけど、初めて遭遇すると焦るやつです。

モジュール側でHttpClientを使って通信とかしているやつを、Titaniumアプリに結合したら、ビルドが通らなくなりました。

[ERROR] System Error while compiling Android classes.dex [ERROR] UNEXPECTED TOP-LEVEL EXCEPTION:
[ERROR] java.lang.IllegalArgumentException: already added: Lorg/apache/james/mime4j/descriptor/BodyDescriptor;

もっと正確にいうと、結合は出来てたのに、あるときを境にビルドできなくなったです。

実はTi側でこんなコードを書いたら、

var xhr = Titanium.Network.createHTTPClient();

ビルドじに必要なライブラリが自動で注入される仕組みらしいです。

んで、そのライブラリ達と同じものを、jar側のlibとかにもぶっこんでると、かぶっとるがな!とビルドがコケる理屈らしいです。多分。

モジュール側で

httpmime-4.1.1.jar

apache-mime4j-0.6.1.jar

を削除すれば、通る様になりました。

 


BASIC認証ムリポ

もともと手順がややこしいく、何かとトラブルが多い AndroidでのBASIC認証 ですが、何とか動いてた既存のコードがTitaniumモジュールでは全く動きません。正直お手上げです。

Titanium Module のActivityでWebViewのBasic認証は無理、が現状のわたしの理解です。

 

  • * *落とし穴多かったし尽くハマりまくった感じだけど、とりあえずなんとかなってきました。

もとより最初から情報を持っていたら、こんなに苦労しなかったのに・・・という事ばかりなので、頑張ってブログ纏めました。

日本語の情報めちゃ少ないんで。

色々Disってる感じになってますが、Titaniumかなり好きになってきました。

Android開発者はもっとTitaniumで書いて、もちょっと盛り上がって欲しいです。Objective-C書かなくても、クロスプラットフォームですよアナタ。

 


参考にさせて頂いたサイト

Android Module Development Guide – Titanium 3.X – Appcelerator Docs

Android Module Development – Part 1 | Appcelerator Titanium Tutorial

Android Module Development – Part 2 | Appcelerator Titanium Tutorial

Android Module Development – Part 3 | Appcelerator Titanium Tutorial

Android Module Development – Part 4 | Appcelerator Titanium Tutorial

Android Module Development – Part 5 | Appcelerator Titanium Tutorial

isis re-direct: 勝手/全/超訳 Titanium Mobile Module Developer Guide for Android