android studio下AIDL 使用

參考
https://www.cnblogs.com/chase1/p/7135961.html
https://developer.android.com/guide/components/aidl

  1. 服務端添加aidl文件
    如下圖所示,在src目錄上,右鍵new一個aidl文件,寫上aidl文件的名字,就自動生成了

結構如下



生成的aidl文件如下,接口裏的方法改成自己實際需要的即可

package com.charliesong.demo0327;

// Declare any non-default types here with import statements

interface IMyAidlInterface {
     String getPid();
     void changeState(int state);
     int getState();
}

然後在下圖所示的地方可以看到自動生成的同名java文件,如果沒有生成,或者你修改了aidl接口文件的方法,那麼點下build》make project也可以生成對應的java文件的。


  1. 寫個service
    主要就是實例化一個binder,也就是我們上邊定義的aidl接口的對象,如下,然後onBind方法裏返回即可。
class ServiceNothing:Service(){
    override fun onBind(intent: Intent?): IBinder? {
        println("ServiceNothing on bind================")
        return binder
    }

    var stateOld=-1
    private val binder=object :IMyAidlInterface.Stub(){
        override fun getPid(): String {

            return packageName
        }

        override fun changeState(state: Int) {
            stateOld=state
        }

        override fun getState(): Int {
          return  stateOld
        }
    }
}
  1. 註冊服務
    intent-filter 是用來在別的app啓動這個服務用的
        <service android:name=".a1.ServiceNothing" >
            <intent-filter>
             <action android:name="com.xx.xxx"/>
              </intent-filter>
        </service>
  1. 複製服務端的aidl目錄的文件到客戶端
    就是main目錄下那個aidl目錄,複製到需要的工程main目錄下,然後make project 。
    使用就比較簡單了,如下bind服務
        Intent intent=new Intent("com.xx.xxx");
        intent.setPackage("com.charliesong.demo0327");
        bindService(intent,conn,Context.BIND_AUTO_CREATE);

conn如下

 IMyAidlInterface aidlInterface;
    private ServiceConnection conn=new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {

            aidlInterface=IMyAidlInterface.Stub.asInterface(service);
          //拿到aidlInterface 就可以調用它裏邊的方法拉。
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            aidlInterface=null;
        }
    };

上邊簡單的就完事了。

自定義類

首先我們可以看到用android studio 右鍵生成的aidl目錄和我們的java目錄不在一起的,在這個aidl目錄下,添加aidl文件是沒啥問題的,系統會自動識別的,如果添加java類,系統是不識別的。
所以我們自定義類有兩種辦法:
一種,你把要自定義的類寫在java目錄下,
另一種,就寫在aidl目錄下,不過需要在build.gradle文件下添加如下代碼

android {
    compileSdkVersion 27
    defaultConfig {
        applicationId "com.charliesong.demo0327"
        minSdkVersion 18
//省略
    }

//需要添加下邊的代碼,主要是把aidl目錄添加到java目錄下
    sourceSets {
        main {
            java.srcDirs = ['src/main/java', 'src/main/aidl']
        }
    }

我們這裏採專用第二種

  1. 首先自定義一個類,實現Parcelable接口
public class OurPacket2 implements Parcelable 

然後添加同名的aidl文件,只需要兩行代碼,一個是package,一個parcelable 後邊跟着類的名字

// OurPacket2.aidl
package com.charliesong.demo0327;

// Declare any non-default types here with import statements
parcelable OurPacket2;
  1. 之後aidl文件裏就可以用這個自定義的類了,如下
    注意:import必須添加,方法的參數前邊必須添加 in ,out 或者inout
    關於這3種tag的介紹可以看這裏https://blog.csdn.net/luoyanglizi/article/details/51958091
package com.charliesong.demo0327;
import com.charliesong.demo0327.OurPacket2;
interface IMyAidlInterface {
     String getPid();
     void setaa(inout OurPacket2 packet);
}

完事編譯,完事掛了。一直提示自定義類找不到readFromParcel方法,那就給他加一個唄。完事就ok了

public class OurPacket2 implements Parcelable {

    public void readFromParcel(Parcel in){
        pid=in.readInt();
        name=in.readString();
    }
  1. 其他步驟就和普通的一樣了。把adil目錄複製過去即可,記得客戶端也添加這個,這樣方便,aidl相關的 都在一個目錄下。
    sourceSets {
        main {
            java.srcDirs = ['src/main/java', 'src/main/aidl']
        }
    }

自定義數據數據流向in,out,inout

上邊有鏈接,有詳細的介紹,可以去看看,下邊只說結論
如下,增加3個方法測試

     void addPacketIn(in OurPacket2 packet);
     void addPacketOut(out OurPacket2 packet);
     void addPacketInOut(inout OurPacket2 packet);

然後服務端的實現如下,就是打印下接收點到的值,並且修改name值

    private val binder=object :IMyAidlInterface.Stub(){
        override fun addPacketIn(packet: OurPacket2) {
            println("addPacket in....${packet}")
            packet.name="in"
        }

        override fun addPacketOut(packet: OurPacket2) {
            println("addPacket out....${packet}")
            packet.name="out"
        }

        override fun addPacketInOut(packet: OurPacket2) {
            println("addPacket inout....${packet}")
            packet.name="in out"
        }

客戶端也一樣,打印,裏邊有打印的日誌

        public void onServiceConnected(ComponentName name, IBinder service) {

            aidlInterface=IMyAidlInterface.Stub.asInterface(service);
            try {

            OurPacket2 packet2=new OurPacket2(111,"aaaa");
            aidlInterface.addPacketIn(packet2);
            System.out.println("client1=========="+packet2);

            OurPacket2 packet22=new OurPacket2(111,"aaaa");
            aidlInterface.addPacketOut(packet22);
            System.out.println("client2=========="+packet22);

            OurPacket2 packet222=new OurPacket2(111,"aaaa");
            aidlInterface.addPacketInOut(packet222);
            System.out.println("client3=========="+packet222);

                //addPacket in....packet===pid:111 name:aaaa
                //addPacket out....packet===pid:0 name:null
                //addPacket inout....packet===pid:111 name:aaaa

                //client1==========packet===pid:111 name:aaaa
                //client2==========packet===pid:0 name:out
                //client3==========packet===pid:111 name:in out
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

in:表示數據只能從客戶端到服務端,單向的,log可以看到服務端修改是無用的,name還是aaaa
out:表示數據只能從服務端傳給客戶端,也是單向的,log可以看到,我們通過客戶端傳了個有數據的packet給服務端,可服務端打印的pid==0,name是null,說明數據傳不過去,相反,在服務端修改了name爲out之後,客戶端打印的packet的name也成了out了
inout:就是雙向的,客戶端可以傳數據給服務端,服務端修改以後,客戶端也被修改了。
簡單看下自動生成的同名接口java文件
只看下in,和out,至於inout就是兩者一起了。
看方法也能看到in的話,使用了參數packet
out的話沒有使用參數packet,不過在方法結尾,把參數packet同步了下最後的數據

            public void addPacketIn(com.charliesong.demo0327.OurPacket2 packet) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((packet != null)) {
                        _data.writeInt(1);
                        packet.writeToParcel(_data, 0);//可以看到數據packet被寫到data裏了
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_addPacketIn, _data, _reply, 0);
                    _reply.readException();
//packet沒有被修改
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

//對於out,packet參數根本就沒被使用,只在最後複製了下reply數據,
            public void addPacketOut(com.charliesong.demo0327.OurPacket2 packet) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_addPacketOut, _data, _reply, 0);
                    _reply.readException();
                    if ((0 != _reply.readInt())) {
                        packet.readFromParcel(_reply);
                    }
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

暫時到這裏

用的不多,都忘了,複習下,以後需要再看,如有錯誤,會及時修改的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章