参考
https://www.cnblogs.com/chase1/p/7135961.html
https://developer.android.com/guide/components/aidl
- 服务端添加aidl文件
如下图所示,在src目录上,右键new一个aidl文件,写上aidl文件的名字,就自动生成了
结构如下
image.png
生成的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文件的。
image.png
- 写个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;
}
};
上边简单的就完事了。
自定义类
image.png首先我们可以看到用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();
}
}
暂时到这里
用的不多,都忘了,复习下,以后需要再看,如有错误,会及时修改的。
网友评论