本サイトからスピンオフした姉妹サイトです。(初版:2014/4/9)Xamarin.AndroidでJavaとの相互運用を実現するアーキテクチャについて、さらにメモリ管理などの注意点を説明。Xamarin.Androidの制限事項についても解説する。第7回からはXamarin.Androidについて取り上げている。前回の「Xamarin.Androidアプリの開発」に続き、今回はJavaとの相互運用などについて説明する。SDKでは、標準的なJavaではないが、ほぼJavaのやり方が通用する。Format)」というバイトコードに再度コンパイルされる。Interface)を使用することが可能で、C++で書かれたネイティブコードを呼び出すことができる。NDKにもこれが含まれる。NDKを使用してビルドしたmonoランタイムの組み込みライブラリが含まれており、これはアプリケーションのリリースビルドのパッケージ時に、依存ネイティブライブラリとしてAndroidアプリケーションパッケージ(=.apkファイル)に同梱される。Xamarin.Androidアプリケーションのスタートアップ時には、このmonoランタイムがJNIの仕組みに基づいてロードされる仕組みになっている。Xamarin.Androidアプリケーションのブートストラップについては、筆者が以前に横浜Androidプラットフォーム部の勉強会で行ったセッションの資料でも、詳しく説明してあるので、興味がある方はそちらを参照されたい。ランタイムのブートストラップは、ネイティブコードとJavaの相互運用の一部にすぎない。Xamarin.iOSがC#でObjective-Cの型を派生させられるのと同様、Xamarin.AndroidはC#でJavaの型を派生させることができる。接着用)クラスが、必要に応じてJavaメソッドをオーバーライドした上で、C#で書かれたロジックをJNI経由で呼び出すようになっている。Wrapper(ACW)」という。これは、Dalvikランタイムが動的にクラスを生成できないという制約上、回避できないことだ。Wrapper(MCW)」ということもある(通常あまり気にする必要はない)。Mono.Android.dllはこのMCWの巨大な集合体だ。これらのJava統合のアーキテクチャについては、公式サイトに比較的詳しいドキュメント(英語)が公開されている。Xamarin.AndroidからJNIを使い倒す方法については、公式サイトに膨大でとても読めない量のドキュメント(英語)が1ページにまとめられているので、興味がある方はそちらを参照されたい。通常のアプリケーション開発において、この知識はそれほど重要ではない。Javaライブラリを使う場合は、後述するバインディング・プロジェクトを使うとよい。メモリ管理Xamarin.Androidでやや面倒なのが、メモリ管理を気にしなければならない点だ。monoランタイムとDalvikランタイムは並行動作している。monoランタイム上にあるオブジェクトは、Dalvik上のJavaオブジェクトとライフサイクルを共にしなければならない。Java側から使われるかもしれないオブジェクトを、mono側で解放するわけにはいかないのである。Dalvik側のGC(ガベージコレクター)をほしいままに操作することはmonoランタイム側ではできないので、mono側のGCにフックを仕込んである。開発者がコードを書くときに意識すべきことは、DalvikのGCとmonoのGCは別々の基準で別々の動作を行っている、ということである。巨大な画像リソースをAndroid.Graphics.Bitmapクラスで読み込んだとしよう。このメモリはDalvik側が管理しているもので(もちろん実際にはJavaのヒープではなくネイティブコードのメモリであろうが、Dalvikの管理下であることに変わりはない)、mono上のメモリをいくら解放しても、この画像リソースが解放されることはない。GCは別々に動作しているが、ある程度のJavaオブジェクトが生成されて閾値に達した場合、mono側でフルGC(monoのGCは世代別である)を呼び出すことで、Java側の不要なオブジェクトの回収を期待する、ということはmono側で行われている。しても手動でJava側のメモリを解放したいけどmono側のJava参照が邪魔になっている場合は、Java.Lang.ObjectのDispose()メソッドを呼び出すことで、危険ではあるが参照を解放できる。Java.Lang.Objectクラスから派生するオブジェクトは、全てGCルートとして登録されるので、そのようなオブジェクトを大量に生成すると、パフォーマンスを劣化させることにもなる。特別にマーシャリングの必要がなければ、Java.Lang.String型よりもSystem.String型のインスタンスを生成する方が、一般的にはコストが低い(マーシャリングにおける、内部的なJavaオブジェクトへの変換など、複雑な要因が絡むので、一概に良いとは言えないが、GCルートを大量に抱え込むことは、一般的にパフォーマンスの悪化につながる)。ガベージコレクションの協調動作の詳細やメモリ効率化のTipsは、公式サイトのドキュメント(英語)を確認されたい。ExportAttributeJava相互運用の補足として、ExportAttributeという機能について説明しておきたい。ACWのJavaコード生成は、自動的に行われるものなので、通常はその内容を意識する必要はない。生成されるJavaコードが重要である場合もある。Java側のフレームワークがリフレクションなどを使用して、メンバーを動的に取得する場合などだ。java.io.Serializableインターフェースでは、readObject()、writeObject()というメンバーを要求するが、これらはインターフェース定義には含まれていないので、実装クラスのACWにも出力されない。Androidには、Parcelableインターフェースという、インテント通信におけるデータのやりとりに使われるシリアライゼーション機構がある。この機構では、デシリアライズするときに、対象クラスから「CREATOR」という、クラス上に定義のないstaticフィールドを取得して、それを使用してデシリアライズされるインスタンスを生成する。C#コードでいくらこのメソッドを定義しても、明示的にそのメソッドが使われない限り、ACWには生成されないし、明示的に使ったとしても、Java上で定義される名前は「CREATOR」にはならない。これらの場合に必要となるJavaコードを、半ば手動で生成できるようにするために、Xamarin.AndroidではExportAttributeという属性をメソッドに指定することで、そのメソッドに対応するJavaメソッドを、指定した名前で生成できる。同様に、ExportFieldAttributeをメソッドに適用すると、対応するJavaフィールドを生成した上で、初期値がそのメソッドの呼び出し結果に初期化される。これらは場合によっては必要になる、Java相互運用のトリックだ。Javaバインディング・プロジェクトJavaバインディング・プロジェクトは、既存のJavaライブラリの.jarファイルを解析して、その型とメンバーに対応する.NETのAPIを生成するためのC#プロジェクトだ(F#はサポートしていないが、ビルドされるものはほぼ自動生成されるコードでしかないし、サポートする必要はないだろう)。使い方は簡単で、.jarファイルをプロジェクトに追加して、「EmbeddedJar」というビルドアクションを指定すれば足りる。あとはビルドするだけだ。Javaバインディング・プロジェクトのビルドは、次の3つのステップで行われる。.jarファイルを解析し、API定義を生成する。これはXML形式になっているAPI定義のXMLデータから、C#のコードを生成するC#のコードをビルドする。ここには、自作のC#ソースコードを追加することもできる生成されるC#のコードには、第8回の「Mono.Android.dll」の節で説明した、C#化が施された内容になっている(enum化やasync対応メンバーの追加などは、手作業で行わなければならない)。現実には、.jarファイルを追加してF5キーを押しただけでビルドが成功することはあまりない。実際にビルドしてみると、ビルドエラーが多発する。jarライブラリをバインドしようとする開発者は、これを全て修正しなければならない。実のところ、Javaバインディング・プロジェクトを使いこなすのは非常に難しい。Frameworkに、WSDLからC#のSOAPクライアントを自動生成するツール「wsdl」とか「svcutil」といったツールがあるが、あれらが.NETから自動生成されていないWSDL文書を処理できないのと同じかそれ以上に、自動的には機能しない。ビルドエラーが多発するのかというと、ひとえにJavaとC#の設計の違いにある。継承1つとっても、Javaではnon-publicなクラスからpublicなクラスを派生させられる。Javaではprotectedなメソッドをpublicでオーバーライドできる。消えているジェネリック(Generics)のメンバーが関与してくれば、それに対応する修正も必要になる。原因はさまざまである。ビルドエラーを修正する方法はある。Javaバインディング・プロジェクトには「Metadata.xml」というファイルが含まれている。これは、XPathで修正対象ノードを記述して、修正方法を指定するものだ。これをじっくり説明するとかなりの長文が必要になってしまうので、ここでは公式サイトのドキュメント(英語)へのリンクを示すだけにとどめておきたい(このドキュメントには、半ばウォークスルー・ドキュメントであるが、追加ソースコードを作る場合の制限事項など、それなりに重要な説明も含まれている)。/>と指定すると、その問題のノードのバインディングが生成されなくなる。Javaバインディング・プロジェクトには、もう一つワナがある。これはJavaのライブラリではなく、Androidのライブラリに特有の問題だが、Xamarin.AndroidではないJavaのAndroidでも「ライブラリ・プロジェクト」があり、これはAndroidのJavaアプリケーションをビルドする際にライブラリとして参照して使用する。このライブラリ・プロジェクトの.jarファイルには、実際にアプリケーションをビルドして実行するために必要なファイルの全ては入っていないのである。具体的には、.jarファイルの中にassetsやres、ネイティブライブラリ(*.so)が含まれていない。jarライブラリはあくまでコンパイルされたJavaクラス群でしかないのである。これでは参照するときにもライブラリ・プロジェクトをソースからビルドしなければならず、再利用性が悪い。そのような「ライブラリ・プロジェクト」をXamarin.Androidでバインドする場合は、単に.jarファイルをバインドするだけではなく、関連するリソースなどを全て含めて.zipアーカイブファイルとしてプロジェクトに追加する必要がある。このためのビルドアクションとして「LibraryProjectZip」というものがある。お手軽なものとして、「LibraryProjectProperties」というビルドアクションもあって、これはJavaのAndroidライブラリ・プロジェクトのproject.propertiesファイルを指定すると、必要な.zipファイルを自動的に作るというものだ(ただしその場合でも、一度、Eclipseなどでライブラリ・プロジェクトをビルドしておかないと、必要な.jarファイルが見つからないことになる)。SDKの新機能としてMaven(=Java用プロジェクト管理ツール)サポートが追加され、新しく「.aar」という拡張子のライブラリ・パッケージが登場した。SDKの仕様の1つだ。この.aarファイルは、実質的にXamarinが「LibraryProjectZip」で実現していたことと同じなので、XamariのJavaバインディング・プロジェクトで、.aarファイルを「LibraryProjectZip」として追加することもできる。Androidのバインディングなど、数多くのプロジェクトが含まれているので、具体例としてはそれらを参照されたい。(多少古い部分はあるが)筆者がJavaバインディングライブラリの詳細についてXamarinユーザーグループのイベントで解説した時に使用したスライドもあるので、こちらも参考にされたい。Xamarin.Androidの制限事項Xamarin.Androidの制限事項は、iOSに比べると少ないが、それでもいくつか気にすべきことがある。Androidでは、Javaのクラスを動的に生成できない(詳しくはこちらの記事が参考になる)。ビルド時に使用していないコードは、アセンブリ・リンカーによって削除されてしまうことから、DLR(動的言語ランタイム)によって動的に型を読み込む場合には、その型がきちんとリンク後も残るようにしなければならない。Xamarin.AndroidのC#/F#コードからのJavaコードの呼び出しは、ビルド時に生成されるJavaのコードを経由して行われる。通常は生成されるJavaコードを気にする必要はないが、Javaのライブラリによっては、リフレクションで動的にJavaクラスのメンバーを拾い上げることがある。このJavaコードの生成は、前述のExportAttributeなどを使えばある程度自由に生成できるが、例えばコンストラクターの生成などはできない。ジェネリックメソッドでExportAttributeやExportFieldAttributeを使うことはできない。これは実行時にJavaコードが生成できない制約による。.NETのジェネリック型のインスタンスを、Dalvik側からインスタンス生成することはできない。Dalvikからのインスタンス生成とは、つまり、Androidのフレームワーク側がインスタンスを生成することを意味する。Activityの派生クラスは、アプリケーションのスタートアップなど、Androidのアプリケーションフレームワークが生成することになる可能性が高いから、これは回避すべきクラス設計だ。次回の予定さて、以上かなり長くなってしまったが、Xamarin.Androidについての解説は以上である。これでもそれなりにはしょっている部分は多いので、気になる部分は適宜、公式サイトのドキュメント(英語)を参照されたい。Studioについて解説する。※以下では、本稿の前後を合わせて5回分(第8回~第12回)のみ表示しています。INDEX]を参照してください。Servicesについて説明する。Xamarin.Androidアプリの開発Xamarin.Androidアプリの作成/ビルド/実行とデバッグに関する重要ポイントついて解説する。Xamarin.AndroidにおけるJava相互運用の仕組みと、Javaバインディング・プロジェクトXamarin.AndroidでJavaとの相互運用を実現するアーキテクチャについて、さらにメモリ管理などの注意点を説明。Xamarin.Androidの制限事項についても解説する。MonoDevelopの基本的な機能を解説。C#コーディング補助機能についても紹介する。MonoDevelopにおけるビルド/実行/デバッグと、iOS/Android向けのGUIデザイナーMonoDevelopでアプリをビルド/実行/デバッグするための機能を解説。iOS/Android向けのGUIデザイナーや、MonoDevelopのカスタムアドインについても紹介する。機械学習エンジニアを目指す人~初中級者のための技術情報サイトです。2023最適なビューへの切り替えを推奨します:スマートフォン用表示に。