編輯:關於Android編程
首先在看之前必須確定你已經部分了解廣播intent的原理(從Java層到native層)。如果一竅不通的話,請先百度看完。
進入正題,廣播intent從Java層最終會調用binder機制來觸發native層的發送,即發送消息BROADCAST_INTENT_TRANSACTION,而這個消息是通過IActivityManager接口處理的,所以我們在程序中必須先獲得這個接口,即如下:
spsm = defaultServiceManager(); sp am = sm->checkService(String16("activity"));
然後我們就得到了am,然後可以繼續使用transact函數來通過消息發送intent。
當然這裡我們還需要一個intent,但是怎麼在native程序中創建intent呢?
首先我們需要創建一個Parcel,這是binder經常使用的一個數據包類。
創建完成後,進行填充,如下所示:
1. data.writeInterfaceToken(String16("android.app.IActivityManager"));
2. data.writeStrongBinder(NULL); /* caller */
/* intent */
3. data.writeString16(String16(action_str)); /* action */
4. data.writeInt32(URI_TYPE_ID); /* Uri - type */
5. data.writeString16(String16(uri_str)); /* uri string if URI_TYPE_ID set */
6. data.writeString16(NULL, 0); /* type */
7. data.writeInt32(0); /* flags */
8. data.writeString16(NULL, 0); /* package name */
9. data.writeString16(NULL, 0); /* ComponentName */
10. data.writeInt32(0); /* source bound - size */
11. data.writeInt32(0); /* Categories - size */
12. data.writeInt32(0); /* selector - size */
13. data.writeInt32(0); /* ClipData */
14. data.writeInt32(-1); /* bundle(extras) size */
/* end of intent */
第一行:當發送消息BROADCAST_INTENT_TRANSACTION時,會檢查token,如果不是IActivityManager.descriptor則會返回false,當然這裡只會打出一段warning信息,不會發送失敗。
第二行:這裡是發送這個binder請求方。
第三行:這個是需要發送intent的action(android.intent.action.*)
第四行:這裡寫入了一個int值,可以有四個,其中三個分別對應三種uri類型:
NULL_TYPE_ID:0
StringUri.TYPE_ID:1
OpaqueUri.TYPE_ID:2
HierarchicalUri.TYPE_ID:3
第五行:這一行存在與否依賴於第四行,規則如下:
如果是NULL_TYPE_ID:該行不可存在
如果是StringUri.TYPE_ID:該行必須是writeString16寫入uri地址
如果是OpaqueUri.TYPE_ID:可以看Uri.java中OpaqueUri類readFrom函數,由多個part組成。
如果是HierarchicalUri.TYPE_ID:可以看Uri.java中HierarchicalUri類readFrom函數,由多個part組成。
第六行-第十三行:在後面的注釋都有解釋。
第十四行:這一行是用來確定有多少extras(即映射參數),比如”key”=”1234”,可以使用getString獲得。
{ /* Extras */
data.writeInt32(-1); /* length */
data.writeInt32(0x4C444E42); // 'B' 'N' 'D' 'L'
int oldPos = data.dataPosition();
{ /* writeMapInternal */
data.writeInt32(1); /* size */
data.writeInt32(VAL_STRING);
data.writeString16(String16("key"));
data.writeInt32(VAL_STRING);
data.writeString16(String16(“1234”));
}
int newPos = data.dataPosition();
data.setDataPosition(oldPos - 8);
data.writeInt32(newPos - oldPos); /* length */
data.setDataPosition(newPos);
}
這裡我們先寫了int值來表示有多少extras,但是只是-1,我們會在之後進行計算來重寫這個值。然後我們寫入了一個magic值(BNDL),這個值在解析extras,即Bundle類中readFromParcelInner函數中會先讀取這個magic值,然後才繼續處理。這裡我們會在寫入前保存當前data位置,然後寫入map的數量,即一組”key”=”1234”,然後寫完後使用新的data位置減去老的位置即獲得了表示長度的data位置,然後寫入新的長度值。這裡關於map表裡的描述符(如VAL_STRING)可以在Parcel.java中readValue函數中找到。
OK,填充完這14行後,如果我們還需要添加一些末尾信息:
data.writeString16(NULL, 0); /* resolvedType */
data.writeStrongBinder(NULL); /* resultTo */
data.writeInt32(-1); /* result code */
data.writeString16(NULL, 0); /* result data */
data.writeInt32(-1); /* no result extra */
data.writeString16(NULL, 0); /* permission */
data.writeInt32(false); /* app operation in AppOpsManager */
data.writeInt32(false); /* serialized */
data.writeInt32(false); /* sticky */
data.writeInt32(false); /* userid */
這裡包括了一些其他需要的屬性,可以看注釋了解。
但是為什麼會需要這樣的順序進行添加呢?
我們可以看ActivityManagerNative.java中對BROADCAST_INTENT_TRANSACTION的處理:
case BROADCAST_INTENT_TRANSACTION:
{
data.enforceInterface(IActivityManager.descriptor);
IBinder b = data.readStrongBinder();
IApplicationThread app =
b != null ? ApplicationThreadNative.asInterface(b) : null;
Intent intent = Intent.CREATOR.createFromParcel(data);
String resolvedType = data.readString();
b = data.readStrongBinder();
IIntentReceiver resultTo =
b != null ? IIntentReceiver.Stub.asInterface(b) : null;
int resultCode = data.readInt();
String resultData = data.readString();
Bundle resultExtras = data.readBundle();
String perm = data.readString();
int appOp = data.readInt();
boolean serialized = data.readInt() != 0;
boolean sticky = data.readInt() != 0;
int userId = data.readInt();
我們可以看到第一行就是判斷剛才說的interface的。
第二行會獲得caller即調用者。
之後會創建intent,這裡會進入Intent.java中newIntent,繼而調用readFromParcel
setAction(in.readString());
mData = Uri.CREATOR.createFromParcel(in);
mType = in.readString();
mFlags = in.readInt();
mPackage = in.readString();
mComponent = ComponentName.readFromParcel(in);
if (in.readInt() != 0) {
mSourceBounds = Rect.CREATOR.createFromParcel(in);
}
int N = in.readInt();
if (N > 0) {
mCategories = new ArraySet();
int i;
for (i=0; i
這裡就會調用Parcel的readString,readInt等一系列函數,每次調用後data位置就會後移。
這裡會創建mData,即我們剛剛說的uri地址,如果你給他的是NULL_TYPE_ID,那麼他直接返回null,如果是string類型,那麼他還會readString一遍,所以必須要配對好寫入一個字符串,不然如果多讀一次會影響data的位置,導致之後的讀取錯誤。接下來一系列的Parcel讀取就會按照剛剛創建時的順序進行。
完成intent創建後,繼續讀取Parcel中的尾部,即我們最後添加的內容。然後所有這些完成後就會調用broadcastIntent發送及處理。
所以發送成功與否關鍵在於intent創建是否正確。
使用ViewPager實現問卷答題效果
總覺得應該在每一個項目中汲取一點溫存。。。哪怕是只有一點點。。。先看實現的效果圖,就是使用Viewpager控件進行左右翻頁,然後設置邊距為負數,進行上一頁和下一頁的部分
android:用sql server2005(或Excel+sql server2005)+sqlserver2sqlite_converter工具
自己寫db文件方法有兩種:1、用sql server2005+sqlserver2sqlite_converter工具(數據在sql server裡面寫)2、用Excel
Android TabActivity使用方法
TabActivity 首先Android裡面有個名為TabActivity來給我們方便使用。其中有以下可以關注的函數: public TabHost getT
模擬自然動畫的精髓——TimeInterpolator與TypeEvaluator
在今天的文章開始之前,有個忙想請大家幫一下,希望在京東、淘寶、當當、亞馬遜購買了我的書《Android群英傳:神兵利器》的朋友們,幫忙去網店上給個簡短的評價,舉手之勞,還