service(服务)是安卓中的四大组件之一,它通常用作在后台处理耗时的逻辑,与Activity一样,它存在自己的生命周期,也需要在清单文件中配置相关信息,本博客将对Service的各个知识点进行详细讲解。

一Service的基本用法:

1使用本地服务

1)服务的启动方式

1通过Context的startService()方法启动服务:以该方法启动的服务,开启该服务的应用组件(如Activity)与该Service不存在关联关系,即使开启该服务的Activity被销毁,Service任能够一直在后台运行。通常,开启的服务执行一个单独的操作且不需向调用者返回一个结果。比如,可能从网络进行下载或者上传一个文件。当任务完成,服务就该自我停止。使用服务于使用Activity非常相似,都是先继承其对应的基类,然后重写其中重要的方法,这些方法就是关于其生命周期回调的方法。代码如下所示:

public class MyService extends Service {

	public static final String TAG = "MyService";

	@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate() executed");
} @Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand() executed");
return super.onStartCommand(intent, flags, startId);
} @Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy() executed");
} @Override
public IBinder onBind(Intent intent) {
return null;
} }

然后再Activity中使用

 Intent startIntent = new Intent(this, MyService.class);
startService(startIntent);

即可开启该服务,程序运行结果如下:



从程序的运行结果来看,可以知道当启动一个Service的时候,会调用该Service中的onCreate()和onStartCommand()方法。

当我们在次点击启动服务的按钮,程序运行结果如下:



可以看到,此时只输出onStartCommand() executed。这说明此时只执行了onStartCommand()方法,而未执行onCreate(),这说明onCreate()方法只会在Service第一次被创建的时候调用,如果当前Service已经被创建过了,则即使多次调用startService()方法,onCreate()方法都不会再执行,这一点非常类似数据库操作中的open一个数据库。

当然上述的例子仅仅只是为了说明上述知识点,因为Service中的代码也仅仅只是打印出log而已,而事实上Service的使用是为了处理一些耗时操作的,如网络请求,文件上传与下载,但都是重写其某个生命周期函数,如onStart(Intent intent, int startId),onDestroy()在这些函数中完成自己的业务逻辑的处理,下面的代码是使用服务来进行网络通信的一个例子。

public class GetMsgService extends Service {
private Client client;
private boolean isStart;
private SharePreferenceUserInfoUtil util;
private ClientInputThread cit;
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
} @Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
client=((MyApplication) getApplication()).getClient();
} @Override
public void onStart(Intent intent, int startId) {
// TODO Auto-generated method stub
super.onStart(intent, startId);
util = new SharePreferenceUserInfoUtil(getApplicationContext(),
Constants.SAVE_USER); new Thread(){
public void run()
{
try {
isStart=client.create();
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//在服务中接受来自服务器端的消息,然后通过广播的形式传递给相应的Activity处理。接受服务器端的消息一般在
//服务中,因为服务可以在后台一直运行 if(isStart)
{
cit=client.getClientInputThread();
if(cit!=null)
{
cit.setMessageListener(new MessageListener() { public void getMessage(TransportObject msg) { if(msg!=null&&msg instanceof TransportObject)
{
//通过广播向Activity传递消息
Intent intent=new Intent();
intent.setAction(Constants.ACTION_MSG);
intent.putExtra(Constants.MSG, msg);
sendBroadcast(intent);
}
}
});
}
else {
Log.i("GetMsgService","服务器端连接暂时出错");
// Toast.makeText(getApplicationContext(), "服务器端连接暂时出错,请稍后重试!",0).show();
}
} }
}.start();
} @Override
public void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
ClientOutputThread out=client.getClientOutputThread();
TransportObject<User> msg=new TransportObject<User>(TranObjectType.LOGOUT);
User user=new User();
user.setId(Integer.parseInt(util.getId()));
msg.setObject(user);
out.setMsg(msg);
//关闭服务时关闭client
out.setStart(false);
client.getClientInputThread().setStart(false); } }

可以看到,我们在getMsgService的onStart方法中开启了一个线程用来进行客户端从服务器端读取信息的操作,在onDestroy()方法中关闭网络请求的操作。

2通过Context的bindService()方法启动服务:顾名思义,以该方法启动的服务,开启该服务的应用组件(如Activity)与该Service被绑定在一起,通常用这种方式开启的服务是为了与开启该服务的应用组件(如Activity)进行消息通信。

首先我们来看一下bindService的签名:

bindService(Intent service, ServiceConnection conn, int flags)

其中service参数是通过intent指定要启动的Service。

conn参数是一个ServiceConnection对象,该对象用于监听访问者与service之间的连接情况,当访问者与Service连接成功时会回调该类的onServiceConnected(ComponentName name, IBinder service)方法,然后将服务中创建的Ibinder对象(此时在Service的onBinder方法中需要返回该Ibinder对象)传递给第二个参数service,通过该Ibinder对象就能与Service进行通信。

第三个参数flags指定绑定时是否自动创建Service,一般我们指定为BIND_AUTO_CREATE(自动创建,如果传入0表示不自动创建)示例代码如下:

Service中的代码:

public class MyService extends Service {

	public static final String TAG = "MyService";

	private MyBinder mBinder = new MyBinder();

	@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate() executed");
} @Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand() executed");
return super.onStartCommand(intent, flags, startId);
} @Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy() executed");
} @Override
public IBinder onBind(Intent intent) {//在onBind(Intent intent)中返回IBinder对象
return mBinder;
} class MyBinder extends Binder {//定义一个类实现IBinder接口( Binder实现了IBinder接口) public void doSomething() {
Log.d("TAG", "doSomething() executed"); } } }

Activity中的代码:

public class MainActivity extends Activity
{
...
private ServiceConnection connection = new ServiceConnection() { @Override
public void onServiceDisconnected(ComponentName name) {
} @Override
public void onServiceConnected(ComponentName name, IBinder service) {
myBinder = (MyService.MyBinder) service;
myBinder.doSomething();
}
};
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent bindIntent = new Intent(this, MyService.class);
bindService(bindIntent, connection, BIND_AUTO_CREATE);//此时使用bindService开启服务
}
... }

即在服务中定义一个IBinder的实例,然后在Service的public IBinder onBind(Intent intent)方法中将其返回,在Activity中定义一个ServiceConnection类的实例,在其onServiceConnected(ComponentName name, IBinder service)方法中获取Service中 返回IBinder对象,利用该对象即可调用Service中的方法进行相互通信。

注意:一个服务在进程中的主线程运行——一个服务不会创建自己的线程,也不会在另外的进程运行(除非另外指定)。这意味着,如果服务需要做一些频繁占用CPU的工作或者会发生阻塞的操作,你需要在服务中另开线程执行任务。避免程序出现ANR。

2使用AIDL跨进程调用服务:

此种情况与上述介绍的使用bindService方法开启的绑定本地的大的框架基本相同,只不过使用AIDL来实现跨进程调用,关于此种情况的介绍,请参看我的博客:安卓中不同APP之间的消息通信中相关的内容。

二Service与线程的关系及IntentService:

事实上Service与线程之间没多大关系,我们之所以把Service与线程放在一起谈论,是为了更清楚的明白在哪些情况下用服务哪些情况下用线程,哪些情况下在服务中开启一个线程,因为Service默认在主线程中运行,不能进行耗时操作,这也是IntentService存在的原因。因为IntentService会创建单独的worker线程来处理intent请求,不需要自己创建一个子线程。

IntentService处理流程

创建默认的一个 worker 线程处理传递给 onStartCommand() 的所有 intent ,不占据应用的主线程

创建一个工作队列一次传递一个 intent 到你实现的 onHandleIntent() 方法,避免了多线程

在所有启动请求被处理后自动关闭服务,不需要调用 stopSelf()

默认提供 onBind() 的实现,且返回 null

默认提供 onStartCommand() 的实现,实现发送intent到工作队列再到onHandleIntent() 方法实现。

正因为如此,所以使用IntentService无需重写onBind(),onStartCommand(),只需重写onHandleIntent() 即可。示例代码如下:

public class HelloIntentService extends IntentService {
/**
* A constructor is required, and must call the super IntentService(String)
* constructor with a name for the worker thread.
*/
public HelloIntentService() {
super("HelloIntentService");
}
/**
* The IntentService calls this method from the default worker thread with
* the intent that started the service. When this method returns, IntentService
* stops the service, as appropriate.
*/
@Override
protected void onHandleIntent(Intent intent) {
// Normally we would do some work here, like download a file.
// For our sample, we just sleep for 5 seconds.
long endTime = System.currentTimeMillis() + 5*1000;
while (System.currentTimeMillis() < endTime) {
synchronized (this) {
try {
wait(endTime - System.currentTimeMillis());
} catch (Exception e) {
}
}
}
}
}

三Service的生命周期

我们先来看一下谷歌官方图片:

从上述图片可以看到两种不同的启动方式其生命周期也不同:

启动的服务: 

startService()->onCreate()->onStartCommand()->running->stopService()/stopSelf()->onDestroy()->stopped 

其中,服务未运行时会调用一次onCreate(),运行时不调用。

绑定的服务: 

bindService()->onCreate()->onBind()->running->onUnbind()->onDestroy()->stopped

服务起始于 onCreate() ,终止于 onDestory()



服务的开关过程,只有 onStartCommand() 可多次调用,其他在一个生命周期只调用一次。



这两个过程不是完全独立,也可以绑定一个由 startService() 启动过的服务

四如何创建不被系统杀死的服务

服务不被杀死包括三种情况

1.系统根据资源分配情况杀死服务

2.用户通过 settings -> Apps -> Running -> Stop
方式杀死服务
3.用户通过 settings -> Apps -> Downloaded -> Force
Stop 方式杀死服务
第一种情况:

用户不干预,完全靠系统来控制,办法有很多。比如 onStartCommand() 方法的返回值设为 START_STICKY ,服务就会在资源紧张的时候被杀掉,然后在资源足够的时候再恢复。当然也可设置为前台服务,使其有高的优先级,在资源紧张的时候也不会被杀掉。

关于 onStartCommand() 方法的返回值做一下简单的介绍:

START_STICKY:如果service进程被kill掉,保留service的状态为开始状态,但不保留递送的intent对象。随后系统会尝试重新创建service,由于服务状态为开始状态,所以创建服务后一定会调用onStartCommand(Intent,int,int)方法。如果在此期间没有任何启动命令被传递到service,那么参数Intent将为null。

START_NOT_STICKY:“非粘性的”。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统不会自动重启该服务。

START_REDELIVER_INTENT:重传Intent。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统会自动重启该服务,并将Intent的值传入。

START_STICKY_COMPATIBILITY:START_STICKY的兼容版本,但不保证服务被kill后一定能重启。

第二种情况:

用户干预,主动杀掉运行中的服务。这个过程杀死服务会通过服务的生命周期,也就是会调用 onDestory() 方法,这时候一个方案就是在 onDestory() 中发送广播开启自己。这样杀死服务后会立即启动。如下:

在onCreate中注册广播,用来开启服务,在服务的onDestroy()中发送广播通知服务开启自己,代码如下:

public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
mBroadcast = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
Intent a = new Intent(ServiceA.this, ServiceA.class);
startService(a);
}
};
mIF = new IntentFilter();
mIF.addAction("listener");
registerReceiver(mBroadcast, mIF);
}
@Override
public void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
Intent intent = new Intent();
intent.setAction("listener");
sendBroadcast(intent);
unregisterReceiver(mBroadcast);
}

第三种情况:

强制关闭就不好解决。这个好像是从包的level去关的,不是走的Service的完整的生命周期。所以在服务里加代码是无法被调用的。处理这个情况的唯一方法是屏蔽掉 force stop 和 uninstall 按钮,让其不可用。

好了,以上就是本人理解的关于Service的相关知识,看官如果觉得不错请不要吝啬点击一下下方的“顶”按钮给我一点鼓励哦!

安卓服务Service详解的更多相关文章

  1. WebSocket安卓客户端实现详解(三)–服务端主动通知

    WebSocket安卓客户端实现详解(三)–服务端主动通知 本篇依旧是接着上一篇继续扩展,还没看过之前博客的小伙伴,这里附上前几篇地址 WebSocket安卓客户端实现详解(一)–连接建立与重连 We ...

  2. 2-4、nginx特性及基础概念-nginx web服务配置详解

    Nginx Nginx:engine X 调用了libevent:高性能的网络库 epoll():基于事件驱动event的网络库文件 Nginx的特性: 模块化设计.较好扩展性(不支持模块动态装卸载, ...

  3. WebSocket安卓客户端实现详解(一)–连接建立与重连

    http://blog.csdn.net/zly921112/article/details/72973054 前言 这里特别说明下因为WebSocket服务端是公司线上项目所以这里url和具体协议我 ...

  4. 10.service 详解

    10.service 详解 什么是service:Kubernetes中的Service 是一个抽象的概念,它定义了Pod的逻辑分组和一种可以访问它们的策略,这组Pod能被Service访问,使用YA ...

  5. 安卓集成发布详解(二)gradle

    转自:http://frank-zhu.github.io/android/2015/06/15/android-release_app_build_gradle/ 安卓集成发布详解(二) 15 Ju ...

  6. Linux:SSH服务配置文件详解

    SSH服务配置文件详解 SSH客户端配置文件 /etc/ssh/ssh——config 配置文件概要 Host * #选项“Host”只对能够匹配后面字串的计算机有效.“*”表示所有的计算机. For ...

  7. Nginx服务优化详解

    Nginx服务优化详解 1.隐藏Nginx版本信息 编辑主配置文件nginx.conf,在http标签中添加代码 server_tokens off;来隐藏软件版本号. 2.更改Nginx服务启动的默 ...

  8. (转)Nginx静态服务配置---详解root和alias指令

    Nginx静态服务配置---详解root和alias指令 原文:https://www.jianshu.com/p/4be0d5882ec5 静态文件 Nginx以其高性能著称,常用与做前端反向代理服 ...

  9. 安卓开发之详解getChildFragmentManager和getsupportFragmentManager和getFragmentManager详解

    安卓开发之详解getChildFragmentManager和getsupportFragmentManager和getFragmentManager详解 getFragmentManager()所得 ...

随机推荐

  1. [BZOJ]1046 上升序列(HAOI2007)

    和字典序有关的题型啊. Description 对于一个给定的S={a1,a2,a3,…,an},若有P={ax1,ax2,ax3,…,axm},满足(x1 < x2 < … < x ...

  2. tf.nn.embedding_lookup TensorFlow embedding_lookup 函数最简单实例

    tf.nn.embedding_lookup TensorFlow embedding_lookup 函数最简单实例 #!/usr/bin/env python # -*- coding: utf-8 ...

  3. 查询优化--小表驱动大表(In,Exists区别)

    Mysql 系列文章主页 =============== 本文将以真实例子来讲解小表驱动大表(In,Exists区别) 1 准备数据 1.1 创建表.函数.存储过程 参照  这篇(调用函数和存储过程批 ...

  4. linux 删除命令

    rm *    文件名rm -r */ 文件夹rm -rf * 文件夹或文件名 -r 代表文件夹之下的都删除掉 -f 代表暴力删除,无需确认直接删完

  5. JS中数组和字符串的方法大全

    数组的方法很多,ECMScript5又提供了好几种方法.有空把之前的云上的笔记整理了一下,方便自己以后查找使用. 一.ECMScript 3的Array.prototype中定义的方法 1.join( ...

  6. 阿里架构师带你深入浅出jvm

    本文跟大家聊聊JVM的内部结构,从组件中的多线程处理,JVM系统线程,局部变量数组等方面进行解析 JVM JVM = 类加载器(classloader) + 执行引擎(execution engine ...

  7. python学习之路前端-Dom

    Dom简介    文档对象模型(Document Object Model,DOM)是一种用于HTML和XML文档的编程接口.它给文档提供了一种结构化的表示方法,可以改变文档的内容和呈现方式.我们最为 ...

  8. 这交互炸了(四) :一分钟让你拥有微信拖拽透明返回PhotoView

    本文已授权微信公众号:鸿洋(hongyangAndroid)原创首发 <交互炸了>或许是一系列高端特效教程, 文中会介绍一些比较炫酷的特效,以及实现的思路.特效实现本身也许不会有太大的难度 ...

  9. 手动创建第一个OC程序

    手动创建第一个OC程序 创建一个文件夹,文件夹内创建一个a.m的OC源文件,并编辑 之前说过,OC是完全兼容C语言的,那么我们先写个下面的程序尝试一下 #include<stdio.h> ...

  10. Xcode 中的断言

    转自:http://weibo.com/p/100808885591f113cdedc3301794e5e7d7e9f0/home?from=page_100808&mod=TAB#_rnd1 ...