注:由于时间关系目前先写个大概,后续再做完善
1.建立aidl
建立aidl文件夹,在文件夹中添加包tk.huayu.api,包中添加以下文件
IService.aidl
package tk.huayu.api;
import tk.huayu.api.Test; // 同一个包下面的也必须导包
interface IService{
// in表示这个参数是作为输入参数使用(in/out/inout),作为返回值时不用加
void fun1(in Test test);
}
Test.aidl
package tk.huayu.api;
parcelable Test; // 声明Test对象
Test2.aidl
package tk.huayu.api;
parcelable Test2; // 声明Test2对象,这个是Test中包含的一个属性(对象)
2.实现aidl用到的对象
新建包tk.huayu.api,这个包必须和aidl中一模一样。在包中添加如下文件
Test.java
package tk.huayu.api;
import android.os.Parcel;
import android.os.Parcelable;
public class Test implements Parcelable {
public int testInt=1;
public String testString="Test";
public Test2 test2 = new Test2();
public Test(){
}
int getTestInt(){
return testInt;
}
/**
* 实现Parcelable接口必须实现的方法:
*
* @return 描述这一Parcel是起什么作用的,通过这一整形每个bit来描述其类型,一般会返回0
*/
@Override
public int describeContents() {
return 0;
}
/**
* 实现Parcelable接口必须实现的方法:
* 将类所需要传输的属性写到Parcel里,在这个方法里都是使用writeXXX()写入到Parcel
* @param dest 提供发送功能的Parcel
* @param flags 用来指定这样的发送方向,与aidl的in、out、inout三种限定符匹配
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
//把该对象所包含的数据写到Parcel
dest.writeInt(testInt);
dest.writeString(testString);
//dest.writeParcelable(test2,flags);
dest.writeValue(test2);
}
/**
* 这个构造方法用于从序列化数据中取出数据。读取的顺序要和writeToParcel()写入的顺序一致
* @param source 数据源
*/
private Test(Parcel source){
testInt = source.readInt();
testString = source.readString();
test2 = (Test2)source.readValue(Test2.class.getClassLoader());
}
/**
* 添加一个静态成员,名为CREATOR,该对象实现了Parcelable.Creator接口
*/
public static final Parcelable.Creator<Test> CREATOR = new Parcelable.Creator<Test>(){
/**
* 从Parcel中读取数据,返回Test对象。读取的顺序要和writeToParcel()写入的顺序一致
* @param source 数据源
* @return 接收到的对象(通过数据源创建的一个对象)
*/
@Override
public Test createFromParcel(Parcel source){
return new Test(source);
}
/**
* 用于创建多个这种实现了Parcelable接口的类。创建多个空对象,不能以某个Parcel对象为基础创建,使用默认的类创始化方法。
* @param size 数组大小
* @return 数组
*/
@Override
public Test[] newArray(int size){
return new Test[size];
}
};
}
Test2.java
package tk.huayu.api;
import android.os.Parcel;
import android.os.Parcelable;
public class Test2 implements Parcelable {
public int testInt=2;
public String testString="Test2";
public Test2(){
}
int getTestInt(){
return testInt;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(testInt);
dest.writeString(testString);
}
private Test2(Parcel source){
testInt = source.readInt();
testString = source.readString();
}
public static final Parcelable.Creator<Test2> CREATOR
= new Parcelable.Creator<Test2>(){
@Override
public Test2 createFromParcel(Parcel source){
return new Test2(source);
}
@Override
public Test2[] newArray(int size){
return new Test2[size];
}
};
}
3.实现接口
新建包package tk.huayu.demo
MyService .java
package tk.huayu.demo;
import android.app.Service;
import android.os.IBinder;
import android.content.Context;
import tk.huayu.api.IService;
public class MyService extends Service{
/**
* 被远程调用,运行在appliction1进程中的Binder_x线程(查看进程号 android.os.Process.myPid())
*/
public IService.Stub stub = new IServiceInterface();
public class IServiceInterface extends IService.Stub // 注意实现的一定要是Stub,否者远端调用得到的将是null()
{
// 实现AIDL文件中接口的定义的各个方法。
public void fun1(Test test){ // 不再注明in,添加public
... // 代码略
Log.d("IService","fun1:"+test);
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
... // 代码略
}
@Override
public IBinder onBind(Intent intent) {
Log.d("MyService ", "onBind "+android.os.Process.myPid());
return stub;
}
@Override
public boolean onUnbind(Intent intent) {
Log.d("MyService ", "onUnbind");
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
super.onDestroy();
}
}
4.声明service
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest ...
>
<!-- 添加service外部访问许可(不添加也可以,只是提示警告)。当然,如果service添加了许可,那么客户端也要添加许可
<uses-permission android:name="tk.huayu.edog"/>, 否则客户端访问时将抛出异常 -->
<permission android:protectionLevel="normal" android:name="tk.huayu.demo"/>
<application ...
>
<!-- 声明service,要添加 android:exported="true"否则service是不允许外部的应用进行远程调用
android:process=":remote" // 添加这边表示service在独立的进程运行 -->
<service
android:name="tk.huayu.demo.MyService"
android:exported="true"
android:permission="tk.huayu.demo"
/>
...
</application>
</manifest>
5.编写客户端
将service的aidl包和api包拷贝到客户端app中(包名不能改变)
MainActivity.java
public class MainActivity extends Activity {
private ServiceConnection serviceConnection = new ServiceConnection(){
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d("Client","onServiceConnected "+name+"IBinder "+service);
IService service = IService.Stub.asInterface(service);
if(service!=null)
{
try {
Log.d("Client", "call fun1");
service.fun1(new Test());
} catch (RemoteException e) {
e.printStackTrace();
Log.d("Client", "service is bad");
}
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d("Client","onServiceConnected "+name);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
...
Intent i = new Intent();
i.setClassName("tk.huayu.demo","tk.huayu.demo.MyService");
this.context.bindService(i, serviceConnection, Context.BIND_AUTO_CREATE);
Log.d("Client", "service bind "+android.os.Process.myPid());
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(serviceConnection);
}
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest ...
>
<!-- 添加许可 -->
<uses-permission android:name="tk.huayu.demo"/>
<application ...
>
...
</application>
</manifest>
注:也可以在客户端实现类似IService接口,将接口实例通过参数注册到service,这样service也能远程调用客户端的函数,如果客户端有多个(一般也是有多个)可以使用RemoteCallbackList进行批量回调。