Q1.知道Service嗎,它有幾種啟動(dòng)方式?
Service是一個(gè)專門在后臺(tái)處理長(zhǎng)時(shí)間任務(wù)的Android組件,它沒(méi)有UI。
當(dāng)應(yīng)用程序被掛到后臺(tái)的時(shí)候,問(wèn)了保證應(yīng)用某些組件仍然可以工作,Service不是獨(dú)立的進(jìn)程,
也不是獨(dú)立的線程,它是依賴于應(yīng)用程序的主線程的,也就是說(shuō),在更多時(shí)候不建議在Service中編寫
耗時(shí)的邏輯和操作,否則會(huì)引起ANR。
它有兩種啟動(dòng)方式,startService和bindService。
Q1.1如果不適合直接在service中做耗時(shí)操作,那對(duì)于耗時(shí)邏輯我們應(yīng)該怎么處理?
當(dāng)我們編寫的耗時(shí)邏輯,不得不被service來(lái)管理的時(shí)候,就需要引入IntentService。 --> Q6
Q2.這兩種啟動(dòng)方式的區(qū)別?
1.startService只是啟動(dòng)Service,啟動(dòng)它的組件(如Activity)和Service并沒(méi)有關(guān)聯(lián),
只有當(dāng)Service調(diào)用stopSelf或者其他組件調(diào)用stopService服務(wù)才會(huì)終止。
2.bindService方法啟動(dòng)Service,其他組件可以通過(guò)回調(diào)獲取Service的代理對(duì)象和Service交互,
而這兩方也進(jìn)行了綁定,當(dāng)啟動(dòng)方銷毀時(shí),Service也會(huì)自動(dòng)進(jìn)行unBind操作,當(dāng)發(fā)現(xiàn)所有綁定
都進(jìn)行了unBind時(shí)才會(huì)銷毀Service
Q2.1什么時(shí)候使用startService,什么時(shí)候使用bindService?
啟動(dòng)狀態(tài),主要用于執(zhí)行后臺(tái)計(jì)算;
綁定狀態(tài),主要用于其它組件和Service的交互
Q3.兩種啟動(dòng)方式對(duì)Service生命周期函數(shù)影響?

service_life.png
Q4.Service的onCreate回調(diào)函數(shù)可以做耗時(shí)的操作嗎?
不可以,因?yàn)镾ervice的onCreate是在主線程(ActivityThread)中調(diào)用的,耗時(shí)操作會(huì)阻塞UI。
Q5.如果需要做耗時(shí)的操作,你會(huì)怎么做?
1.線程和Handler方式
2.Q1.1使用IntentService --> Q6
Q6.是否知道IntentService,在什么場(chǎng)景下使用IntentService?
當(dāng)我們編寫的耗時(shí)邏輯,不得不被service來(lái)管理的時(shí)候,就需要引入IntentService,
IntentService是繼承Service的,那么它包含了Service的全部特性,
當(dāng)然也包含service的生命周期,那么與service不同的是,IntentService在執(zhí)行onCreate操作
的時(shí)候,內(nèi)部開(kāi)了一個(gè)線程,去你執(zhí)行你的耗時(shí)操作。
Q6.1IntentService的原理
IntentService中提供了這么一個(gè)方法:
protected abstract void onHandleIntent(Intent intent);
這是一個(gè)抽象方法,也就是說(shuō)具體的實(shí)現(xiàn)需要被延伸到子類。
onHandleIntent()方法是什么時(shí)候被調(diào)用的呢?讓我們具體看IntentService的內(nèi)部實(shí)現(xiàn):
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1);
}
}
public IntentService(String name) {
super();
mName = name;
}
public void setIntentRedelivery(boolean enabled) {
mRedelivery = enabled;
}
@Override
public void onCreate() {
super.onCreate();
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
@Override
public void onStart(Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
IntentService在執(zhí)行onCreate的方法的時(shí)候,其實(shí)開(kāi)了一個(gè)線程HandlerThread,并獲得了當(dāng)前
線程隊(duì)列管理的looper,并且在onStart的時(shí)候,把消息置入了消息隊(duì)列:
@Override
public void handleMessage(Message msg) {
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1);
}
在消息被handler接受并且回調(diào)的時(shí)候,執(zhí)行了onHandlerIntent方法,該方法的實(shí)現(xiàn)是子類去做的。
我們可以得出這樣的結(jié)論:IntentService是通過(guò)Handler looper message的方式實(shí)現(xiàn)了一個(gè)
多線程的操作,同時(shí)耗時(shí)操作也可以被這個(gè)線程管理和執(zhí)行,同時(shí)不會(huì)產(chǎn)生ANR的情況。
Q6.2使用intentService與service有什么不同呢?(好處)
1.直接 創(chuàng)建一個(gè)默認(rèn)的工作線程,該線程執(zhí)行所有的intent傳遞給onStartCommand()以區(qū)別于
應(yīng)用程序的主線程
2.直接創(chuàng)建一個(gè)工作隊(duì)列,來(lái)逐個(gè)發(fā)送intent給onHandleIntent()
多線程
3.當(dāng)請(qǐng)求完成后自己會(huì)調(diào)用stopSelf(),所以你就不用調(diào)用該方法了
4.提供的默認(rèn)實(shí)現(xiàn)onBind()返回null,所以也不需要重寫這個(gè)方法
5.默認(rèn)實(shí)現(xiàn)的onStartCommand()的目的是將intent插入到工作隊(duì)列中
我們需要做的就是實(shí)現(xiàn)onHandlerIntent()方法,還有,構(gòu)造函數(shù)是必需的,而且必須調(diào)用
超IntentService(字符串) ,因?yàn)楣ぷ骶€程的構(gòu)造函數(shù)必須使用一個(gè)名稱
public HelloIntentService() {
super("HelloIntentService");
}
那么它為什么不用stopself()方法呢?因?yàn)閔andlerMessage方法里當(dāng)處理完請(qǐng)求后就會(huì)調(diào)用
stopself()方法了,外界就不用調(diào)用了:
@Override
public void handleMessage(Message msg) {
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1);
}
Q7.工作場(chǎng)景:如果一個(gè)應(yīng)用要從網(wǎng)絡(luò)上下載MP3文件,并在Activity上展示進(jìn)度條,這個(gè)Activity要求是可以轉(zhuǎn)屏的。那么在轉(zhuǎn)屏?xí)rActvitiy會(huì)重啟,如何保證下載的進(jìn)度條能正確展示進(jìn)度呢?
錯(cuò)誤方法:
(1)在轉(zhuǎn)屏前將進(jìn)度緩存,轉(zhuǎn)屏后再讀出來(lái)。
(2)使用android:configChanges設(shè)置,讓轉(zhuǎn)屏?xí)rActivity不銷毀和重建
針對(duì)第1個(gè)方案,我會(huì)繼續(xù)問(wèn)他將進(jìn)度值存在哪里? 轉(zhuǎn)屏的過(guò)程中,我們知道Activity的重建
算是比較耗時(shí)的,會(huì)可能會(huì)有幾百毫秒以上,那么這時(shí)候下載線程仍然在工作,進(jìn)度肯定和保存時(shí)的進(jìn)度
不一致了,如何處理這個(gè)問(wèn)題呢?
第2個(gè)方案,大家可以自己展開(kāi)思考,實(shí)際的項(xiàng)目中可能會(huì)需要額外做一些事情來(lái)處理ContentView
的橫豎布局的問(wèn)題。
如果使用Service來(lái)解決這個(gè)問(wèn)題,看似是比較完美的,不過(guò)就會(huì)涉及Activity(UI)和 Service
的交互問(wèn)題
Q7.1工作場(chǎng)景:如果我們有一個(gè)后臺(tái)服務(wù),是每隔一段時(shí)間請(qǐng)求一次服務(wù)器,類似于心跳服務(wù),只是沒(méi)有心跳服務(wù)那么頻繁,例如每2個(gè)小時(shí)執(zhí)行一次連接服務(wù)器操作,這樣的話,我們的應(yīng)用可能已經(jīng)退出了,而我們?nèi)孕枰@個(gè)服務(wù)時(shí)開(kāi)啟的,如何實(shí)現(xiàn)呢?
思路:重寫一個(gè)IntentService,仿效系統(tǒng)的IntentService,只是讓線程執(zhí)行完畢的時(shí)候,
不再銷毀這個(gè)Service(也就是刪掉源碼中的stopSelf語(yǔ)句,那么就不會(huì)出現(xiàn)銷毀這個(gè)Service了),
那么這樣這個(gè)Service就能夠長(zhǎng)時(shí)間運(yùn)行下去,同時(shí)不用獨(dú)立創(chuàng)建AsyncTask和Runable了,同時(shí)
可以直接在onHandleIntent中執(zhí)行一些長(zhǎng)時(shí)間的聯(lián)網(wǎng)操作了
Q8.對(duì)Service的認(rèn)識(shí),什么時(shí)候選擇使用service(使用場(chǎng)景)?
當(dāng)我們知道了Service的用途,心中有一個(gè)Service相關(guān)的概念時(shí),針對(duì)實(shí)際的場(chǎng)景還是要做具體的分析
再?zèng)Q定是否使用Service。因?yàn)镾ervice仍然是在主線程中調(diào)用,還是要開(kāi)線程才能處理長(zhǎng)時(shí)間的工作,
Service和UI的交互也讓這個(gè)方式變得不那么簡(jiǎn)便。如果你只需要在當(dāng)前界面去做一些耗時(shí)操作,
界面退出或改變時(shí),工作也要停止,那么這時(shí)直接使用Thread(或者AsyncTask, ThreadHandler)
會(huì)更合適你