目录

  • 远程 Service
  • AIDL 跨进程通信
  • 远程 Service 样例
  • 程序需求
  • 功能设计
  • 代码编写
  • IMyAidlInterface.aidl
  • MyService.java
  • MainActivity
  • Activity_main.xml
  • 运行效果
  • 参考资料

远程 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 的功能。使用的步骤如下:

  1. 定义 AIDL 接口,为 Service 建立接口 IService;
  2. Client 连接 Service 获得 Stub 对象;
  3. 在 IService 中具体实现 IService.Stub;
  4. 直接调用 IService.Stub 里面的方法。

android 向service启动和生命周期 android service aidl_ide


客户端调用远程 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 的话就需要写完代码后建立一下:

android 向service启动和生命周期 android service aidl_android_02

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 向service启动和生命周期 android service aidl_xml_03


非法输入。

android 向service启动和生命周期 android service aidl_android_04


android 向service启动和生命周期 android service aidl_ide_05


加减乘操作。

android 向service启动和生命周期 android service aidl_ide_06


android 向service启动和生命周期 android service aidl_android_07


android 向service启动和生命周期 android service aidl_xml_08


解除绑定。

android 向service启动和生命周期 android service aidl_xml_09

参考资料

《Android 移动应用开发》,杨谊 主编、喻德旷 副主编,人民邮电出版社