Service
一际度、基礎(chǔ)知識(shí)
1、定義
服務(wù)窒所,屬于Android中的計(jì)算型組件
2吵取、作用
提供需要在后臺(tái)長(zhǎng)期運(yùn)行的服務(wù)(如復(fù)雜計(jì)算皮官、下載等等)
3盔憨、特點(diǎn)
- 長(zhǎng)生命周期的婿奔、沒(méi)有用戶界面萍摊、在后臺(tái)運(yùn)行冰木、用戶不手動(dòng)關(guān)閉不會(huì)停止
- 從Context派生出來(lái)的歇终,也有g(shù)etResources()评凝,getContentResolver()等方法
二奕短、相關(guān)方法
1翎碑、Context相關(guān)方法
-
startService(Intent intent) ComponentName
啟動(dòng)一個(gè)Service,訪問(wèn)者和Service之間沒(méi)有關(guān)聯(lián)达椰,即使訪問(wèn)者退出了啰劲,Service仍然運(yùn)行 -
stopService (Intent intent) boolean
之后會(huì)自動(dòng)調(diào)用內(nèi)部方法:onDestory() -
bindService(Intent service, ServiceConnection conn, int flags) boolean
訪問(wèn)者與Service綁在了一起,訪問(wèn)者一旦退出栓辜,Service也將終止藕甩。conn:該參數(shù)是一個(gè)ServiceConnection(I)對(duì)象狭莱,用于監(jiān)聽(tīng)訪問(wèn)者和Service之間的連接情況,若連接成功骤素,將回調(diào)ServiceConnection對(duì)象的onServiceConnected(ComponentName name, IBinder service)
方法谆甜,當(dāng)異常終止連接時(shí)规辱,回調(diào)ServiceConnection對(duì)象的onServiceDisconnected(ComponentName name)
方法(若訪問(wèn)者主動(dòng)調(diào)用unbindService(ServiceConnection conn)
斷開(kāi)連接改淑,不會(huì)回調(diào)此方法)朵夏,flags:指定綁定時(shí)若Service未創(chuàng)建是否自動(dòng)創(chuàng)建仰猖,值:0或BIND_AUTO_CREATE
unbindService(ServiceConnection conn)
2饥侵、內(nèi)部自動(dòng)調(diào)用方法(生命周期方法)
-
onBind(Intent intent) IBinder
應(yīng)用程序可以通過(guò)IBinder與Service組件進(jìn)行通信,綁定該Service時(shí)回調(diào)該方法膨疏。
在綁定本地Service的情況下佃却,onBind(Intent service)方法返回的IBinder對(duì)象會(huì)傳給ServiceConnection對(duì)象的onServiceConnected(ComponentName name, IBinder service)的service參數(shù),IBinder相當(dāng)于一個(gè)代理人的角色洒闸,實(shí)現(xiàn)互相通信,所以onBind方法不應(yīng)該返回一個(gè)null(一般返回一個(gè)繼承Binder類(lèi)的對(duì)象掀宋,可以操作Service中的內(nèi)容湃鹊,一般使用private class指定币呵,里面有多個(gè)方法余赢,要暴露什么方法,使用接口去定義)举塔,在onServiceConnected方法就可以使用該代理人央渣。作用:暴露一些方法痹屹,改變服務(wù)的狀態(tài)
-
onUnbind(Intent intent) boolean
當(dāng)綁定在該Service上的所有客戶端都斷開(kāi)連接時(shí)回調(diào)該方法 -
onStartCommand(Intent intent, int flags, int startId) int
調(diào)用startService(Intent)方法啟動(dòng)Service時(shí)回調(diào)該方法志衍,會(huì)被調(diào)用多次 -
onCreate() void
第一次被創(chuàng)建時(shí)回調(diào)該方法,僅被調(diào)用一次 onDestroy() void
stopSelf()
3春叫、onStartCommand(Intent intent, int flags, int startId)
intent :?jiǎn)?dòng)時(shí)暂殖,啟動(dòng)組件傳遞過(guò)來(lái)的Intent,如Activity可利用Intent封裝所需要的參數(shù)并傳遞給Service
flags:表示啟動(dòng)請(qǐng)求時(shí)是否有額外數(shù)據(jù)晨横∈中危可選值有0伙狐,START_FLAG_REDELIVERY鳞骤,START_FLAG_RETRY,0代表沒(méi)有美旧,它們具體含義如下:
a)START_FLAG_REDELIVERY
這個(gè)值代表返回值為START_REDELIVER_INTENT榴嗅,而且在上一次服務(wù)被殺死前會(huì)去調(diào)用stopSelf()
方法停止服務(wù)嗽测。
b)START_FLAG_RETRY
該flag代表當(dāng)onStartCommand調(diào)用后一直沒(méi)有返回值時(shí),會(huì)嘗試重新去調(diào)用onStartCommand()晤愧。startId : 指明當(dāng)前服務(wù)的唯一ID官份,與
stopSelfResult (int startId)
配合使用,stopSelfResult 可以更安全地根據(jù)ID停止服務(wù)钠右。
實(shí)際上onStartCommand的返回值int類(lèi)型才是最最值得注意的爬舰,它有三種可選值情屹, START_STICKY,START_NOT_STICKY惜颇,START_REDELIVER_INTENT凌摄,它們具體含義如下:
START_STICKY
當(dāng)Service因內(nèi)存不足而被系統(tǒng)kill后,一段時(shí)間后內(nèi)存再次空閑時(shí)器予,系統(tǒng)將會(huì)嘗試重新創(chuàng)建此Service乾翔,一旦創(chuàng)建成功后將回調(diào)onStartCommand方法,但其中的Intent將是null勾习,除非有掛起的Intent巧婶,如pendingintent艺栈,這個(gè)狀態(tài)下比較適用于不執(zhí)行命令、但無(wú)限期運(yùn)行并等待作業(yè)的媒體播放器或類(lèi)似服務(wù)毅人。START_NOT_STICKY
當(dāng)Service因內(nèi)存不足而被系統(tǒng)kill后丈莺,即使系統(tǒng)內(nèi)存再次空閑時(shí)弛秋,系統(tǒng)也不會(huì)嘗試重新創(chuàng)建此Service蟹略。除非程序中再次調(diào)用startService啟動(dòng)此Service,這是最安全的選項(xiàng)茅茂,可以避免在不必要時(shí)以及應(yīng)用能夠輕松重啟所有未完成的作業(yè)時(shí)運(yùn)行服務(wù)空闲。START_REDELIVER_INTENT
當(dāng)Service因內(nèi)存不足而被系統(tǒng)kill后,則會(huì)重建服務(wù)跌榔,并通過(guò)傳遞給服務(wù)的最后一個(gè) Intent 調(diào)用 onStartCommand(),任何掛起 Intent均依次傳遞担平。與START_STICKY不同的是暂论,其中的傳遞的Intent將是非空,是最后一次調(diào)用startService中的intent闻蛀。這個(gè)值適用于主動(dòng)執(zhí)行應(yīng)該立即恢復(fù)的作業(yè)(例如下載文件)的服務(wù)役衡。
4、Service生命周期
4.1盗尸、單獨(dú)調(diào)用
- startService()->onCreate()->onStartCommand()->onStop()->onDestory()
- bindService()->onCreate()->onBind()->onUnbind()->onDestory()
4.2、混合調(diào)用
- onCreate()->onStartCommand()->onBind()->onUnbind()->onRebind()
說(shuō)明:混合調(diào)用時(shí)亏拉,要兩兩對(duì)應(yīng)莽使,不要相互嵌套(類(lèi)似于html標(biāo)簽)
服務(wù)只能解綁一次,多次會(huì)報(bào)錯(cuò)
建議在Activity的onDestroy方法中解綁掉服務(wù)
startService用于保證服務(wù)的后臺(tái)運(yùn)行亿笤,bindService用于調(diào)用服務(wù)的方法
三净薛、Service分類(lèi)
1、本地Service
這是最普通爆班、最常用的后臺(tái)服務(wù)Service。
1.1枢舶、使用步驟
步驟1:新建子類(lèi)繼承Service類(lèi)
需重寫(xiě)父類(lèi)的onCreate()躏尉、onStartCommand()胀糜、onDestroy()和onBind()方法
步驟2:構(gòu)建用于啟動(dòng)Service的Intent對(duì)象
步驟3:調(diào)用startService()啟動(dòng)Service、調(diào)用stopService()停止服務(wù)
步驟4:在AndroidManifest.xml里注冊(cè)Service
屬性說(shuō)明:
-
android:name
Service的類(lèi)名 -
android:label
Service的名字,若不設(shè)置括堤,默認(rèn)為Service類(lèi)名 -
android:icon
Service的圖標(biāo) -
android:permission
聲明此Service的權(quán)限,提供了該權(quán)限的應(yīng)用才能控制或連接此服務(wù) -
android:process
表示該服務(wù)是否在另一個(gè)進(jìn)程中運(yùn)行(遠(yuǎn)程服務(wù)) 不設(shè)置默認(rèn)為本地服務(wù);remote則設(shè)置成遠(yuǎn)程服務(wù) -
android:enabled
是否可用即是否可以被系統(tǒng)實(shí)例化 -
android:exported
是否能被其他應(yīng)用隱式調(diào)用轧抗。 默認(rèn)值是由service中有無(wú)intent-filter決定的,如果有intent-filter分唾,默認(rèn)值為true绽乔,否則為false。為false的情況下睦授,即使有intent-filter匹配去枷,也無(wú)法打開(kāi)竖螃,即無(wú)法被其他應(yīng)用隱式調(diào)用
2、可通信Service
實(shí)例Demo
步驟1:在新建子類(lèi)繼承Service類(lèi)录粱,并新建一個(gè)子類(lèi)繼承自Binder類(lèi)、寫(xiě)入與Activity關(guān)聯(lián)需要的方法、創(chuàng)建實(shí)例
public class MyService extends Service {
private MyBinder mBinder = new MyBinder();
@Override
public void onCreate() {
super.onCreate();
System.out.println("執(zhí)行了onCreat()");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
System.out.println("執(zhí)行了onStartCommand()");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
System.out.println("執(zhí)行了onDestory()");
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
System.out.println("執(zhí)行了onBind()");
//返回實(shí)例
return mBinder;
}
@Override
public boolean onUnbind(Intent intent) {
System.out.println("執(zhí)行了onUnbind()");
return super.onUnbind(intent);
}
//新建一個(gè)子類(lèi)繼承自Binder類(lèi)
class MyBinder extends Binder {
public void service_connect_Activity() {
System.out.println("Service關(guān)聯(lián)了Activity,并在Activity執(zhí)行了Service的方法");
}
}
}
步驟2:在Activity通過(guò)調(diào)用MyBinder類(lèi)中的public方法來(lái)實(shí)現(xiàn)Activity與Service的聯(lián)系脂凶,即實(shí)現(xiàn)了Activity指揮Service干什么Service就去干什么的功能
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private Button startService;
private Button stopService;
private Button bindService;
private Button unbindService;
private MyService.MyBinder myBinder;
//創(chuàng)建ServiceConnection的匿名類(lèi)
private ServiceConnection connection = new ServiceConnection() {
//重寫(xiě)onServiceConnected()方法和onServiceDisconnected()方法
//在Activity與Service建立關(guān)聯(lián)和解除關(guān)聯(lián)的時(shí)候調(diào)用
@Override
public void onServiceDisconnected(ComponentName name) {
}
//在Activity與Service解除關(guān)聯(lián)的時(shí)候調(diào)用
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//實(shí)例化Service的內(nèi)部類(lèi)myBinder
//通過(guò)向下轉(zhuǎn)型得到了MyBinder的實(shí)例
myBinder = (MyService.MyBinder) service;
//在Activity調(diào)用Service類(lèi)的方法
myBinder.service_connect_Activity();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startService = (Button) findViewById(R.id.startService);
stopService = (Button) findViewById(R.id.stopService);
startService.setOnClickListener(this);
stopService.setOnClickListener(this);
bindService = (Button) findViewById(R.id.bindService);
unbindService = (Button) findViewById(R.id.unbindService);
bindService.setOnClickListener(this);
unbindService.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
//點(diǎn)擊啟動(dòng)Service
case R.id.startService:
//構(gòu)建啟動(dòng)服務(wù)的Intent對(duì)象
Intent startIntent = new Intent(this, MyService.class);
//調(diào)用startService()方法-傳入Intent對(duì)象,以此啟動(dòng)服務(wù)
startService(startIntent);
break;
//點(diǎn)擊停止Service
case R.id.stopService:
//構(gòu)建停止服務(wù)的Intent對(duì)象
Intent stopIntent = new Intent(this, MyService.class);
//調(diào)用stopService()方法-傳入Intent對(duì)象,以此停止服務(wù)
stopService(stopIntent);
break;
//點(diǎn)擊綁定Service
case R.id.bindService:
//構(gòu)建綁定服務(wù)的Intent對(duì)象
Intent bindIntent = new Intent(this, MyService.class);
//調(diào)用bindService()方法,以此停止服務(wù)
bindService(bindIntent,connection,BIND_AUTO_CREATE);
//參數(shù)說(shuō)明
//第一個(gè)參數(shù):Intent對(duì)象
//第二個(gè)參數(shù):上面創(chuàng)建的Serviceconnection實(shí)例
//第三個(gè)參數(shù):標(biāo)志位
//這里傳入BIND_AUTO_CREATE表示在Activity和Service建立關(guān)聯(lián)后自動(dòng)創(chuàng)建Service
//這會(huì)使得MyService中的onCreate()方法得到執(zhí)行罪帖,但onStartCommand()方法不會(huì)執(zhí)行
break;
//點(diǎn)擊解綁Service
case R.id.unbindService:
//調(diào)用unbindService()解綁服務(wù)
//參數(shù)是上面創(chuàng)建的Serviceconnection實(shí)例
unbindService(connection);
break;
default:
break;
}
}
}
3、前臺(tái)Service
前臺(tái)Service和后臺(tái)Service(普通)最大的區(qū)別就在于:
-
前臺(tái)Service在下拉通知欄有顯示通知(如下圖)坐昙,但后臺(tái)Service沒(méi)有炸客;
前臺(tái)Service優(yōu)先級(jí)較高戈钢,不會(huì)由于系統(tǒng)內(nèi)存不足而被回收开仰;后臺(tái)Service優(yōu)先級(jí)較低梨州,當(dāng)系統(tǒng)出現(xiàn)內(nèi)存不足情況時(shí),很有可能會(huì)被回收
//用法很簡(jiǎn)單每窖,只需要在原有的Service類(lèi)對(duì)onCreate()方法進(jìn)行稍微修改即可
@Override
public void onCreate() {
super.onCreate();
System.out.println("執(zhí)行了onCreat()");
//添加下列代碼將后臺(tái)Service變成前臺(tái)Service
//構(gòu)建"點(diǎn)擊通知后打開(kāi)MainActivity"的Intent對(duì)象
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
//新建Builer對(duì)象
Notification.Builder builer = new Notification.Builder(this);
builer.setContentTitle("前臺(tái)服務(wù)通知的標(biāo)題");//設(shè)置通知的標(biāo)題
builer.setContentText("前臺(tái)服務(wù)通知的內(nèi)容");//設(shè)置通知的內(nèi)容
builer.setSmallIcon(R.mipmap.ic_launcher);//設(shè)置通知的圖標(biāo)
builer.setContentIntent(pendingIntent);//設(shè)置點(diǎn)擊通知后的操作
Notification notification = builer.getNotification();//將Builder對(duì)象轉(zhuǎn)變成普通的notification
startForeground(1, notification);//讓Service變成前臺(tái)Service,并在系統(tǒng)的狀態(tài)欄顯示出來(lái)
}
4稽莉、使用場(chǎng)景
四劈猪、其他
1、Service和Thread的區(qū)別
Service和Thread之間沒(méi)有任何關(guān)系常侦,之所以有不少人會(huì)把它們聯(lián)系起來(lái),主要因?yàn)镾ervice的后臺(tái)概念
后臺(tái)的定義:后臺(tái)任務(wù)運(yùn)行完全不依賴UI坡倔,即使Activity被銷(xiāo)毀致讥,或者程序被關(guān)閉,只要進(jìn)程還在请契,后臺(tái)任務(wù)就可以繼續(xù)運(yùn)行
一般來(lái)說(shuō)涌韩,會(huì)將Service和Thread聯(lián)合著用,即在Service中再創(chuàng)建一個(gè)子線程(工作線程)去處理耗時(shí)操作邏輯雇毫,如下:
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
//新建工作線程
new Thread(new Runnable() {
@Override
public void run() {
// 開(kāi)始執(zhí)行后臺(tái)任務(wù)
}
}).start();
return super.onStartCommand(intent, flags, startId);
}
class MyBinder extends Binder {
public void service_connect_Activity() {
//新建工作線程
new Thread(new Runnable() {
@Override
public void run() {
// 執(zhí)行具體的下載任務(wù)
}
}).start();
}
}
2. IntentService
2.1馅闽、定義
Android里的一個(gè)封裝類(lèi)局骤,繼承四大組件之一的Service
2.2庄涡、作用
處理異步請(qǐng)求 & 實(shí)現(xiàn)多線程
2.1撕捍、使用場(chǎng)景
線程任務(wù)需按順序在后臺(tái)執(zhí)行
- 最常見(jiàn)的場(chǎng)景:離線下載
- 不符合多個(gè)數(shù)據(jù)同時(shí)請(qǐng)求的場(chǎng)景:所有的任務(wù)都在同一個(gè)Thread looper里執(zhí)行
2.1默色、使用步驟
步驟1:定義 IntentService的子類(lèi)
需傳入線程名稱、復(fù)寫(xiě)onHandleIntent()方法
public class myIntentService extends IntentService {
/**
* 在構(gòu)造函數(shù)中傳入線程名字
**/
public myIntentService() {
// 調(diào)用父類(lèi)的構(gòu)造函數(shù)
// 參數(shù) = 工作線程的名字
super("myIntentService");
}
/**
* 復(fù)寫(xiě)onHandleIntent()方法
* 根據(jù) Intent實(shí)現(xiàn) 耗時(shí)任務(wù) 操作
**/
@Override
protected void onHandleIntent(Intent intent) {
// 根據(jù) Intent的不同吃度,進(jìn)行不同的事務(wù)處理
String taskName = intent.getExtras().getString("taskName");
switch (taskName) {
case "task1":
Log.i("myIntentService", "do task1");
break;
case "task2":
Log.i("myIntentService", "do task2");
break;
default:
break;
}
}
@Override
public void onCreate() {
Log.i("myIntentService", "onCreate");
super.onCreate();
}
/**
* 復(fù)寫(xiě)onStartCommand()方法
* 默認(rèn)實(shí)現(xiàn) = 將請(qǐng)求的Intent添加到工作隊(duì)列里
**/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("myIntentService", "onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
Log.i("myIntentService", "onDestroy");
super.onDestroy();
}
}
步驟2:在Manifest.xml中注冊(cè)服務(wù)
<service android:name=".myIntentService">
<intent-filter >
<action android:name="cn.scu.finch"/>
</intent-filter>
</service>
步驟3:在Activity中開(kāi)啟Service服務(wù)
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 同一服務(wù)只會(huì)開(kāi)啟1個(gè)工作線程
// 在onHandleIntent()函數(shù)里,依次處理傳入的Intent請(qǐng)求
// 將請(qǐng)求通過(guò)Bundle對(duì)象傳入到Intent,再傳入到服務(wù)里
// 請(qǐng)求1
Intent i = new Intent("cn.scu.finch");
Bundle bundle = new Bundle();
bundle.putString("taskName", "task1");
i.putExtras(bundle);
startService(i);
// 請(qǐng)求2
Intent i2 = new Intent("cn.scu.finch");
Bundle bundle2 = new Bundle();
bundle2.putString("taskName", "task2");
i2.putExtras(bundle2);
startService(i2);
startService(i); //多次啟動(dòng)
}
}
測(cè)試結(jié)果
3、進(jìn)程優(yōu)先級(jí)
- 前臺(tái)進(jìn)程:某個(gè)Activity可見(jiàn)亿虽,獲得焦點(diǎn)
- 可見(jiàn)進(jìn)程:某個(gè)Activity可見(jiàn),但是沒(méi)有焦點(diǎn)
- 服務(wù)進(jìn)程:有一個(gè)服務(wù)在后臺(tái)運(yùn)行
- 后臺(tái)進(jìn)程:沒(méi)有任何服務(wù)收毫,打開(kāi)一個(gè)Activity然后最小化(容易被回收)
- 空進(jìn)程:沒(méi)有任何活動(dòng)組件存在的進(jìn)程(容易被回收)
4此再、AIDL
4.1、簡(jiǎn)介
為了實(shí)現(xiàn)跨進(jìn)程通信(IPC)策吠,實(shí)現(xiàn)進(jìn)程之間的數(shù)據(jù)交換
4.2、步驟
①:服務(wù)端創(chuàng)建.aidl文件
②:服務(wù)端創(chuàng)建Service蟀给,并在onBind中返回一個(gè)IBinder(接口對(duì)象(代理人))
IBinder binder = new IMyAidlInterface.Stub() {
@Override
public int add(int num1, int num2) throws RemoteException {
return num1 + num2;
}
};
③:客戶端創(chuàng)建(復(fù)制)和服務(wù)端一樣的.aidl文件(包名也必須一致)
④:客戶端創(chuàng)建ServiceConnection的子類(lèi),并實(shí)現(xiàn)其onServiceConnected(ComponentName name, IBinder service)前普,在方法中(service就是中間人)iMyAdil = IMyAidlInterface.Stub.asInterface(service)
汁政,客戶端可以使用服務(wù)端的方法了
⑤:客戶端bindService
4.3勺鸦、AIDL支持的數(shù)據(jù)類(lèi)型(支持其實(shí)就是定義aidl的時(shí)候换途,參數(shù)可以使用的類(lèi)型)
基本數(shù)據(jù)類(lèi)型(除short),String懈息,CharSequence,List(僅支持ArrayList)姑宽,Map(僅支持HashMap)炮车,Parcelable
注意:
①:非基本數(shù)據(jù)類(lèi)型瘦穆,需要用in,out,inout指定數(shù)據(jù)的走向
②:復(fù)雜類(lèi)型(如Book)必須實(shí)現(xiàn)Parcelable难审,且需要Book.aidl(內(nèi)容parcelable Book;)—— 包名必須和Book.java相同,無(wú)論是否相同的包派昧,都需要導(dǎo)入包
③:AIDL接口中只支持方法,不支持聲明靜態(tài)變量
參考文獻(xiàn)
Android四大組件:Service史上最全面解析
Android 多線程 解析:IntentService(含源碼解析)