Service并不是在单独进程中运行,也是运行在应用程序进程的主线程中,在执行具体耗时任务过程中要手动开启子线程,应用程序进程被杀死,所有依赖该进程的服务也会停止运行。
由于ANR对Activity和BroadcastReceiver响应时间的限制(Activity对事件响应不超过5秒,BroadcastReceiver执行不超过10秒),使得在其中都不适合执行较耗时操作,这样像网络、数据库、复杂计算这类耗时操作的执行就需要一个组件来承担。Service作为Android四大组件之一,其功能之一就是耗时操作的执行,主要功能如下:
- 执行需要长时间运行的操作,这个操作不与用户进行交互,如网络下载、大文件I/O、复杂计算。
- 应用内或应用间数据通信,Android每个应用程序都在自己的dalvik虚拟机中运行,一个应用是不允许访问其他应用的内存信息的,为此Android引入了Content Provider在不同应用间共享数据,BroadcastReceiver广播信息给不同应用程序,但Content Provider更多用于数据的共享,BroadcastReceiver广播的信息会被所有应用接收较耗费系统资源,对于两个应用间动态的进行交互还需要通过Service来完成。
Service的使用
Service的创建和Activity类似,也是通过Intent来实现的,既然是安卓四大组件之一,那么它也需要在清单文件中进行注册的。具体步骤如下。
Service的创建
新建一个TgsService继承自Service,并重写父类的onCreate()、onStartCommand()和onDestroy()方法,如下面的代码所示:
代码语言:javascript复制public class TgsService extends Service {
public static final String TAG = "TgsService";
@Override public void onCreate() { super.onCreate(); Log.d(TAG, "onCreate"); }
@Override public int onStartCommand(Intent intent, int flags, int startId) { Log.d(TAG, "onStartCommand"); return super.onStartCommand(intent, flags, startId); }
@Override public void onDestroy() { super.onDestroy(); Log.d(TAG, "onDestroy"); }
@Override public IBinder onBind(Intent intent) { return null; }}
Service的注册
在清单文件中注册它,如下所示:
代码语言:javascript复制<service android:name="com.tgs.demo.TgsService" android:enabled="true" android:exported="true"/>
- enabled属性:是指该服务是否能够被实例化。如果设置为true,则能够被实例化,否则不能被实例化。默认值是true,一般情况下,我们都会需要实例化,所以也可以选择不设置。
- exported属性:用于指示该服务是否能够被其他应用程序组件调用或跟它交互。如果设置为true,则能够被调用或交互(通常如果一个服务需要跨进程使用需要这么设置),设置为false时,只有同一个应用程序的组件或带有相同用户ID的应用程序才能启动或绑定该服务。
Service的启动
接下来创建一个TgsActivity的测试活动,用于在其中创建TgsService对象,并在点击按钮时启动服务,示例代码如下:
代码语言:javascript复制public class TgsActivity extends Activity implements View.OnClickListener {
private Button startBtn;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_start);
startBtn = (Button) findViewById(R.id.btn_start_service);
startBtn.setOnClickListener(this); }
@Override public void onClick(View v) { if (v != null) { switch (v.getId()) { case R.id.btn_start_service: Intent startIntent = new Intent(this, TgsService.class); startService(startIntent); break; } } }}
Service和Thread
如第一节所介绍的,Service是一个运行于后台的服务,一些比较耗时的操作也可以放在这里运行。而Thread我们大家都知道,是用于开启一个子线程,在这里去执行一些耗时操作就不会阻塞主线程的运行。这就会让人对这两个概念产生混淆了。
Service和Thread到底有什么关系呢?什么时候应该用Service,什么时候又应该用Thread?答案是:Service和Thread之间没有任何关系!前面有提到,Service其实是运行在主线程里的,因此它和Thread并没有关系。
Service与Thread的区别如下:
- Service是android的一种机制,当它运行的时候如果是Local Service,那么对应的Service是运行在主进程的main线程上的。如果是Remote Service,那么对应的Service则是运行在独立进程的main线程上。
- Thread是程序执行的最小单元,可以用Thread来执行一些异步的操作。
- 在应用中,如果是长时间的在后台运行,而且不需要交互的情况下,使用服务。同样是在后台运行,不需要交互的情况下,如果只是完成某个任务,之后就不需要运行,而且可能是多个任务,需要长时间运行的情况下使用线程。
- 如果任务占用CPU时间多,资源大的情况下,要使用线程。
那么如果用户既想使用Service的优点,又想使用Thread的优点,要怎么实现?关于这一点,Google已经帮我们想到了。即下节要介绍的IntentService。
IntentService
IntentService的概念
IntentService是Android中的一个系统封装类,继承自四大组件之一的Service,主要用于处理异步请求,实现多线程,它有以下特点:
- 是一种特殊的Service,继承自Service并且本身就是一个抽象类。
- 用于在后台执行耗时的异步任务,当任务完成后会自动停止。
- 有较高的优先级,不易被系统杀死(继承自Service的缘故),因此比较适合执行一些高优先级的异步任务。
- 内部通过HandlerThread和Handler实现异步操作。
- 创建IntentService时,只需实现onHandleIntent和构造方法,onHandleIntent为异步方法,可以执行耗时操作。
IntentService的创建
编写自己的Service类继承IntentService,并重写其中的onHandleIntent(Intent)方法,该方法是IntentService的一个抽象方法,用来处理我们通过startService方法开启的服务,传入参数Intent就是开启服务的Intent,如下所示:
代码语言:javascript复制public class TgsIntentService extends IntentService {
private static final String TAG = TgsIntentService.class.getSimpleName();
public TgsService() { super(TAG); }
@Override protected void onHandleIntent(Intent intent) { // 在这里添加我们要执行的异步代码 }}
IntentService的注册
接下来在AndroidManifest文件中的Application标签下添加刚刚创建的服务,如下所示:
代码语言:javascript复制<service android:name="com.tgs.demo.TgsIntentService" />
IntentService的启动
然后创建一个TgsActivity的测试活动,并在点击按钮时调用startService系统函数来开启IntentService的服务,示例代码如下:
代码语言:javascript复制public class TgsActivity extends Activity implements View.OnClickListener {
private Button startIntentServiceBtn;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_start);
startIntentServiceBtn = (Button) findViewById(R.id.btn_start_intent_service);
startIntentServiceBtn.setOnClickListener(this); }
@Override public void onClick(View v) { if (v != null) { switch (v.getId()) { case R.id.btn_start_intent_service: Intent intent = new Intent(TgsActivity.this, TgsIntentService.class); startService(intent); break; } } }}
Service的终止
一个已经启动了的Service必须管理它自己的生命期,系统不会停止或销毁这种Service,除非内存不够用了。Service在onStartCommand()返回后会继续运行。所以,service必须调用stopSelf()停止自己或由另一个组件调用stopService()来停止它。
一旦通过stopSelf()或stopService()发出了停止请求,系统就会尽可能快地销毁service。
关于Service的终止,需要注意以下几点:
- startService要stopSelf或者stopService才能终结。
- bindService要组件全部解绑后才会终结。
- 低内存的时候系统会主动停止和回收后台Service。
- 前台service很少被系统杀死,后台service随着时间推移变得更加可能被系统杀死。
- service被杀后会重启,但是取决于onStartCommand的返回值。
接下来创建一个TgsActivity的测试活动,用于在其中创建TgsService对象,并在点击按钮时停止服务,示例代码如下:
代码语言:javascript复制public class TgsActivity extends Activity implements View.OnClickListener {
private Button stopBtn;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_start);
stopBtn = (Button) findViewById(R.id.btn_stop_service);
stopBtn.setOnClickListener(this); }
@Override public void onClick(View v) { if (v != null) { switch (v.getId()) { case R.id.btn_stop_service: Intent stopIntent = new Intent(this, TgsService.class); stopService(stopIntent); break; } } }}
参考链接
- https://developer.android.com/reference/android/app/Service
- https://developer.android.com/reference/android/app/Service#WhatIsAService
- https://developer.android.com/reference/android/app/Service#ServiceLifecycle
- https://developer.android.com/reference/android/app/Service#Permissions
- https://developer.android.com/reference/android/app/Service#ProcessLifecycle
- https://developer.android.com/reference/android/app/Service#LocalServiceSample
- https://developer.android.com/reference/android/app/Service#RemoteMessengerServiceSample
客户端27
Android开发12
客户端 · 目录
上一篇Android多线程的种类及使用方法下一篇Android组件之ContentProvider