Service类似于Activity,也是一个context,并能够响应intent。其中最常用的是IntentService。

创建IntentService


public class PollService extends IntentService {private static final String TAG = "PollService";public PollService() {super(TAG);}@Overrideprotected void onHandleIntent(Intent intent) {Log.i(TAG, "new intent : " + intent)}}

服务的intent又称命令(command)。每一条命令都要求服务完成某项具体的任务。根据服务的种类不同,服务执行命令的方式也不尽相同。

IntentService逐个执行命令队列里的命令,接收到首个命令时,IntentService即完成启动,并触发一个后台线程,然后将命令放入队列。

随后,IntentService继续按顺序执行每一条命令,并同时为每一条命令在后台线程上调用onHandleIntent(Intent)方法。新进命令总是放在队列末尾。最后,执行完队列中全部命令后,服务也随即停止并被销毁。以上描述仅适用于IntentService。

服务的作用

在用户离开当前应用,服务依然可以在后台运行。

Android为用户提供了关闭后台应用网络连接的功能。对于非常耗电的应用而言,这项功能极大的改善手机的续航能力。这也就意味着在后台连接网络时,需检查后台网络的可用性。

    @Overrideprotected void onHandleIntent(Intent intent) {ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);@SuppressWarnings("deprecation")boolean isNetworkAvailable = cm.getBackgroundDataSetting()&& cm.getActiveNetworkInfo() != null;if (!isNetworkAvailable)return;SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);String query = prefs.getString(FlickrFetchr.PREF_SEARCH_QUERY, null);//使用SharedPreferences保存最近一次获取的结果String lastResultId = prefs.getString(FlickrFetchr.PREF_LAST_RESULT_ID,null);ArrayList<GalleryItem> items;if (query != null) {items = new FlickrFetchr().search(query);} else {items = new FlickrFetchr().fetchItems();}if (items.size() == 0)return;String resultId = items.get(0).getId();if (!resultId.equals(lastResultId)) {Log.i(TAG, "Got a new result: " + resultId);            }prefs.edit().putString(FlickrFetchr.PREF_LAST_RESULT_ID, resultId).commit();}

为什么需要两处检查呢?在Android旧版本系统中,应检查getBackgroundDataSetting()方法的返回结果,如果返回结果为false,表示不允许使用后台数据,那我们就解脱了。当然,如果不去检查,也能随意使用后台数据。但这样做很可能会出问题(电量耗光或应用运行缓慢)。

而在Android 4.0 中,后台数据设置直接会禁用网络。这也是为什么需要检查getActiveNetworkInfo()方法是否返回空值的原因。如果返回空值,则网络不可用。对用户来说,这是好事,因为这意味着后台数据设置总是按用户的预期行事。当然,对开发者来说,还有一些额外的工作要做。

要使用getActiveNetworkInfo()方法。还需获取ACCESS_NETWORK_STATE权限。

使用AlarmManager延迟运行服务

为保证服务在后台的切实可行,当没有activity在运行时,需要通过某种方式在后台执行一些任务。比方说,设置一个5分钟间隔的定时器。

一种方式是调用Handler的sendMessageDelayed(…)或者postDelayed(…)方法。但如果用户离开当前应用,进程就会停止,handler消息也会随之消亡,因此该解决方案并不可靠。

因此,我们应转而使用AlarmManager。AlarmManager是可以发送Intent的系统服务。

如何告诉AlarmManager发送什么样的intent呢?使用PendingIntent。我们可以使用PendingIntent打包intent:“我想启动PollService服务。”然后,将其发送给系统中的其他部件,如AlarmManager。

    public static void setServiceAlarm(Context context, boolean isOn) {Intent intent = new Intent(context, PollService.class);PendingIntent pi = PendingIntent.getService(context, 0, intent, 0);AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);if (isOn) {alarmManager.setRepeating(AlarmManager.RTC,System.currentTimeMillis(), POLL_INTERVAL, pi);} else {alarmManager.cancel(pi);pi.cancel();}}

PendingIntent.getService(…)方法打包了一个Context.startService(Intent)方法的调用。它有四个参数:一个用来发送intent的Context、一个区分PendingIntent来源的请求代码,待发的Intent对象以及一组用来决定如何创建PendingIntent的标识符。

设置定时器可调用AlarmManager.setRepeating(…)方法。该方法同样具有四个参数:一个描述定时器时间基准的常量、定时器运行的开始时间、定时器循环的时间以及一个到时要发送的PendingIntent。

取消定时器可调用AlarmManager.cancel(PendingIntent)方法。通常,也需同步取消PendingIntent。

PendingIntent

PendingIntent是一种token对象。调用PendingIntent.getService(…)方法获取PendingIntent时,我们告诉操作系统:”请记住,我需要使用startService(Intent)方法发送这个intent“。随后,调用PendingIntent对象的send()方法时,操作系统会按照我们的要求发送原来封装的intent。

PendingIntent真正精妙的地方在于,将PendingIntent token 交给其他应用使用时,它代表当前应用发送token对象。另外,PendingIntent本身存在于操作系统而不是token里,因此实际上我们控制着它。如果不顾及别人感受的话,也可以交给别人一个PendingIntent对象后,立即撤销它,让send()方法啥也做不了。

如果使用同一个intent请求PendingIntent两次,得到的PendingIntent仍会是同一个。我们可借此测试某个PendingIntent是否已经存在,或撤销已发出的PendingIntent。

使用PendingIntent管理定时器

一个PendingIntent只能登记一个定时器。这也是isOn值为false时,setServiceAlarm(Context context, boolean isOn)方法的工作原理:调用AlarmManager.cancel(PendingIntent)方法取消定时器,然后同步取消PendingIntent。

既然撤销定时器也即撤销了PendingIntent,可通过检查PendingIntent是否存在,确认定时器激活与否。传入PendingIntent.FLAG_NO_CREATE标志给PendingIntent.getService(…)方法即可。该标志表示如果PendingIntent不存在,则返回null值,而不是创建它。

public static boolean isServiceAlarmOn(Context context) {Intent intent = new Intent(context, PollService.class);PendingIntent pi = PendingIntent.getService(context, 0, intent,PendingIntent.FLAG_NO_CREATE);return pi != null;}

控制定时器的开启与停止

添加服务开关

<menu xmlns:android="http://schemas.android.com/apk/res/android" ><itemandroid:id="@+id/menu_item_search"android:actionViewClass="android.widget.SearchView"android:icon="@android:drawable/ic_menu_search"android:showAsAction="ifRoom"android:title="@string/search"/><itemandroid:id="@+id/menu_item_clear"android:icon="@android:drawable/ic_menu_close_clear_cancel"android:showAsAction="ifRoom"android:title="@string/clear_search"/><itemandroid:id="@+id/menu_item_toggle_polling"android:showAsAction="ifRoom"android:title="@string/start_polling"/></menu>

菜单切换实现

    @Overridepublic boolean onOptionsItemSelected(MenuItem item) {switch (item.getItemId()) {.....case R.id.menu_item_toggle_polling://开启或停止服务boolean shouldStartAlarm = !PollService.isServiceAlarmOn(getActivity());PollService.setServiceAlarm(getActivity(), shouldStartAlarm);if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)getActivity().invalidateOptionsMenu();return true;default:return super.onOptionsItemSelected(item);}}

更新选项菜单项

即使是旧式选项菜单,也不会在每次使用时重新实例化生成。如需更新某个选项菜单项的内容,我们应在onPrepareOptionsMenu(Menu menu)方法添加代码实现。除了菜单的首次创建外,每次菜单需要配置都会调用该方法。

@Overridepublic void onPrepareOptionsMenu(Menu menu) {super.onPrepareOptionsMenu(menu);MenuItem menuItem = menu.findItem(R.id.menu_item_toggle_polling);if(PollService.isServiceAlarmOn(getActivity())){menuItem.setTitle(R.string.stop_polling);}else {menuItem.setTitle(R.string.start_polling);}}

在3.0以前版本的设备中,每次显示菜单时都会调用该方法,这保证了菜单项总能显示正确的文字信息。而在3.0版本以后的设备中,操作栏无法自动更新自己,因此,需通过Activity.invalidateOptionsMenu()方法回调onPrepareOptionsMenu(menu)方法并刷新新菜单项。

通知信息

如果服务需要与用户进行信息沟通,通知信息(notification)永远是个不错的选择。通知信息是指显示在通知抽屉上的消息条目,用户可向下滑动屏幕读取。

为发送通知信息,首先需创建一个Notification对象。Notification需使用构造对象完成创建。Notification应至少具备:
1、首次显示通知信息时,在状态栏上显示的ticker text;
2、ticker text消失后,在状态栏上显示的图标;
3、代表通知信息自身,在通知抽屉显示的视图;
4、用户点击抽屉中的通知信息,触发PendingIntent。

Resources resources = getResources();
PendingIntent pi = PendingIntent.getActivity(this, 0, new Intent(this, PhotoGalleryActivity.class), 0);Notification notification = new NotificationCompat.Builder(this)
//配置ticker text                 .setTicker(resources.getString(R.string.new_pictures_title))
//配置小图标                 .setSmallIcon(android.R.drawable.ic_menu_report_image)
//配置Notification在下拉抽屉中的外观,图标值来自于setSmallIcon(int)方法                 .setContentTitle(resources.getString(R.string.new_pictures_title))                  .setContentText(resources.getString(R.string.new_pictures_text))
//定义点击Notification时所触发的动作
.setContentIntent(pi)
//用户点击Notification后,该消息从消息抽屉中删除
.setAutoCancel(true)
.build();NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);/*** 传入的整数参数是通知消息的标识的标识符,在整个应用中该值是唯一的。如使用同一ID发送两条消息,则第二条消息会替换掉第一条消息。* 这也是进度条或其他动态视觉效果的实现方式。*/
notificationManager.notify(0, notification);

其他类型服务



代码地址

后台服务(Service)相关推荐

  1. Android后台服务Service

    后台服务 桌面应用程序:可见 服务:不可见  长期在后台运行 帮助应用执行耗时的操作 安卓的服务:安卓四大组件之一  不可见   后台长期运行 界面与服务有时候要执行数据交互 如何创建服务: 1. 创 ...

  2. Android移动开发之【Android实战项目】后台服务Service

    桌面应用程序:可见 服务:不可见 长期在后台运行 帮助应用执行耗时的操作 安卓的服务:安卓四大组件之一 不可见 后台长期运行 界面与服务有时候要执行数据交互 文章目录 如何创建服务 创建一个类 继承S ...

  3. Android判断后台服务(Service)是否运行

    今天在项目中需要判断某个服务是否在后台运行,因此写了一个工具类方便大家调用,话不多说,上代码. *** 服务工具类* * @author Administrator* */ public class ...

  4. Android 后台服务(Service)

    声明:各个方法的用法都在代码的注释里:可以自行观看 Service的代码: public class MyService extends Service {MyBinder myBinder = ne ...

  5. Android后台服务---无交互时的Service

    Service是Android四大组件之一,他主要负责后台服务,长时间运行. 一:主要特点 (1)优先级大于Activity,即使Activity被停止后,他也不会轻易被终止 (2)即使被系统终止,在 ...

  6. 安卓服务service全解,生命周期,前台服务、后台服务,启动注销、绑定解绑,注册

    全栈工程师开发手册 (作者:栾鹏) python教程全解 定义服务(服务的生命周期) 调用context.startService()时依次执行 ->onCreate()- >onStar ...

  7. 后台服务启动前台Service(跨进程)

    在开发中,很多时候我们会碰到在hmi里面做了页面布局,但是要求开机就显示. 我们的Service是开机自启动的,但是hmi不是开机自启动. 那么这个时候我们需要在hmi添加一个hmiService,用 ...

  8. 如何检查后台服务(Android的Service类)是否正在运行?

    描述: 如何检查后台服务(Android的Service类)是否正在运行?我希望我的Activity能够显示Service的状态,然后我可以打开或者关闭它. 回答: Android系统提供了一个函数A ...

  9. Service后台服务控制音乐的播放暂停和停止,播放完自动播放下一曲

    //添加获得sd卡的状态权限,和读取sd卡的权限 <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FIL ...

  10. 开机启动一个服务Service,启动后没有界面后台暗暗运行

    原文来自:http://blog.163.com/shaocpa@126/blog/static/553577572012418103732417/ 如果开机启动一个Activity,开机首先看的界面 ...

最新文章

  1. 渗透各行各业,这家RPA外企宣布全面进军中国市场
  2. C++中 何时用. 何时用-」
  3. 走过13,展望14----希望是个好东西
  4. es6 name属性
  5. Redis两种持久化方式(RDBAOF)
  6. python querystring encode_Java UriUtils.encodeQueryParam方法代碼示例
  7. kibana操作elasticsearch:查看映射关系
  8. for of 和 for in 在数组和对象中的区别
  9. postgresql 遍历字符串数组_每日一道编程题(348):1005.K次取反后最大化的数组和...
  10. Intel 64/x86_64/x86/IA-32处理器标志寄存器详解(2) - 32位EFLAGS - 80386(386, Intel386)/80486(486, Intel486)
  11. 滴滴怒怼美团;阿里麻吉宝刷屏;B站、爱奇艺上市 | CSDN 极客头条
  12. git中clone出来项目,导入到eclipse并运行web项目
  13. 数据分析(商业数据分析师理论认知之二商业分析师入门指南)
  14. QT基于mysql数据库的链接-全网最强万能通用法
  15. springboot2------自定义消息转换器
  16. 使用EasyExcel下载,文件名乱码问题处理
  17. 计算机技术中常用的术语CAE是指_,计算机技术中常用的术语CAE是指______。A:计算机辅助设计B:计算机辅助制造C:计算机辅助工程D:计...
  18. 新闻列表中,常用的文字超出后显示省略号..
  19. 将一个整数分解为质数的乘积的实现方式
  20. 【5. ROS机器人的运动控制】

热门文章

  1. 【人脸识别】HOG特征提取人脸识别【含Matlab源码 641期】
  2. django对比数据并调用企业微信接口群发
  3. ffmpeg如何进行高清图片转码
  4. 如何在ubuntu中设置webcam
  5. 关于针式打印机格式设置
  6. Sublime配置java运行环境(IntelliJ IDEA也许更好用)
  7. 使用有限内存对大型数据文件排序
  8. 【多图超详细】从零开始安装配置Cuckoo sandbox并提交样本进行分析
  9. 帮派红包 概率_电子游戏如何教我关于连锁帮派
  10. kubernetes之Pod控制器详解