Android Service生命周期及用法

参考: Android Service 服务(一)—— Service

    Android Service生命周期及用法!

一、 Service简介

Service是android 系统中的四大组件之一(Activity、Service、BroadcastReceiver、ContentProvider),它跟Activity的级别差不多,但不能自己运行只能后台运行,并且可以和其他组件进行交互。service可以在很多场合的应用中使用,比如播放多媒体的时候用户启动了其他Activity这个时候程序要在后台继续播放,比如检测SD卡上文件的变化,再或者在后台记录你地理信息位置的改变等等,总之服务总是藏在后台的。

Service的启动有两种方式:context.startService() 和 context.bindService()

 

二、 Service启动流程

context.startService() 启动流程:

context.startService()  -> onCreate()  -> onStart()  -> Service running  -> context.stopService()  -> onDestroy()  -> Service stop 

 

如果Service还没有运行,则android先调用onCreate(),然后调用onStart();

如果Service已经运行,则只调用onStart(),所以一个Service的onStart方法可能会重复调用多次。 

如果stopService的时候会直接onDestroy,如果是调用者自己直接退出而没有调用stopService的话,Service会一直在后台运行,该Service的调用者再启动起来后可以通过stopService关闭Service。

所以调用startService的生命周期为:onCreate --> onStart (可多次调用) --> onDestroy

 

context.bindService()启动流程:

context.bindService()  -> onCreate()  -> onBind()  -> Service running  -> onUnbind()  -> onDestroy()  -> Service stop
 

onBind()将返回给客户端一个IBind接口实例,IBind允许客户端回调服务的方法,比如得到Service的实例、运行状态或其他操作。这个时候把调用者(Context,例如Activity)会和Service绑定在一起,Context退出了,Srevice就会调用onUnbind->onDestroy相应退出。 

所以调用bindService的生命周期为:onCreate --> onBind(只一次,不可多次绑定) --> onUnbind --> onDestory。

在Service每一次的开启关闭过程中,只有onStart可被多次调用(通过多次startService调用),其他onCreate,onBind,onUnbind,onDestory在一个生命周期中只能被调用一次。

 

 

三、 Service生命周期 

Service的生命周期并不像Activity那么复杂,它只继承了onCreate()、onStart()、onDestroy()三个方法

当我们第一次启动Service时,先后调用了onCreate()、onStart()这两个方法;当停止Service时,则执行onDestroy()方法。

这里需要注意的是,如果Service已经启动了,当我们再次启动Service时,不会在执行onCreate()方法,而是直接执行onStart()方法。

它可以通过Service.stopSelf()方法或者Service.stopSelfResult()方法来停止自己,只要调用一次stopService()方法便可以停止服务,无论调用了多少次的启动服务方法。

 

四、Context.startService()和Context.bindService()方法区别
 
这两个方法都可以启动Service,但是它们的使用场合有所不同:

    1. 使用startService()方法启用服务,调用者与服务之间没有关连,即使调用者退出了,服务仍然运行。
    如果打算采用Context.startService()方法启动服务,在服务未被创建时,系统会先调用服务的onCreate()方法,接着调用onStart()方法。
    如果调用startService()方法前服务已经被创建,多次调用startService()方法并不会导致多次创建服务,但会导致多次调用onStart()方法。
    采用startService()方法启动的服务,只能调用Context.stopService()方法结束服务,服务结束时会调用onDestroy()方法。

    2. 使用bindService()方法启用服务,调用者与服务绑定在了一起,调用者一旦退出,服务也就终止,大有“不求同时生,必须同时死”的特点。
    onBind()只有采用Context.bindService()方法启动服务时才会回调该方法。该方法在调用者与服务绑定时被调用,当调用者与服务已经绑定,多次调用Context.bindService()方法并不会导致该方法被多次调用。
    采用Context.bindService()方法启动服务时只能调用onUnbind()方法解除调用者与服务解除,服务结束时会调用onDestroy()方法。
 
看看官方给出的比较流程示意图:
两种比较
 
    官方文档告诉我们,一个service可以同时start并且bind,在这样的情况,系统会一直保持service的运行状态如果service已经start了或者BIND_AUTO_CREATE标志被设置。如果没有一个条件满足,那么系统将会调用onDestory方法来终止service.所有的清理工作(终止线程,反注册接收器)都在onDestory中完成。
 
五、示范代码
 

第一步:新建一个Android工程,我这里命名为ServiceDemo.

第二步:修改main.xml代码,我这里增加了四个按钮,代码如下:

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     android:orientation="vertical"
 4     android:layout_width="fill_parent"
 5     android:layout_height="fill_parent"
 6     >
 7     <TextView
 8         android:id="@+id/text"  
 9         android:layout_width="fill_parent" 
10         android:layout_height="wrap_content" 
11         android:text="@string/hello"
12         />
13     <Button
14         android:id="@+id/startservice"
15         android:layout_width="fill_parent"
16         android:layout_height="wrap_content"
17         android:text="startService"
18     />
19     <Button
20         android:id="@+id/stopservice"
21         android:layout_width="fill_parent"
22         android:layout_height="wrap_content"
23         android:text="stopService"
24     />
25     <Button
26         android:id="@+id/bindservice"
27         android:layout_width="fill_parent"
28         android:layout_height="wrap_content"
29         android:text="bindService"
30     />
31     <Button
32         android:id="@+id/unbindservice"
33         android:layout_width="fill_parent"
34         android:layout_height="wrap_content"
35         android:text="unbindService"
36     />
37 </LinearLayout>

 

第三步:新建一个Service,命名为MyService.java代码如下:

 1 package com.tutor.servicedemo;
 2 import android.app.Service;
 3 import android.content.Intent;
 4 import android.os.Binder;
 5 import android.os.IBinder;
 6 import android.text.format.Time;
 7 import android.util.Log;
 8 public class MyService extends Service {
 9     //定义个一个Tag标签
10     private static final String TAG = "MyService";
11     //这里定义吧一个Binder类,用在onBind()有方法里,这样Activity那边可以获取到
12     private MyBinder mBinder = new MyBinder();
13     @Override
14     public IBinder onBind(Intent intent) {
15         Log.e(TAG, "start IBinder~~~");
16         return mBinder;
17     }
18     @Override
19     public void onCreate() {
20         Log.e(TAG, "start onCreate~~~");
21         super.onCreate();
22     }
23     
24     @Override
25     public void onStart(Intent intent, int startId) {
26         Log.e(TAG, "start onStart~~~");
27         super.onStart(intent, startId);    
28     }
29     
30     @Override
31     public void onDestroy() {
32         Log.e(TAG, "start onDestroy~~~");
33         super.onDestroy();
34     }
35     
36     
37     @Override
38     public boolean onUnbind(Intent intent) {
39         Log.e(TAG, "start onUnbind~~~");
40         return super.onUnbind(intent);
41     }
42     
43     //这里我写了一个获取当前时间的函数,不过没有格式化就先这么着吧
44     public String getSystemTime(){
45         
46         Time t = new Time();
47         t.setToNow();
48         return t.toString();
49     }
50     
51     public class MyBinder extends Binder{
52         MyService getService()
53         {
54             return MyService.this;
55         }
56     }
57 }

 

第四步:修改ServiceDemo.java,代码如下:

package com.tutor.servicedemo;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
public class ServiceDemo extends Activity implements OnClickListener{
   
    private MyService  mMyService;
    private TextView mTextView;
    private Button startServiceButton;
    private Button stopServiceButton;
    private Button bindServiceButton;
    private Button unbindServiceButton;
    private Context mContext;
    
    //这里需要用到ServiceConnection在Context.bindService和context.unBindService()里用到
    private ServiceConnection mServiceConnection = new ServiceConnection() {
        //当我bindService时,让TextView显示MyService里getSystemTime()方法的返回值    
        public void onServiceConnected(ComponentName name, IBinder service) {
            // TODO Auto-generated method stub
            mMyService = ((MyService.MyBinder)service).getService();
            mTextView.setText("I am frome Service :" + mMyService.getSystemTime());
        }
        
        public void onServiceDisconnected(ComponentName name) {
            // TODO Auto-generated method stub
            
        }
    };
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        setupViews();
    }
    
    public void setupViews(){
    
        mContext = ServiceDemo.this;
        mTextView = (TextView)findViewById(R.id.text);
       
        startServiceButton = (Button)findViewById(R.id.startservice);
        stopServiceButton = (Button)findViewById(R.id.stopservice);
        bindServiceButton = (Button)findViewById(R.id.bindservice);
        unbindServiceButton = (Button)findViewById(R.id.unbindservice);
        
        startServiceButton.setOnClickListener(this);
        stopServiceButton.setOnClickListener(this);
        bindServiceButton.setOnClickListener(this);
        unbindServiceButton.setOnClickListener(this);
    }
   
    public void onClick(View v) {
        // TODO Auto-generated method stub
        if(v == startServiceButton){
            Intent i  = new Intent();
            i.setClass(ServiceDemo.this, MyService.class);
            mContext.startService(i);
        }else if(v == stopServiceButton){
            Intent i  = new Intent();
            i.setClass(ServiceDemo.this, MyService.class);
            mContext.stopService(i);
        }else if(v == bindServiceButton){
            Intent i  = new Intent();
            i.setClass(ServiceDemo.this, MyService.class);
            mContext.bindService(i, mServiceConnection, BIND_AUTO_CREATE);
        }else{
            mContext.unbindService(mServiceConnection);
        }
    }

}

 

第五步:修改AndroidManifest.xml代码

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
 3     package="com.tutor.servicedemo"  
 4     android:versionCode="1"
 5     android:versionName="1.0">
 6     
 7     <application android:icon="@drawable/ic_launcher" android:label="@string/app_name">
 8         <activity android:name=".ServiceDemo"
 9                android:label="@string/app_name">
10             <intent-filter>
11                 <action android:name="android.intent.action.MAIN"/>
12                 <category android:name="android.intent.category.LAUNCHER"/>
13         </intent-filter>        
14         </activity>
15         
16         <service android:name=".MyService" android:exported="true"> </service>                
17    </application>
18 
19     <uses-sdk android:minSdkVersion="7" />
20 </manifest>

 

第六步:执行上述工程,效果图如下:

点击startServie按钮时先后执行了Service中onCreate()->onStart()这两个方法,打开Logcat视窗效果如下图:

我们这时可以按HOME键进入Settings(设置)->Applications(应用)->Running Services(正在运行的服务)看一下我们新启动了一个服务,效果如下:

点击stopService按钮时,Service则执行了onDestroy()方法,效果图如下所示:

这时候我们再次点击startService按钮,然后点击bindService按钮(通常bindService都是bind已经启动的Service),我们看一下Service执行了IBinder()方法,以及TextView的值也有所变化了,如下两张图所示:

 

最后点击unbindService按钮,则Service执行了onUnbind()方法,如下图所示:

六、 拓展知识(进程和声明周期)

Android操作系统尝试尽可能长时间的保持应用的进程,但当可用内存很低时最终要移走一部分进程。怎样确定那些程序可以运行,那些要被销毁,Android让每一个进程在一个重要级的基础上运行,重要级低的进程最有可能被淘汰,一共有5级,下面这个列表就是按照重要性排列的:

1 一个前台进程显示的是用户此时需要处理和显示的。下列的条件有任何一个成立,这个进程都被认为是在前台运行的。
        a 与用户正发生交互的。
        b 它控制一个与用户交互的必须的基本的服务。
        c 有一个正在调用生命周期的回调函数的service(如onCreate()、onStar()、onDestroy())
        d 它有一个正在运行onReceive()方法的广播接收对象。
只有少数的前台进程可以在任何给定的时间内运行,销毁他们是系统万不得已的、最后的选择——当内存不够系统继续运行下去时。通常,在这一点上,设备已经达到了内存分页状态,所以杀掉一些前台进程来保证能够响应用户的需求。

2 一个可用进程没有任何前台组件,但它仍然可以影响到用户的界面。下面两种情况发生时,可以称该进程为可用进程。
        它是一个非前台的activity,但对用户仍然可用(onPause()方法已经被调用)这是可能发生的,例如:前台的activity是一个允许上一个activity可见的对话框,即当前activity半透明,能看到前一个activity的界面,它是一个服务于可用activity的服务。

3 一个服务进程是一个通过调用startService()方法启动的服务,并且不属于前两种情况。尽管服务进程没有直接被用户看到,但他们确实是用户所关心的,比如后台播放音乐或网络下载数据。所以系统保证他们的运行,直到不能保证所有的前台可见程序都正常运行时才会终止他们。

4 一个后台进程就是一个非当前正在运行的activity(activity的onStop()方法已经被调用),他们不会对用户体验造成直接的影响,当没有足够内存来运行前台可见程序时,他们将会被终止。通常,后台进程会有很多个在运行,所以他们维护一个LRU最近使用程序列表来保证经常运行的activity能最后一个被终止。如果一个activity正确的实现了生命周期的方法,并且保存它当前状态,杀死这些进程将不会影响到用户体验。

5 一个空线程没有运行任何可用应用程序组,保留他们的唯一原因是为了设立一个缓存机制,来加快组件启动的时间。系统经常杀死这些内存来平衡系统的整个系统的资源,进程缓存和基本核心缓存之间的资源。
Android把进程里优先级最高的activity或服务,作为这个进程的优先级。例如,一个进程拥有一个服务和一个可见的activity,那么这个进程将会被定义为可见进程,而不是服务进程。

此外,如果别的进程依赖某一个进程的话,那么被依赖的进程会提高优先级。一个进程服务于另一个进程,那么提供服务的进程不会低于获得服务的进程。例如,如果进程A的一个内容提供商服务于进程B的一个客户端,或者进程A的一个service被进程B的一个组件绑定,那么进程A至少拥有和进程B一样的优先级,或者更高。

因为一个运行服务的进程的优先级高于运行后台activity的进程,一个activity会准备一个长时间运行的操作来启动一个服务,而不是启动一个线程–尤其是这个操作可能会拖垮这个activity。例如后台播放音乐的同时,通过照相机向服务器发送一张照片,启动一个服务会保证这个操作至少运行在service 进程的优先级下,无论这个activity发生了什么,广播接收者应该作为一个空服务而不是简单的把耗时的操作单独放在一个线程里。 

 

 

参考推荐:

android service 学习

Android Service生命周期及用法

Android生命周期之service/Broadcast

Android BroadcastReceiver 学习

Android之BroadcastReceiver的使用

Android BroadcastReceiver启动Service 

 

 

posted on 2013-04-17 16:31  林盛  阅读(292)  评论(0)    收藏  举报