android绑定Service(含IPC)
来源:互联网 发布:首席体验官 知乎 编辑:程序博客网 时间:2024/06/06 13:14
前言:四大组件中的service是其中除了activity之外用得最多的可能就是它了,当然,其他两个组件有它们自己的应用场合,这个在每个应用中使用情况可能不同,需要根据应用的需要选择使用相应的组件来完成任务。这篇文章将介绍如何绑定一个服务Service,使得客户端和Service进行通讯。
一、绑定Service
绑定一个服务,首先需要定义一个类继承系统的Service基类,然后必须重写onBind()
方法并返回一个IBinder对象给客户端使用,这个返回的IBinder对象作为这个Service的代理对象,通过它可以使得客户端和这个服务进行通讯。通常,多个不同的客户端可以同时绑定这服务,但是需要注意的是onBind()
方法并不会在每次客户端绑定服务的时候都会调用,它只会在第一个客户端绑定这个服务的时候调用,所以,如果是多个客户端绑定相同的服务,那么只会有一个相同的IBinder对象。因此,绑定服务关键在于定义一个自己的IBinder对象,然后在onBind()
方法中返回给客户端使用。下面我们就做关键的一步,创建IBinder对象。
有以下三种方式可以得到IBinder对象:
- 继承Binder类:这种方式适用于你的服务只有你自己的应用或者和你的这app在相同的进程里面的客户端使用,如果要让不同进程的应用使用你的服务,那么这种方式就行不通了。
- 使用messenger:这种方式适用于你的服务需要在不同的进程间通讯(IPC),但这种方式有一个缺点就是所有处理请求(消息)都是在单一的线程中(没必要再将你的service设置成线程安全的了),也就是说不能处理多线程并发的情况。
- 使用AIDL:使用AIDL实现绑定服务就是为了解决上面两种情况的弊端,一个是service不能在进程间通讯,另一个就是service不能处理多线程的情况,但是,你的应用除非必须要使用AIDL,否则上面两种可以满足需求的情况下就没必要使用AIDL(消耗系统资源)。
1. 继承Binder类
要绑定一个服务(和服务通讯),那么客户端需要得到service的一个实例(引用)或者一个接口,我们知道在客户端使用了bindService()
绑定了一个服务后系统会调用该服务的onBind()
方法(第一次绑定该服务)返回一个IBinder对象给客户端,那么我们可以通过这个IBinder对象(一种通讯接口)和service进行交互(通讯)。通常我们在自定义的Binder类中定义一些public方法,这样,客户端可以通过这个方法获取service的实例或者做其他的一些事情,下面,我们通过一个例子来理解这种方法绑定服务。
LocalService.java
package com.example.lt.boundservice;import android.app.Service;import android.content.Intent;import android.os.Binder;import android.os.IBinder;import android.support.annotation.Nullable;public class LocalService extends Service{ private String[] names = {"吕布","赵子龙","关羽"}; private IBinder myBinder = new MyBinder(); @Nullable @Override public IBinder onBind(Intent intent) { return myBinder; } public class MyBinder extends Binder{ public LocalService getService(){ return LocalService.this; } } public String getName(int postion){ return names[postion]; }}
客户端端绑定服务:
package com.example.lt.boundservice;import android.content.ComponentName;import android.content.Context;import android.content.Intent;import android.content.ServiceConnection;import android.os.Bundle;import android.os.IBinder;import android.support.design.widget.FloatingActionButton;import android.support.design.widget.Snackbar;import android.support.v7.app.AppCompatActivity;import android.support.v7.widget.Toolbar;import android.view.View;import android.view.Menu;import android.view.MenuItem;import android.widget.TextView;public class MainActivity extends AppCompatActivity { private LocalService localService; private TextView textView; private boolean mBound; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = (TextView) findViewById(R.id.textView); } public void getName(View view){ if(mBound) { textView.setText(localService.getName(1)); } } @Override protected void onStart() { super.onStart(); // 绑定服务 Intent intent = new Intent(MainActivity.this,LocalService.class); mBound = bindService(intent, conn, Context.BIND_AUTO_CREATE); } ServiceConnection conn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { // 这里的service就是onBind返回的那个IBinder对象 System.out.println("ComponentName:"+name); LocalService.MyBinder myBinder = (LocalService.MyBinder) service; localService = myBinder.getService(); } @Override public void onServiceDisconnected(ComponentName name) { System.out.println("onServiceDisconnected"); System.out.println("ComponentName:"+name); } }; @Override protected void onStop() { super.onStop(); unbindService(conn); }}
这里需要注意的是绑定服务可能失败,所以要在确认绑定服务成功的情况下使用service,这种通过绑定启动一个服务的方式会将service和客户端(这里是activty)绑定在一起,所以需要在合适的时间绑定和取消绑定service(管理service的生命周期)。
2. 使用Messenger
如果你的service需要在不同的进程间通讯,那么你可以使用Messenger去提供一个接口给你的service,使得你的service可以在不同的进程间通讯(IPC)。
通常,可以分为如下几个步骤来使用Messenger绑定服务:
- 在你的service里面提供一个Handler,这个Handler用来处理客户端发送过来的消息(响应);
- 创建这个Handler的对象,并用这个Handler对象创建一个Messenger对象(这个Messenger对象持有handler的引用);
- 在onBind()方法中返回这个Messenger对象创建的IBinder对象;
- 客户端绑定服务得到返回的IBinder对象,并用这个对象在客户端初始化Messenger对象,这个对象持有service的Handler对象的引用,这样客户端通过这个Messenger对象发送消息给service。
按照上面的步骤,咋们来写个测试代码来实践一下。由于是测试在进程间进行通讯,所以,我们需要创建两个项目(应用),一个包含那个远程服务,一个用来访问那个远程服务。
创建两个项目(应用),一个叫RemoteService,一个叫RemoteClient
(1)在RemoteService项目中创建远程服务RemoteService
package com.example.lt.messengerdemo;import android.app.Service;import android.content.Intent;import android.os.Handler;import android.os.IBinder;import android.os.Message;import android.os.Messenger;import android.support.annotation.Nullable;import android.widget.Toast;public class RemoteService extends Service{ private static final int SAY_HELLO = 0; @Nullable @Override public IBinder onBind(Intent intent) { // 3. 通过Messenger对象返回一个IBinder对象 return mMessenger.getBinder(); } /** * 1. 创建一个Handler用来接收,处理客户端的消息 */ final Handler mHandler = new Handler(){ @Override public void handleMessage(Message msg) { switch (msg.what){ case SAY_HELLO: Toast.makeText(RemoteService.this,"hello,I am RemoteService",Toast.LENGTH_SHORT).show(); break; default: super.handleMessage(msg); } } }; /** * 2. 通过这个Handler创建一个Messenger对象 */ final Messenger mMessenger = new Messenger(mHandler);}
在清单文件中注册这个服务,并给一个action,方便客户端通过这个action来绑定这个服务:
<service android:name="com.example.lt.messengerdemo.RemoteService"> <intent-filter> <action android:name="com.example.lt.messengerdemo.RemoteService"> </action> </intent-filter></service>
可以看到,在这一步中完成了三面的三个步骤,接下来,我们需要在客户端来绑定这个远程服务。
(2)远程客户端绑定服务
package com.example.lt.remoteclient;import android.content.ComponentName;import android.content.Context;import android.content.Intent;import android.content.ServiceConnection;import android.os.Bundle;import android.os.IBinder;import android.os.Message;import android.os.Messenger;import android.os.RemoteException;import android.support.design.widget.FloatingActionButton;import android.support.design.widget.Snackbar;import android.support.v7.app.AppCompatActivity;import android.support.v7.widget.Toolbar;import android.view.View;import android.view.Menu;import android.view.MenuItem;import android.widget.Toast;public class MainActivity extends AppCompatActivity { private Messenger mMessenger; private boolean mBound; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } @Override protected void onStart() { super.onStart(); Intent intent = new Intent(); intent.setAction("com.example.lt.messengerdemo.RemoteService"); intent.setPackage("com.example.lt.messengerdemo"); mBound = bindService(intent, conn, Context.BIND_AUTO_CREATE); } ServiceConnection conn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { mMessenger = new Messenger(service); } @Override public void onServiceDisconnected(ComponentName name) { } }; @Override protected void onStop() { super.onStop(); unbindService(conn); } public void hello(View view) throws RemoteException { if(mBound) { Message message = Message.obtain(); message.what = 0; mMessenger.send(message); }else{ Toast.makeText(MainActivity.this,"还没接通呢?",Toast.LENGTH_SHORT).show(); } }}
这里先在onStart()
方法中绑定远程服务,然后在绑定服务成功后的那个回调方法onServiceConnected
中通过得到的IBinder对象创建Messenger对象,然后用户点击hello按钮后向远程服务发送一个消息(调用hello()
方法),最后在onStop()
方法中取消绑定服务。
注意:android5.0后对service的隐式启动做了一些限制,隐式启动需要设置action和package(service所在的那个应用的包名)。
客户端布局文件:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:orientation="horizontal" android:gravity="center" android:layout_height="match_parent" > <Button android:onClick="hello" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="hello" /></LinearLayout>
这里就不贴出测试结果的动态图了,直接说个测试结果吧,测试结果是我们点击hello按钮后,系统弹出一个“hello,I am RemoteService”吐司,看到这个结果,我们就知道远程服务响应了客户端的请求(处理了接收到的消息)。
二、管理Service的生命周期
我们知道,客户端可以通过bindService()
绑定Service和startService()
启动服务一个非常明显的区别就是前者会将Service的生命周期和客户端生命周期绑定在一起(通常是activity),后者Service的生命周期由这个服务自己管理,所以当我们绑定Service来启动服务的时候,我们需要在合适的时候(不需要和Service通讯)绑定和解除绑定服务,比如:当我们Activity在不可见的时候不需要服务(通讯),可见的时候需要服务(通讯),那么,我们就可以在Activity的onStart()
中绑定服务,在onStop()
中解除绑定;当我们需要Service一直运行在后台,直到Activity被销毁,那么,我们就可以在onCreate()
和onDestory()
中分别绑定和解除绑定服务,但通常不会再onResume()
和onPause()
中绑定和解除绑定服务,下面我们看一张图来理解Service生命周期:
这是android官网的一一张图片,这里这个说明,当客户端通过startService()
方式启动服务的时候,系统会调用服务的onStartCommand()
方法(我们一般在这个方法里面做任务)而不会调用onBind()
方法;当客户端通过bindService()
方法启动服务的时候,系统会调用onBind()
方法将Service和客户端绑定在一起,而不会调用onStartCommand
方法。
到这里,绑定一个服务的前2种方式基本介绍完了,关于AIDL绑定服务,大家可以参考:android使用AIDL实现跨进程通讯(IPC)。
总结:
绑定服务可以有很多种方式,具体通过什么方式,这里有一个原则,如果service只服务于自己的这个应用或者在同一进程(通常不同应用在不同的进程中)中共享,那么通过第一种方式就行了;如果你应用的service需要向远程客户端提供服务,但不需要在这个service中处理多线程并发,那么可以选择messenger方式绑定服务;如果你的service既要在不同的应用(进程)中通讯(IPC),又要在service中处理多线程,那么可以考虑使用AIDL绑定服务。不过,到这里,我好奇的是Messenger绑定远程服务实现IPC是否能够像AIDL一样传递数据(包括自定义类型)呢?还是只能跨进程传递消息?求解。
- android绑定Service(含IPC)
- Android:远程服务Service(含AIDL & IPC讲解)
- Android:远程服务Service(含AIDL & IPC讲解)
- Android IPC机制(一)——绑定Service实现本地通信
- Android IPC 系列(1):Service
- Android IPC Binder,Service,Service manager
- Android基础——Service IPC通讯(Messenger实现)
- Android中的Service与进程间通信(IPC)详解
- android IPC binder 的(service 和 client)简单应用
- Android中的Service与进程间通信(IPC)详解
- Android -- 跨应用绑定service(AIDL)
- Android---服务(Service)的绑定服务
- Android:Service非绑定
- android service-绑定
- android service绑定
- Android Service绑定
- Android Service(绑定BindService)
- Android service基础绑定一点通(绑定方法说明)
- Python核心概念之数据模型和执行模型
- 剑指offer——反转链表
- safari 插件(如Xmarks)的设置、登陆、禁用等
- 用java写的远程监控程序
- 小晴天老师系列--我有一个数列!(暴力)
- android绑定Service(含IPC)
- 从Java到Groovy——Differences with Java
- 我参与的一个项目的继续总结:经验篇
- CentOS7安装Oracle_jdk7
- hdu2045不容易系列之(3)—— LELE的RPG难题
- 快速入门lua脚本语言
- CodeForce 570C Replacement (暴力)
- 编译PHP的正解
- JavaBean网页电子时钟