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. elementtaryos root密码更改

    在elementtaryos 终端中使用root 账户但不幸忘记密码怎么办?请进行如下操作...... 1.进入高级选项选中recovery mode 2.按e编辑,找到recovery nomode ...

  2. 基于linux内核包过滤技术的应用网关

    目录 基于linux内核包过滤技术的应用网关 硬件形态 基本原理 应用场景 主要功能 其他功能 客户定制 基于linux内核包过滤技术的应用网关 硬件形态 基本原理 应用场景 媒体内容过滤和深度识别 ...

  3. jquery 方法总结

    1.   给列表中 某个 标签添加 点击方法 $(function(){ $(".delete").click(function(){ var href = $(this).att ...

  4. MYSQL分区表详解

    分区表对用户来说是一个独立的逻辑表,但是底层是多个物理字表组成的.分区代码实际上是对一组底层表的句柄对象封装.对分区表的请求,都会通过句柄对象转化成储存引擎的接口调用.所以分区对于SQL层来说是一个完 ...

  5. rem.js的用法及在浏览器端的适配

    首先介绍下自己,我是从后端转前端,对于前端的见识还不深望各位全当一个新手自述....... 随着移动设配的更新换代,市场上涌现了大量的非主流设备分辨率,比如华为手机...... 更新换代快的大前提下自 ...

  6. ListView与ArrayAdapter的搭配使用

    在android中,ListView是一种很重要的控件,一般的使用中,常建立一个所需类型的ArrayList,再通过ArrayAdapter把ListView绑定到ArrayList上,通过Array ...

  7. css样式查找遇到的问题汇总

    利用css简单排除元素的第一个子元素 例如:排除表格的第一行 /*除了表格的第一行其他都显示为红色*/ table tr+tr{ background-color:red;/*除了表格的第一行其他都显 ...

  8. TOJ 2641 Gene

    描述 How can millions of different and complex structures be built using only a few simple building bl ...

  9. 如何给MySql创建连接用户并授权

    一般在为MySql创建用户时建议使用GRANT前台命令,当然如果对我们开发者而言,方法还有很多种,比如使用INSERT命令,甚至是直接修改mysql user数据表,但仍然建议按照MySQL规范去授权 ...

  10. C语言中extern的用法--转

    http://blog.sina.com.cn/s/blog_52deb9d50100ml6y.html 在C语言中,修饰符extern用在变量或者函数的声明前,用来说明“此变量/函数是在别处定义的, ...