Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 在Android中使用native程序(非Java)來廣播intent

在Android中使用native程序(非Java)來廣播intent

編輯:關於Android編程

首先在看之前必須確定你已經部分了解廣播intent的原理(從Java層到native層)。如果一竅不通的話,請先百度看完。

進入正題,廣播intent從Java層最終會調用binder機制來觸發native層的發送,即發送消息BROADCAST_INTENT_TRANSACTION,而這個消息是通過IActivityManager接口處理的,所以我們在程序中必須先獲得這個接口,即如下:

    sp sm = 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創建是否正確。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved