Android基礎之四大元件-詳解Service
1 介紹
Service是android的一種機制。Service是執行在後臺的程式碼,不能與使用者互動,可以執行在自己的程序,也可以執行在其他應用程式的上下文裡。需要通過某一個Activity或其他Context物件來呼叫。
1.1 Service的生命週期
被啟動的服務的生命週期:如果一個Service被某個Activity呼叫 Context.startService方法啟動,那麼不管是否有Activity使用bindService繫結或unbindService解除繫結到該Service,該Service都在後臺執行。如果一個Service被startService方法多次啟動,那麼onCreate方法只會呼叫一次,onStartCommand將會被呼叫多次(對應呼叫startService的次數),並且系統只會建立Service的一個例項(因此只需要一次呼叫stopService)
被繫結的服務的生命週期:如果一個Service被某個Activity呼叫 Context.bindService方法繫結啟動,不管呼叫bindService呼叫幾次,onCreate方法都只會呼叫一次,onBind方法也只會呼叫一次,同時onStartCommand方法始終不會被呼叫。當連線建立之後,Service將會一直執行,除非呼叫Context.unbindService斷開連線或者之前呼叫bindService的Context不存在了(如Activity被finish的時候),系統將會自動停止Service,對應onDestroy將被呼叫。
被啟動又被繫結的服務的生命週期:如果一個Service又被啟動又被繫結,則該Service將會一直在後臺執行。並且不管如何呼叫,onCreate始終只會呼叫一次,對應startService呼叫多少次,Service的onStartCommand便會呼叫多少次。呼叫unbindService將不會停止Service,需要unbindService與stopService同時呼叫或Service的stopSelf來停止服務。
當服務被停止時清除服務:當一個Service被終止(①呼叫stopService;②呼叫stopSelf;③不再有繫結的連線,沒有被啟動)時,onDestroy方法將會被呼叫,在這裡你應當做一些清除工作,如停止在Service中建立並執行的執行緒。
1.2 註冊Service
無論使用哪種啟動方法,都需要在xml裡註冊你的Service:
<service
android:name=".packnameName.youServiceName"
android:enabled="true" />
1.3 什麼時候使用Service
- 播放多媒體的時候,使用者啟動了其他的Activity這個時候程式要在後臺繼續播放。
- 後臺service下載外掛、更新包。
- 檢測SD卡上檔案的變化。
- 在後臺記錄你地理位置的改變等等。
1.4 Service分為本地服務(LocalService)和遠端服務(RemoteService)
預設情況下是在同一個主執行緒中。但可以通過配,android:process=”:remote” 屬性讓 Service 執行在不同的程序。
本地服務依附在主程序上而不是獨立的程序,這樣在一定程度上節約了資源,另外Local服務因為是在同一程序因此不需要IPC,也不需要AIDL。相應bindService會方便很多。主程序被Kill後,服務便會終止。
遠端服務為獨立的程序,對應程序名格式為所在包名加上你指定的android:process字串。由於是獨立的程序,因此在Activity所在程序被Kill的時候,該服務依然在執行,不受其他程序影響,有利於為多個程序提供服務具有較高的靈活性。該服務是獨立的程序,會佔用一定資源,並且使用AIDL進行IPC稍微麻煩一點。
2 Service的具體問題
2.1 Service可以執行耗時操作嗎
不能,超過20s就會出現ARN。
2.2 Service的啟動方法,互相的區別
(1)startService啟動的服務:主要用於啟動一個服務執行後臺任務,不進行通訊。停止服務使用stopService;當用訪問者啟動之後,如果訪問者不主動關閉,Service就不會關閉。
(2)bindService啟動的服務:該方法啟動的服務可以進行通訊,停止服務使用unbindService。應用程式可以通過ServiceConnection進行資料互動。在實現Service時重寫的onBind方法中,其返回的物件會傳給ServiceConnection物件的onServiceConnected(ComponentName name, IBinder service)中的service引數;也就是說獲取了serivce這個引數就得到了Serivce元件返回的值。
//其中只要與Service連線成功conn就會呼叫其onServiceConnected方法。
Context.bindService(Intent intent,ServiceConnection conn,int flag)
(3)startService同時也bindService 啟動的服務:停止服務應同時使用stepService與unbindService。
2.3 Service與Activity怎麼實現通訊(程序內通訊)
(1)通過Binder物件。同進程下Activity與Service雙向通訊,Activity呼叫bindService (Intent service, ServiceConnection conn, int flags)方法。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
<application
<service android:name=".MyService" />
</application>
</manifest>
/**
* 第1步,新建一個繼承自Service的類MyService;然後在AndroidManifest.xml裡註冊這個Service
*/
public class MyService extends Service {
private static final String TAG = "xiaoguan";
//第4.1步,例項化一個MyBinder物件
private MyBinder mBinder = new MyBinder();
private OnTestListener mListener;
@Override
public void onCreate() {
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
//第4.2步,在onBind回撥方法裡面返回這個mBinder物件
return mBinder;
}
//第3步,新建一個繼承自Binder的類MyBinder
public class MyBinder extends Binder {
//Activity通過Binder來呼叫Service的方法將訊息傳給Service
public void testMethod(String msg) {
Log.d(TAG, "receive message from activity: "+msg);
//並回調mListener.onTest告訴Activity已收到訊息
mListener.onTest("hi, activity");
}
//MyBinder 裡面提供一個註冊回撥的方法
public void setOnTestListener(OnTestListener listener) {
mListener = listener;
}
}
//自定義一個回撥介面
public interface OnTestListener {
void onTest(String str);
}
}
public class MainActivity extends Activity {
private static final String TAG = "xiaoguan";
private MyService.MyBinder mBinder;
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//第5步,所說的在Activity裡面取得Service裡的binder物件
mBinder = (MyService.MyBinder) service;
//第6步,註冊自定義回撥
mBinder.setOnTestListener(new MyService.OnTestListener() {
@Override
public void onTest(String str) {
Log.d(TAG, "receive message from service: "+str);
}
});
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(MainActivity.this, MyService.class);
//第2步,Activity裡面使用bindService方式啟動MyService,也就是綁定了MyService
bindService(intent,mConnection,BIND_AUTO_CREATE);
findViewById(R.id.bt1).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//點選按鈕呼叫mBinder裡面的方法,傳送訊息給Service
mBinder.testMethod("hi, service");
}
});
}
}
//結果列印
receive message from activity: hi, service
receive message from service: hi, activity
(2)通過廣播。Service向Activity傳送訊息,可以使用廣播,當然Activity要註冊相應的接收器。比如Service要向多個Activity傳送同樣的訊息的話,用這種方法就更好。
2.4 Service裡面可以彈出dialog或Toast
(1)Toast通常使用Activity和Application的context,也可以使用Service、ContentProvider和BroadcastReceiver的context。但是在IntentService的onHandleIntent()不能使用,因為其在子執行緒中。
(2)Dialog只能在Activity中使用,其他元件使用會丟擲異常。也有方法可以實現,如下(加個許可權,在manifest中新增此許可權以彈出dialog)。
<uses-permission Android:name="android.permission.SYSTEM_ALERT_WINDOW" />
dialog.getWindow().setType((WindowManager.LayoutParams.TYPE_SYSTEM_ALERT));
Builder builder = new AlertDialog.Builder(this);
builder.setMessage("是否重啟服務");
builder.setNegativeButton("取消", new OnClickListense() {
@Override
public void onClick(DialogInterface dialog, int which) {
// to do
}
});
builder.setPositiveButton("確定", new OnClickListense() {
@Override
public void onClick(DialogInterface dialog, int which) {
// to do
}
});
final AlertDialog dialog = buidler.create();
//在dialog show前新增此程式碼,表示該dialog屬於系統dialog。
dialog.getWindow().setType((WindowManager.LayoutParams.TYPE_SYSTEM_ALERT));
new Thread() {
public void run() {
SystemClock.sleep(2000);
hanlder.post(new Runnable() {
@Override
public void run() {
dialog.show();
}
});
};
}.start();
2.5 為什麼開啟Service去建立子執行緒而不是Activity中
(1)若直接在Activity中新開一條執行緒來做耗時操作,當該Activity退出到桌面或其他情況時將成為一個後臺程序。
(2)若在Service中新啟動執行緒,則此時Android會依據程序中當前活躍元件重要程度,將其判斷為服務程序,優先順序比(1)高。
因為服務程序的優先順序比後臺程序的優先順序高,所以對於一個需要啟動一個長時間操作的activity來說,開啟service去建立子執行緒比Activity中建立子執行緒更好,尤其是對於操作將很可能超出activity的持續時間時。
比如要上傳一個圖片檔案,應該開啟一個service來進行上傳工作,這樣在使用者離開activity時工作仍在進行。使用service將會保證操作至少有服務程序的優先順序。