Unless you specify otherwise, most of the operations you do in an app run in the foreground on a special thread called the UI thread. 除非特别指定,一般情况下所有在前台执行的操作都运行在名为UI的线程中。可能会存在某些隐患,因为部分在UI界面上的耗时操作可能会影响到界面的响应性能。UI界面的性能问题会容易惹恼用户,甚至可能导致系统的ANR。为了避免这样的问题,Android Framework提供了几个类,用来帮助你把那些耗时操作移动到后台线程中执行。

This annoys your users, and can even cause system errors. To avoid this, the Android framework offers several classes that help you off-load operations onto a separate thread running in the background. 经常使用的是IntentService类。

主题一:如何创建IntentService实例? --> 创建后台Service

The IntentService class provides a straightforward structure for running an operation on a single background thread. This allows it to handle long-running operations without affecting your user interface's responsiveness. 可以处理一个耗时的任务并确保不影响到UI的响应性能,IntentService的运行不受UI生命周期的影响。

IntentService存在的一些局限性:

It can't interact directly with your user interface. To put its results in the UI, you have to send them to an Activity.

IntentService不能直接和Activity交互,必须将运行结果反馈给Activity。

Work requests run sequentially. If an operation is running in an IntentService, and you send it another request, the request waits until the first operation is finished.

工作任务队列是顺序执行的;如果一个任务在IntentService中执行,此时再发送一个新的任务请求,这个任务会一直等待直到前一个任务执行完成为止。

An operation running on an IntentService can't be interrupted.

在IntentService中运行的任务不会被打断,除非进程被kill。

虽然有上述的几点局限,但IntentService仍然是执行简单后台任务的最佳选择。

如何创建IntentService?

为了在app中创建IntentService组件,需要继承IntentService,并覆写onHandleIntent()。

public class RSSPullService extends IntentService {
@Override
protected void onHandleIntent(Intent workIntent) {
// Gets data from the incoming Intent
String dataString = workIntent.getDataString();
...
// Do work here, based on the contents of dataString
...
}
}

需要注意的是:其他Service中的回调方法,比如:onStartCommand()会被自动调用。因此,在IntentService中应该避免覆写这些回调方法。

如何在AndroidManifest.xml文件中声明该Service?

注意:IntentService同样需要在AndroidManifest.xml文件中声明,类似于Service。

    <application
android:icon="@drawable/icon"
android:label="@string/app_name">
...
<!--
Because android:exported is set to "false",
the service is only available to this app.
-->
<service
android:name=".RSSPullService"
android:exported="false"/>
...
<application/>

在<service>标签中并没有使用<intent-filter>,因此Activity需要使用显式Intent传递任务请求给IntentService。同时,也意味着只有在同一个app或者其他使用同一个UserID的组件才能够访问到这个Service。

主题二:向IntentService发送任务请求

如何发送一个Intent来触发IntentService执行后台任务?

通过Intent可以传递一些数据给后台任务,可以在Activity和Fragment的任何时间点发送这个Intent。

执行步骤如下:

步骤一:创建Intent对象

/*
* Creates a new Intent to start the RSSPullService
* IntentService. Passes a URI in the
* Intent's "data" field.
*/
mServiceIntent = new Intent(getActivity(), RSSPullService.class);
mServiceIntent.setData(Uri.parse(dataUrl));

必须是显式的Intent,同时可以附带有相关的数据。

步骤二:启动该Intent,执行startService(Intent intent)

// Starts the IntentService
getActivity().startService(mServiceIntent);

注意可以在Activity或者Fragment的任何位置发送任务请求。例如,如果你先获取用户输入,您可以从响应按钮单击或类似手势的回调方法里面发送任务请求。一旦执行了startService(),IntentService在自己本身的onHandleIntent()方法里面开始执行这个任务,任务结束之后,会自动停止这个Service。

主题三:返回IntentService执行结果

我们需要获取在IntentService中执行的结果,而IntentService则需要反馈执行的结果和任务执行的状态。比如:根据后台任务执行的进度,让Activity更新UI。推荐的方法是使用LocalBroadcastManager,这个类可以限制Broadcast中的Intent只在本地App中使用。

如何反馈执行状态?

在IntentService中,创建Intent,并通过广播的方式发送出去,该Intent则附带有状态信息。

public final class Constants {
...
// Defines a custom Intent action
public static final String BROADCAST_ACTION =
"com.example.android.threadsample.BROADCAST";
...
// Defines the key for the status "extra" in an Intent
public static final String EXTENDED_DATA_STATUS =
"com.example.android.threadsample.STATUS";
...
}
public class RSSPullService extends IntentService {
...
/*
* Creates a new Intent containing a Uri object
* BROADCAST_ACTION is a custom Intent action
*/
Intent localIntent =
new Intent(Constants.BROADCAST_ACTION)
// Puts the status into the Intent
.putExtra(Constants.EXTENDED_DATA_STATUS, status);
// Broadcasts the Intent to receivers in this app.
LocalBroadcastManager.getInstance(this).sendBroadcast(localIntent);
...
}

如何接受上述步骤发出的广播?

具体实践如下:

为了接受广播的数据对象,需要使用BroadcastReceiver的子类并实现BroadcastReceiver.onReceive() 的方法,这里可以接收LocalBroadcastManager发出的广播数据。

// Broadcast receiver for receiving status updates from the IntentService
private class ResponseReceiver extends BroadcastReceiver
{
// Prevents instantiation
private DownloadStateReceiver() {
}
// Called when the BroadcastReceiver gets an Intent it's registered to receive
@
public void onReceive(Context context, Intent intent) {
...
/*
* Handle Intents here.
*/
...
}
}

一旦定义了BroadcastReceiver,也应该定义actions,categories与data用过滤广播。为了实现这些,需要使用IntentFilter。

// Class that displays photos
public class DisplayActivity extends FragmentActivity {
...
public void onCreate(Bundle stateBundle) {
...
super.onCreate(stateBundle);
...
// The filter's action is BROADCAST_ACTION
IntentFilter mStatusIntentFilter = new IntentFilter(
Constants.BROADCAST_ACTION); // Adds a data filter for the HTTP scheme
mStatusIntentFilter.addDataScheme("http");
...

为了给系统注册这个BroadcastReceiver和IntentFilter,需要通过LocalBroadcastManager执行registerReceiver()的方法。

        // Instantiates a new DownloadStateReceiver
DownloadStateReceiver mDownloadStateReceiver =
new DownloadStateReceiver();
// Registers the DownloadStateReceiver and its intent filters
LocalBroadcastManager.getInstance(this).registerReceiver(
mDownloadStateReceiver,
mStatusIntentFilter);
...

一个BroadcastReceiver可以处理多种类型的广播数据。每个广播数据都有自己的ACTION。这个功能使得不用定义多个不同的BroadcastReceiver来分别处理不同的ACTION数据。为BroadcastReceiver定义另外一个IntentFilter,只需要创建一个新的IntentFilter并重复执行registerReceiver()即可。

        /*
* Instantiates a new action filter.
* No data filter is needed.
*/
statusIntentFilter = new IntentFilter(Constants.ACTION_ZOOM_IMAGE);
...
// Registers the receiver with the new filter
LocalBroadcastManager.getInstance(getActivity()).registerReceiver(
mDownloadStateReceiver,
mIntentFilter);

发送一个广播Intent并不会启动或重启一个Activity。即使是你的app在后台运行,Activity的BroadcastReceiver也可以接收、处理Intent对象;但是这不会迫使你的app进入前台。当你的app不可见时,如果想通知用户一个发生在后台的事件,建议使用Notification。永远不要为了响应一个广播Intent而去启动Activity。

如何运行后台Service?的更多相关文章

  1. Android 8.0 启动后台service 出错 IllegalStateException: Not allowed to start service Intent

    错误原因: Android 8.0 不再允许后台service直接通过startService方式去启动, 具体行为变更如下: 如果针对 Android 8.0 的应用尝试在不允许其创建后台服务的情况 ...

  2. Android中如何像 360 一样优雅的杀死后台Service而不启动

    http://my.oschina.net/mopidick/blog/277813 目录[-] 一.已知的 kill 后台应用程序的方法 方法: kill -9 pid 二.终极方法,杀死后台ser ...

  3. Remote System Explorer Operation总是运行后台服务,卡死eclipse

    阿里云 > 教程中心 > android教程 > Remote System Explorer Operation总是运行后台服务,卡死eclipse Remote System E ...

  4. Android 启动后台运行程序(Service)

    Android开发中,当需要创建在后台运行的程序的时候,就要使用到Service.Service 可以分为有无限生命和有限生命两种.特别需要注意的是Service跟Activities是不同的(简单来 ...

  5. 判断后台service是否在运行

    public static boolean isServiceRunning(Context mContext,String className) { boolean isRunning = fals ...

  6. Win10/UWP开发—使用Cortana语音与App后台Service交互

    上篇文章中我们介绍了使用Cortana调用前台App,不熟悉的移步到:Win10/UWP开发—使用Cortana语音指令与App的前台交互,这篇我们讲讲如何使用Cortana调用App的后台任务,相比 ...

  7. 安卓利用Handlers,AsyncTask和Loaders运行后台程序

    安卓的用户界面线程(user interface thread) 1.1 主线程 安卓修改用户界面并从一个单一用户界面线程中处理输入事件,这个线程也被称作主线程(main thread) Androi ...

  8. ansible shell 之运行后台程序

    最近在使用ansible shell模块启动一个shell编写的脚本,该脚本主要功能式加载java的classpath并在后台运行这个java程序. 该脚本在linux shell中可以正常启动和停止 ...

  9. 跟着拉大锯大神学Android——网络编程中运行后台服务器端口占用问题

    拉大锯网页地址:https://www.sunofbeach.net/u/1153952789488054272 跟着拉大锯大神学Android,在学到网络编程时,使用了大神搭建的用于学习的后台服务器 ...

随机推荐

  1. AGC027B Garbage Collector

    一道很好的构造题 原题链接 很快就能想到,捡每个垃圾的能量可以最后再算.然后,对于每个垃圾,在路上耗费的能量仅与它是第几个被捡的有关,于是我们考虑将垃圾分组. 首先,我们定义\(F(x,i)\)为某次 ...

  2. 越光后端开发——ygapi(3.引入xadmin)

    1.引入xadmin 1.将xadmin文件夹放入extra_apps目录下: 2.在每个app下新建adminx.py 1.apps/users/目录下新建adminx.py: import xad ...

  3. 记一次504 Gateway Time-out

    使用curl请求是超时,查了下资料原来是端口被占用,造成了死锁,记录下 首先要知道为什么会出现死锁? 在我们访问页面的时候这个端口进程就已经被使用,当我们再在页面中curl请求其他页面因为没有其他的端 ...

  4. 面试:atoi() 与 itoa()函数的内部实现(转)

    原 面试:atoi() 与 itoa()函数的内部实现 2013年04月19日 12:05:56 王世晖 阅读数:918   #include <stdio.h> #include < ...

  5. JavaScript类型判断详解(Object.prototype.toString.call()方法进行数据类型的可靠判断)

    前言 在编写一些类库中,我们经常需要判断一些未知的用户的输入和配置,故而需要进行一系列的类型判断.故而总结下JS是如何进行类型判断的 typeof typeof操作符返回一个字符串,表示未经计算的操作 ...

  6. HTML(八)HTML meta标签&base标签

    HTML meta元素 标签(meta-information)用于提供页面有关的元数据,除了提供文档字符集.使用语言.作者等基本信息外,还涉及对关键词和网页等级的设定.通过设置不同的属性,元数据可以 ...

  7. [物理学与PDEs]第1章第5节 Maxwell 方程组的数学结构, 电磁场的波动性 5.3 电磁场的波动性, 自由电磁波

    1. 由 Maxwell 方程组易知 $$\beex \bea \cfrac{1}{c^2}\cfrac{\p^2{\bf E} }{\p t^2}-\lap{\bf E}  &=-\sex{ ...

  8. python学习01

    1.python怎样运行? 1)打开运行窗口,输入python 2)打开运行窗口,直接输入 python + python代码  `python -c "print(1/6.878)&quo ...

  9. PHP 【五】

    函数 如要在页面加载时执行脚本,可以把它放到函数里 函数是通过调用函数来执行的 可在页面的任何位置调用函数 <?phpfunction functionName(){    // 要执行的代码} ...

  10. redis 数据结构及应用场景

    1. String 常用命令: get.set.incr.decr.mget等 应用场景: String是最常用的数据类型,普通的key/value都可以归为此类,value其实不仅是String,也 ...