目錄:
Service定義
官方對服務(wù)的解釋
通俗的解釋
服務(wù)能做什么
服務(wù)的生命周期
通常服務(wù)有兩種形式
創(chuàng)建一個(gè)最簡單的服務(wù)
服務(wù)的回調(diào)函數(shù)詳解
一 啟動(dòng)型startService
二 綁定Service
創(chuàng)建一個(gè)前臺服務(wù)
IntentService(異步的试躏,會(huì)自動(dòng)停止的服務(wù))
注意事項(xiàng)
實(shí)戰(zhàn)
- 音樂播放器
- 文件下載器
Service定義
官方對服務(wù)的解釋
- Android四大組件之一
- Service是一個(gè)可以在后臺執(zhí)行長時(shí)間運(yùn)行操作而不提供用戶界面的應(yīng)用組件拜效。
- 服務(wù)可由其他應(yīng)用組件啟動(dòng)抱虐,而且即使用戶切換到其他應(yīng)用昆禽,服務(wù)仍將在后臺繼續(xù)運(yùn)行俏让。
- 組件可以綁定到服務(wù)晶渠,以與之進(jìn)行交互侦镇,甚至是執(zhí)行進(jìn)程間通信 (IPC)
通俗的解釋
- 服務(wù)是Android中實(shí)現(xiàn)程序后臺的解決方案乾吻,不依賴任何用戶界面髓梅,即使程序被切換到后臺,或者用戶打開了另外一個(gè)應(yīng)用程序绎签,服務(wù)還能保持運(yùn)行
- 服務(wù)非常適合那些不需要和用戶交互而且還要求長期運(yùn)行的任務(wù)
- 服務(wù)依賴于創(chuàng)建服務(wù)是所在的應(yīng)用程序進(jìn)程枯饿。當(dāng)宿主應(yīng)用進(jìn)程被殺,所有依賴該進(jìn)程會(huì)馬上停止運(yùn)行
- 服務(wù)像Activity那樣也是默認(rèn)運(yùn)行在主線程中诡必,如果有耗時(shí)任務(wù)還是要在服務(wù)內(nèi)部創(chuàng)建子線程奢方,不然程序會(huì)GG。
服務(wù)能做什么
- 用于處理網(wǎng)絡(luò)事務(wù)(下載文件)
- 播放音樂(音樂播放器)
- 執(zhí)行文件I/O(讀寫文件)
- 與內(nèi)容提供器進(jìn)行交互
服務(wù)的生命周期
- 服務(wù)像Activity那樣有自己的生命周期,但是沒有Activity那么復(fù)雜
通常服務(wù)有兩種形式
啟動(dòng)
應(yīng)用組件(如 Activity)通過調(diào)用 startService() 啟動(dòng)服務(wù),以無限期運(yùn)行
- 一旦啟動(dòng)愉老,服務(wù)即可在后臺無限期運(yùn)行场绿,即使啟動(dòng)服務(wù)的組件已被銷毀也不受影響。
- 已啟動(dòng)的服務(wù)通常是執(zhí)行單一操作嫉入,而且不會(huì)將結(jié)果返回給調(diào)用方焰盗。(通俗的說就是邏輯控制寫死在Service里了,不能用別的應(yīng)用組件控制咒林,孤獨(dú)走完自己的生命周期熬拒,而且還是被父母規(guī)劃好的)
綁定
應(yīng)用組件通過調(diào)用 bindService() 綁定到服務(wù)(其實(shí)這個(gè)時(shí)候也啟動(dòng)了服務(wù))
- 提供了一個(gè)客戶端-服務(wù)器接口,允許組件與服務(wù)進(jìn)行交互垫竞、發(fā)送請求澎粟、獲取結(jié)果,甚至是利用進(jìn)程間通信 (IPC) 跨進(jìn)程執(zhí)行這些操作欢瞪。 (可以控制的活烙,基本靠自己行走江湖。盲仔:我用雙手成就夢想)
- 僅當(dāng)與另一個(gè)應(yīng)用組件綁定時(shí)遣鼓,綁定服務(wù)才會(huì)運(yùn)行啸盏。
- 多個(gè)組件可以同時(shí)綁定到該服務(wù),但全部取消綁定后骑祟,該服務(wù)即會(huì)被銷毀回懦。
需要注意的是服務(wù)可以同時(shí)以這兩種方式運(yùn)行,看上面的服務(wù)的生命周期可以看出次企,問題只是在于你是否實(shí)現(xiàn)了一組回調(diào)方法
onStartCommand()(允許組件啟動(dòng)服務(wù))和 onBind()(允許綁定服務(wù))
創(chuàng)建一個(gè)最簡單的服務(wù)
- ** 啟動(dòng)startService()形式**
新建一個(gè)類MyService
public class MyService extends Service {
private static final String TAG = "MyService";
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy");
}
}
Activity中MainActivity
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private static final String TAG = "MainActivity";
private Button mStartService;
private Button mStopService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
iniView();
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.button_start_service:
Intent startIntent = new Intent(MainActivity.this, MyService.class);
//startService啟動(dòng)形式
startService(startIntent);
break;
case R.id.button_stop_service:
Intent stopIntent = new Intent(MainActivity.this, MyService.class);
//“啟動(dòng)”服務(wù)的停止
stopService(stopIntent);
break;
}
}
private void iniView() {
mStartService = (Button) findViewById(R.id.button_start_service);
mStopService = (Button) findViewById(R.id.button_stop_service);
mStartService.setOnClickListener(this);
mStopService.setOnClickListener(this);
}
}
記得在AndroidManifest文件中注冊服務(wù)
<service android:name=".service.MyService"/>
布局文件是2個(gè)按鈕mStartService 怯晕,mStopService
這樣子最簡單的 “啟動(dòng)”服務(wù)就寫好了,看下
模擬器采用的是Android7.0的分屏功能抒巢,上面是我們的demo贫贝,下半部分是開發(fā)者選項(xiàng)里的正在運(yùn)行的服務(wù)
可以看到,我們點(diǎn)擊startService時(shí)蛉谜,Activity啟動(dòng)了“啟動(dòng)”服務(wù)稚晚,開始跑“啟動(dòng)”服務(wù)的生命周期onCreate()-->onStartCommand(),同時(shí)正在系統(tǒng)多了個(gè)MyService的服務(wù)
當(dāng)我們按下stopService后型诚,生命周期走到了盡頭onDestroy()客燕,同時(shí)系統(tǒng)MyService服務(wù)消失
- ** 綁定bindService()形式**
剛剛的“啟動(dòng)”服務(wù)里面實(shí)現(xiàn)的onBind方法返回的是null,
這是“啟動(dòng)”服務(wù)和“綁定”服務(wù)的的區(qū)別所在
有3種狰贯,分別是擴(kuò)展 Binder 類(這里用的也搓,也是常用的)
使用 Messenger赏廓,使用 AIDL(這兩種以后再更新)
首先在服務(wù)MyService里面新建一個(gè)內(nèi)部類MyBinder
public class MyBinder extends Binder {
public void StartBB() {
Log.d(TAG, "StartBB:BBBBBBBBBB");
}
}
然后在MyService里直接實(shí)例化
private MyBinder mBinder = new MyBinder();
然后將onBind的返回值改為MyBinder的對象mBinder
接著在Activity中添加ServiceConnection 的實(shí)現(xiàn),其實(shí)就是Activity與Service合作的紐帶傍妒,后面會(huì)細(xì)說
private ServiceConnection mConnection = new ServiceConnection() {
//bind服務(wù)幔摸,onCreate之后
@Override
public void onServiceConnected(ComponentName name, IBinder binder) {
mBinder = (MyService.MyBinder) binder;
mBinder.StartBB();
Log.d(TAG, "onServiceConnected");
}
//unBind服務(wù)時(shí),在onDestroy之前
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG, "onServiceDisconnected");
}
};
初始化一下MyBinder
private MyService.MyBinder mBinder;
添加倆個(gè)按鈕去控制“綁定服務(wù)”
case R.id.bind_service:
Intent bindIntent = new Intent(MainActivity.this, MyService.class);
//這里的三個(gè)參數(shù)后面會(huì)細(xì)說
bindService(bindIntent, mConnection, BIND_AUTO_CREATE);
break;
case R.id.unbind_sservice:
//多次取消綁定程序會(huì)crash
unbindService(mConnection);
break;
然后看
可以看到生命周期 為onCreate-->onBind-->onDestroy
onServiceConnected是ServiceConnection紐帶的回調(diào)
服務(wù)的回調(diào)函數(shù)
一 啟動(dòng)型startService
- onCreate與onDestroy和Activity一樣颤练,
onStartCommand
int onStartCommand (Intent intent, int flags, int startId)
1 intent
這個(gè)intent是startService(intent)中的intent既忆,在創(chuàng)建啟動(dòng)的服務(wù)所在的組件中如果需要傳遞給Service中數(shù)據(jù),可以將要傳遞的數(shù)據(jù)放放到這個(gè)Intent里面
2 flags
服務(wù)被系統(tǒng)殺死后的重新啟動(dòng)方式嗦玖,有3種
- START_NOT_STICKY(非粘性啟動(dòng)患雇,系統(tǒng)殺死后不再重新創(chuàng)建該Service)
如果系統(tǒng)在 onStartCommand() 返回后終止服務(wù),則除非有掛起 Intent 要傳遞宇挫,否則系統(tǒng)不會(huì)重建服務(wù)苛吱。這是最安全的選項(xiàng),可以避免在不必要時(shí)以及應(yīng)用能夠輕松重啟所有未完成的作業(yè)時(shí)運(yùn)行服務(wù)
-
START_STICKY(粘性啟動(dòng)器瘪,默認(rèn)的返回值)
如果系統(tǒng)在 onStartCommand() 返回后終止服務(wù)翠储,則會(huì)重建服務(wù)并調(diào)用 onStartCommand(),但不會(huì)重新傳遞最后一個(gè) Intent(意思就是說此時(shí)intent的數(shù)據(jù)已經(jīng)是null了)娱局。相反彰亥,除非有掛起 Intent 要啟動(dòng)服務(wù)(在這種情況下,將傳遞這些 Intent )衰齐,否則系統(tǒng)會(huì)通過空 Intent 調(diào)用 onStartCommand()。继阻、
這適用于不執(zhí)行命令耻涛、但無限期運(yùn)行并等待作業(yè)的媒體播放器(或類似服務(wù))。
-
START_REDELIVER_INTENT(再交付)
如果系統(tǒng)在 onStartCommand() 返回后終止服務(wù)瘟檩,則會(huì)重建服務(wù)抹缕,并通過傳遞給服務(wù)的最后一個(gè) Intent 調(diào)用 onStartCommand()。任何掛起 Intent 均依次傳遞墨辛。這適用于主動(dòng)執(zhí)行應(yīng)該立即恢復(fù)的作業(yè)(例如下載文件)的服務(wù)(就是需要重啟卓研,需要保留Intent的服務(wù))
3 startId
每次創(chuàng)建啟動(dòng)Service的唯一身份ID,每次每次startService睹簇,這個(gè)startId均不相同
- 用于處理多個(gè)onStartCommand請求時(shí)奏赘,關(guān)閉Service時(shí)使用
創(chuàng)建啟動(dòng)的Service實(shí)際使用業(yè)務(wù)邏輯
- 在onStartCommand中開啟子線程進(jìn)行耗時(shí)操作
- 子線程結(jié)束用stopSelf(startId)(在本Service中)殺死自己這個(gè)Service
- 或者另外在別的組件中用stopService結(jié)束Service
二 綁定Service
1 onBind
Return the communication channel to the service. May return null if clients can not bind to the service.
返回通信的類給服務(wù),如果Binder沒有綁定到Service可以返回null
2 Binder的子類
public class MyBinder extends Binder {
// 內(nèi)部業(yè)務(wù)邏輯太惠,別的組件(Activity)可以調(diào)用
}
3 ServiceConnection類的實(shí)現(xiàn)
- ServiceConnection的對象其實(shí)就是應(yīng)用組件(這里是Activity)與Service的紐帶磨淌,實(shí)現(xiàn)方法有倆個(gè)
onServiceConnected()//bind服務(wù)時(shí),onCreate之后
onServiceDisconnected()//unBind服務(wù)時(shí)凿渊,在onDestroy之前
4 bindService(Intent service, ServiceConnection conn, int flags)
-flags
BIND_AUTO_CREATE:表示在Activity和Service建立關(guān)聯(lián)后自動(dòng)創(chuàng)建Service梁只,這會(huì)使得MyService中的onCreate()方法得到執(zhí)行缚柳,但onStartCommand()方法不會(huì)執(zhí)行
其他的基本用不上。
創(chuàng)建一個(gè)前臺服務(wù)
只要在onCreate方法中添加
manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification = new NotificationCompat.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("標(biāo)題")
.setContentText("內(nèi)容")
.build();
manager.notify(1, notification);
//啟動(dòng)前臺服務(wù)
startForeground(1, notification);```
全部代碼掛一下
- MainActivity
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private static final String TAG = "MainActivity";
private Button mStartService;
private Button mStopService;
private Button BindService;
private Button unBindService;
private MyService.MyBinder mBinder;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
iniView();
}
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder binder) {
mBinder = (MyService.MyBinder) binder;
mBinder.StartBB();
Log.d(TAG, "onServiceConnected");
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG, "onServiceDisconnected");
}
};
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.button_start_service:
Intent startIntent = new Intent(MainActivity.this, MyService.class);
startService(startIntent);
break;
case R.id.button_stop_service:
Intent stopIntent = new Intent(MainActivity.this, MyService.class);
stopService(stopIntent);
break;
case R.id.bind_service:
Intent bindIntent = new Intent(MainActivity.this, MyService.class);
bindService(bindIntent, mConnection, BIND_AUTO_CREATE);
break;
case R.id.unbind_sservice:
unbindService(mConnection);
break;
}
}
private void iniView() {
mStartService = (Button) findViewById(R.id.button_start_service);
mStopService = (Button) findViewById(R.id.button_stop_service);
BindService = (Button) findViewById(R.id.bind_service);
unBindService = (Button) findViewById(R.id.unbind_sservice);
mStartService.setOnClickListener(this);
mStopService.setOnClickListener(this);
BindService.setOnClickListener(this);
unBindService.setOnClickListener(this);
}
}
- MyService
public class MyService extends Service {
private MyBinder mBinder = new MyBinder();
private static final String TAG = "MyService";
NotificationManager manager;
@Nullable
@Override
public IBinder onBind(Intent intent) {
// return null;
Log.d(TAG, "onBind");
return mBinder;
}
@Override
public void onCreate() {
super.onCreate();
manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification = new NotificationCompat.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("標(biāo)題")
.setContentText("內(nèi)容")
.build();
manager.notify(1, notification);
startForeground(1, notification);
Log.d(TAG, "onCreate");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
manager.cancel(1);
Log.d(TAG, "onDestroy");
}
public class MyBinder extends Binder {
public void StartBB() {
new Thread(new Runnable() {
@Override
public void run() {
//處理具體的邏輯
stopSelf();
}
}).start();
Log.d(TAG, "StartBB:BBBBBBBBBB");
}
}
}
這里要導(dǎo)入v4包
IntentService(異步的搪锣,會(huì)自動(dòng)停止的服務(wù))
從上面知道秋忙,服務(wù)默認(rèn)是運(yùn)行在主線程中的,如果直接在服務(wù)中處理一些耗時(shí)的邏輯构舟,就可能會(huì)出現(xiàn)ANR灰追,所以我們一般都在服務(wù)的具體方法里開啟一個(gè)新的線程去處理具體的邏輯,然后旁壮,這種類型的服務(wù)一旦啟動(dòng)后监嗜,就會(huì)一直運(yùn)行,要想停止服務(wù)就得調(diào)用stopSeft()
然后Android專門提供了IntentService來簡單的創(chuàng)建異步抡谐,自動(dòng)停止的服務(wù)
然后我們寫個(gè)簡單的demo裁奇,
新建一個(gè)IntentServiceDemo類
public class IntentServiceDemo extends IntentService {
private static final String TAG = "IntentServiceDemo";
/**
* 必要的,這里不寫manifest文件會(huì)報(bào)錯(cuò)
*
* */
public IntentServiceDemo() {
super(TAG);//調(diào)用有參構(gòu)造方法
}
/**
* Creates an IntentService. Invoked by your subclass's constructor.
*
* @param name Used to name the worker thread, important only for debugging.
*/
public IntentServiceDemo(String name) {
super(name);
}
@Override
protected void onHandleIntent(Intent intent) {
Log.d(TAG, "IntentServiceDemo當(dāng)前線程:" + Thread.currentThread().getId());
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy");
}
}
MainActivity中直接:
Log.d(TAG, "MainActivity當(dāng)前線程:" + Thread.currentThread().getId());
Intent intentService = new Intent(this, IntentServiceDemo.class);
startService(intentService);
可以看到麦撵,Activity線程與服務(wù)不一樣刽肠,并且邏輯完服務(wù)就GG了
注意事項(xiàng)
服務(wù)是一種即使用戶未與應(yīng)用交互也可在后臺運(yùn)行的組件。 因此免胃,您應(yīng)僅在必要時(shí)才創(chuàng)建服務(wù)
如需在主線程外部執(zhí)行工作音五,不過只是在用戶正在與應(yīng)用交互時(shí)才有此需要,則應(yīng)創(chuàng)建新線程而非服務(wù)羔沙。 例如躺涝,如果您只是想在 Activity 運(yùn)行的同時(shí)播放一些音樂,則可在 onCreate() 中創(chuàng)建線程扼雏,在 onStart() 中啟動(dòng)線程坚嗜,然后在 onStop() 中停止線程。您還可以考慮使用 AsyncTask 或 HandlerThread诗充,而非傳統(tǒng)的 Thread 類苍蔬。
為了確保應(yīng)用的安全性,請始終使用顯式 Intent 啟動(dòng)或綁定 Service蝴蜓,且不要為服務(wù)聲明 Intent 過濾器碟绑。
長時(shí)間運(yùn)行服務(wù)老被殺死(耗電)
1.提高進(jìn)程優(yōu)先級,使用 setForeground(true)
2.onStartCommand返回START_STICKY
3.設(shè)置屬性android:persistent=true(設(shè)置為永久性應(yīng)用,系統(tǒng)啟動(dòng)的時(shí)候就會(huì)啟動(dòng))
4.在onDestory中重新啟動(dòng)service
5.開啟單獨(dú)進(jìn)程,也就是指定android:process
- 小米手機(jī)針對onStartCommand的flags啟動(dòng)模式
小米财著,魅族,華為等手機(jī)針對此處做了一定的修改抓狭。在“自啟動(dòng)管理”中有一個(gè)自啟動(dòng)應(yīng)用列表,默認(rèn)情況下造烁,只有少應(yīng)用(如微信否过、QQ午笛、YY、360等)默認(rèn)是可以自啟動(dòng)的苗桂,其他應(yīng)用默認(rèn)都是禁止的药磺。用戶可以手動(dòng)添加自啟動(dòng)應(yīng)用,添加后的應(yīng)用中如果Started Service onStartCommand(...)回調(diào)返回值是START_STICKY或START_REDELIVER_INTENT煤伟,當(dāng)用戶在手機(jī)上長按Home鍵結(jié)束App后癌佩,接下來未來的某個(gè)時(shí)間內(nèi),當(dāng)系統(tǒng)內(nèi)存足夠可用時(shí)便锨,Service依然可以按照上述規(guī)定重啟围辙。當(dāng)然,如果用戶在 設(shè)置 >> 應(yīng)用 >> 強(qiáng)制kill掉App進(jìn)程放案,此時(shí)Service是不會(huì)重啟的姚建。
實(shí)戰(zhàn)
Service 與 MediaPlayer學(xué)習(xí)后音樂播放器的實(shí)現(xiàn)
參考 自