編輯:關於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控件進行左右翻頁,然後設置邊距為負數,進行上一頁和下一頁的部分
自己寫db文件方法有兩種:1、用sql server2005+sqlserver2sqlite_converter工具(數據在sql server裡面寫)2、用Excel
TabActivity 首先Android裡面有個名為TabActivity來給我們方便使用。其中有以下可以關注的函數: public TabHost getT
在今天的文章開始之前,有個忙想請大家幫一下,希望在京東、淘寶、當當、亞馬遜購買了我的書《Android群英傳:神兵利器》的朋友們,幫忙去網店上給個簡短的評價,舉手之勞,還