AIDL 运程服务 Remote Service

Remote Service拓展

一、简介:

通常每个应用程序都在他自己的进程内运行,但有时需要在进程之间传递对象(IPC 通信),你可以通过应用程序 UI 的方式写个运行在一个不同的进程中的 service。在 android 平台中,一个进程通常不能访问其他进程中的内存区域。所以,他们需要把对象拆分成操作系统能理解的简单形式,以便伪装成对象跨越边界访问。编写这种伪装代码相当的枯燥乏味,好在 android 为我们提供了 AIDL 工具可以来做这件事。
 
AIDL( android 接口描述语言)是一个 IDL 语言,他可以生成一段代码,可以使在一个 android 设备上运行的两个进程使用内部通信进程进行交互。如果你需要在一个进程中(例如在一个 Activity 中)访问另一个进程中(例如一个 Service )某个对象的方法,你就可以使用 AIDL 来生成这样的代码来伪装传递各种参数。
 
进程间通信.调用者和 Service 如果不在一个进程内, 就需要使用 android 中的远程 Service 调用机制.
android 使用 AIDL 定义进程间的通信接口. AIDL 的语法与 java 接口类似, 需要注意以下几点:

1. AIDL 文件必须以 .aidl 作为后缀名.
2. AIDL 接口中用到的数据类型, 除了基本类型, String, List, Map, CharSequence 之外, 其他类型都需要导包, 即使两种在同一个包内. List 和 Map 中的元素类型必须是 AIDL 支持的类型.
3. 接口名需要和文件名相同.
4. 方法的参数或返回值是自定义类型时, 该自定义的类型必须实现了 Parcelable 接口.
5. 所有非 java 基本类型参数都需要加上 in, out, inout 标记, 以表明参数是输入参数, 输出参数, 还是输入输出参数.
6. 接口和方法前不能使用访问修饰符和 static, final 等修饰.

二、代码:

1.创建 AIDL 文件

package com.xjl.service.aidl;

interface IMusicControlService{

void play();
void stop();
void pause();
}

/**
* Description: aidl 服务端
*/
public class RemoteMusicService extends Service {

private MediaPlayer mMediaPlayer;

private final Stub mBind = new Stub() {

@Override
public void play() throws RemoteException {

if (mMediaPlayer == null) {

mMediaPlayer = mMediaPlayer.create(RemoteMusicService.this,
R.raw.mother);
mMediaPlayer.setLooping(false);
}

if (!mMediaPlayer.isPlaying()) {

mMediaPlayer.start();
}
}

@Override
public void stop() throws RemoteException {

if (mMediaPlayer != null) {

mMediaPlayer.stop();

try {

mMediaPlayer.prepare(); // 在调用stop后如果需要再次通过start进行播放,需要之前调用prepare函数
} catch (IOException ex) {
ex.printStackTrace();
}
}
}

@Override
public void pause() throws RemoteException {

if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {
mMediaPlayer.pause();
}
}
};

@Override
public IBinder onBind(Intent intent) {
return mBind;
}

@Override
public void onDestroy() {
super.onDestroy();

if (mMediaPlayer != null) {
mMediaPlayer.stop();
mMediaPlayer.release();
}
}
}

/**
* Description: aidl 主页面
*/
public class PlayRemoteMusic extends Activity implements OnClickListener {

private Button playBtn;
private Button stopBtn;
private Button pauseBtn;
private Button exitBtn;

private IMusicControlService musicService;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

setContentView(R.layout.activity_bind_music_service);

playBtn = (Button) findViewById(R.id.playBindMusic);
stopBtn = (Button) findViewById(R.id.stopBindMusic);
pauseBtn = (Button) findViewById(R.id.pauseBindMusic);
exitBtn = (Button) findViewById(R.id.exitBindMusic);

playBtn.setOnClickListener(this);
stopBtn.setOnClickListener(this);
pauseBtn.setOnClickListener(this);
exitBtn.setOnClickListener(this);

connection();
}

private void connection() {

Intent intent = new Intent(this, RemoteMusicService.class);
bindService(intent, sc, Context.BIND_AUTO_CREATE); // bindService
}

@Override
public void onClick(View v) {

try {

switch (v.getId()) {

case R.id.playBindMusic:

musicService.play();
break;
case R.id.stopBindMusic:

if (musicService != null) {

musicService.stop();
}
break;
case R.id.pauseBindMusic:

if (musicService != null) {

musicService.pause();
}
break;
case R.id.exitBindMusic:

this.finish();
break;
}
} catch (Exception e) {
e.printStackTrace();
}
}

private ServiceConnection sc = new ServiceConnection() {

@Override
public void onServiceConnected(ComponentName name, IBinder service) { // connect
// Service

musicService = IMusicControlService.Stub.asInterface(service);
}

@Override
public void onServiceDisconnected(ComponentName name) { // disconnect
// Service

musicService = null;
}
};

@Override
public void onDestroy() {
super.onDestroy();

if (sc != null) {

unbindService(sc);
}
}
}
【运行结果和 bindService 类似】

Remote Service流程总结:

1、 Activity(客户端)中,Intent intent = new Intent("com.homer.remote.remoteMusicReceiver");构建intent,然后bindService(intent, sc, Context.BIND_AUTO_CREATE);绑定服务

2、 Activity(客户端)中,通过ServiceConnection()重载onServiceConnected()建立连接,获取Service.Stub实例;onServiceDisconnected()释放连接(与bindService类似)

3、 Service中,通过重载onBind(Intent intent) 返回Service.Stub实例,但Service.Stub类是由aidl文件生成的接口类中的一个内部类Stub类,Service来继承该Stub类

4、 Activity中,通过操作Service实例(musicService),执行音乐播放操作(play、pause、stop等)


三、使用aidl实现两个程序跨进程通信

本文提供了一个关于AIDL使用的简单易懂的例子,分为客户端和服务端两部分,分别为客户端和服务端新建一个eclipse工程,实现了从客户端向服务端发送请求,服务端打印log的功能。

客户端和服务端的源码结构如下:

            客户端                                        服务端

 

这里需要注意的是 aidl 所建的包名与文件名必须一样。当然如果不一样的话,照样可以运行Service,但是,调用回调 mService.invokTest(); 时会报错。

1.AIDL 文件如下:

package com.xjl.service.aidl;

interface MInterface{

void invokTest();
}

2.客户端代码

/**
* Description: 客户端代码
*/
public class MainActivity extends Activity implements OnClickListener {

protected static final String TAG = "@@@MainActivity";
private Button btnOk;
private Button btnCancel;
private Button btnCallBack;

private MInterface mService;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

btnOk = (Button) this.findViewById(R.id.btnOk);
btnCancel = (Button) this.findViewById(R.id.btnCancle);
btnCallBack = (Button) this.findViewById(R.id.btnCallBack);

btnOk.setOnClickListener(this);
btnCancel.setOnClickListener(this);
btnCallBack.setOnClickListener(this);
}

@Override
public void onClick(View v) {

switch (v.getId()) {
case R.id.btnOk:

Bundle args = new Bundle();
Intent intent = new Intent("com.xjl.aidl.service");
intent.putExtras(args);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
break;

case R.id.btnCancle:

unbindService(mConnection);
break;

case R.id.btnCallBack:

try {
Log.i(TAG, "current Thread id = "
+ Thread.currentThread().getId());
mService.invokTest();
} catch (RemoteException e) {

}
break;
}
}

private ServiceConnection mConnection = new ServiceConnection() {

@Override
public void onServiceConnected(ComponentName name, IBinder service) {

Log.e(TAG, "connect service");
mService = MInterface.Stub.asInterface(service);
}

@Override
public void onServiceDisconnected(ComponentName name) {

Log.e(TAG, "connect service");
mService = null;
}
};
}

客户端在执行 bindService 的时候,成功绑定服务之后,会回调 mConnection 的 onServiceConnected(),并且传回了服务端的通信接口 IBinder,此 IBinder 即服务 onBind() 时返回的 IBinder,详见 mAIDLService.java。
在 onServiceConnected(),客户端成功获取了服务端通信接口,实际上是本地代理对象,该对象存在于客户端进程空间,客户端只和代理对象交互,真正的IPC通信是本地代理对象和服务端的通信。

3.服务端
public class MAIDLService extends Service {
private static final String TAG = "@@@AIDLService";

private void Log(String str) {
Log.e(TAG, "----------" + str + "----------");
}

public void onCreate() {
Log("service created");
}

public void onStart(Intent intent, int startId) {
Log("service started id = " + startId);
}

public IBinder onBind(Intent t) {
Log("service on bind");
return mBinder;
}

public void onDestroy() {
Log("service on destroy");
super.onDestroy();
}

public boolean onUnbind(Intent intent) {
Log("service on unbind");
return super.onUnbind(intent);
}

public void onRebind(Intent intent) {
Log("service on rebind");
super.onRebind(intent);
}

private final MInterface.Stub mBinder = new MInterface.Stub() {

public void invokTest() throws RemoteException {

Log.e(TAG, "remote call from client! current thread id = "
+ Thread.currentThread().getId());
}
};
}

注意onBind()函数,返回了mBinder,而mBinder实现了mInterface.Stub,实现了mInterface接口,执行了打印log的操作。
 
整个交互流程如下:
1.客户端通过绑定服务,获取了服务的句柄(本地代理对象);
2.客户端执行 onClick(),调用本地代理对象的 invokTest() 函数,本地代理对象调用 mRemote.transact() 发出远程调用请求(见mInterface.java);
3.服务端响应 onTransact() 执行 this.invokTest(),并将执行结果返回;
 
由于客户端只和本地代理对象即服务句柄通信,由代理对象进行真正的 IPC 操作,所以对客户端来说,IPC过程是透明的,调用远程操作如同调用本地操作一样。在客户端调用 transact() 时,会将服务描述 DSCRIPTION 写入到 data 里,在客户端 onTransact 时会验证,如果两个不一样,则不能通信。而 DSCRIPTION 是根据 mInterface 包名和接口名自动生成的,这就是为什么两个工程里的 mInterface.aidl 要在同一个包的原因。
 
在这个过程中,mInterface.aidl 起到了桥梁的作用,规定统一了客户端和服务端的通信接口,使得客户端和服务端得以成功的通信。
具体的通信 transact 和 onTransact 的过程也就是利用 Binder 驱动通信的过程,在这里就不多叙述。


最后补上两个工程的 AndroidManifest.xml,其中客户端的没有什么不同,只是服务端的 Service 配置要注意。

<service android:name="com.xjl.service.service.MAIDLService" >

<intent-filter>
<action android:name="com.xjl.aidl.service" />
</intent-filter>
</service>

【运行结果】

TAG
Text
点击 ok 按钮:

@@@AIDLService
----------service created----------
@@@AIDLService
----------service on bind----------
@@@MainActivity
connect service
点击 Cancle 按钮:

@@@AIDLService
----------service on unbind----------
@@@AIDLService
----------service on destroy----------
点击 CallBack 按钮:

@@@MainActivity
current Thread id = 1
@@@AIDLService
remote call from client! current thread id = 297

原文地址:https://www.cnblogs.com/zx-blog/p/11835701.html