參考
https://www.cnblogs.com/chase1/p/7135961.html
https://developer.android.com/guide/components/aidl
- 服務端添加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文件的。
- 寫個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
}
}
}
- 註冊服務
intent-filter 是用來在別的app啓動這個服務用的
<service android:name=".a1.ServiceNothing" >
<intent-filter>
<action android:name="com.xx.xxx"/>
</intent-filter>
</service>
- 複製服務端的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']
}
}
我們這裏採專用第二種
- 首先自定義一個類,實現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;
- 之後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();
}
- 其他步驟就和普通的一樣了。把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();
}
}
暫時到這裏
用的不多,都忘了,複習下,以後需要再看,如有錯誤,會及時修改的。