Service概念
Service
是一個可以在后臺執(zhí)行長時間運(yùn)行操作而不提供用戶界面的應(yīng)用組件。服務(wù)可由其他應(yīng)用組件啟動亥曹,而且即使用戶切換到其他應(yīng)用,服務(wù)仍將在后臺繼續(xù)運(yùn)行。 此外埠帕,組件可以綁定到服務(wù),以與之進(jìn)行交互玖绿,甚至是執(zhí)行進(jìn)程間通信 (IPC)敛瓷。 例如,服務(wù)可以處理網(wǎng)絡(luò)事務(wù)斑匪、播放音樂呐籽,執(zhí)行文件 I/O 或與內(nèi)容提供程序交互,而所有這一切均可在后臺進(jìn)行蚀瘸。
需要注意的是:
- Service 不是線程狡蝶,它運(yùn)行于其托管進(jìn)程的main線程之上(Local or Remote)。
- 因為Service仍舊運(yùn)行在main線程上贮勃,如果程序要進(jìn)行某些耗時操作贪惹,則需另開線程,否則就有可能出現(xiàn)主線程阻塞寂嘉,輕則影響用戶體驗奏瞬,重則導(dǎo)致ANR(Application Not Responding)的產(chǎn)生。
Service or Thread?
單從Service的概念來看泉孩,似乎Service可以做的所有工作Thread也能做硼端,那么Service和Thread到底該如何選擇呢?
簡單的說可以通過以下兩個原則進(jìn)行選擇:
- 如果在用戶與應(yīng)用程序進(jìn)行界面交互時需要在主線程之外執(zhí)行后臺工作寓搬,則可以選擇使用線程(比如:處于Activity運(yùn)行狀態(tài)珍昨,需要加載一些大圖片用于界面顯示,頁面BGM的播放等)
- 如果希望在Activity不可見時,應(yīng)用程序仍舊可以在后臺執(zhí)行一些工作則最好使用Service來實現(xiàn)(比如:用戶在Activity顯示狀態(tài)時開啟了網(wǎng)絡(luò)下載的工作并且希望在應(yīng)用程序后臺時下載工作仍然進(jìn)行)曼尊。
可能有些人會想:使用線程也可以在應(yīng)用程序后臺時執(zhí)行下載工作啊酬诀。
針對后臺下載,現(xiàn)在來設(shè)想一下以下情況:
在Activity中開啟下載線程之后骆撇,由于某些情況該Activity被銷毀或重啟了(配置發(fā)生改變瞒御、界面不可見后內(nèi)存過低被系統(tǒng)回收),此時我們將不在持有該下載線程的引用神郊!如果用戶此時想要暫停下載肴裙,我們是沒有辦法對前面創(chuàng)建的下載線程執(zhí)行該操作的!而且也不利于系統(tǒng)資源的管理涌乳。
使用Service還有其他幾個好處:
- 如果在應(yīng)用程序中啟動了服務(wù)蜻懦,則應(yīng)用程序的優(yōu)先級將被提高。僅當(dāng)內(nèi)存過低且必須回收系統(tǒng)資源時進(jìn)程才會被殺死夕晓。
- 如果服務(wù)被系統(tǒng)殺死宛乃,我們可以通過指定onStartCommand的返回值的方式來促使系統(tǒng)在資源可用時重啟服務(wù)。
Service的兩種用法的不同
- 首先我們需要自定義一個Service類蒸辆,代碼如下
public class MyService extends Service {
public MyService() {
}
/**
* 該方法是Service類中的唯一一個abstract方法征炼,必須重寫
* 當(dāng)另一個組件想通過調(diào)用 bindService() 與服務(wù)綁定(例如執(zhí)行 RPC)時,系統(tǒng)將調(diào)用此方法躬贡。
* 在此方法的實現(xiàn)中谆奥,您必須通過返回 IBinder 提供一個接口,供客戶端用來與服務(wù)進(jìn)行通信拂玻。
* 請務(wù)必實現(xiàn)此方法酸些,但如果您并不希望允許綁定,則應(yīng)返回 null檐蚜。
*/
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return null;
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
//系統(tǒng)默認(rèn)返回START_STICKY
//當(dāng)服務(wù)被系統(tǒng)kill之后執(zhí)行重啟服務(wù)的操作
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
}
}
onCreate():當(dāng)服務(wù)首次被使用時魄懂,該方法將會首先被調(diào)用。如果服務(wù)已經(jīng)處于使用狀態(tài)則該方法不會被調(diào)用熬甚。
onStartCommand():當(dāng)其他組件通過startService()啟動服務(wù)時逢渔,該方法將被調(diào)用(onCreate()之后)∠缋ǎ可以通過該方法的返回值控制Service在被系統(tǒng)kill之后的重啟行為:
START_NOT_STICKY:當(dāng)Service被系統(tǒng)kill之后肃廓,將不會執(zhí)行重啟操作。
START_STICKY:當(dāng)Service被系統(tǒng)kill之后诲泌,將會通過調(diào)用onStartCommand()來執(zhí)行重啟操作盲赊。但是傳遞給該方法的Intent將是一個空Intent。適用于不執(zhí)行命令敷扫、但無限期運(yùn)行并等待作業(yè)的媒體播放器(或類似服務(wù))哀蘑。
START_REDELIVER_INTENT:當(dāng)Service被系統(tǒng)kill之后诚卸,將會通過調(diào)用onStartCommand()來執(zhí)行重啟操作。傳遞給該方法的Intent是最后一個傳遞給服務(wù)的Intent.這適用于主動執(zhí)行應(yīng)該立即恢復(fù)的作業(yè)(例如下載文件)的服務(wù)绘迁。onBind():當(dāng)其他組件通過bindService的方式綁定服務(wù)時合溺,該方法將被調(diào)用。需要返回一個借口用來與其他組件之間進(jìn)行通信缀台。該方法必須重寫棠赛,如果不希望Service通過綁定的形式被使用則返回null。
onDestroy():當(dāng)服務(wù)被銷毀時將調(diào)用此方法膛腐。
啟動服務(wù):如果僅通過該方式使用服務(wù)睛约,則在希望銷毀服務(wù)時只需調(diào)用一次stopService()或stopSelf()(無論調(diào)用多少次startService),服務(wù)自動進(jìn)入銷毀流程哲身。
綁定服務(wù):如果僅通過該方式使用服務(wù)辩涝,則在希望銷毀服務(wù)時必須調(diào)用多次unBindService()(與調(diào)用bindService()次數(shù)相對應(yīng))之后,服務(wù)才會進(jìn)入銷毀流程勘天。
啟動+綁定:當(dāng)通過該方式使用服務(wù)時怔揩,不能單純的通過stop和unBind的方式停止服務(wù)。必須同時滿足Service stop并且沒有和任何組件bind的條件才會進(jìn)入銷毀流程误辑。
通過bindService使用服務(wù)的簡單實現(xiàn)代碼:
MainActivity.java
package com.example.myservice;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.support.v7.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
private MyService.Download mDownload;
private ServiceConnection mConnection = new ServiceConnection() {
//iBinder與onBind()返回值對應(yīng)
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
mDownload = (MyService.Download)iBinder;
mDownload.startDownload();
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
mDownload.stopDownload();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(this, MyService.class);
bindService(intent, mConnection, Service.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
unbindService(mConnection);
super.onDestroy();
}
}
MyService.java
package com.example.myservice;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.SystemClock;
import android.util.Log;
public class MyService extends Service {
private final String TAG = "MyService";
private Download mDownload = new Download();
public MyService() {
}
class Download extends Binder {
private int mProgress = 0;
private boolean isRun;
private Thread downThread = new Thread(new Runnable() {
@Override
public void run() {
while (mProgress < 100 && isRun) {
SystemClock.sleep(50);
mProgress++;
Log.d(TAG,"down progress is:"+ mProgress);
}
}
});
public void startDownload() {
isRun = true;
downThread.start();
}
public void stopDownload(){
isRun = false;
}
public int getDownloadProgress(){
return mProgress;
}
}
/**
* 該方法是Service類中的唯一一個abstract方法沧踏,必須重寫
* 當(dāng)另一個組件想通過調(diào)用 bindService() 與服務(wù)綁定(例如執(zhí)行 RPC)時,系統(tǒng)將調(diào)用此方法巾钉。
* 在此方法的實現(xiàn)中,您必須通過返回 IBinder 提供一個接口秘案,供客戶端用來與服務(wù)進(jìn)行通信砰苍。
* 請務(wù)必實現(xiàn)此方法,但如果您并不希望允許綁定阱高,則應(yīng)返回 null赚导。
*/
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return mDownload;
}
@Override
public void onCreate() {
Log.d(TAG, "onCreate");
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
//系統(tǒng)默認(rèn)返回START_STICKY
//當(dāng)服務(wù)被系統(tǒng)kill之后執(zhí)行重啟服務(wù)的操作
Log.d(TAG, "onStartCommand()");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
Log.d(TAG, "onDestroy()");
super.onDestroy();
}
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.myservice">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".MyService"
android:enabled="true"
android:exported="true"></service>
</application>
</manifest>