Intent

Android SDK WG 第2回 セッション
(2008/11/29)
Intent
SDK WG
江川 崇
©Japan Android Group, 2008
1
Intentってなに?
IntentのJavaDocを見てみましょう
URLはこちら
http://code.google.com/android/reference/androi
d/content/Intent.html
©Japan Android Group, 2008
2
JavaDocの冒頭
An intent is an abstract description of an
operation to be performed.
Intentとは実行される操作の抽象的な単位
です。
©Japan Android Group, 2008
3
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でバックグラウンドのサービスと
コミュニケーションをとるために使われます。
©Japan Android Group, 2008
4
どういうことか?
前回のSDK WGのセッションで学んだこと
一つのアクティビティ内のウィジェットやイベント
は単一のスレッドで実行される
Handlerドリブン、Looperでのループ
・・・が、サービスや他のアプリは、同一のスレッド
ではないし、プロセスも別な可能性あり
©Japan Android Group, 2008
5
どういうことか?
やり取りする方法を開発者が作るとしたら?
こいつは同じプロセスだけど別スレッドだから、
プロセス領域に置かれる変数などで、スレッド間
で共有する仕組みを使おう。
こいつはプロセスが別だから、プロセス間通信
の仕組みを使って送ろう。あるいは、プロセス間
で参照できるような共有メモリやセマフォなどを
使ってやろう
タイミングや仕組みを気にせずに、やってくれ
る仕組があればいいなあ
©Japan Android Group, 2008
6
Intentが楽にする
この実現手段がIntent
つまり
アクティビティ
サービス
他のアプリケーション
などは、各々固有のライフサイクルがある。
Intentは、これらを実行時にくっつける
糊(のり)のような働きをする。
©Japan Android Group, 2008
7
Intentのいいところ
アプリケーションからはあくまでも「何をやり
たいのか」を伝えるだけでよい
スレッド間やプロセス間でありがちな、同期
や排他等をいちいち実装しなくてよい
Intent = 「意図、意向」
仕組みの細部を意識することなくプロセス間
通信が実現できる
「適切」な宛先に対して、「適切」なタイミング
で送り届けられる
©Japan Android Group, 2008
8
Intentの持つ属性
1809行目
private
private
private
private
private
private
private
String mAction;
Uri mData;
String mType;
ComponentName mComponent;
int mFlags;
HashSet<String> mCategories;
Bundle mExtras;
©Japan Android Group, 2008
9
Action
private String mAction;
そのインテントが期待されている振舞いの分類
・・・2種類ある。
Activity Action
Activityを起動したいときに使うもの
アノテーション
@SdkConstantType.ACTIVITY_INTENT_ACTION
Broadcast Action
ブロードキャスト(全体周知)したいときに使うもの
アノテーション
@SdkConstantType.BROADCAST_INTENT_ACTION
©Japan Android Group, 2008
10
Action
Activity Action
例)
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";
©Japan Android Group, 2008
11
Action
Broadcast Action
例)
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";
©Japan Android Group, 2008
12
Uri
private Uri mData;
このインテントの取り扱い方に関する情報
such as a person record in the contacts database
「連絡先データベースの人情報とか」
アクションと組み合わせて使うらしい
ACTION_VIEW content://contacts/1
「識別子が1の人の情報を表示してほしい」
Dataと言っているが、具体的なコンテンツの中身
ではないので注意
©Japan Android Group, 2008
13
Uri
Uriクラス
RFC2396やRFC2732で規定されているURI表現を
取り扱うことの出来るクラス
標準のjava.net.URIのようなもの
java.net.URLEncoderとかを使っている
URI表現の一般的な構成
[スキーム:]スキーム固有部分[#フラグメント]
ex)
http://www.example.com/index.html#p2
content://foo/bar
mailto:[email protected]
©Japan Android Group, 2008
14
Uri
なんでこんなことしてるの?
プロセスをまたがったやり取りを実現する技術要
素は、多くの場合ハードウェアを超えて、ネット
ワーク上の他のコンポーネントに対してアクセス
することも想定している。
このようなケースでは、
宛先(あるいは情報のありか)の特定
操作(情報に対するI/Oなど)の表明
などを、ネットワーク上で使い勝手がよく一般に浸
透しているURI表現で実現することが多い
RESTはHTTPを使って操作を定義
dRubyの「druby://」
©Japan Android Group, 2008
15
でもちょっと問題が。。
• デバイス間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の伝達手段としては役に立たな
いものであると結論付けました。
©Japan Android Group, 2008
16
Flags, Categories, Extras
private int mFlags;
Intentに対する特別な情報を持ったフラグ
FLAG_*の定数参照
private HashSet<String> mCategories;
アクションに対する追加情報(分類)
IntentFilterなどを使う時にちょっと便利
private Bundle mExtras;
アプリからの情報を出し入れできる入れ物
Bundleの66行目:
new HashMap<String, Object>()
©Japan Android Group, 2008
17
ComponentName
private ComponentName mComponent;
インテントの宛先のこと
明示的なインテント(Explicit Intents)
Intent(Context packageContext, Class cls)や、
setComponentメソッドなどによって、そのインテントを取
り扱うコンポーネントを「明示的」に指定されたもの
宛先を特定する情報は他には要らない
暗黙的なインテント(Implicit Intents)
宛先のコンポーネントを指定されていないもの
そのIntentを動かすにあたってどのコンポーネントが最
適であるかを決定するための十分な情報が必要
©Japan Android Group, 2008
18
実際に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行目
が呼ばれている模様
©Japan Android Group, 2008
19
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;
}
©Japan Android Group, 2008
20
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;
}
©Japan Android Group, 2008
21
結局のところ
オブジェクトをマーシャル、アンマーシャルし
て渡す仕組みだろう
よくあるプロセス間通信の実装方式
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行目
©Japan Android Group, 2008
22
結局のところ
mRemoteは、android.os.IBinder
ServiceのときにBinderを実装すると思うが、それ
と同じノリ。Serviceを使えば理解が深まる。
Binderのtransactメソッドは同期
Parcelにマーシャルしたものを置くだけ
broadcastでもアプリ側に制御はすぐ返ってくる
BinderやParcelの中身や振る舞いをちゃんと
理解すれば、腑に落ちると思う
©Japan Android Group, 2008
23