Android Service的兩種啟動方式


參考鏈接

1. 概念

開始,先稍稍講一點android中Service的概念和用途吧~

Service分為本地服務(LocalService)和遠程服務(RemoteService)

1、本地服務依附在主進程上而不是獨立的進程,這樣在一定程度上節約了資源,另外Local服務因為是在同一進程因此不需要IPC,

也不需要AIDL。相應bindService會方便很多。主進程被Kill后,服務便會終止。

2、遠程服務為獨立的進程,對應進程名格式為所在包名加上你指定的android:process字符串。由於是獨立的進程,因此在Activity所在進程被Kill的時候,該服務依然在運行,

不受其他進程影響,有利於為多個進程提供服務具有較高的靈活性。該服務是獨立的進程,會占用一定資源,並且使用AIDL進行IPC稍微麻煩一點。

按使用方式可以分為以下三種:

1、startService 啟動的服務:主要用於啟動一個服務執行后台任務,不進行通信。停止服務使用stopService;

2、bindService 啟動的服務:該方法啟動的服務可以進行通信。停止服務使用unbindService;

3、startService 同時也 bindService 啟動的服務:停止服務應同時使用stepService與unbindService

2. Service 與 Thread 的區別

很多時候,你可能會問,為什么要用 Service,而不用 Thread 呢,因為用 Thread 是很方便的,比起 Service 也方便多了,下面我詳細的來解釋一下。

1). Thread:Thread 是程序執行的最小單元,它是分配CPU的基本單位。可以用 Thread 來執行一些異步的操作。

2). Service:Service 是android的一種機制,當它運行的時候如果是Local Service,那么對應的 Service 是運行在主進程的 main 線程上的。如:onCreate,onStart 這些函數在被系統調用的時候都是在主進程的 main 線程上運行的。如果是RemoteService,那么對應的 Service 則是運行在獨立進程的 main 線程上。因此請不要把 Service 理解成線程,它跟線程半毛錢的關系都沒有!

既然這樣,那么我們為什么要用 Service 呢?其實這跟 android 的系統機制有關,我們先拿 Thread 來說。Thread 的運行是獨立於 Activity 的,也就是說當一個 Activity 被 finish 之后,如果你沒有主動停止 Thread 或者 Thread 里的 run 方法沒有執行完畢的話,Thread 也會一直執行。因此這里會出現一個問題:當 Activity 被 finish 之后,你不再持有該 Thread 的引用。另一方面,你沒有辦法在不同的 Activity 中對同一 Thread 進行控制。

舉個例子:如果你的 Thread 需要不停地隔一段時間就要連接服務器做某種同步的話,該 Thread 需要在 Activity 沒有start的時候也在運行。這個時候當你 start 一個 Activity 就沒有辦法在該 Activity 里面控制之前創建的 Thread。因此你便需要創建並啟動一個 Service ,在 Service 里面創建、運行並控制該 Thread,這樣便解決了該問題(因為任何 Activity 都可以控制同一 Service,而系統也只會創建一個對應 Service 的實例)。

因此你可以把 Service 想象成一種消息服務,而你可以在任何有 Context 的地方調用 Context.startService、Context.stopService、Context.bindService,Context.unbindService,來控制它,你也可以在 Service 里注冊 BroadcastReceiver,在其他地方通過發送 broadcast 來控制它,當然這些都是 Thread 做不到的。

3.  Service和Activity通信

       需要用到bindService,通過onBind()方法來實現,看下面bindService的例子

4. Service的生命周期


第一種方式:通過StartService啟動Service

通過startService啟動后,service會一直無限期運行下去,只有外部調用了stopService()或stopSelf()方法時,該Service才會停止運行並銷毀。

要創建一個這樣的Service,你需要讓該類繼承Service類,然后重寫以下方法:

  • onCreate()
    1.如果service沒被創建過,調用startService()后會執行onCreate()回調;
    2.如果service已處於運行中,調用startService()不會執行onCreate()方法。
    也就是說,onCreate()只會在第一次創建service時候調用,多次執行startService()不會重復調用onCreate(),此方法適合完成一些初始化工作。

  • onStartCommand()
    如果多次執行了Context的startService()方法,那么Service的onStartCommand()方法也會相應的多次調用。onStartCommand()方法很重要,我們在該方法中根據傳入的Intent參數進行實際的操作,比如會在此處創建一個線程用於下載數據或播放音樂等。

  • onBind()
    Service中的onBind()方法是抽象方法,Service類本身就是抽象類,所以onBind()方法是必須重寫的,即使我們用不到。

  • onDestory()
    在銷毀的時候會執行Service該方法。

這幾個方法都是回調方法,且在主線程中執行,由android操作系統在合適的時機調用。

startService代碼實例

創建TestOneService,並在manifest里注冊。

需要注意,項目中的每一個Service都必須在AndroidManifest.xml中注冊才行,所以還需要編輯AndroidManifest.xml文件,代碼如下所示:

 1 <?xml version="1.0" encoding="utf-8"?>  
 2 <manifest xmlns:android="http://schemas.android.com/apk/res/android"  
 3     package="com.example.servicetest"  
 4     android:versionCode="1"  
 5     android:versionName="1.0" >  
 6   
 7     <uses-sdk  
 8         android:minSdkVersion="14"  
 9         android:targetSdkVersion="17" />  
10   
11     <application  
12         android:allowBackup="true"  
13         android:icon="@drawable/ic_launcher"  
14         android:label="@string/app_name"  
15         android:theme="@style/AppTheme" >  
16           
17     ……  
18   
19         <service android:name="com.example.servicetest.TestOneService" >  
20         </service>  
21     </application>  
22   
23 </manifest>  

在MainActivty中操作TestOneService,code如下:

 1 /**
 2  * Created by Kathy on 17-2-6.
 3  */
 4  
 5 public class TestOneService extends Service{
 6  
 7     @Override
 8     public void onCreate() {
 9         Log.i("Kathy","onCreate - Thread ID = " + Thread.currentThread().getId());
10         super.onCreate();
11     }
12  
13     @Override
14     public int onStartCommand(Intent intent, int flags, int startId) {
15         Log.i("Kathy", "onStartCommand - startId = " + startId + ", Thread ID = " + Thread.currentThread().getId());
16         return super.onStartCommand(intent, flags, startId);
17     }
18  
19     @Nullable
20     @Override
21     public IBinder onBind(Intent intent) {
22         Log.i("Kathy", "onBind - Thread ID = " + Thread.currentThread().getId());
23         return null;
24     }
25  
26     @Override
27     public void onDestroy() {
28         Log.i("Kathy", "onDestroy - Thread ID = " + Thread.currentThread().getId());
29         super.onDestroy();
30     }
31 }

在MainActivity中三次startService,之后stopService。

 1 /**
 2  * Created by Kathy on 17-2-6.
 3  */
 4  
 5 public class MainActivity extends AppCompatActivity {
 6  
 7     @Override
 8     protected void onCreate(Bundle savedInstanceState) {
 9         super.onCreate(savedInstanceState);
10         setContentView(R.layout.activity_main);
11         Log.i("Kathy", "Thread ID = " + Thread.currentThread().getId());
12         Log.i("Kathy", "before StartService");
13  
14         //連續啟動Service
15         Intent intentOne = new Intent(this, TestOneService.class);
16         startService(intentOne);
17         Intent intentTwo = new Intent(this, TestOneService.class);
18         startService(intentTwo);
19         Intent intentThree = new Intent(this, TestOneService.class);
20         startService(intentThree);
21  
22         //停止Service
23         Intent intentFour = new Intent(this, TestOneService.class);
24         stopService(intentFour);
25  
26         //再次啟動Service
27         Intent intentFive = new Intent(this, TestOneService.class);
28         startService(intentFive);
29  
30         Log.i("Kathy", "after StartService");
31     }
32 }

打印出的Log如下:

        02-06 15:19:45.090 8938-8938/? I/Kathy: Thread ID = 1
        02-06 15:19:45.090 8938-8938/? I/Kathy: before StartService
        02-06 15:19:45.233 8938-8938/? I/Kathy: onCreate - Thread ID = 1
        02-06 15:19:45.234 8938-8938/? I/Kathy: onStartCommand - startId = 1, Thread ID = 1
        02-06 15:19:45.234 8938-8938/? I/Kathy: onStartCommand - startId = 2, Thread ID = 1
        02-06 15:19:45.235 8938-8938/? I/Kathy: onStartCommand - startId = 3, Thread ID = 1
        02-06 15:19:45.236 8938-8938/? I/Kathy: onDestroy - Thread ID = 1
        02-06 15:19:45.237 8938-8938/? I/Kathy: onCreate - Thread ID = 1
        02-06 15:19:45.237 8938-8938/? I/Kathy: onStartCommand - startId = 1, Thread ID = 1
        02-06 15:19:45.238 8938-8938/? I/Kathy: after StartService

 分析:
1.主線程打印出是1,所有回調方法中打印出的執行線程ID都是1,證明回調方法都是在主線程中執行的
2.三次調用startService,只觸發一次onCreate回調,觸發了三次onStartCommand回調,且startId分別為1,2,3。證明 多次startService不會重復執行onCreate回調,但每次都會執行onStartCommand回調

第二種方式:通過bindService啟動Service

bindService啟動服務特點:
1.bindService啟動的服務和調用者之間是典型的client-server模式。調用者是client,service則是server端。service只有一個,但綁定到service上面的client可以有一個或很多個。這里所提到的client指的是組件,比如某個Activity。
2.client可以通過IBinder接口獲取Service實例,從而實現在client端直接調用Service中的方法以實現靈活交互,這在通過startService方法啟動中是無法實現的。
3.bindService啟動服務的生命周期與其綁定的client息息相關。當client銷毀時,client會自動與Service解除綁定。當然,client也可以明確調用Context的unbindService()方法與Service解除綁定。當沒有任何client與Service綁定時,Service會自行銷毀

bindService代碼實例

交互界面設計如下:


ActivityA界面布局.png

ActivityB界面布局.png

1.創建一個TestTwoService繼承Service(Server)
2.創建ActivityA,可以通過bindService綁定服務(client)
3.創建ActivityB,可以通過bindService綁定服務(client)
4.ActivityA可以跳轉到ActivityB

TestTwoService創建如下:
要想讓Service支持bindService調用方式,需要做以下事情:
1.在Service的onBind()方法中返回IBinder類型的實例。
2.onBInd()方法返回的IBinder的實例需要能夠返回Service實例本身。通常,最簡單的方法就是在service中創建binder的內部類,加入類似getService()的方法返回Service,這樣綁定的client就可以通過getService()方法獲得Service實例了。

 1 /**
 2  * Created by Kathy on 17-2-6.
 3  */
 4  
 5 public class TestTwoService extends Service{
 6  
 7     //client 可以通過Binder獲取Service實例
 8     public class MyBinder extends Binder {
 9         public TestTwoService getService() {
10             return TestTwoService.this;
11         }
12     }
13  
14     //通過binder實現調用者client與Service之間的通信
15     private MyBinder binder = new MyBinder();
16  
17     private final Random generator = new Random();
18  
19     @Override
20     public void onCreate() {
21         Log.i("Kathy","TestTwoService - onCreate - Thread = " + Thread.currentThread().getName());
22         super.onCreate();
23     }
24  
25     @Override
26     public int onStartCommand(Intent intent, int flags, int startId) {
27         Log.i("Kathy", "TestTwoService - onStartCommand - startId = " + startId + ", Thread = " + Thread.currentThread().getName());
28         return START_NOT_STICKY;
29     }
30  
31     @Nullable
32     @Override
33     public IBinder onBind(Intent intent) {
34         Log.i("Kathy", "TestTwoService - onBind - Thread = " + Thread.currentThread().getName());
35         return binder;
36     }
37  
38     @Override
39     public boolean onUnbind(Intent intent) {
40         Log.i("Kathy", "TestTwoService - onUnbind - from = " + intent.getStringExtra("from"));
41         return false;
42     }
43  
44     @Override
45     public void onDestroy() {
46         Log.i("Kathy", "TestTwoService - onDestroy - Thread = " + Thread.currentThread().getName());
47         super.onDestroy();
48     }
49  
50     //getRandomNumber是Service暴露出去供client調用的公共方法
51     public int getRandomNumber() {
52         return generator.nextInt();
53     }
54 }

client端要做的事情:
1.創建ServiceConnection類型實例,並重寫onServiceConnected()方法和onServiceDisconnected()方法。
2.當執行到onServiceConnected回調時,可通過IBinder實例得到Service實例對象,這樣可實現client與Service的連接。
3.onServiceDisconnected回調被執行時,表示client與Service斷開連接,在此可以寫一些斷開連接后需要做的處理。

創建ActivityA,代碼如下:

 1 /**
 2  * Created by Kathy on 17-2-6.
 3  */
 4 public class ActivityA extends Activity implements Button.OnClickListener {
 5     private TestTwoService service = null;
 6     private boolean isBind = false;
 7  
 8     private ServiceConnection conn = new ServiceConnection() {
 9         @Override
10         public void onServiceConnected(ComponentName name, IBinder binder) {
11             isBind = true;
12             TestTwoService.MyBinder myBinder = (TestTwoService.MyBinder) binder;
13             service = myBinder.getService();
14             Log.i("Kathy", "ActivityA - onServiceConnected");
15             int num = service.getRandomNumber();
16             Log.i("Kathy", "ActivityA - getRandomNumber = " + num);
17         }
18  
19         @Override
20         public void onServiceDisconnected(ComponentName name) {
21             isBind = false;
22             Log.i("Kathy", "ActivityA - onServiceDisconnected");
23         }
24     };
25  
26     protected void onCreate(Bundle savedInstanceState) {
27         super.onCreate(savedInstanceState);
28         setContentView(R.layout.activity_a);
29         Log.i("Kathy", "ActivityA - onCreate - Thread = " + Thread.currentThread().getName());
30  
31         findViewById(R.id.btnBindService).setOnClickListener(this);
32         findViewById(R.id.btnUnbindService).setOnClickListener(this);
33         findViewById(R.id.btnStartActivityB).setOnClickListener(this);
34         findViewById(R.id.btnFinish).setOnClickListener(this);
35     }
36  
37     @Override
38     public void onClick(View v) {
39         if (v.getId() == R.id.btnBindService) {
40             //單擊了“bindService”按鈕
41             Intent intent = new Intent(this, TestTwoService.class);
42             intent.putExtra("from", "ActivityA");
43             Log.i("Kathy", "----------------------------------------------------------------------");
44             Log.i("Kathy", "ActivityA 執行 bindService");
45             bindService(intent, conn, BIND_AUTO_CREATE);
46         } else if (v.getId() == R.id.btnUnbindService) {
47             //單擊了“unbindService”按鈕
48             if (isBind) {
49                 Log.i("Kathy",
50                         "----------------------------------------------------------------------");
51                 Log.i("Kathy", "ActivityA 執行 unbindService");
52                 unbindService(conn);
53             }
54         } else if (v.getId() == R.id.btnStartActivityB) {
55             //單擊了“start ActivityB”按鈕
56             Intent intent = new Intent(this, ActivityB.class);
57             Log.i("Kathy",
58                     "----------------------------------------------------------------------");
59             Log.i("Kathy", "ActivityA 啟動 ActivityB");
60             startActivity(intent);
61         } else if (v.getId() == R.id.btnFinish) {
62             //單擊了“Finish”按鈕
63             Log.i("Kathy",
64                     "----------------------------------------------------------------------");
65             Log.i("Kathy", "ActivityA 執行 finish");
66             this.finish();
67         }
68     }
69  
70     @Override
71     protected void onDestroy() {
72         super.onDestroy();
73         Log.i("Kathy", "ActivityA - onDestroy");
74     }
75 }

創建ActivityB,代碼如下:

 1 /**
 2  * Created by Kathy on 17-2-6.
 3  */
 4 public class ActivityB extends Activity implements Button.OnClickListener {
 5  
 6     private TestTwoService service = null;
 7  
 8     private boolean isBind = false;
 9  
10     private ServiceConnection conn = new ServiceConnection() {
11         @Override
12         public void onServiceConnected(ComponentName name, IBinder binder) {
13             isBind = true;
14             TestTwoService.MyBinder myBinder = (TestTwoService.MyBinder)binder;
15             service = myBinder.getService();
16             Log.i("Kathy", "ActivityB - onServiceConnected");
17             int num = service.getRandomNumber();
18             Log.i("Kathy", "ActivityB - getRandomNumber = " + num);
19         }
20  
21         @Override
22         public void onServiceDisconnected(ComponentName name) {
23             isBind = false;
24             Log.i("Kathy", "ActivityB - onServiceDisconnected");
25         }
26     };
27  
28     @Override
29     protected void onCreate(Bundle savedInstanceState) {
30         super.onCreate(savedInstanceState);
31         setContentView(R.layout.activity_b);
32  
33         findViewById(R.id.btnBindService).setOnClickListener(this);
34         findViewById(R.id.btnUnbindService).setOnClickListener(this);
35         findViewById(R.id.btnFinish).setOnClickListener(this);
36     }
37  
38     @Override
39     public void onClick(View v) {
40         if(v.getId() == R.id.btnBindService){
41             //單擊了“bindService”按鈕
42             Intent intent = new Intent(this, TestTwoService.class);
43             intent.putExtra("from", "ActivityB");
44             Log.i("Kathy", "----------------------------------------------------------------------");
45             Log.i("Kathy", "ActivityB 執行 bindService");
46             bindService(intent, conn, BIND_AUTO_CREATE);
47         }else if(v.getId() == R.id.btnUnbindService){
48             //單擊了“unbindService”按鈕
49             if(isBind){
50                 Log.i("Kathy", "----------------------------------------------------------------------");
51                 Log.i("Kathy", "ActivityB 執行 unbindService");
52                 unbindService(conn);
53             }
54         }else if(v.getId() == R.id.btnFinish){
55             //單擊了“Finish”按鈕
56             Log.i("Kathy", "----------------------------------------------------------------------");
57             Log.i("Kathy", "ActivityB 執行 finish");
58             this.finish();
59         }
60     }
61     @Override
62     public void onDestroy(){
63         super.onDestroy();
64         Log.i("Kathy", "ActivityB - onDestroy");
65     }
66 }

測試步驟1

step1: 點擊ActivityA的bindService按鈕
step2: 再點擊ActivityA的unbindService按鈕
Log輸出:

 1 02-07 14:09:38.031 1738-1738/com.demo.kathy.demo I/Kathy: ActivityA - onCreate - Thread = main
 2 02-07 14:09:39.488 1738-1738/com.demo.kathy.demo I/Kathy: ----------------------------------------------------------------------
 3 02-07 14:09:39.488 1738-1738/com.demo.kathy.demo I/Kathy: ActivityA 執行 bindService
 4 02-07 14:09:39.496 1738-1738/com.demo.kathy.demo I/Kathy: TestTwoService - onCreate - Thread = main
 5 02-07 14:09:39.497 1738-1738/com.demo.kathy.demo I/Kathy: TestTwoService - onBind - Thread = main
 6 02-07 14:09:39.500 1738-1738/com.demo.kathy.demo I/Kathy: ActivityA - onServiceConnected
 7 02-07 14:09:39.500 1738-1738/com.demo.kathy.demo I/Kathy: ActivityA - getRandomNumber = -1046987376
 8 02-07 14:09:50.866 1738-1738/com.demo.kathy.demo I/Kathy: ----------------------------------------------------------------------
 9 02-07 14:09:50.866 1738-1738/com.demo.kathy.demo I/Kathy: ActivityA 執行 unbindService
10 02-07 14:09:50.870 1738-1738/com.demo.kathy.demo I/Kathy: TestTwoService - onUnbind - from = ActivityA
11 02-07 14:09:50.871 1738-1738/com.demo.kathy.demo I/Kathy: TestTwoService - onDestroy - Thread = main

總結調用bindService之后發生的事情:
1.client執行bindService()
2.如果Service不存在,則Service執行onCreate(),onBind()
3.client實例ServiceConnection執行onServiceConnected()方法

總結調用unbindService之后發生的事情:
1.client執行unbindService()
2.client與Service解除綁定連接狀態
3.Service檢測是否還有其他client與其連接,如果沒有Service執行onUnbind()和onDestroy()

測試步驟2

step1: 點擊ActivityA的bindService按鈕
step2: 再點擊ActivityA的Finish按鈕
Log 輸出:

02-07 14:49:16.626 12566-12566/com.demo.kathy.demo I/Kathy: ActivityA - onCreate - Thread = main
02-07 14:49:18.102 12566-12566/com.demo.kathy.demo I/Kathy: ----------------------------------------------------------------------
02-07 14:49:18.102 12566-12566/com.demo.kathy.demo I/Kathy: ActivityA 執行 bindService
02-07 14:49:18.105 12566-12566/com.demo.kathy.demo I/Kathy: TestTwoService - onCreate - Thread = main
02-07 14:49:18.110 12566-12566/com.demo.kathy.demo I/Kathy: TestTwoService - onBind - Thread = main
02-07 14:49:18.112 12566-12566/com.demo.kathy.demo I/Kathy: ActivityA - onServiceConnected
02-07 14:49:18.112 12566-12566/com.demo.kathy.demo I/Kathy: ActivityA - getRandomNumber = -318399886
02-07 14:49:19.540 12566-12566/com.demo.kathy.demo I/Kathy: ----------------------------------------------------------------------
02-07 14:49:19.540 12566-12566/com.demo.kathy.demo I/Kathy: ActivityA 執行 finish
02-07 14:49:19.789 12566-12566/com.demo.kathy.demo I/Kathy: ActivityA - onDestroy
02-07 14:49:19.798 12566-12566/com.demo.kathy.demo I/Kathy: TestTwoService - onUnbind - from = ActivityA
02-07 14:49:19.798 12566-12566/com.demo.kathy.demo I/Kathy: TestTwoService - onDestroy - Thread = main

 

總結:如果client銷毀,那么client會自動與Service解除綁定。

測試步驟3

step1: 點擊ActivityA的bindService按鈕
step2: 點擊ActivityA的startActivity B按鈕,切換到ActivityB
step3: 點擊ActivityB中的bindService按鈕
step4: 點擊ActivityB中的unbindService按鈕
step5: 點擊ActivityB中的Finish按鈕
step6: 點擊ActivityA中的unbindService按鈕
得到Log:

02-07 14:55:04.390 12566-12566/com.demo.kathy.demo I/Kathy: ActivityA - onCreate - Thread = main
02-07 14:55:08.191 12566-12566/com.demo.kathy.demo I/Kathy: ----------------------------------------------------------------------
02-07 14:55:08.191 12566-12566/com.demo.kathy.demo I/Kathy: ActivityA 執行 bindService
02-07 14:55:08.197 12566-12566/com.demo.kathy.demo I/Kathy: TestTwoService - onCreate - Thread = main
02-07 14:55:08.198 12566-12566/com.demo.kathy.demo I/Kathy: TestTwoService - onBind - Thread = main
02-07 14:55:08.205 12566-12566/com.demo.kathy.demo I/Kathy: ActivityA - onServiceConnected
02-07 14:55:08.205 12566-12566/com.demo.kathy.demo I/Kathy: ActivityA - getRandomNumber = -706215542
02-07 14:55:23.261 12566-12566/com.demo.kathy.demo I/Kathy: ----------------------------------------------------------------------
02-07 14:55:23.261 12566-12566/com.demo.kathy.demo I/Kathy: ActivityA 啟動 ActivityB
02-07 14:55:29.239 12566-12566/com.demo.kathy.demo I/Kathy: ----------------------------------------------------------------------
02-07 14:55:29.239 12566-12566/com.demo.kathy.demo I/Kathy: ActivityB 執行 bindService
02-07 14:55:29.241 12566-12566/com.demo.kathy.demo I/Kathy: ActivityB - onServiceConnected
02-07 14:55:29.241 12566-12566/com.demo.kathy.demo I/Kathy: ActivityB - getRandomNumber = 1827572726
02-07 14:55:33.951 12566-12566/com.demo.kathy.demo I/Kathy: ----------------------------------------------------------------------
02-07 14:55:33.951 12566-12566/com.demo.kathy.demo I/Kathy: ActivityB 執行 unbindService
02-07 14:55:36.645 12566-12566/com.demo.kathy.demo I/Kathy: ----------------------------------------------------------------------
02-07 14:55:36.645 12566-12566/com.demo.kathy.demo I/Kathy: ActivityB 執行 finish
02-07 14:55:36.852 12566-12566/com.demo.kathy.demo I/Kathy: ActivityB - onDestroy
02-07 14:55:43.137 12566-12566/com.demo.kathy.demo I/Kathy: ----------------------------------------------------------------------
02-07 14:55:43.137 12566-12566/com.demo.kathy.demo I/Kathy: ActivityA 執行 unbindService
02-07 14:55:43.143 12566-12566/com.demo.kathy.demo I/Kathy: TestTwoService - onUnbind - from = ActivityA
02-07 14:55:43.143 12566-12566/com.demo.kathy.demo I/Kathy: TestTwoService - onDestroy - Thread = main

 

總結bindService的生命周期:
1.點擊ActivityA的bindService按鈕
第一次調用bindService會實例化TestTwoService,然后執行其onBind()方法,得到IBinder類型的實例,將其作為參數傳入ActivityA的ServiceConnection的onServiceConnected方法中,標志着ActivityA與TestTwoService建立了綁定

2.點擊ActivityB中的bindService按鈕
由於TestTwoService已處於運行狀態,所以再次調用bindService不會重新創建它的實例,所以也不會執行TestTwoService的onCreate()方法和onBind()方法。ActivityB與ActivityA共享IBinder實例。此時有兩個client與TestTwoService綁定

3.點擊ActivityB中的unbindService按鈕
ActivityB與TestTwoService解除了綁定,當沒有任何client與Service綁定時,才會執行Service的onUnbind()方法。此時,ActivityA還在綁定連接中,所以不會執行Service的解綁方法

4.點擊ActivityA中的unbindService按鈕
ActivityA執行unbindService之后,ActivityA與TestTwoService就解除綁定了,這樣就沒有client與TestTwoService綁定,這時候Android會銷毀TestTwoService,在銷毀前會先執行TestTwoService的onUnbind()方法,然后才會執行其onDestroy()方法,這樣TestService就銷毀了。

如何保證Service不被殺死?

1. onStartCommand方式中,返回START_STICKY

首先我們來看看onStartCommand都可以返回哪些值:

調用Context.startService方式啟動Service時,如果Android面臨內存匱乏,可能會銷毀當前運行的Service,待內存充足時可以重建Service。而Service被Android系統強制銷毀並再次重建的行為依賴於Service的onStartCommand()方法的返回值。

  • START_NOT_STICKY
    如果返回START_NOT_STICKY,表示當Service運行的進程被Android系統強制殺掉之后,不會重新創建該Service
    。當然如果在其被殺掉之后一段時間又調用了startService,那么該Service又將被實例化。那什么情境下返回該值比較恰當呢?
    如果我們某個Service執行的工作被中斷幾次無關緊要或者對Android內存緊張的情況下需要被殺掉且不會立即重新創建這種行為也可接受,那么我們便可將 onStartCommand的返回值設置為START_NOT_STICKY。
    舉個例子,某個Service需要定時從服務器獲取最新數據:通過一個定時器每隔指定的N分鍾讓定時器啟動Service去獲取服務端的最新數據。當執行到Service的onStartCommand時,在該方法內再規划一個N分鍾后的定時器用於再次啟動該Service並開辟一個新的線程去執行網絡操作。假設Service在從服務器獲取最新數據的過程中被Android系統強制殺掉,Service不會再重新創建,這也沒關系,因為再過N分鍾定時器就會再次啟動該Service並重新獲取數據。

  • START_STICKY
    如果返回START_STICKY,表示Service運行的進程被Android系統強制殺掉之后,Android系統會將該Service依然設置為started狀態(即運行狀態),但是不再保存onStartCommand方法傳入的intent對象,然后Android系統會嘗試再次重新創建該Service,並執行onStartCommand回調方法,但是onStartCommand回調方法的Intent參數為null,也就是onStartCommand方法雖然會執行但是獲取不到intent信息。如果你的Service可以在任意時刻運行或結束都沒什么問題,而且不需要intent信息,那么就可以在onStartCommand方法中返回START_STICKY,比如一個用來播放背景音樂功能的Service就適合返回該值。

  • START_REDELIVER_INTENT
    如果返回START_REDELIVER_INTENT,表示Service運行的進程被Android系統強制殺掉之后,與返回START_STICKY的情況類似,Android系統會將再次重新創建該Service,並執行onStartCommand回調方法,但是不同的是,Android系統會再次將Service在被殺掉之前最后一次傳入onStartCommand方法中的Intent再次保留下來並再次傳入到重新創建后的Service的onStartCommand方法中,這樣我們就能讀取到intent參數。只要返回START_REDELIVER_INTENT,那么onStartCommand重的intent一定不是null。如果我們的Service需要依賴具體的Intent才能運行(需要從Intent中讀取相關數據信息等),並且在強制銷毀后有必要重新創建運行,那么這樣的Service就適合返回START_REDELIVER_INTENT。

2.提高Service的優先級
在AndroidManifest.xml文件中對於intent-filter可以通過android:priority = "1000"這個屬性設置最高優先級,1000是最高值,如果數字越小則優先級越低,同時適用於廣播。

3.提升Service進程的優先級

當系統進程空間緊張時,會依照優先級自動進行進程的回收。
Android將進程分為6個等級,按照優先級由高到低依次為:

  • 前台進程foreground_app
  • 可視進程visible_app
  • 次要服務進程secondary_server
  • 后台進程hiddena_app
  • 內容供應節點content_provider
  • 空進程empty_app
    可以使用startForeground將service放到前台狀態,這樣低內存時,被殺死的概率會低一些。

4.在onDestroy方法里重啟Service
當service走到onDestroy()時,發送一個自定義廣播,當收到廣播時,重新啟動service。

5.系統廣播監聽Service狀態
6.將APK安裝到/system/app,變身為系統級應用




FROM: 

Android Service兩種啟動方式詳解(總結版)

http://www.jianshu.com/p/4c798c91a613

 

Android Service完全解析,關於服務你所需知道的一切(上)

http://blog.csdn.net/guolin_blog/article/details/11952435/

 

Android中Service的使用詳解和注意點(LocalService)

http://www.cnblogs.com/linlf03/p/3296323.html

 

Android四大組件:Service服務史上最全面解析

http://www.jianshu.com/p/d963c55c3ab9


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM