Service作为Android的四大组件之一,你或许会经常用它。当提到它时,我们都随口说,它会在后台执行长时间的任务,但是,这种表述真的对么?你是否真的了解Service,就让我们来揭开Service的真面目。

Service

Android Developer对于Service如下定义,

A Service is an application component that can perform long-running operations in the background and does not provide a user interface. Another application component can start a service and it will continue to run in the background even if the user switches to another application. Additionally, a component can bind to a service to interact with it and even perform interprocess communication (IPC). For example, a service might handle network transactions, play music, perform file I/O, or interact with a content provider, all from the background.

简单翻译一下,一个服务(Service)是一个应用的组件,其可以在后台执行长时间的操作,而且不提供UI。其他应用组件可以开启一个服务,该服务将会在后台一直运行,纵使用户切换到了其他应用。另外,一个组件可以捆绑到服务上,来与其交互,甚至执行IPC。例如,一个服务可以执行网络链接、播放音乐、执行I/O或者和一个内容提供者(Content Provider)交互,都是在后台运行。

或许,我们对Service的误解就来源于这句话,perform long-running operations in the background 不就是可以在后台执行上时间操作的意思么。意思,的确是这个意思,但是,我们是否理解错了呢?

Developer关于Service有个注意事项:

Caution: A service runs in the main thread of its hosting process—the service does not create its own thread and does not run in a separate process (unless you specify otherwise). This means that, if your service is going to do any CPU intensive work or blocking operations (such as MP3 playback or networking), you should create a new thread within the service to do that work. By using a separate thread, you will reduce the risk of Application Not Responding (ANR) errors and the application’s main thread can remain dedicated to user interaction with your activities.

大致的意思是,
一个服务(service)运行在主线程中,服务并不创建自己的线程,也不在隔离进程中运行(除非你指定)。这意味着,如果你的服务要执行CPU费时操作或阻塞操作,你需要在服务中创建新的线程来执行该操作。使用其他线程,可以避免ANR错误,保证应用的主线程可以与用户交互。

看到了吧。虽然service是在后台执行,但是还是在主线程运行的,而主线程是什么呢?就是UI线程,负责屏幕事件的分发和相应,如果在service中执行长时间的操作,就会造成UI线程阻塞,屏幕无响应(无法分发屏幕事件),甚至出现ANR(Application Not Responding)现象。

让我们来测试下,service是否运行在主线程,Android提供了Thread.currentThread().getId()来得到当前线程的Id,我们可以在主线程和service中得到线程Id,然后判断是否一致,代码如下

public class MainActivity extends ActionBarActivity {    private String TAG = "MYSERVICE";    private Button button1, button2;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        button1 = (Button) findViewById(R.id.button1);        button1.setOnClickListener(new View.OnClickListener() {            public void onClick(View v) {                Log.i(TAG, "Main Thread ID; " + Thread.currentThread().getId());                startService(new Intent(MainActivity.this, MyService.class));                }        });    }}

Activity代码很简单,就是点击按钮,输出当前线程id,startservice,service代码如下,

public class MyService extends Service {    private String TAG = "MYSERVICE";    @Override    public IBinder onBind(Intent intent) {        // TODO Auto-generated method stub        Log.i(TAG, "onBind");        return null;    }    @Override    public void onCreate() {        // TODO Auto-generated method stub        super.onCreate();        Log.i(TAG, "onCreate");    }    @Override    @Deprecated    public void onStart(Intent intent, int startId) {        // TODO Auto-generated method stub        super.onStart(intent, startId);        Log.i(TAG, "onStart");    }    @Override    public int onStartCommand(Intent intent, int flags, int startId) {        // TODO Auto-generated method stub        Log.i(TAG, "onStartCommand");        Log.i(TAG, "Service Thread ID; " + Thread.currentThread().getId());        return super.onStartCommand(intent, flags, startId);    }    @Override    public void onDestroy() {        // TODO Auto-generated method stub        super.onDestroy();        Log.i(TAG, "onDestroy");    }    @Override    public boolean onUnbind(Intent intent) {        // TODO Auto-generated method stub        return super.onUnbind(intent);    }}

service的onStartCommand方法中,我们打印出service的thread Id。然后运行,查看结果是否一致,

11-18 02:36:12.644 6099-6099/paul.example.servicetest I/MYSERVICE: Main Thread ID; 1
11-18 02:36:12.654 6099-6099/paul.example.servicetest I/MYSERVICE: onCreate
11-18 02:36:12.654 6099-6099/paul.example.servicetest I/MYSERVICE: onStartCommand
11-18 02:36:12.654 6099-6099/paul.example.servicetest I/MYSERVICE: Service Thread ID; 1
11-18 02:36:12.654 6099-6099/paul.example.servicetest I/MYSERVICE: onStart

上面的输出结果,证明了service其实是在主线程中运行的。如果我们在service的onStartCommand添加Thread.sleep(5000)语句,让其休眠5s中,运行程序,开启service,你会发现,开启service后,activity的页面无法相应了。这是由于service运行在主线程(UI线程)中,service的休眠也会导致UI线程的休眠,所以UI就无法相应啦。所以虽然说service执行后台任务,但是如果该任务是耗时的话,还是得新建线程执行的哦。

service & thread

关于何时用service,何时用thread,Developer给出的建议是,

Should you use a service or a thread?

A service is simply a component that can run in the background even when the user is not interacting with your application. Thus, you should create a service only if that is what you need.

If you need to perform work outside your main thread, but only while the user is interacting with your application, then you should probably instead create a new thread and not a service. For example, if you want to play some music, but only while your activity is running, you might create a thread in onCreate(), start running it in onStart(), then stop it in onStop(). Also consider using AsyncTask or HandlerThread, instead of the traditional Thread class. See the Processes and Threading document for more information about threads.

Remember that if you do use a service, it still runs in your application’s main thread by default, so you should still create a new thread within the service if it performs intensive or blocking operations.

简单总结一下,
1. 如果你仅仅需要执行后台任务,并不需要和用户交互,此时你可以使用service;
2. 如果你需要在主线程在执行任务,并且当需要和用户交互的时候,此时你可以选择新建一个thread而非service,例如,如果你仅仅需要在activity运行的时候播放音乐,你可以选择在Activity的onCreate方法中新建线程,在onStart方法中运行,在onStop方法中停止。或者,可以采用 AsyncTask或Handler线程。

或许,你会疑问,既然 service并不能执行执行耗时的后台任务,那么为什么还会存在service呢。

其实大家不要把后台和子线程联系在一起就行了,这是两个完全不同的概念。Android的后台就是指,它的运行是完全不依赖UI的。即使Activity被销毁,或者程序被关闭,只要进程还在,Service就可以继续运行。比如说一些应用程序,始终需要与服务器之间始终保持着心跳连接,就可以使用Service来实现。你可能又会问,前面不是刚刚验证过Service是运行在主线程里的么?在这里一直执行着心跳连接,难道就不会阻塞主线程的运行吗?当然会,但是我们可以在Service中再创建一个子线程,然后在这里去处理耗时逻辑就没问题了。

额,既然在Service里也要创建一个子线程,那为什么不直接在Activity里创建呢?这是因为Activity很难对Thread进行控制,当Activity被销毁之后,就没有任何其它的办法可以再重新获取到之前创建的子线程的实例。而且在一个Activity中创建的子线程,另一个Activity无法对其进行操作。但是Service就不同了,所有的Activity都可以与Service进行关联,然后可以很方便地操作其中的方法,即使Activity被销毁了,之后只要重新与Service建立关联,就又能够获取到原有的Service中Binder的实例。因此,使用Service来处理后台任务,Activity就可以放心地finish,完全不需要担心无法对后台任务进行控制的情况。

好了,本篇就介绍到这里,下篇介绍service的使用。

更多相关文章

  1. java中线程与线程池的利弊(android适用)
  2. Android 开发中的多线程编程技术
  3. Android线程Handler的学习
  4. 后台动态添加布局文件、控件与动态设置属性2
  5. Android消息队列(三)--多线程与消息处理

随机推荐

  1. Android(安卓)短信拦截+来去电话拦截
  2. Android利用SQLite制作最简单成语小词典
  3. android获取图片文件头信息
  4. Android的屏幕多样性支持
  5. 关于Android(安卓)ListView组件中android
  6. Android 关闭开启蓝牙
  7. Android 为图片添加文字水印
  8. 使用 Android NDK 重用现有的 C 代码
  9. TecentUtil 腾讯微博 android
  10. Android一直震动源码