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

來源:互聯網
上載者:User

首先在看之前必須確定你已經部分瞭解廣播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建立是否正確。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.