Service 簡介
Service
是 Android
四大組件之一吧凉,一般而言,Activity
用于提供一個可供交互的屏幕界面瞻想,以此完成一些任務(wù)咆耿。而Service
則與 Activity
不同,它主要用于執(zhí)行一些不需要與用戶進(jìn)行交互且需要長時間運(yùn)行的任務(wù)故硅。值得注意是庶灿,Service雖然是處理后臺任務(wù)的,但它仍然運(yùn)行在主線程中吃衅,所以往踢,如果需要執(zhí)行耗時任務(wù),還需要開啟一個子線程徘层,不然會出現(xiàn) ANR(Application Not Responding)
現(xiàn)象峻呕。慶幸的是,Android
中存在一個 IntentService
類趣效,它擁有自己的線程瘦癌,是獨(dú)立于 主線程的,所以我們需要使用Service
的時候可以直接繼承自 IntentService
跷敬。
Service 的兩種啟動方法
- Context.startService()
如果我們需要程序在后臺執(zhí)行一項任務(wù)讯私,即使當(dāng)前用戶并沒有與程序發(fā)生交互,而且這項任務(wù)的執(zhí)行并不需要用戶的干預(yù)干花,即不需要與用戶發(fā)生過多的交互妄帘,我們可以選擇該方法。
當(dāng) Context.startService()
方法調(diào)用后池凄,系統(tǒng)會首先調(diào)用 Service
的 onCreate()
方法(第一次運(yùn)行調(diào)用該方法抡驼,之后就不再調(diào)用),然后調(diào)用 onStartCommand(Intent, int, int)
肿仑。該Service
會一直運(yùn)行下去致盟,直到 Context.stopService()
或者 StopSelf()
被調(diào)用碎税。
onStartCommand(Intent, int, int)
方法會返回一個整形常量,該常量用于告訴系統(tǒng)如何處理 Service
的重啟操作馏锡,其中三個返回值為
返回 START_STICKY
代表當(dāng)前系統(tǒng)出于某些原因關(guān)閉Service
時雷蹂,Service
會被重新啟動。但是杯道,當(dāng)系統(tǒng)重啟 Service 時匪煌, onStartCommand()
參數(shù)中的Intent
會被置為 null
。
返回START_NOT_STICKY
意味著 Service
不會在系統(tǒng)關(guān)閉它時重新啟動党巾,適合執(zhí)行一次性的操作 萎庭。
返回 START_REDELIVER_INTENT
的效果和 START_STICKY
基本一樣,但是 onStartCommand()
會接收到Service
被銷毀之前接收到的最后一個 Intent
齿拂。
- Context.bindService()
對應(yīng)于上一個方法驳规,如果該后臺任務(wù)需要與用戶發(fā)生頻繁的交互,可以采用該方法署海,當(dāng)然這種方法比上一種方法復(fù)雜些吗购。
當(dāng)Context.bindService()
方法調(diào)用后,如果該 Service 還不存在砸狞,那么 首先調(diào)用onCreate()
方法捻勉,然后調(diào)用onBind(Intent)
方法。 該 Service
會一直運(yùn)行只要Connection
是建立著的趾代。
Service 的使用
1.如果采用的是Context.startService()
,簡單用法如下:
public class MyService extends Service{
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
Log.d("MyService", "onCreate");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("MyService", "onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
}
然后在Activity
中調(diào)用startService(Intent)
即可贯底。
這種方法,我們可以在onCreate()
方法中初始化一些我們需要的變量撒强,在 onStartCommand()
方法中執(zhí)行任務(wù)禽捆,因為該方法 在每次啟動服務(wù)時都會被調(diào)用,而onCreate()
只在服務(wù)創(chuàng)建時執(zhí)行一次飘哨。
使用這種方法胚想,當(dāng)服務(wù)完成時,可以使用發(fā)送 Broadcast
的方法通知Activity
已經(jīng)完成芽隆,然后停止該服務(wù)浊服。
2.使用 Context.bindService()
,簡單用法如下
public class MyLocalService extends Service {
private LocalBinder mLocalBinder = new LocalBinder();
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mLocalBinder;
}
public void doLongRunningOperation() {
//執(zhí)行耗時任務(wù)
}
public class LocalBinder extends Binder {
public MyLocalService getService() {
return MyLocalService.this;
}
}
}
public class MainActivity extends AppCompatActivity implements ServiceConnection{
private MyLocalService mLocalService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
protected void onResume() {
super.onResume();
Intent intent = new Intent(this,MyLocalService.class);
bindService(intent,this,BIND_AUTO_CREATE);
}
@Override
protected void onPause() {
super.onPause();
if (mLocalService != null){
unbindService(this);
}
}
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
mLocalService = ((MyLocalService.LocalBinder)iBinder).getService();
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
mLocalService = null;
}
}
如圖示,當(dāng)在Activity
中調(diào)用 bindService()
之后胚吁,接著就會調(diào)用 Service 中的onBind()
方法牙躺,該方法會返回一個IBinder
給onServiceConnected()
方法,然后就可以通過 IBinder
獲得該 Service
對象腕扶,接著就可以像使用一個 普通 Java
對象使用它了孽拷。
請注意,以上兩種方法在執(zhí)行耗時任務(wù)時半抱,都需要手動的去開啟一個子線程脓恕,為了方便和高效膜宋,我們可以使用 IntentService
工具類。
IntentService 的使用
IntentService
在Service
中包裝了一個處理后臺線程的 Handler
炼幔,多個調(diào)用會被內(nèi)部的Hnadler
放到隊列中秋茫,所以能確保在同一時間只能有一個 Intent
被處理,基于IntentService
的Service
會一直處于啟動狀態(tài)乃秀,直到隊列中沒有要處理的任務(wù)肛著。
public class MyIntentService extends IntentService{
public static String ACTION_DOWNLOAD = "com.example.myintentservice.download";
public MyIntentService(String name) {
super(name);
setIntentRedelivery(false);
}
@Override
protected void onHandleIntent(Intent intent) {
String action = intent.getAction();
if (ACTION_DOWNLOAD.equals(action)){
download();
}
}
private void download(){
//Download
}
}
這個時候,我們需要在清單文件中為 Service 添加相應(yīng)的intent-filter
环形,然后以帶有特定action
的Intent
調(diào)用 Context.startService()
方法策泣。
Service 的通信
上面已經(jīng)提到過很多次衙傀,如果使用Context.startService()
,和 Activity
交互是很不方便的抬吟,就好像Activity
提醒 Service
你可以運(yùn)行了,然后Service
就開始自顧自的運(yùn)行统抬,直到運(yùn)行結(jié)束火本,通過 Broadcast Receiver
發(fā)送一條廣播,告訴Activity
我完成任務(wù)了聪建,你可以調(diào)用 stopService()
了钙畔。如果僅僅是這樣,是很不利于Service
和 Activity
之間進(jìn)行大規(guī)慕痿铮快速更新操作的擎析。那么該怎么辦呢?
Context.bindService
天生就是解決這個問題的挥下,誰讓咱在創(chuàng)建的時候還能返回一個IBinder
呢揍魂。 下面就來使用它完成一個下載任務(wù)
public class MyLocalService extends Service {
private LocalBinder mLocalBinder = new LocalBinder();
private Callback mCallback;
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mLocalBinder;
}
public void doLongRunningOperation() {
new MyAsyncTask().execute();
}
public class LocalBinder extends Binder {
public MyLocalService getService() {
return MyLocalService.this;
}
}
public interface Callback{
public void onOperationProgress(int progress);
public void onOperationCompleted(boolean complete);
}
public void setCallback(Callback callback){
mCallback = callback;
}
private final class MyAsyncTask extends AsyncTask<Void,Integer,Boolean>{
@Override
protected Boolean doInBackground(Void... voids) {
int progress = 0;
for (progress = 0;progress<=100;progress+=25) {
publishProgress(progress);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return true;
}
@Override
protected void onProgressUpdate(Integer... values) {
if (mCallback != null){
mCallback.onOperationProgress(values[0]);
}
}
@Override
protected void onPostExecute(Boolean aBoolean) {
if (mCallback != null && aBoolean){
mCallback.onOperationCompleted(true);
}
}
}
}
public class MainActivity extends AppCompatActivity implements ServiceConnection,MyLocalService.Callback{
private MyLocalService mLocalService;
private Button mButton;
private ProgressDialog mDialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mButton = (Button) findViewById(R.id.start);
mDialog = new ProgressDialog(this);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mDialog.setTitle("Download");
mDialog.show();
mLocalService.doLongRunningOperation();
}
});
}
@Override
protected void onResume() {
super.onResume();
Intent intent = new Intent(this,MyLocalService.class);
bindService(intent,this,BIND_AUTO_CREATE);
}
@Override
protected void onPause() {
super.onPause();
if (mLocalService != null){
mLocalService.setCallback(null);
unbindService(this);
}
}
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
mLocalService = ((MyLocalService.LocalBinder)iBinder).getService();
mLocalService.setCallback(this);
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
mLocalService = null;
}
@Override
public void onOperationProgress(int progress) {
mDialog.setMessage(progress+"%");
}
@Override
public void onOperationCompleted(boolean complete) {
if (complete){
mDialog.dismiss();
Toast.makeText(this, "Download success", Toast.LENGTH_SHORT).show();
}
}
}