參考:
一. 什么是服務
服務是一個可以在后臺執行長時間運行操作而不提供用戶界面的應用組件。服務可由其他應用組件啟動,而且即使用戶切換到其他應用,服務仍將在后臺繼續運行。 此外,組件可以綁定到服務,以與之進行交互,甚至是執行進程間通信 (IPC)。 例如,服務可以處理網絡事務、播放音樂,執行文件 I/O 或與內容提供程序交互,而所有這一切均可在后臺進行。
二. 服務的兩種啟動方式
- start模式啟動
- 當應用組件(如 Activity)通過調用
startService()
啟動服務時,服務即處于“啟動”狀態。一旦啟動,服務即可在后臺無限期運行,即使啟動服務的組件已被銷毀也不受影響。已啟動的服務通常是執行單一操作,而且不會將結果返回給調用方。例如,它可能通過網絡下載或上傳文件。 操作完成后,服務會自行停止運行。
- bind模式啟動
- 當應用組件通過調用
bindService()
綁定到服務時,服務即處于“綁定”狀態(與活動綁定)。綁定服務提供了一個客戶端-服務器接口,允許組件與服務進行交互、發送請求、獲取結果,甚至是利用進程間通信 (IPC) 跨進程執行這些操作。 僅當與另一個應用組件綁定時,綁定服務才會運行。 多個組件可以同時綁定到該服務,但全部取消綁定后,該服務即會被銷毀。
三. Start模式啟動服務
- 該模式會回調的方法
/**
* 該方法是必須實現的抽象方法,用于bind模式啟動,在調用bindService之后會調用,用于將服務和調用組件進行綁定。在start模式的時候返回null即可
* @param intent 用來綁定該服務的intent
* @return 返回的就是自定義的用于操作服務的接口
*/
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "onBind: ");
Log.i(TAG, "onBind: intent "+intent);
Log.i(TAG, "onBind: data "+intent.getStringExtra("data"));
return mDownloadBinder;
}
/**
* 該方法在服務只在第一次啟動/綁定的時候會調用
*/
@Override
public void onCreate() {
Log.i(TAG, "onCreate: ");
super.onCreate();
}
/**
*
* 該方法在每次調用startService方法的時候都會調用,
* 一旦執行此方法,服務即會啟動并可在后臺無限期運行。
* 服務工作完成后,需要通過調用 stopSelf() 或 stopService() 來停止服務。
*
* @param intent 就是用來啟動該服務的Intent,傳遞的數據可以通過該intent拿出來
* @param flags
* @param startId
* @return
*/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "onStartCommand: ");
Log.i(TAG, "onStartCommand: "+intent);
Log.i(TAG, "onStartCommand: data "+intent.getStringExtra("data"));
Log.i(TAG, "onStartCommand: flags "+flags);
Log.i(TAG, "onStartCommand: startId "+startId);
return super.onStartCommand(intent, flags, startId);
}
/**
* 該方法在調用stopService/unbindService方法的時候會調用,服務停止運行
*/
@Override
public void onDestroy() {
Log.i(TAG, "onDestroy: ");
super.onDestroy();
}
- 使用該模式啟動服務的步驟
- 新建sevice類繼承自Service
- 至少實現onStartCommand回調方法
- 注冊服務
- 調用startService(Intent)方法啟動該服務,該服務就會一直在后臺運行,直到調用stopService(Intent)或stopSelf(Intent)方法
四. bind方式啟動服務
- 該模式會回調的方法
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "onBind: ");
Log.i(TAG, "onBind: intent "+intent);
Log.i(TAG, "onBind: data "+intent.getStringExtra("data"));
return mDownloadBinder;
}
/**
* 當與服務連接的所有組件都斷開連接的時候調用(Called when all clients have disconnected
* from a particular interface published by the service.
* The default implementation does nothing and returns false.)
*
* @param intent 用來綁定服務的intent
* @return 默認返回false
*/
@Override
public boolean onUnbind(Intent intent) {
Log.i(TAG, "onUnbind: ");
Log.i(TAG, "onUnbind: intent "+intent.getStringExtra("data"));
return super.onUnbind(intent);
}
/**
* 當有新的組件綁定到服務的時候會調用該方法
* @param intent 用來綁定服務的intent
*/
@Override
public void onRebind(Intent intent) {
Log.i(TAG, "onRebind: ");
super.onRebind(intent);
}
- 使用bind方式啟動服務的步驟
- 創建Binder類作為操作服務的接口
/**
* 用于Activity操作Service的類,給與服務綁定的組件一個操作服務的接口
*/
class DownloadBinder extends Binder{
public void startDownload(){
Log.i(TAG, "startDownload: ");
}
public void getProgress(){
Log.i(TAG, "getProgress: ");
}
}
- 實現onBind方法并返回Binder對象
- Activity實現ServiceConnection類,重寫
onServiceConnected
和onServiceDisconnected
方法
private MyService.DownloadBinder mDownloadBinder;//Binder對象,用來操作服務
private ServiceConnection mConnection = new ServiceConnection() {
/**
* 活動與服務綁定的時候調用
* @param name
* @param service
*/
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.i(TAG, "onServiceConnected: name " + name);
mDownloadBinder = (MyService.DownloadBinder) service;//將IBinder參數強轉,實例化自定義的Binder對象
mDownloadBinder.startDownload();//調用Binder的方法操作服務
mDownloadBinder.getProgress();
}
/**
* 活動與服務解綁的時候調用
* @param name
*/
@Override
public void onServiceDisconnected(ComponentName name) {
Log.i(TAG, "onServiceDisconnected: ");
}
};
- 調用bindService方法進行綁定
/*參數:Intent,ServiceConnection,標志位(BIND_AUTO_CREATE表示活動與服務綁定之后自動創建服務)*/
bindService(startService, mConnection, BIND_AUTO_CREATE);
五. 前臺服務
- 通過調用startForeground方法(借助通知)使服務顯示在通知欄就是前臺服務(如通知欄顯示的QQ這種)
- 使用方法(該段代碼寫在Service類的onCreate方法中)
Intent intent = new Intent(this,MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,0,intent,0);
Notification notification = new NotificationCompat.Builder(this)
.setContentTitle("This is title")
.setContentText("This is text")
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.mipmap.ic_launcher)
.setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher))
.setContentIntent(pendingIntent)
.build();
startForeground(1,notification);
六. IntentService
- 什么是IntentService?
- 因為服務是運行在主線程的,所以當需要進行耗時操作的時候,就需要在服務內開啟子線程,然后在運行結束的時候調用stopSelf方法結束該服務。為了避免這一大堆麻煩事,IntentService就誕生了。
- IntentService做的事(來自官網)
- 創建默認的工作線程,用于在應用的主線程外執行傳遞給 [onStartCommand()](https://developer.android.com/reference/android/app/Service.html?hl=zh-cn#onStartCommand(android.content.Intent, int, int))的所有 Intent。
- 創建工作隊列,用于將 Intent 逐一傳遞給 onHandleIntent()實現,這樣您就永遠不必擔心多線程問題。
- 在處理完所有啟動請求后停止服務,因此您永遠不必調用 stopSelf()。
- 提供 onBind()的默認實現(返回 null)。
- 提供 [onStartCommand()](https://developer.android.com/reference/android/app/IntentService.html?hl=zh-cn#onStartCommand(android.content.Intent, int, int))的默認實現,可將 Intent 依次發送到工作隊列和 onHandleIntent()實現。
- 綜上所述,您只需實現 onHandleIntent()來完成客戶端提供的工作即可。(不過,您還需要為服務提供小型構造函數。)
- 使用方法
- 新建一個類繼承自IntentService
- 實現無參構造函數并調用父類的有參構造函數(必須)
- 實現onHandleIntent方法(該方法運行在子線程),在該方法內進行想要的操作
public class MyIntentService extends IntentService {
private final String TAG = getClass().getSimpleName();
/**
* 必須實現的無參構造函數,且必須調用父類有參構造方法
*/
public MyIntentService(){
super("MyIntentService");
}
/**
* 處理具體邏輯的抽象方法,該方法運行在子線程
* @param intent 啟動服務用的intent
*/
@Override
protected void onHandleIntent(@Nullable Intent intent) {
Log.i(TAG, "onHandleIntent: ");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 服務銷毀時會調用
*/
@Override
public void onDestroy() {
Log.i(TAG, "onDestroy: ");
super.onDestroy();
}
}
- 正常調用startService方法啟動服務
七. 源碼
- MainActivity
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
/**
* start service
*/
private Button mBtStartService;
/**
* stop service
*/
private Button mBtStopService;
private final String TAG = getClass().getSimpleName();
/**
* bind service
*/
private Button mBindService;
/**
* unbind service
*/
private Button mUnbindService;
private MyService.DownloadBinder mDownloadBinder;//Binder對象,用來操作服務
private ServiceConnection mConnection = new ServiceConnection() {
/**
* 活動與服務綁定的時候調用
* @param name
* @param service
*/
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.i(TAG, "onServiceConnected: name " + name);
mDownloadBinder = (MyService.DownloadBinder) service;//將IBinder參數強轉,實例化自定義的Binder對象
mDownloadBinder.startDownload();//調用Binder的方法操作服務
mDownloadBinder.getProgress();
}
/**
* 活動與服務解綁的時候調用
* @param name
*/
@Override
public void onServiceDisconnected(ComponentName name) {
Log.i(TAG, "onServiceDisconnected: ");
}
};
/**
* start service
*/
private Button mBtBindService;
/**
* start activity
*/
private Button mBtStartActivity;
/**
* start intentservice
*/
private Button mStartIntentService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
@Override
public void onClick(View v) {
Intent startService = new Intent(MainActivity.this, MyService.class);
startService.putExtra("data", "data");
switch (v.getId()) {
case R.id.bt_bind_service:
Log.i(TAG, "onClick: " + startService);
startService(startService);
break;
case R.id.bt_stop_service:
//Log.i(TAG, "onClick: "+startService);
stopService(startService);
break;
case R.id.bind_service:
/*參數:Intent,ServiceConnection,標志位(BIND_AUTO_CREATE表示活動與服務綁定之后自動創建服務)*/
bindService(startService, mConnection, BIND_AUTO_CREATE);
//mDownloadBinder.startDownload();
break;
case R.id.unbind_service:
unbindService(mConnection);
break;
case R.id.bt_start_activity:
startActivity(new Intent(MainActivity.this, SecondActivity.class));
break;
case R.id.start_intent_service:
startService(new Intent(MainActivity.this,MyIntentService.class));
break;
}
}
private void initView() {
mBtStartService = (Button) findViewById(R.id.bt_bind_service);
mBtStartService.setOnClickListener(this);
mBtStopService = (Button) findViewById(R.id.bt_stop_service);
mBtStopService.setOnClickListener(this);
mBindService = (Button) findViewById(R.id.bind_service);
mBindService.setOnClickListener(this);
mUnbindService = (Button) findViewById(R.id.unbind_service);
mUnbindService.setOnClickListener(this);
mBtBindService = (Button) findViewById(R.id.bt_bind_service);
mBtBindService.setOnClickListener(this);
mBtStartActivity = (Button) findViewById(R.id.bt_start_activity);
mBtStartActivity.setOnClickListener(this);
mStartIntentService = (Button) findViewById(R.id.start_intent_service);
mStartIntentService.setOnClickListener(this);
}
}
- MyService
public class MyService extends Service {
private final String TAG = getClass().getSimpleName();
private DownloadBinder mDownloadBinder = new DownloadBinder();
/**
* 用于Activity操作Service的類,給與服務綁定的組件一個操作服務的接口
*/
class DownloadBinder extends Binder{
public void startDownload(){
Log.i(TAG, "startDownload: ");
}
public void getProgress(){
Log.i(TAG, "getProgress: ");
}
}
public MyService() {
}
/**
* 該方法是必須實現的抽象方法,用于bind模式啟動;
* 在調用bindService之后會調用,用于將服務和調用組件進行綁定。
* 在start模式的時候返回null即可
* @param intent 用來綁定該服務的intent
* @return 返回的就是自定義的用于操作服務的接口
*/
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "onBind: ");
Log.i(TAG, "onBind: intent "+intent);
Log.i(TAG, "onBind: data "+intent.getStringExtra("data"));
return mDownloadBinder;
}
/**
* 當與服務連接的所有組件都斷開連接的時候調用(Called when all clients have disconnected
* from a particular interface published by the service.
* The default implementation does nothing and returns false.)
*
* @param intent 用來綁定服務的intent
* @return 默認返回false
*/
@Override
public boolean onUnbind(Intent intent) {
Log.i(TAG, "onUnbind: ");
Log.i(TAG, "onUnbind: intent "+intent.getStringExtra("data"));
return super.onUnbind(intent);
}
/**
* 當有新的組件綁定到服務的時候會調用該方法
* @param intent 用來綁定服務的intent
*/
@Override
public void onRebind(Intent intent) {
Log.i(TAG, "onRebind: ");
super.onRebind(intent);
}
/**
* 該方法在服務只在第一次啟動/綁定的時候會調用
*/
@Override
public void onCreate() {
Log.i(TAG, "onCreate: ");
/*//將該服務變成前臺服務
Intent intent = new Intent(this,MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,0,intent,0);
Notification notification = new NotificationCompat.Builder(this)
.setContentTitle("This is title")
.setContentText("This is text")
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.mipmap.ic_launcher)
.setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher))
.setContentIntent(pendingIntent)
.build();
startForeground(1,notification);*/
super.onCreate();
}
/**
*
* 該方法在每次調用startService方法的時候都會調用,
* 一旦執行此方法,服務即會啟動并可在后臺無限期運行。
* 服務工作完成后,需要通過調用 stopSelf() 或 stopService() 來停止服務。
*
* @param intent 就是用來啟動該服務的Intent,傳遞的數據可以通過該intent拿出來
* @param flags
* @param startId
* @return
*/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "onStartCommand: ");
Log.i(TAG, "onStartCommand: "+intent);
Log.i(TAG, "onStartCommand: data "+intent.getStringExtra("data"));
Log.i(TAG, "onStartCommand: flags "+flags);
Log.i(TAG, "onStartCommand: startId "+startId);
return super.onStartCommand(intent, flags, startId);
}
/**
* 該方法在調用stopService/unbindService方法的時候會調用,服務停止運行
*/
@Override
public void onDestroy() {
Log.i(TAG, "onDestroy: ");
super.onDestroy();
}
}
- MyIntentService
public class MyIntentService extends IntentService {
private final String TAG = getClass().getSimpleName();
/**
* 必須實現的無參構造函數,且必須調用父類有參構造方法
*/
public MyIntentService(){
super("MyIntentService");
}
/**
* 處理具體邏輯的抽象方法,該方法運行在子線程
* @param intent 啟動服務用的intent
*/
@Override
protected void onHandleIntent(@Nullable Intent intent) {
Log.i(TAG, "onHandleIntent: ");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 服務銷毀時會調用
*/
@Override
public void onDestroy() {
Log.i(TAG, "onDestroy: ");
super.onDestroy();
}
}
- manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="cn.foxnickel.servicedemo">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
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>
<service android:name=".MyIntentService"/>
<activity android:name=".SecondActivity">
</activity>
</application>
</manifest>
七. 注意
- 服務并不是運行在獨立的進程中的,而是依賴于創建服務的應用程序進程,當創建該服務的進程被殺掉時,服務也會停止。
- 服務的所有代碼都運行在主線程中,并不會自己開啟線程。