參考:
一. 什么是服務(wù)
服務(wù)是一個可以在后臺執(zhí)行長時間運行操作而不提供用戶界面的應(yīng)用組件。服務(wù)可由其他應(yīng)用組件啟動跟压,而且即使用戶切換到其他應(yīng)用,服務(wù)仍將在后臺繼續(xù)運行霍转。 此外衷模,組件可以綁定到服務(wù),以與之進行交互硼端,甚至是執(zhí)行進程間通信 (IPC)。 例如棵譬,服務(wù)可以處理網(wǎng)絡(luò)事務(wù)显蝌、播放音樂,執(zhí)行文件 I/O 或與內(nèi)容提供程序交互订咸,而所有這一切均可在后臺進行曼尊。
二. 服務(wù)的兩種啟動方式
- start模式啟動
- 當應(yīng)用組件(如 Activity)通過調(diào)用
startService()
啟動服務(wù)時,服務(wù)即處于“啟動”狀態(tài)脏嚷。一旦啟動骆撇,服務(wù)即可在后臺無限期運行,即使啟動服務(wù)的組件已被銷毀也不受影響父叙。已啟動的服務(wù)通常是執(zhí)行單一操作神郊,而且不會將結(jié)果返回給調(diào)用方。例如趾唱,它可能通過網(wǎng)絡(luò)下載或上傳文件涌乳。 操作完成后,服務(wù)會自行停止運行甜癞。
- bind模式啟動
- 當應(yīng)用組件通過調(diào)用
bindService()
綁定到服務(wù)時夕晓,服務(wù)即處于“綁定”狀態(tài)(與活動綁定)。綁定服務(wù)提供了一個客戶端-服務(wù)器接口悠咱,允許組件與服務(wù)進行交互蒸辆、發(fā)送請求征炼、獲取結(jié)果,甚至是利用進程間通信 (IPC) 跨進程執(zhí)行這些操作躬贡。 僅當與另一個應(yīng)用組件綁定時谆奥,綁定服務(wù)才會運行。 多個組件可以同時綁定到該服務(wù)拂玻,但全部取消綁定后酸些,該服務(wù)即會被銷毀。
三. Start模式啟動服務(wù)
- 該模式會回調(diào)的方法
/**
* 該方法是必須實現(xiàn)的抽象方法纺讲,用于bind模式啟動擂仍,在調(diào)用bindService之后會調(diào)用,用于將服務(wù)和調(diào)用組件進行綁定熬甚。在start模式的時候返回null即可
* @param intent 用來綁定該服務(wù)的intent
* @return 返回的就是自定義的用于操作服務(wù)的接口
*/
@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;
}
/**
* 該方法在服務(wù)只在第一次啟動/綁定的時候會調(diào)用
*/
@Override
public void onCreate() {
Log.i(TAG, "onCreate: ");
super.onCreate();
}
/**
*
* 該方法在每次調(diào)用startService方法的時候都會調(diào)用,
* 一旦執(zhí)行此方法乡括,服務(wù)即會啟動并可在后臺無限期運行。
* 服務(wù)工作完成后诲泌,需要通過調(diào)用 stopSelf() 或 stopService() 來停止服務(wù)。
*
* @param intent 就是用來啟動該服務(wù)的Intent,傳遞的數(shù)據(jù)可以通過該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);
}
/**
* 該方法在調(diào)用stopService/unbindService方法的時候會調(diào)用敷扫,服務(wù)停止運行
*/
@Override
public void onDestroy() {
Log.i(TAG, "onDestroy: ");
super.onDestroy();
}
- 使用該模式啟動服務(wù)的步驟
- 新建sevice類繼承自Service
- 至少實現(xiàn)onStartCommand回調(diào)方法
- 注冊服務(wù)
- 調(diào)用startService(Intent)方法啟動該服務(wù),該服務(wù)就會一直在后臺運行绘迁,直到調(diào)用stopService(Intent)或stopSelf(Intent)方法
四. bind方式啟動服務(wù)
- 該模式會回調(diào)的方法
@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;
}
/**
* 當與服務(wù)連接的所有組件都斷開連接的時候調(diào)用(Called when all clients have disconnected
* from a particular interface published by the service.
* The default implementation does nothing and returns false.)
*
* @param intent 用來綁定服務(wù)的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);
}
/**
* 當有新的組件綁定到服務(wù)的時候會調(diào)用該方法
* @param intent 用來綁定服務(wù)的intent
*/
@Override
public void onRebind(Intent intent) {
Log.i(TAG, "onRebind: ");
super.onRebind(intent);
}
- 使用bind方式啟動服務(wù)的步驟
- 創(chuàng)建Binder類作為操作服務(wù)的接口
/**
* 用于Activity操作Service的類卒密,給與服務(wù)綁定的組件一個操作服務(wù)的接口
*/
class DownloadBinder extends Binder{
public void startDownload(){
Log.i(TAG, "startDownload: ");
}
public void getProgress(){
Log.i(TAG, "getProgress: ");
}
}
- 實現(xiàn)onBind方法并返回Binder對象
- Activity實現(xiàn)ServiceConnection類缀台,重寫
onServiceConnected
和onServiceDisconnected
方法
private MyService.DownloadBinder mDownloadBinder;//Binder對象,用來操作服務(wù)
private ServiceConnection mConnection = new ServiceConnection() {
/**
* 活動與服務(wù)綁定的時候調(diào)用
* @param name
* @param service
*/
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.i(TAG, "onServiceConnected: name " + name);
mDownloadBinder = (MyService.DownloadBinder) service;//將IBinder參數(shù)強轉(zhuǎn)哮奇,實例化自定義的Binder對象
mDownloadBinder.startDownload();//調(diào)用Binder的方法操作服務(wù)
mDownloadBinder.getProgress();
}
/**
* 活動與服務(wù)解綁的時候調(diào)用
* @param name
*/
@Override
public void onServiceDisconnected(ComponentName name) {
Log.i(TAG, "onServiceDisconnected: ");
}
};
- 調(diào)用bindService方法進行綁定
/*參數(shù):Intent,ServiceConnection,標志位(BIND_AUTO_CREATE表示活動與服務(wù)綁定之后自動創(chuàng)建服務(wù))*/
bindService(startService, mConnection, BIND_AUTO_CREATE);
五. 前臺服務(wù)
- 通過調(diào)用startForeground方法(借助通知)使服務(wù)顯示在通知欄就是前臺服務(wù)(如通知欄顯示的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膛腐?
- 因為服務(wù)是運行在主線程的,所以當需要進行耗時操作的時候鼎俘,就需要在服務(wù)內(nèi)開啟子線程哲身,然后在運行結(jié)束的時候調(diào)用stopSelf方法結(jié)束該服務(wù)。為了避免這一大堆麻煩事贸伐,IntentService就誕生了律罢。
- IntentService做的事(來自官網(wǎng))
- 創(chuàng)建默認的工作線程,用于在應(yīng)用的主線程外執(zhí)行傳遞給 [onStartCommand()](https://developer.android.com/reference/android/app/Service.html?hl=zh-cn#onStartCommand(android.content.Intent, int, int))的所有 Intent。
- 創(chuàng)建工作隊列误辑,用于將 Intent 逐一傳遞給 onHandleIntent()實現(xiàn)孙技,這樣您就永遠不必擔心多線程問題辅髓。
- 在處理完所有啟動請求后停止服務(wù)饮怯,因此您永遠不必調(diào)用 stopSelf()狠毯。
- 提供 onBind()的默認實現(xiàn)(返回 null)遗锣。
- 提供 [onStartCommand()](https://developer.android.com/reference/android/app/IntentService.html?hl=zh-cn#onStartCommand(android.content.Intent, int, int))的默認實現(xiàn)生真,可將 Intent 依次發(fā)送到工作隊列和 onHandleIntent()實現(xiàn)铣除。
- 綜上所述闹丐,您只需實現(xiàn) onHandleIntent()來完成客戶端提供的工作即可阱高。(不過赚导,您還需要為服務(wù)提供小型構(gòu)造函數(shù)。)
- 使用方法
- 新建一個類繼承自IntentService
- 實現(xiàn)無參構(gòu)造函數(shù)并調(diào)用父類的有參構(gòu)造函數(shù)(必須)
- 實現(xiàn)onHandleIntent方法(該方法運行在子線程)赤惊,在該方法內(nèi)進行想要的操作
public class MyIntentService extends IntentService {
private final String TAG = getClass().getSimpleName();
/**
* 必須實現(xiàn)的無參構(gòu)造函數(shù)吼旧,且必須調(diào)用父類有參構(gòu)造方法
*/
public MyIntentService(){
super("MyIntentService");
}
/**
* 處理具體邏輯的抽象方法,該方法運行在子線程
* @param intent 啟動服務(wù)用的intent
*/
@Override
protected void onHandleIntent(@Nullable Intent intent) {
Log.i(TAG, "onHandleIntent: ");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 服務(wù)銷毀時會調(diào)用
*/
@Override
public void onDestroy() {
Log.i(TAG, "onDestroy: ");
super.onDestroy();
}
}
- 正常調(diào)用startService方法啟動服務(wù)
七. 源碼
- 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對象未舟,用來操作服務(wù)
private ServiceConnection mConnection = new ServiceConnection() {
/**
* 活動與服務(wù)綁定的時候調(diào)用
* @param name
* @param service
*/
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.i(TAG, "onServiceConnected: name " + name);
mDownloadBinder = (MyService.DownloadBinder) service;//將IBinder參數(shù)強轉(zhuǎn)圈暗,實例化自定義的Binder對象
mDownloadBinder.startDownload();//調(diào)用Binder的方法操作服務(wù)
mDownloadBinder.getProgress();
}
/**
* 活動與服務(wù)解綁的時候調(diào)用
* @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:
/*參數(shù):Intent,ServiceConnection,標志位(BIND_AUTO_CREATE表示活動與服務(wù)綁定之后自動創(chuàng)建服務(wù))*/
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的類,給與服務(wù)綁定的組件一個操作服務(wù)的接口
*/
class DownloadBinder extends Binder{
public void startDownload(){
Log.i(TAG, "startDownload: ");
}
public void getProgress(){
Log.i(TAG, "getProgress: ");
}
}
public MyService() {
}
/**
* 該方法是必須實現(xiàn)的抽象方法寸齐,用于bind模式啟動抄谐;
* 在調(diào)用bindService之后會調(diào)用斯稳,用于將服務(wù)和調(diào)用組件進行綁定。
* 在start模式的時候返回null即可
* @param intent 用來綁定該服務(wù)的intent
* @return 返回的就是自定義的用于操作服務(wù)的接口
*/
@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;
}
/**
* 當與服務(wù)連接的所有組件都斷開連接的時候調(diào)用(Called when all clients have disconnected
* from a particular interface published by the service.
* The default implementation does nothing and returns false.)
*
* @param intent 用來綁定服務(wù)的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);
}
/**
* 當有新的組件綁定到服務(wù)的時候會調(diào)用該方法
* @param intent 用來綁定服務(wù)的intent
*/
@Override
public void onRebind(Intent intent) {
Log.i(TAG, "onRebind: ");
super.onRebind(intent);
}
/**
* 該方法在服務(wù)只在第一次啟動/綁定的時候會調(diào)用
*/
@Override
public void onCreate() {
Log.i(TAG, "onCreate: ");
/*//將該服務(wù)變成前臺服務(wù)
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();
}
/**
*
* 該方法在每次調(diào)用startService方法的時候都會調(diào)用,
* 一旦執(zhí)行此方法憎茂,服務(wù)即會啟動并可在后臺無限期運行。
* 服務(wù)工作完成后板乙,需要通過調(diào)用 stopSelf() 或 stopService() 來停止服務(wù)蛋铆。
*
* @param intent 就是用來啟動該服務(wù)的Intent,傳遞的數(shù)據(jù)可以通過該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);
}
/**
* 該方法在調(diào)用stopService/unbindService方法的時候會調(diào)用放接,服務(wù)停止運行
*/
@Override
public void onDestroy() {
Log.i(TAG, "onDestroy: ");
super.onDestroy();
}
}
- MyIntentService
public class MyIntentService extends IntentService {
private final String TAG = getClass().getSimpleName();
/**
* 必須實現(xiàn)的無參構(gòu)造函數(shù)玛瘸,且必須調(diào)用父類有參構(gòu)造方法
*/
public MyIntentService(){
super("MyIntentService");
}
/**
* 處理具體邏輯的抽象方法,該方法運行在子線程
* @param intent 啟動服務(wù)用的intent
*/
@Override
protected void onHandleIntent(@Nullable Intent intent) {
Log.i(TAG, "onHandleIntent: ");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 服務(wù)銷毀時會調(diào)用
*/
@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>
七. 注意
- 服務(wù)并不是運行在獨立的進程中的,而是依賴于創(chuàng)建服務(wù)的應(yīng)用程序進程渺绒,當創(chuàng)建該服務(wù)的進程被殺掉時芒篷,服務(wù)也會停止采缚。
- 服務(wù)的所有代碼都運行在主線程中扳抽,并不會自己開啟線程贸呢。