本篇文章主要介紹以下幾個(gè)知識(shí)點(diǎn):
- 服務(wù)的基本用法
- 服務(wù)的生命周期
- 服務(wù)的其他用法
10.2 服務(wù)的基本用法
服務(wù)(Service)是 Android 中實(shí)現(xiàn)程序后臺(tái)運(yùn)行的解決方案硝训,它非常適合去執(zhí)行那些不需要和用戶交互而且還要求長(zhǎng)期運(yùn)行的任務(wù)赏寇。
??服務(wù)并不是運(yùn)行在一個(gè)獨(dú)立的進(jìn)程當(dāng)中的,而是依賴于創(chuàng)建服務(wù)時(shí)所在的應(yīng)用程序進(jìn)程斥赋。
??服務(wù)并不會(huì)自動(dòng)開啟線程,所有代碼默認(rèn)運(yùn)行在主線程中昌罩。
10.2.1 定義一個(gè)服務(wù)
在項(xiàng)目中定義一個(gè)服務(wù):在 Android Studio 中右擊 com.wonderful.myfirstcode.chapter10.service包(你項(xiàng)目所在的包名)→New→Service→Service琐驴,會(huì)彈出如下窗口:
上面將服務(wù)命名為 MyService,Exported 表示是否允許除了當(dāng)前程序之外的其他程序訪問(wèn)這個(gè)服務(wù)嘀韧,Enabled 表示是否啟用這個(gè)服務(wù)篇亭。完成創(chuàng)建后的 MyService 如下:
public class MyService extends Service {
public MyService() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
}
可以看到,onBind() 方法是 Service 中唯一的一個(gè)抽象方法锄贷,必須在子類中實(shí)現(xiàn)译蒂。在服務(wù)中添加處理事情的邏輯還要重寫 Service 中的另外一些方法,如下:
public class MyService extends Service {
. . .
/**
* 在服務(wù)創(chuàng)建時(shí)調(diào)用
*/
@Override
public void onCreate() {
super.onCreate();
Log.d("------MyService------", "onCreate: ");
}
/**
* 在每次服務(wù)啟動(dòng)時(shí)調(diào)用
* 若服務(wù)一旦啟動(dòng)就立刻執(zhí)行某個(gè)動(dòng)作谊却,可以將邏輯寫在此方法中
*/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("------MyService------", "onStartCommand: ");
return super.onStartCommand(intent, flags, startId);
}
/**
* 在服務(wù)銷毀時(shí)調(diào)用柔昼,回收不再使用的資源
*/
@Override
public void onDestroy() {
super.onDestroy();
Log.d("------MyService------", "onDestroy: ");
}
}
另外需注意的是,每一個(gè)服務(wù)都需要在 AndroidManifest.xml 文件中進(jìn)行注冊(cè)才能生效(安卓四大組件的共有特點(diǎn))炎辨。當(dāng)然捕透,剛才創(chuàng)建服務(wù)時(shí) AS 已經(jīng)自動(dòng)幫我們注冊(cè)好了:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.wonderful.myfirstcode">
<application
android:name=".MyApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
. . .
<service
android:name=".chapter10.service.MyService"
android:enabled="true"
android:exported="true">
</service>
</application>
</manifest>
以上,就將一個(gè)服務(wù)定義好了。
10.2.2 啟動(dòng)和停止服務(wù)
啟動(dòng)和停止服務(wù)的方法主要是借助 Intent 來(lái)實(shí)現(xiàn)的乙嘀。下面就在項(xiàng)目中嘗試去啟動(dòng)和停止服務(wù)末购。
在布局中添加兩個(gè)按鈕,分別用于啟動(dòng)和停止服務(wù):
public class MyServiceActivity extends AppCompatActivity implements View.OnClickListener{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my_service);
Button start_service = (Button) findViewById(R.id.start_service);
Button stop_service = (Button) findViewById(R.id.stop_service);
start_service.setOnClickListener(this);
stop_service.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.start_service:
Intent startIntent = new Intent(this,MyService.class);
startService(startIntent);// 啟動(dòng)服務(wù)
break;
case R.id.stop_service:
Intent stopIntent = new Intent(this,MyService.class);
stopService(stopIntent);// 停止服務(wù)
break;
default:
break;
}
}
}
上述代碼虎谢,啟動(dòng)服務(wù) startService() 和停止服務(wù) stopService() 方法都是定義在 Context 類中的盟榴,在活動(dòng)里可以直接調(diào)用。當(dāng)然嘉冒,停止服務(wù)也可以在 MyService 的任何一個(gè)位置調(diào)用 stopSelf() 方法曹货,讓服務(wù)自己停止下來(lái)。
運(yùn)行程序讳推,點(diǎn)擊啟動(dòng)服務(wù)顶籽,打印日志如下:
點(diǎn)擊停止服務(wù),打印日志如下:
值得注意的是银觅,onCreate() 在服務(wù)第一次創(chuàng)建時(shí)調(diào)用礼饱,onStartCommand() 在每次啟動(dòng)服務(wù)時(shí)都會(huì)調(diào)用,上面第一次點(diǎn)擊啟動(dòng)服務(wù)時(shí)兩個(gè)方法都會(huì)執(zhí)行究驴,之后再點(diǎn)擊啟動(dòng)服務(wù)按鈕就只有 onStartCommant() 方法執(zhí)行了镊绪。
10.2.3 活動(dòng)和服務(wù)進(jìn)行通信
上面一節(jié)中,雖然服務(wù)在活動(dòng)里啟動(dòng)洒忧,但啟動(dòng)之后活動(dòng)與服務(wù)就沒(méi)什么關(guān)系了蝴韭。若要在活動(dòng)中指定服務(wù)做什么,就要借助服務(wù)里面的 onBind() 方法了熙侍。
下面舉個(gè)例子榄鉴,若在 MyService 里提供一個(gè)下載功能,然后在活動(dòng)中可以決定何時(shí)開始下載蛉抓,以及隨時(shí)查看下載進(jìn)度庆尘。實(shí)現(xiàn)這個(gè)功能的思路是創(chuàng)建一個(gè)專門的 Binder 對(duì)象來(lái)對(duì)下載功能進(jìn)行管理,如下:
public class MyService extends Service {
private DownloadBinder mBinder = new DownloadBinder();
class DownloadBinder extends Binder{
// 模擬開始下載方法
public void startDownload(){
Log.d("------MyService------", "startDownload: ");
}
// 模擬查看下載進(jìn)度方法
public int getProgress(){
Log.d("------MyService------", "getProgress: ");
return 0;
}
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
. . .
}
接著在布局中添加兩個(gè)按鈕巷送,用于綁定服務(wù)和取消綁定服務(wù):
public class MyServiceActivity extends AppCompatActivity implements View.OnClickListener{
private MyService.DownloadBinder downloadBinder;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 服務(wù)綁定成功后調(diào)用
downloadBinder = (MyService.DownloadBinder) service;
downloadBinder.startDownload();
downloadBinder.getProgress();
}
@Override
public void onServiceDisconnected(ComponentName name) {
// 服務(wù)解除綁定后調(diào)用
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my_service);
Button bind_service = (Button) findViewById(R.id.bind_service);
Button unbind_service = (Button) findViewById(R.id.unbind_service);
bind_service.setOnClickListener(this);
unbind_service.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.bind_service:
Intent bindIntent = new Intent(this,MyService.class);
// 綁定服務(wù)驶忌,三個(gè)參數(shù):Intent對(duì)象、ServiceConnection實(shí)例笑跛、標(biāo)志位
// (BIND_AUTO_CREATE 表示活動(dòng)和服務(wù)綁定后自動(dòng)創(chuàng)建服務(wù))
bindService(bindIntent,connection,BIND_AUTO_CREATE);
break;
case R.id.unbind_service:
// 解綁服務(wù)
unbindService(connection);
break;
default:
break;
}
}
}
上述代碼付魔,首先創(chuàng)建了一個(gè) ServiceConnection 的匿名類,里面重寫兩個(gè)方法堡牡,在 onServiceConnected() 中獲取 DownloadBinder 的實(shí)例抒抬,接下來(lái)就根據(jù)具體場(chǎng)景來(lái)調(diào)用 DownloadBinder 中的任何公共方法。
運(yùn)行程序晤柄,點(diǎn)擊綁定服務(wù),打印日志如下:
點(diǎn)擊解綁服務(wù)妖胀,打印日志如下:
值得注意的是芥颈,任何一個(gè)服務(wù)在整個(gè)應(yīng)用程序內(nèi)都是通用的惠勒,即 MyService 可以和任何一個(gè)活動(dòng)進(jìn)行綁定,而且綁定完成后都可以獲取到相同的 DownloadBinder 實(shí)例爬坑。
10.3 服務(wù)的生命周期
前面使用到的 onCreate()纠屋、onStartCommand()、onBind()盾计、onDestroy() 等方法都是在服務(wù)的生命周期內(nèi)可能回調(diào)的方法售担,具體如下圖所示:
值得注意的是,當(dāng)我們對(duì)一個(gè)服務(wù)既調(diào)用了 startService() 方法署辉,又調(diào)用了 bindService() 方法時(shí)族铆,要同時(shí)調(diào)用 stopService() 和 unbindService() 方法,onDestroy() 方法才會(huì)執(zhí)行哭尝。
10.4 服務(wù)的更多技巧
10.4.1 使用前臺(tái)服務(wù)
服務(wù)的優(yōu)先級(jí)較低哥攘,當(dāng)系統(tǒng)內(nèi)存不足時(shí),可能會(huì)回收正在后臺(tái)運(yùn)行的服務(wù)材鹦,若要避免被回收逝淹,可以考慮使用前臺(tái)服務(wù)。
前臺(tái)服務(wù)和普通服務(wù)的區(qū)別在于桶唐,它會(huì)一直有一個(gè)正在運(yùn)行的圖標(biāo)在系統(tǒng)的狀態(tài)欄顯示栅葡,下拉狀態(tài)欄可以看到更加詳細(xì)的信息,類似于通知的效果尤泽。
創(chuàng)建一個(gè)前臺(tái)服務(wù)如下:
public class MyService extends Service {
. . .
/**
* 在服務(wù)創(chuàng)建時(shí)調(diào)用
*/
@Override
public void onCreate() {
super.onCreate();
Log.d("------MyService------", "onCreate: ");
Intent intent = new Intent(this,MyServiceActivity.class);
PendingIntent pi = PendingIntent.getActivity(this,0,intent,0);
Notification notification = new NotificationCompat.Builder(this)
.setContentTitle("這是標(biāo)題")
.setContentText("這是內(nèi)容")
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.mipmap.ic_launcher)
.setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher))
.setContentIntent(pi)
.build();
//讓MyService變成一個(gè)前臺(tái)服務(wù)欣簇,并在系統(tǒng)狀態(tài)欄顯示出來(lái)
startForeground(1,notification);
}
. . .
}
前臺(tái)服務(wù)的用法就這么簡(jiǎn)單,和創(chuàng)建通知的方法類似安吁。運(yùn)行程序醉蚁,點(diǎn)擊開啟服務(wù)或綁定服務(wù),效果如下:
10.4.2 使用 IntentService
之前提到過(guò)服務(wù)中的代碼都是默認(rèn)運(yùn)行在主線程當(dāng)中,若直接在服務(wù)里處理耗時(shí)操作拂酣,容易出現(xiàn) ANR(Application Not Responding)的情況通孽。
為避免上述情況,應(yīng)該在服務(wù)的每個(gè)具體的方法里開啟一個(gè)子線程滥玷,在子線程里處理耗時(shí)操作。因此一個(gè)比較標(biāo)準(zhǔn)的服務(wù)可以寫成如下形式:
public class MyService extends Service {
. . .
/**
* 在每次服務(wù)啟動(dòng)時(shí)調(diào)用
*/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
new Thread(new Runnable() {
@Override
public void run() {
// 處理具體的邏輯
}
}).start();
return super.onStartCommand(intent, flags, startId);
}
}
服務(wù)開啟后會(huì)一直處于運(yùn)行狀態(tài)巍棱,必須調(diào)用 stopService() 或者 stopSelf() 才能停止服務(wù)惑畴,所以要實(shí)現(xiàn)一個(gè)服務(wù)在執(zhí)行完畢后自動(dòng)停止,可以這樣寫:
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
new Thread(new Runnable() {
@Override
public void run() {
// 處理具體的邏輯
stopSelf();
}
}).start();
return super.onStartCommand(intent, flags, startId);
}
當(dāng)然航徙,為了可以簡(jiǎn)單地創(chuàng)建一個(gè)異步地如贷、會(huì)自動(dòng)停止地服務(wù),Android 專門提供了一個(gè)** IntentService **類。
下面介紹下 IntentService 類的用法杠袱,創(chuàng)建一個(gè) MyIntentService 類繼承自 IntentService尚猿,如下:
public class MyIntentService extends IntentService {
public MyIntentService() {
super("MyIntentService");//調(diào)用父類的有參構(gòu)造函數(shù)
}
/**
* 此方法在子線程中運(yùn)行,可以處理一些具體的邏輯楣富,且不用擔(dān)心 ANR 問(wèn)題
* @param intent
*/
@Override
protected void onHandleIntent(Intent intent) {
// 打印當(dāng)前線程的 id
Log.d("MyIntentService", "onHandleIntent: 線程id是 "+ Thread.currentThread().getId());
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d("MyIntentService", "onDestroy: 服務(wù)停止");
}
}
下面舉個(gè)例子來(lái)證實(shí)下凿掂,在布局中添加個(gè)按鈕用于啟動(dòng) MyIntentService 這個(gè)服務(wù),如下:
public class MyServiceActivity extends AppCompatActivity implements View.OnClickListener{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my_service);
Button start_intent_service = (Button) findViewById(R.id.start_intent_service);
start_intent_service.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.start_intent_service:
// 打印主線程的 id
Log.d("MyServiceActivity", "onClick: 主線程id:"+ Thread.currentThread().getId());
Intent intentService = new Intent(this,MyIntentService.class);
startService(intentService);
break;
}
}
}
不要忘了在 AndroidManifest.xml 里注冊(cè)服務(wù)(當(dāng)然也可以用 AS 提供的快捷方式創(chuàng)建服務(wù)):
<service android:name=".chapter10.service.MyIntentService" />
運(yùn)行程序纹蝴,點(diǎn)擊按鈕庄萎,打印日志如下:
可以看到,MyIntentService 在運(yùn)行完畢后自動(dòng)停止了塘安。
本篇文章介紹到這糠涛,下一小節(jié)進(jìn)入服務(wù)的最佳實(shí)踐。