Android 你不知道的Service(服务) & Thread(线程)

原创 2015年11月18日 15:05:49

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的使用。

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

Service和Thread的关系

不少Android初学者都可能会有这样的疑惑,Service和Thread到底有什么关系呢?什么时候应该用Service,什么时候又应该用Thread?答案可能会有点让你吃惊,因为Service和Th...

Service与线程和进程的关系

一直搞不懂Service到底和进程线程的关系,因为这里我曲解了一个概念,就是Activity与主线程之间的关系,...

线程与服务的区别

经常会碰到的一个问题就是线程和服务有什么区别啊?线程与进程有什么区别啊: 1.线程与进程的区别: 进程是指运行中的应用程序,每一个进程都有自己独立的内存空间。一个应用程序可以同时启动多个进...

Service是跑在主线程中

Service是跑在主线程中,所以的耗时操作还是要起线程(Task)来执行 比如播放多媒体的时候用户启动了其他Activity这个时候程序要在后台继续播放,比如检测SD卡上文件的变化,再或者在后台记...
  • oLevin
  • oLevin
  • 2013年11月23日 12:38
  • 1472

service怎么运行到非UI线程

我们都知道android中service是运行在UI线程中的,今天面试遇到一个问题,怎样让service运行到非UI线程中?我知道service在注册的时候可以通过android:process=":...

Android四大组件Service之两种的多线程启动方式

由于Service常常用于处理比较耗时的后台服务,在不适用多线程时,所有的组件服务都是在主线程中执行的,必然会影响用户体验和整体性能,下面是两种启动Service线程的方式。 第一种方法:在Ser...

Android开发 四大组件是否运行在主线程中?

Android的四大组件是不是运行在主线程中?打了log,做个笔记. 先上图: 图没有说服力,因为log是我写的.哈哈. 1.activity 在onCreate()里面打印当前的线程...

android中Service使用startService

Service作为android四大组件之一,那么什么情况下我们会使用到Service呢?在这里我把它归纳成两种情况得运用场景: 1、用于长期执行某些操作,并且甚至与UI(主)线程没有交互。比如启动a...

Android中Service(服务)和Thread(线程)的关系

一、Service(服务) Service是Android中四大组件之一,在Android开发中起到非常重要的作用,先来看一下官方对Service的定义: A Service is a...

Android DDMS查看Threads情况

有时候程序运行出现死锁或者信号量卡死是很纠结的问题,单看代码很难分析定位问题,这时候可以借助DDMS来查看threads的运行情况,一目了然。     手机连接上USB,确保adb连通,然后启动...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Android 你不知道的Service(服务) & Thread(线程)
举报原因:
原因补充:

(最多只允许输入30个字)