Android SDK WG 第2回 セッション(2008/11/29) Intent 江川 崇 ©Japan Android Group, 2008
IntentのJavaDocを見てみましょう URLはこちら http://code.google.com/android/reference/android/content/Intent.html
An intent is an abstract description of an operation to be performed. JavaDocの冒頭 An intent is an abstract description of an operation to be performed. Intentとは実行される操作の抽象的な単位です。
JavaDocの冒頭 It can be used with startActivity to launch an Activity, broadcastIntent to send it to any interested BroadcastReceiver components, and startService(Intent, Bundle) or bindService(Intent, String, ServiceConnection, int) to communicate with a background Service. It(Intent)は、アクティビティを起動するためにstartActivityで使われますし、broadcastIntentであらゆるBroadcastReceiverコンポーネントに対して送られます。また、startServiceや、bindServiceでバックグラウンドのサービスとコミュニケーションをとるために使われます。
前回のSDK WGのセッションで学んだこと どういうことか? 前回のSDK WGのセッションで学んだこと 一つのアクティビティ内のウィジェットやイベントは単一のスレッドで実行される Handlerドリブン、Looperでのループ ・・・が、サービスや他のアプリは、同一のスレッドではないし、プロセスも別な可能性あり
タイミングや仕組みを気にせずに、やってくれる仕組があればいいなあ どういうことか? やり取りする方法を開発者が作るとしたら? こいつは同じプロセスだけど別スレッドだから、プロセス領域に置かれる変数などで、スレッド間で共有する仕組みを使おう。 こいつはプロセスが別だから、プロセス間通信の仕組みを使って送ろう。あるいは、プロセス間で参照できるような共有メモリやセマフォなどを使ってやろう タイミングや仕組みを気にせずに、やってくれる仕組があればいいなあ
などは、各々固有のライフサイクルがある。 Intentは、これらを実行時にくっつける糊(のり)のような働きをする。 つまり アクティビティ サービス 他のアプリケーション などは、各々固有のライフサイクルがある。 Intentは、これらを実行時にくっつける糊(のり)のような働きをする。
アプリケーションからはあくまでも「何をやりたいのか」を伝えるだけでよい Intentのいいところ アプリケーションからはあくまでも「何をやりたいのか」を伝えるだけでよい スレッド間やプロセス間でありがちな、同期や排他等をいちいち実装しなくてよい Intent = 「意図、意向」 仕組みの細部を意識することなくプロセス間通信が実現できる 「適切」な宛先に対して、「適切」なタイミングで送り届けられる
private String mAction; private Uri mData; private String mType; Intentの持つ属性 1809行目 private String mAction; private Uri mData; private String mType; private ComponentName mComponent; private int mFlags; private HashSet<String> mCategories; private Bundle mExtras;
private String mAction; そのインテントが期待されている振舞いの分類 ・・・2種類ある。 Activity Action Activityを起動したいときに使うもの アノテーション@SdkConstantType.ACTIVITY_INTENT_ACTION Broadcast Action ブロードキャスト(全体周知)したいときに使うもの アノテーション@SdkConstantType.BROADCAST_INTENT_ACTION
Activity Action 例) Action ACTION_VIEW・・・「何らかの表示をしたい」とき 例) ACTION_VIEW・・・「何らかの表示をしたい」とき 558行目:public static final String ACTION_VIEW = “android.intent.action.VIEW”; 593行目:デフォルトはこれ ACTION_DIAL・・・「電話アプリ」を起動したいとき 851行目: public static final String ACTION_DIAL = "android.intent.action.DIAL"; という形で進める
Broadcast Action 例) Action ACTION_BOOT_COMPLETED ・・・システムの「起動が完了した」とき 例) ACTION_BOOT_COMPLETED ・・・システムの「起動が完了した」とき 1096行目: public static final String ACTION_BOOT_COMPLETED = "android.intent.action.BOOT_COMPLETED"; ACTION_CONFIGURATION_CHANGED・・・コンフィグの状態が変わったとき 1162行目: public static final String ACTION_CONFIGURATION_CHANGED = "android.intent.action.CONFIGURATION_CHANGED"; という形で進める
private Uri mData; Uri このインテントの取り扱い方に関する情報 アクションと組み合わせて使うらしい such as a person record in the contacts database 「連絡先データベースの人情報とか」 アクションと組み合わせて使うらしい ACTION_VIEW content://contacts/1 「識別子が1の人の情報を表示してほしい」 Dataと言っているが、具体的なコンテンツの中身ではないので注意
Uriクラス Uri RFC2396やRFC2732で規定されているURI表現を取り扱うことの出来るクラス URI表現の一般的な構成 標準のjava.net.URIのようなもの java.net.URLEncoderとかを使っている URI表現の一般的な構成 [スキーム:]スキーム固有部分[#フラグメント] ex) http://www.example.com/index.html#p2 content://foo/bar mailto:foo@example.com
Uri なんでこんなことしてるの? プロセスをまたがったやり取りを実現する技術要素は、多くの場合ハードウェアを超えて、ネットワーク上の他のコンポーネントに対してアクセスすることも想定している。 このようなケースでは、 宛先(あるいは情報のありか)の特定 操作(情報に対するI/Oなど)の表明 などを、ネットワーク上で使い勝手がよく一般に浸透しているURI表現で実現することが多い RESTはHTTPを使って操作を定義 dRubyの「druby://」
デバイス間Intentの廃止 でもちょっと問題が。。 原則として、Intentは同一デバイス内に限定 In the end, we determined that the Intent system, as designed for local use, did not lend itself well to being the vehicle for a Remote Procedure Call (RPC). Android Developers Blog 2008/8/25 Intentはローカルユースのために設計されており、RPCの伝達手段としては役に立たないものであると結論付けました。
Flags, Categories, Extras private int mFlags; Intentに対する特別な情報を持ったフラグ FLAG_*の定数参照 private HashSet<String> mCategories; アクションに対する追加情報(分類) IntentFilterなどを使う時にちょっと便利 private Bundle mExtras; アプリからの情報を出し入れできる入れ物 Bundleの66行目: new HashMap<String, Object>()
private ComponentName mComponent; インテントの宛先のこと 明示的なインテント(Explicit Intents) Intent(Context packageContext, Class cls)や、setComponentメソッドなどによって、そのインテントを取り扱うコンポーネントを「明示的」に指定されたもの 宛先を特定する情報は他には要らない 暗黙的なインテント(Implicit Intents) 宛先のコンポーネントを指定されていないもの そのIntentを動かすにあたってどのコンポーネントが最適であるかを決定するための十分な情報が必要
StartActivityForResultを投げてみる 実際にIntentを投げてみる StartActivityForResultを投げてみる Activity#startActivityForResult → Instrumentation#execStartActivity 1418行目 → 1437行目で int result = ActivityManagerNative.getDefault() .startActivity(whoThread, intent, intent.resolveTypeIfNeeded(who.getContentResolver()), null, 0, token, target != null ? target.mEmbeddedID : null, requestCode, false, false); これは、ソース的には ActivityManagerNative#startActivity 979行目 が呼ばれている模様
ActivityManagerNative#startActivity :979行目 public int startActivity(IApplicationThread caller, Intent intent, ・・・) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeStrongBinder(caller != null ? caller.asBinder() : null); intent.writeToParcel(data, 0); data.writeString(resolvedType); data.writeTypedArray(grantedUriPermissions, 0); data.writeInt(grantedMode); data.writeStrongBinder(resultTo); data.writeString(resultWho); data.writeInt(requestCode); data.writeInt(onlyIfNeeded ? 1 : 0); data.writeInt(debug ? 1 : 0); mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0); reply.readException(); int result = reply.readInt(); reply.recycle(); data.recycle(); return result; }
ActivityManagerNative#broadcastIntent :1084行目 public int broadcastIntent(IApplicationThread caller, ・・・) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeStrongBinder(caller != null ? caller.asBinder() : null); intent.writeToParcel(data, 0); data.writeString(resolvedType); data.writeStrongBinder(resultTo != null ? resultTo.asBinder() : null); data.writeInt(resultCode); data.writeString(resultData); data.writeBundle(map); data.writeString(requiredPermission); data.writeInt(serialized ? 1 : 0); data.writeInt(sticky ? 1 : 0); mRemote.transact(BROADCAST_INTENT_TRANSACTION, data, reply, 0); reply.readException(); int res = reply.readInt(); reply.recycle(); data.recycle(); return res; }
Parcelのobtainメソッド:235行目 結局のところ オブジェクトをマーシャル、アンマーシャルして渡す仕組みだろう よくあるプロセス間通信の実装方式 Parcelからオブジェクトを出し入れしているだけ public static Parcel obtain() { final Parcel[] pool = sOwnedPool; synchronized (pool) { Parcel p; for (int i=0; i<POOL_SIZE; i++) { p = pool[i]; if (p != null) { pool[i] = null; if (DEBUG_RECYCLE) { p.mStack = new RuntimeException(); } return p; return new Parcel(0); Parcelのobtainメソッド:235行目
mRemoteは、android.os.IBinder 結局のところ mRemoteは、android.os.IBinder ServiceのときにBinderを実装すると思うが、それと同じノリ。Serviceを使えば理解が深まる。 Binderのtransactメソッドは同期 Parcelにマーシャルしたものを置くだけ broadcastでもアプリ側に制御はすぐ返ってくる BinderやParcelの中身や振る舞いをちゃんと理解すれば、腑に落ちると思う