Service—AIDL跨进程通信
1 介绍
我们都知道在Android系统中,进程之间一般无法直接进行数据交换,因为他们的内存并不是共享的。那么我们在开发一个应用程序时往往需要调用某个其它进程中的service,这时候就需要跨进程间通信。Android提供了一种叫作AIDL(Android Interface Definition Language)的接口语言来解决这种通信问题。这位爷看着唬人,其实很简单,其语法与Java基本一样。但它的特殊之处有:
AIDL定义的接口必须以.aidl结尾
AIDL文件中,除了基本数据类型、String、List、Map、CharSequence外,都需要导入包才能使用。
2 AIDL创建
定义一个AIDL接口,形式如下。可以看到,与Java定义接口简直一毛一样,很是简单。
interface IDog {
String getColor();
float getWeight();
}
定义好接口后,ADT工具会自动生成一个IDog.java接口,在该接口里会创建一个Stub内部类,该内部类同时实现了IBinder、ICat两个接口。
3 服务端 Server
我们易银一个这样的场景,有个服务端运行在进程1中,有个客户端运行在进程2中,客户端想访问一下服务端并取点数据出来现实显示。这时就用到我们这个AIDL了。我们说定义好AIDL文件后,会自动生产一个IDog.java类,不用去尝试去看这个类的代码,你会大失所望,没必要看。然后我们指导,这个类有个内部Stub类,在服务端我们需要继承这个类,并实现其内部方法,实现的这个类将会作为远程调用Service的回调类,注意它同时实现了IBinder接口,因此可作为Service的onBind()方法的返回值。
废话少说,来看一下如何实现。
public class MyService extends Service {
IDog.Stub binder;
String[] color = new String[] {"赤","橙","红","绿","青"};
float[] weight = new float[] {12.1f,12.2f,12.3f,12.4f,12.5f};
Timer timer;
int rand;
public class DogBinder extends IDog.Stub {
@Override
public String getColor() {
return color[rand];
}
@Override
public float getWeight() {
return weight[rand];
}
}
public MyService() {
}
@Override
public IBinder onBind(Intent intent) {
return binder;
}
@Override
public void onCreate() {
super.onCreate();
binder = new DogBinder();
timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
rand = (int)(Math.random()*5);
Log.i("MyService","rand="+rand);
}
},0,100);
}
}
这里用到了Timer,怎么使用我在附录中说明一下。
4 客户端Customer
客户端要想调用到上面的service,需要先将上面所创建的AIDL文件连同它所在目录com/example/server/IDog.aidl一块复制到客户端代码中来。与server端类似,ADT工具会自动生成IDog.java类。那么在客户端又是如何跨进程调用到server的,上代码。
public class MainActivity extends AppCompatActivity {
Button get;
TextView show;
IDog dogBinder;
//与普通调用service基本相同,也要创建ServiceConnection对象
ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//重点:需要使用asInterface方法,该方法会新创建一个Binder代理对象
dogBinder = IDog.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
dogBinder = null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
show = findViewById(R.id.show);
//还是创建一个Intent
Intent intent = new Intent();
intent.setPackage("com.example.server");
intent.setAction("com.example.server.MY_SERVICE");
//套路相同,使用bindService方法绑定service
bindService(intent,conn, Service.BIND_AUTO_CREATE);
//这里,通过一个按钮点击来控制客户端访问server端
get = findViewById(R.id.get);
get.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
//终于要访问server端了,内心有一丝骚动
show.setText(dogBinder.getColor()+" "+dogBinder.getWeight());
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
}
}
附录
Timer
Timer是一种定时器工具,用来在一个后台线程计划执行指定任务。它可以计划执行一个任务一次或反复多次。
TimerTask一个抽象类,它的实现类代表一个可以被Timer计划的任务。
timer.schedule(new TimerTask(){//我懒了}, 0, 1000);
第一个参数:TimerTask对象,使用者要继承TimerTask类,并实现 public void run() 方法,因为 TimerTask 类实现了 Runnable 接口。
第二个参数:delay,延迟时间,表示调用 schedule() 方法后还需要等待多长时间去执行 run() 方法。
第三个参数:period,周期,表示每隔多长时间调用一次 run() 方法
asInterface()方法
asInterface()方法的作用总结
(1)在客户进程中调用asInterface(),会新创建一个Binder代理对象,代理对象中包含了引用对象。
(2)在服务进程中调用asInterface(),直接返回参数的指针。因为在服务进程中可以直接调用服务类,无须再创建代理对象了。
实例代码
已经调通,运行没有问题,仅供学习使用,勿喷bug
server端
~~~~~~~~~~~~~AndroidManifest.xml~~~~~~~~~~~~~~
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.server">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<service
android:name=".MyService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.example.server.MY_SERVICE" />
</intent-filter>
</service>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
~~~~~~~~~~~~~MyService.java~~~~~~~~~~~~~~
package com.example.server;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
import java.util.Timer;
import java.util.TimerTask;
public class MyService extends Service {
IDog.Stub binder;
String[] color = new String[] {"赤","橙","红","绿","青"};
float[] weight = new float[] {12.1f,12.2f,12.3f,12.4f,12.5f};
Timer timer;
int rand;
public class DogBinder extends IDog.Stub {
@Override
public String getColor() {
return color[rand];
}
@Override
public float getWeight() {
return weight[rand];
}
}
public MyService() {
}
@Override
public IBinder onBind(Intent intent) {
return binder;
}
@Override
public void onCreate() {
super.onCreate();
binder = new DogBinder();
timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
rand = (int)(Math.random()*5);
Log.i("MyService","rand="+rand);
}
},0,100);
}
}
~~~~~~~~~~~~~IDog.aidl~~~~~~~~~~~~~~
package com.example.server;
// Declare any non-default types here with import statements
interface IDog {
String getColor();
float getWeight();
}
~~~~~~~~~~~~~MainActivity.java~~~~~~~~~~~~~~
package com.example.server;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
实例代码
customer端
~~~~~~~~~~~~~AndroidManifest.xml~~~~~~~~~~~~~~
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.customer">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
~~~~~~~~~~~~~MainActivity.java~~~~~~~~~~~~~~
package com.example.customer;
import androidx.appcompat.app.AppCompatActivity;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import com.example.server.IDog;
public class MainActivity extends AppCompatActivity {
Button get;
TextView show;
IDog dogBinder;
ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
dogBinder = IDog.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
show = findViewById(R.id.show);
Intent intent = new Intent();
intent.setPackage("com.example.server");
intent.setAction("com.example.server.MY_SERVICE");
bindService(intent,conn, Service.BIND_AUTO_CREATE);
get = findViewById(R.id.get);
get.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
show.setText(dogBinder.getColor()+" "+dogBinder.getWeight());
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
}
}
~~~~~~~~~~~~~IDog.aidl~~~~~~~~~~~~~~
// IDog.aidl
package com.example.server;
// Declare any non-default types here with import statements
interface IDog {
String getColor();
float getWeight();
}