[Android每日精彩]Service

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ì)更合適你
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容