Android中的Service与进程间通信(IPC)详解

Service

什么是Service

在后台长期运行的没有界面的组件。其他组件可以启动Service让他在后台运行,或者绑定Service与它进行交互,甚至实现进程间通信(IPC)。例如,可以让服务在后台处理网络交互,播放音乐,文件I/O,或者与ContentProvider交互。

创建一个Service

  1. 新建一个类,继承Service,重写相关方法,如onBind,onUnBind,onCreate,onDestorey。
  2. 在AndroidManifest.xml中配置Service和相关权限。
<manifest ... >
  ...
  <application ... >
      <service android:name=".MyService" />
      ...
  </application>
</manifest>

开启服务:

Intent service = new Intent(this,Service.class);
startService(service);

停止服务:

Intent service = new Intent(this,Service.class);
stopService(service);

绑定服务:

private boolean mIsBound = false;
private ServiceConnection mConnection = new ServiceConnection() {
       // 服务连接成功回调
       @Override
       public void onServiceConnected(ComponentName name, IBinder service) {
           MyService.MyBinder binder = (MyService.MyBinder) service;

       }
       // 服务失去连接回调
       @Override
       public void onServiceDisconnected(ComponentName name) {

       }
   };

@Event(value = R.id.btn_bind_service)
private void onBindServiceClick(View view) {
    bindService(getServiceIntent(), mConnection, Context.BIND_AUTO_CREATE);// 绑定时如果没有创建Service则自动创建Service。
    mIsBound = true;
}

解绑服务:

@Event(value = R.id.btn_unbind_service)
private void onUnbindServiceClick(View view) {
    if (!mIsBound) {
        ToastUtil.show("未绑定服务");
        return;
    }
    try {
        unbindService(mConnection);//注意:ServiceConnection要传绑定时的ServiceConnection对象,否则会报错。
    } catch (Exception e) {
        ToastUtil.show("解除綁定服务失败");
        e.printStackTrace();
    }
    mIsBound = false;
}

Service的生命周期

public class MyService extends Service {

    // 服务创建
    @Override
    public void onCreate() {
        super.onCreate();
    }

    // 每次startService都会调用;通过bindService方式启动服务,该方法不会被调用
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }

    // 服务销毁
    @Override
    public void onDestroy() {
        super.onDestroy();
    }

    // bindService时调用,返回一个IBinder对象,用于与Service交互,IBinder就是Service的代理
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    // unbindService时调用
    @Override
    public boolean onUnbind(Intent intent) {
        return super.onUnbind(intent);
    }
}

startService:onCreate——>onStartCommand

stopService:onDestory

注意:服务只会被创建一次,如果服务已经创建,并且没有销毁,多次调用startService方法,只会执行onStartCommand方法。

bindService:onCreate——>onBind

unbindService:onUnbind——>onDestory

注意:

  • 如果多次bindService,onBind方法只会在第一次绑定时被调用;同样,多次startService,onCreate方法也只会在第一次创建时被调用;
  • 服务只能被解绑一次,服务需要先绑定才能解除绑定,多次解绑会报错。
  • 通过bindService方式启动的Service,在调用unbindService时就会自动销毁。
  • 服务只会停止一次,多次调用stopService()的方法无效,但不报错。
  • 每次调用startService()开启服务都会执行onStartCommand()方法。

如何调用Service里的方法

由于系统框架在创建服务的时候会创建与之对应的上下文,直接new出来的服务对象是没有上下文的,所以直接new服务对象调用其方法会报异常。

与Service之间交互都是通过其代理人(IBinder)来间接调用Service里的方法的。

这样设计主要出于安全考虑,有限的暴露出一些方法,而不是直接返回服务对象,因为服务对象里可能有一些成员变量和方法不允许外界直接访问,需要保护起来。

一般IBinder(代理人)也应该设计成私有的,因为是IBinder中的一些数据也需要保护起来,只需要暴露出一些指定的方法,那么外界如何引用IBinder对象呢?通过接口引用代理人,在接口定义供外界调用的方法,让IBinder类实现该接口。

bindService与startService

bindService与startService的区别:

  • 绑定服务:可以间接调用服务里面的方法;如果绑定的Activity被销毁了,服务也会跟着销毁。
  • 开启服务:不可以调用服务里面的方法;如果开启服务的Activity销毁,服务还可以长期的在后台运行。

既要保证服务长期在后台运行,又想去调用服务里面的方法。

步骤:

1. startService(),保证服务在后台长期的运行;

2. bindService(),获取中间人(IBinder对象),间接的调用服务里面的方法;

这时,解绑服务并不会导致服务销毁,服务可长期在后台运行。

注意:如果服务已经被绑定,直接调用stopService()是停不掉的,必须先解除绑定服务再调stopService(),服务才会被销毁。

示例代码

@ContentView(value = R.layout.activity_main)
public class MainActivity extends AppCompatActivity {

    private boolean mIsBound = false;
    private ServiceConnection mConnection = new ServiceConnection() {
        // 服务连接成功回调
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            LogUtil.d(name + " onServiceConnected");
            PayService.PayBinder binder = (PayService.PayBinder) service;
            binder.pay(100);
        }
        // 服务失去连接回调
        @Override
        public void onServiceDisconnected(ComponentName name) {
            LogUtil.d(name + " onServiceDisconnected");
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        x.view().inject(this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //当Activity被销毁时解绑服务,因为如果已经绑定服务不显式解绑会报异常。
        onUnbindServiceClick(null);
    }

    private Intent getServiceIntent() {
        return new Intent(this, PayService.class);
    }

    // 启动服务
    @Event(value = R.id.btn_start_service)
    private void onStartServiceClick(View view) {
        startService(getServiceIntent());
    }

    // 绑定服务
    @Event(value = R.id.btn_bind_service)
    private void onBindServiceClick(View view) {
        bindService(getServiceIntent(), mConnection, Context.BIND_AUTO_CREATE);// 绑定时如果没有创建服务则自动创建Service。
        mIsBound = true;
    }

    // 解绑服务
    @Event(value = R.id.btn_unbind_service)
    private void onUnbindServiceClick(View view) {
        if (!mIsBound) {
            ToastUtil.show("未绑定服务");
            return;
        }
        try {
            unbindService(mConnection);//注意:ServiceConnection要传绑定时的ServiceConnection对象,否则会报错。
        } catch (Exception e) {
            e.printStackTrace();
        }
        mIsBound = false;
    }

    // 停止服务
    @Event(value = R.id.btn_stop_service)
    private void onStopServiceClick(View view) {
        stopService(getServiceIntent());
    }

}

参考文档:

https://developer.android.com/reference/android/app/Service.html

https://developer.android.com/guide/components/services.html

使用AIDL实现进程间通信

AIDL(Android Interface Definition Language)用于进程间通信接口的定义,是一种进程间通讯的规范 。

Service端:

1.New一个aidl文件在src目录下

2.在aidl文件中定义Service中对外开放的接口

// IPayService.aidl
package linchaolong.android.aidldemo.service;

// Declare any non-default types here with import statements

/**
 * AIDL Demo
 *
 * Created by linchaolong on 2016/4/22.
 */
interface IPayService {

    void pay(int price);

    void startTimer();

    void stopTimer();

    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     *
     * 翻译:
     *
     * 展示一些可以在AIDL中用作参数和返回值的基本类型。
     *
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);
}

aidl语言中没有权限修饰符,因为进程间通信接口权限肯定是public的。

3.aidl编写完成后,make一下工程,在build目录下就会生成该aidl文件对应的java文件,比如我这是IPayService.java。

4.在IPayService中有一个Stub静态类,继承了Binder和实现了IPayService接口,定义一个Binder类继承IPayService.Stub并实现相关接口。

    @Override
    public IBinder onBind(Intent intent) {
        if (mBinder == null) {
            mBinder = new PayBinder();
        }
        return mBinder; // 其他应用绑定服务时返回binder对象
    }

    // Binder
    public class PayBinder extends IPayService.Stub {

        public void pay(int price) {
            PayService.this.pay(price);
        }

        public void startTimer() {
            PayService.this.startTimer();
        }

        public void stopTimer() throws RemoteException {
            PayService.this.stopTimer();
        }

        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
            // Does nothing
        }
    }

5.在AndroidManifest.xml配置Service

<service
    android:name=".service.PayService"
    android:enabled="true"
    android:exported="true">
    <intent-filter>
        <action android:name="linchaolong.android.aidldemo.service.PayService" />
    </intent-filter>
</service>

到这里Service端就完成了,其他应用需要调用该Service只需要把aidl文件拷贝到自己工程的src目录下(make一下),并绑定服务即可得到IBinder对象,通过IBinder对象可以实现与Service的交互。

调用示例:

在onServiceConnected回调里,调用YourServiceInterface.Stub.asInterface(service)把IBinder对象转换为YourServiceInterface类型。

    private IPayService iPayService;

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            // 获取远程接口实例
            iPayService = IPayService.Stub.asInterface(service);
        }
        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.e(TAG, "Service has unexpectedly disconnected");
            iPayService = null;
        }
    };

绑定远程服务并调用IPC方法

    /**
     * 判断是否已经绑定远程服务
     *
     * @return
     */
    private boolean isBinded() {
        return mIsBound && iPayService != null;
    }

    private Intent getServiceIntent() {
        return new Intent("linchaolong.android.aidldemo.service.PayService");
    }

    // 绑定远程服务
    @Event(value = R.id.btn_bind_remote_service)
    private void bindRemoteService(View view) {
        if (isBinded()) {
            showToast("已绑定远程服务");
            return;
        }
        bindService(getServiceIntent(), mConnection, Context.BIND_AUTO_CREATE);
        mIsBound = true;
    }

    // 调用远程服务方法
    @Event(value = R.id.btn_call_service_pay)
    private void callServicePay(View view) {
        if (!isBinded()) {
            showToast("未绑定远程服务");
            return;
        }
        try {
            // 通过IBinder对象调用远程服务中方法
            iPayService.pay(100);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

Demo地址:https://coding.net/u/linchaolong/p/AIDLDemo/git

参考文档:

http://developer.android.com/guide/components/aidl.html

时间: 2024-10-02 00:03:29

Android中的Service与进程间通信(IPC)详解的相关文章

Android 中各种权限深入体验及详解

Android 中各种权限深入体验及详解 分类: Android2012-07-15 19:27 2822人阅读 评论(0) 收藏 举报 androidpermissionsinstallersystemserviceinteger 一. 权限(permission) 权限用来描述是否拥有做某件事的权力.Android系统中权限分为普通级别(Normal),危险级别(dangerous),签名级别(signature)和系统/签名级别(signature or system).系统中所有预定义的

Android中自定义View、ViewGroup理论基础详解

Android自身提供了许多widgets,但是有时候这些widgets并不能满足我们的需求,这时我们就需要自定义View,本文会详细说明自定义View的各种理论基础,只有理解了这些知识,我们才能更好地实现各种功能的控件. 我觉得自定义View中最重要的部分就是绘图和交互,自定义的绘图使得你的View与众不同,交互使用户可以与你的View进行交互,而绘图的前提是View的量算与布局,交互的基础是触摸事件,所以量算.布局.绘图.触摸事件这些是自定义View的核心. 除此之外,一个设计友好的自定义V

Android中Serializable和Parcelable序列化对象详解

学习内容: 1.序列化的目的 2.Android中序列化的两种方式 3.Parcelable与Serializable的性能比较 4.Android中如何使用Parcelable进行序列化操作 5.Parcelable的工作原理 6.相关实例 1.序列化的目的 (1).永久的保存对象数据(将对象数据保存在文件当中,或者是磁盘中 (2).通过序列化操作将对象数据在网络上进行传输(由于网络传输是以字节流的方式对数据进行传输的.因此序列化的目的是将对象数据转换成字节流的形式) (3).将对象数据在进程

[转]Android中attrs.xml文件的使用详解

转自:http://blog.csdn.net/jiangwei0910410003/article/details/17006087 Android中在values中定义一个attrs.xml,然后自己定义一个组件MyView attrs.xml内容如下: [html] view plaincopy <?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleabl

android中xml设置Animation动画效果详解

在 android 中, Animation 动画效果的实现可以通过两种方式进行实现,一种是 tweened animation 渐变动画,另一种是 frame by frame animation 画面转换动画. tweened animation 渐变动画有以下两种类型: 1.alpha 渐变透明度动画效果 2.scale 渐变尺寸伸缩动画效果 frame by frame animation 画面转换动画有以下两种类型: 1.translate 画面转换位置移动动画效果 2.rotate

android中常见的命名及其特点详解

Paseal命名法 Paseal命名法特点:String MyName-DelphiInt MyAge每个单词首字母大写 Camel命名法 Camel(驼峰的意思)命名法特点:String myName-java首字母小写,其他每一个首字母大写它常用在java命名 匈牙利命名法 匈牙利命名法特点:String strMyName-VB6 用一个前缀来标定一些东西,可以标定它的类型也可以标定它的特征:int intMyNameString sMyNameHWND这种命名方法在老的VB中使用 下划线

Android(java)学习笔记137:Android中SimpleAdapter,ArrayAdapter和BaseAdapter详解

1.SimpleAdapter(很多时候需要继承它,来自定义adapter): simpleAdapter的扩展性最好,可以定义各种各样的布局出来,可以放上ImageView(图片)等.可以显示比较复杂的列表,包括每行显示图片.文字等,但不能对列表进行后期加工(在java代码中加工),也是只是单纯的负责显示 public class ImageListView extends ListActivity { @Override public void onCreate(Bundle savedIn

Android 中的 Service 全面总结详解【下】

上一篇文章Android 中的 Service 全面总结详解[下] 介绍了Service的一些知识以及本地Service的使用,如果对Service还不太了解的建议先看下上篇文章:本文主要接着上一篇讲下远程服务的使用: 在说到远程服务的时候,我们需要先了解一些预备的知识: 首先来了解一下AIDL机制: AIDL的作用 由于每个应用程序都运行在自己的进程空间,并且可以从应用程序UI运行另一个服务进程,而且经常会在不同的进程间传递对象.在Android平台,一个进程通常不能访问另一个进程的内存空间,

(转载)Android中的Service:Binder,Messenger,AIDL(2)

前言 前面一篇博文介绍了关于Service的一些基本知识,包括service是什么,怎么创建一个service,创建了一个service之后如何启动它等等.在这一篇博文里有一些需要前一篇铺垫的东西,建议没有看过前一篇博文的同学先去看一下前一篇: Android中的Service:默默的奉献者 (1) . 但是在前一篇博文中也有一些遗漏的东西--主要是关于bindService()这一块的具体细节.由于这一块涉及的东西还是比较多,所以在这里单独提出来了.闲话不多说,进入正文. 1.bindServ