Android:远程 Service 和 AIDL 跨进程通信
远程 Service
Service 是运行在主线程里的,如果直接在 Service 中处理一些耗时的逻辑,就会导致程序 ANR。可以在 Service 中开启线程去执行耗时任务,也可以索性将原来的 Service 转换成一个远程 Service。
将 Service 转换成远程 Service 只需要在注册 Service 的时候,在 AndroidManifest.xml 文件中将 android:process 属性指定成“:remote”。
<service android:name=".MyService"
android:process=":remote">
</service>
AIDL 跨进程通信
此时 Bind Service 让 Activity 和 Service 建立关联时,由于 Activity 和 Service 运行在两个不同的进程当中,不能再使用传统方式建立关联。
可以使用 AIDL(Android Interface Definition Language)Android 接口定义语言来进行跨进程通信(IPC),它可以让某个 Service 与多个应用程序组件之间进行跨进程通信,帮助 IPC 之间接口的建立,从而实现多个应用程序共享同一个 Service 的功能。使用的步骤如下:
- 定义 AIDL 接口,为 Service 建立接口 IService;
- Client 连接 Service 获得 Stub 对象;
- 在 IService 中具体实现 IService.Stub;
- 直接调用 IService.Stub 里面的方法。
客户端调用远程 Service 时,Android 不是直接返回 Service 对象到客户端,而是通过 onBind() 方法把 Service 的代理对象 IBinder 发送给客户端,所以 AIDL 远程接口的实现类就是 IBinder 的实现类。
远程 Service 样例
程序需求
基于 Service 组件技术,编程实现一个可以完成计算任意的两数的加、减、乘运算的远程服务。
功能设计
首先需要新建一个 aidl,里面写一个接口作为我们要提供的服务,接着写一个该接口的实现类。最后完成 Activity,实现对远程服务的绑定,并且测试提供的加减乘的操作。
代码编写
IMyAidlInterface.aidl
// IMyAidlInterface.aidl
package com.example.test5;
// Declare any non-default types here with import statements
interface IMyAidlInterface {
double add(double a, double b);
double subtract(double a, double b);
double multiply(double a, double b);
}
注意我们是把接口写在 .aidl 文件中,此时如果是 eclipse 可以自动生成 IService 文件,但是如果是用 Android studio 的话就需要写完代码后建立一下:
MyService.java
package com.example.test5;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
public class MyService extends Service {
//定义内部类MyServiceImpl继承AIDL文件自动生成的内部类,并且实现定义的接口方法
private class MyServiceImpl extends IMyAidlInterface.Stub{
@Override
public double add(double a, double b) throws RemoteException {
// TODO Auto-generated method stub
return a + b;
}
@Override
public double subtract(double a, double b) throws RemoteException {
// TODO Auto-generated method stub
return a - b;
}
@Override
public double multiply(double a, double b) throws RemoteException {
// TODO Auto-generated method stub
return a * b;
}
}
@Override
public IBinder onBind(Intent arg0) {
return new MyServiceImpl(); //返回AIDL实现
}
@Override
public void onDestroy(){
super.onDestroy();
}
}
MainActivity
注意不能在没有绑定服务的时候解绑服务,否则调用方法会导致系统崩溃。此时应该设置一个信号量来确定状态,只有当服务绑定了才可以解绑。
package com.example.test5;
import android.app.Activity;
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.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
public class MainActivity extends Activity {
static final String TAG = "IMyAidlInterface";
IMyAidlInterface iService = null;
EditText num1Text, num2Text;
TextView textView;
double num1, num2;
boolean flag = false; //信号量,防止在没有绑定服务的情况下解绑
private ServiceConnection conn = new ServiceConnection(){
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 返回AIDL接口对象,然后可以调用AIDL方法
iService = IMyAidlInterface.Stub.asInterface(service);
textView.setText("绑定 Service 成功!");
flag = true; //状态设置为已绑定服务
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.i(TAG, "释放Service");
}
};
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//定位xml中的2个操作数和输出文本的位置
num1Text = (EditText) findViewById(R.id.etnum1);
num2Text = (EditText) findViewById(R.id.etnum2);
textView = (TextView) findViewById(R.id.textView);
}
public void doClick(View v) {
double result = 0.0; //运算结果
switch (v.getId()) {
// 绑定服务
case R.id.button1:
if(!flag){
Intent bindIntent = new Intent(this, MyService.class);
startService(bindIntent);
bindService(bindIntent, conn, BIND_AUTO_CREATE);
}
break;
// 解绑服务
case R.id.button5:
if(flag) {
unbindService(conn);
textView.setText("解除绑定 Service 成功!");
flag = false;
}
break;
// 运行服务提供的操作
default:
if(getTwoNum()){
try {
switch (v.getId()) {
case R.id.button2:
result = iService.add(num1, num2);
break;
case R.id.button3:
result = iService.subtract(num1, num2);
break;
case R.id.button4:
result = iService.multiply(num1, num2);
break;
}
}
catch (RemoteException e) {
Log.e(TAG,"调用出错!");
e.printStackTrace();
}
textView.setText("" + result);
}
else{
// 提示用户正确输入
textView.setText("非法输入,请重新输入!");
}
break;
}
}
public boolean getTwoNum(){
try{
//获取两个操作数
num1 = Double.parseDouble(num1Text.getText().toString());
num2 = Double.parseDouble(num2Text.getText().toString());
}catch (Exception e) {
//数据非法或者没输入,返回false
return false;
}
return true;
}
}
Activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<TextView
android:id="@+id/tvnum1"
android:textSize="20dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="操作数1:"
android:layout_weight="1"/>
<EditText
android:id="@+id/etnum1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="@+id/tvhigh"
android:layout_alignParentRight="true"
android:hint="请输入数字"
android:gravity="center"
android:layout_weight="2"/>
</LinearLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<TextView
android:id="@+id/tvnum2"
android:textSize="20dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignRight="@+id/tvhigh"
android:layout_below="@+id/tvhigh"
android:text="操作数2:"
android:layout_weight="1"/>
<EditText
android:id="@+id/etnum2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="@+id/tvweight"
android:layout_alignLeft="@+id/ethigh"
android:hint="请输入数字"
android:gravity="center"
android:layout_weight="2"/>
</LinearLayout>
<Button
android:id="@+id/button1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Bind Service"
android:textSize="20dp"
android:onClick="doClick" />
<Button
android:id="@+id/button2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="add"
android:textSize="20dp"
android:onClick="doClick"/>
<Button
android:id="@+id/button3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="subtract"
android:textSize="20dp"
android:onClick="doClick"/>
<Button
android:id="@+id/button4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="multiply"
android:textSize="20dp"
android:onClick="doClick"/>
<Button
android:id="@+id/button5"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Unbind Service"
android:textSize="20dp"
android:onClick="doClick"/>
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="20sp"
android:gravity="center"
android:text="result" />
</LinearLayout>
运行效果
绑定服务。
非法输入。
加减乘操作。
解除绑定。
参考资料
《Android 移动应用开发》,杨谊 主编、喻德旷 副主编,人民邮电出版社