Android创建服务之started service详细介绍
发布时间:2022-12-09 15:10:29
创建started service
应用组件(例如Activity)调用startService()来启动一个Service,将需要的参数通过Intent传给Service,Service将会在onStartCommand函数中获得Intent。
有两种方式可以创建started service,一种是扩展Service类,另外一种是扩展IntentService类
扩展Service
这是所有服务的基类。扩展这个类的时候,特别重要的一点是,需要创建一个新的线程来做服务任务,因为service默认是运行在你的主线程(UI线程)中的,它会使你的主线程运行缓慢。
扩展IntentService
这是一个service的子类,他可以在一个工作线程中处理所有的启动请求。如果你不需要同一时刻出来所有的服务请求,使用IntentService是一个很好的选择。你需要做的仅仅是实现onHandlerIntent()方法,通过这个函数处理接受的每一个启动请求。
下面我们学习如何扩展IntentService类和Service类
扩展IntentService类
IntentService做了什么?
1.创建一个独立于主线程的工作线程,用于执行通过onStartCommand()传来的所有的intent。
2.创建一个工作队列,将接受的所有intent一个一个的传给onHandlerIntent(),所以同一时间内你只处理一个intent,不用担心多线程的问题。
3.当所有的请求都处理完后,停止服务,所以你不需要手动调用stopSelf()。
4.提供onBind()函数的默认实现,返回null
5.提供onStartCommand()函数的默认实现,它把intent发送到工作队列中去,然后工作队列再发送到你的onHandlerIntent()函数中。
有了上面的基础,你仅仅要做的就是实现onHandlerIntent()。并且实现一个小小的构造函数。
参考下面的例子:
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) {
}
}
}
}
}
如果你要实现其他的回调函数,例如 onCreate , onStartCommand ,onDestroy 一定记得调用父类相应的函数,这样IntentService才能正确的处理工作线程。
例如,你需要在onStartCommand函数中弹出一个提示,那么你可以这样写:
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
return super.onStartCommand(intent,flags,startId);
}
有一点例外的是,如果你需要其他组件绑定服务,那么你的onBind函数不需要调用父类的onBind。
在下一节中,你将会看到通过扩展service类来实现与本节相同的服务,所不同的是代码会更多,但是同样意味着更灵活,特别是你需要同时处理多个请求时,比较适合直接扩展Service。
扩展Service类
如同你在上一节看到的一样,使用IntentService来实现一个started service非常简单。如果你需要你的service来执行多个线程,那么你需要扩展Service类去处理每个intent。
作为对比,下面的例子用Service类实现了和上一节中一样功能的服务。
public class HelloService extends Service {
private Looper mServiceLooper;
private ServiceHandler mServiceHandler;
// Handler that receives messages from the thread
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
// 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) {
}
}
}
// Stop the service using the startId, so that we don't stop
// the service in the middle of handling another job
stopSelf(msg.arg1);
}
}
@Override
public void onCreate() {
// Start up the thread running the service. Note that we create a
// separate thread because the service normally runs in the process's
// main thread, which we don't want to block. We also make it
// background priority so CPU-intensive work will not disrupt our UI.
HandlerThread thread = new HandlerThread("ServiceStartArguments",
Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
// Get the HandlerThread's Looper and use it for our Handler
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
// For each start request, send a message to start a job and deliver the
// start ID so we know which request we're stopping when we finish the job
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
mServiceHandler.sendMessage(msg);
// If we get killed, after returning from here, restart
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
// We don't provide binding, so return null
return null;
}
@Override
public void onDestroy() {
Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
}
}
如同你看到的,比IntentService的例子多了好多代码。
因为你自己处理每次的onStartcommand(),所以你可以在同一时间处理多个请求。这个例子没有这样做,但是如果你愿意,你完全可以每收到一个请求的时候,创建一个新线程并且立即运行,而不是等到上一个请求处理完成后再运行。
注意,onStartCommand()函数必须返回一个整数。这个整数描述了当系统杀死该服务时将要如何处理该服务。返回值必须是下面几个值:
START_NOT_STICKY
在onStartCommand()返回后,系统杀死服务,服务将不会重启,除非有pending intents(目前笔者还不懂什么是pending Intents)要投递。这比较适合非常容易就可以重新启动未完成任务的情况。
START_STICKY
在系统杀死服务后,重启服务并且调用onStartCommand(),但是不重新投递上一次的intent。系统会传给onStartCommand()函数null,除非有pending intent来启动服务,会传给onStartCommand()相应的intent。这种做法比较适合媒体播放服务,不需要执行命令,但是独立运行,常处于等待任务的服务。
START_REDELIVER_INTENT
在系统杀死服务后,重启服务并且调用onStartCommand(),参数传入上一次的intent,然后接下来是pending intent传入onStartCommand()。这比较适合于正在执行一项工作,它不能被立刻恢复,例如下载文件。
启动Service
你可以从一个Activity或者其他组件调用startService(intent)来启动服务。Android系统会调用服务的onStartCommand()函数并且传给它intent。
用上一节的HelloService来做个例子:
Intent intent = new Intent(this, HelloService.class);
startService(intent);
startService()会立刻返回,Android系统会调用服务的onStartCommand()函数。如果这是服务没有在运行,系统会首先调用onCreate()函数,然后会紧接着调用onStartCommand()函数。
如果服务没有提供绑定,那么通过startService()函数传入的intent就是应用组件和服务进行交互的唯一途径。如果你想服务发送结果给客户组件,那么客户组件需要在启动服务时,创建一个用于广播的PendingIntent,通过intent投递给服务。这样,服务就可以利用广播把结果发送给客户组件。
多个请求会导致服务的onStartCommand()被调用多次。但是,只需要一个停止请求(stopSelf() 或 stopService())就会使服务停止。
停止服务
一个启动的服务必须管理自己的生命周期。因为除非系统需要回收资源的时候,系统不会停止或者销毁服务。所以,服务必须通过调用stopSelf()来停止自己,或者有其他组件调用stopService()。
一担收到stopSelf()或stopService()的停止请求,系统会立刻销毁服务。
如果你的服务同时处理多个服务请求,当某个请求完成时就不能马上停止服务,因为你可能已经又接受了一个新的请求(在第一个请求完成时结束服务会终止第二个请求)。为了解决这个问题,你可以调用stopSelf(int)函数来保证你的停止请求总是基于最近的一次服务请求。这是因为,调用stopSelf(int)函数
会把onStartCommand()函数传入的startId传给停止请求。当你的startId和最近一次接受的服务请求不匹配时,服务不会结束。
注意:stopSelf(int)函数只是简单的与最近一次收到的startId比较,如果你的服务处理过程是多线程的,可能后收到的服务请求先完成,那stopSelf(int)的方案不适合你,你应该手动管理接受到的服务请求和完成的服务,比如在onStartCommand()函数中把startId记录到一个表中,在完成服务任务时在表中记录完成状态,在确保表中任务都完成的情况下直接调用stopSelf()来停止服务。
在前台运行服务
一个前台服务是用户能够很明显意识到的服务,当可用内存比较低的时候,系统不会杀掉前台服务。一个前台服务必须提供一个状态栏通知,放在正在运行条目里,这意味着只要服务没有停止或者一直是前台服务,这个通知就不会被消失。
举个例子,一个音乐播放器服务应该被设置为前台运行,因为用户非常明显知道他在运行。这个状态栏通知可以显示正在播放的歌曲,并且可以允许用户点击进入音乐播放界面。
要使服务运行在前台只要调用startForeground()。这个方法有两个参数:一个通知的整数ID和一个状态栏通知。代码片段:
Notification notification = new Notification(R.drawable.icon, getText(R.string.ticker_text),
System.currentTimeMillis());
Intent notificationIntent = new Intent(this, ExampleActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.setLatestEventInfo(this, getText(R.string.notification_title),
getText(R.string.notification_message), pendingIntent);
startForeground(ONGOING_NOTIFICATION_ID, notification);
使用stopForeground()函数可以把服务从前台移除。这个函数带一个布尔值参数,来表示是否从状态栏移除通知。这个函数不会使服务停止。如果把前台服务停止掉,那么通知也会同时被移除。


猜你喜欢
- 这篇文章主要介绍了springmvc如何使用POJO作为参数,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需
- FTP(File Transfer Protocol)就是文件传输协议。通过FTP客户端从远程FTP服务器上拷贝文件到本地计算机称为下载,将
- W3C制定了XML DOM标准,.Net为了支持W3C的标准,从1.1版本开始就引入了XmlDocument类。我在前一篇博客中,介绍了如何
- 一、新建项目并设置界面新建项目:选择Windows窗体项目应用(.Net Framework):设置项目名和路径:新建项目如下:设置界面:将
- Android虚拟键盘的弹起会遮挡住部分ui,虽然通过在清单文件中设置,可以随着虚拟键盘的弹出,布局往上推,但是面对登陆界面时,并没有太大的
- 前言最近VS2019正式版发布了,装下来顺便试用了一下C#8.0,最大的看点应该就是可空引用类型了。不过C#8.0仍然处于Beta的状态,而
- 以下是代码:package cn.study.concurrency.ch11;/** * 锁分段 * @author xiaof * */
- 本文实例分析了win7中C#的winForm编程使用savefiledialog不能弹出保存窗体的解决方法。分享给大家供大家参考。具体分析如
- createcriteria和or的区别mybatis generator插件生成的example中,有createcriteria和or方
- 1、概述 限流的含义是在单位时间内确保发往某个模块的请求数量小于某个数值,比如在实现秒杀功能时,需要确保在10秒内发往支付模块的请求数量小
- ssm(spring springMVC mybatis)1.创建项目file->new->project2.新建的maven项
- 一、网络爬虫的基本知识网络爬虫通过遍历互联网络,把网络中的相关网页全部抓取过来,这体现了爬的概念。爬虫如何遍历网络呢,互联网可以看做是一张大
- 引言使用标准框架验证Java bean的基础知识 - JSR 380,也称为Bean Validation 2.0。当然,验证用户输入在大多
- spring boot 请求后缀匹配spring boot 项目中添加这个类可以实现url不同后缀区分了public class UrlMa
- C#调用MFC 窗口 DLLMFC DLL创建一个窗口类,加public和AFX_EXT_CLASSMFC DLL属性注意MFC的使用:在共
- 混合开发简介使用Flutter从零开始开发App是一件轻松惬意的事情,但对于一些成熟的产品来说,完全摒弃原有App的历史沉淀,全面转向Flu
- 本文实例为大家分享了Springboot整合pagehelper分页展示的具体代码,供大家参考,具体内容如下一、添加依赖查找maven中pa
- 目的:在使用mybatis框架中mapper文件有自动生成,但有时需要自己添加sql语句进行开发,当遇到需要使用 if进行条件判断的时候该怎
- 一.服务端代码:import java.net.*; // for Socket, ServerSocket, and InetAddres
- 下拉刷新在越来越多的App中使用,已经形成一种默认的用户习惯,遇到列表显示的内容时,用户已经开始习惯性的拉拉。在交互习惯上已经形成定性。之前