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();
}
// 服务失去连接回调
@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();
} catch (RemoteException e) {
e.printStackTrace();
}
}

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

参考文档: 
http://developer.android.com/guide/components/aidl.html

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

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

    Android 中各种权限深入体验及详解 分类: Android2012-07-15 19:27 2822人阅读 评论(0) 收藏 举报 androidpermissionsinstallersyst ...

  2. Android中xml设置Animation动画效果详解

    在 Android 中, Animation 动画效果的实现可以通过两种方式进行实现,一种是 tweened animation 渐变动画,另一种是 frame by frame animation ...

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

    学习内容: 1.序列化的目的 2.Android中序列化的两种方式 3.Parcelable与Serializable的性能比较 4.Android中如何使用Parcelable进行序列化操作 5.P ...

  4. Android中attrs.xml文件的使用详解

    $*********************************************************************************************$ 博主推荐 ...

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

    Paseal命名法 Paseal命名法特点:String MyName-DelphiInt MyAge每个单词首字母大写 Camel命名法 Camel(驼峰的意思)命名法特点:String myNam ...

  6. Android中的ImageView的scaleType属性详解

    ImageView的Scaletype决定了图片在View上显示时的样子,如进行何种比例的缩放,及显示图片的整体还是部分,等等. 设置的方式包括: 1. 在layout xml中定义android:s ...

  7. Android中的Coroutine协程原理详解

    前言 协程是一个并发方案.也是一种思想. 传统意义上的协程是单线程的,面对io密集型任务他的内存消耗更少,进而效率高.但是面对计算密集型的任务不如多线程并行运算效率高. 不同的语言对于协程都有不同的实 ...

  8. Android中自定义属性attr.xml的格式详解

    1. reference:参考某一资源ID.     (1)属性定义:             <declare-styleable name = "名称">      ...

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

    前言 前面一篇博文介绍了关于Service的一些基本知识,包括service是什么,怎么创建一个service,创建了一个service之后如何启动它等等.在这一篇博文里有一些需要前一篇铺垫的东西,建 ...

随机推荐

  1. 为什么有些网站PING不通但又能访问.

    一PING它,出现request timed out,发关4个送数据包,返回0个.丢失率100%,明明不通但为什么又能够访问? 应该是该网站禁用了ICMP回应或者开启了ICMP过滤.如果设置了ICMP ...

  2. JavaWeb学习笔记(三)—— Servlet

    一.Servlet概述 1.1 什么是Servlet Servlet是是sun公司提供一套规范(接口),是JavaWeb的三大组件之一(Servlet.Filter.Listener),它属于动态资源 ...

  3. poj 1220 NUMBER BASE CONVERSION

    NUMBER BASE CONVERSION Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 5976   Accepted: ...

  4. dataTable 加了竖向滚动条导致列头样式错位的问题 / 亲测可用,不好用你打我,用好了记得点推荐

    tab在没有显示之前,容器是没有高度宽度的,而dt在自动计算高度和宽度时是获取的外部容器的高度和宽度,当切换tab时,dt获取不到这个高度宽度,导致列头都挤在一起,是用下面代码解决此问题 $('a[d ...

  5. Oracle Date Function 讲解和事例

    1 year=1*12 months 1 day=24 hours=24*(1*60mins)=24*60*(1*60 seconds) 1 week =7 days 注意: 黑色字体是 oracle ...

  6. springboot+Druid+oracle 配置p6spy

    p6spy可以将带参数的sql直接打出来方便调试. 1.gradle中引入 compile group: 'p6spy', name: 'p6spy', version: '3.8.1' 2.reso ...

  7. JDK7之HashMap源码

    并发场景下使用HashMap的问题分析:疫苗:Java HashMap的死循环 http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6423457 ...

  8. Nginx启动关闭和重启、文档直接下载不阅览

    nginx启动相关 启动:sbin/nginx -c conf/nginx.conf 关闭:sbin/nginx -s stop 重启(重新加载配置文件):sbin/nginx -s reload 检 ...

  9. Kubernetes系列:(1) 初探

    1. 背景 在部门内容组织了一次K8s的培训,普及了下K8s的概念.框架.操作等,为便于后期查阅,也为了进一步深究K8s,因此开展K8s系列,周期不定- 2. 概念 (1) 含义:来自希腊语,意为&q ...

  10. FZU 1921——栀子花开——————【线段树单点更新】

    栀子花开 Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Submit Status  ...