? ? ? ?Service是Android四大組件之一,主要用于執行需要長時間運行的任務或者處理一些耗時的邏輯。Service可以在程序退出后,在后臺繼續運行。
一、Service的基本用法
啟動Service的方法和啟動Activity很類似,都需要借助Intent來實現,構建出一個Intent對象,調用startService()方法來啟動Service。然后同樣構建出了一個Intent對象,并調用stopService()方法來停Service。當啟動一個Service的時候,會調用該Service中的onCreate()和onStartCommand()方法。
啟動Service:
Intent startIntent =newIntent(this,MyService.class);
startService(startIntent);
關閉service:
Intent pauseIntent =newIntent(this,MyService.class);
stopService(pauseIntent);
? ? ? ?onCreate()方法只會在Service第一次被創建的時候調用,如果當前Service已經被創建過了,不管怎樣調用startService()方法,onCreate()方法都不會再執行,只會執行onStartCommand()方法。啟動Service之后,就可以在onCreate()或onStartCommand()方法里去執行一些具體的邏輯了。
? ? ? ?項目中的每一個Service都必須在AndroidManifest.xml中注冊。
二、Service和Activity通信
? ? ? Service中有一個onBind()方法,這個方法就是用來和Activity通信的。通常會新增了一個類繼承自Binder類,然后在這個類里寫我們的邏輯,再通過onBind()方法將這個類的實例返回到activity中。因此service類應該是這樣的:
public class MyService extends Service {
public static final String?TAG?="MyService";
private MyBinder?mBinder?=new MyBinder();
@Override
public void onCreate()?{
super.onCreate();
Log.i(TAG,"onCreate()?executed");
}
@Override
public int onStartCommand(Intent?intent,int flags,int startId)?{
Log.i(TAG,"onStartCommand()?executed");
return super.onStartCommand(intent,?flags,?startId);
}
@Override
public void onDestroy()?{
super.onDestroy();
Log.i(TAG,"onDestroy()?executed");
}
@Override
publicI Binder?onBind(Intent?intent)?{
return mBinder;
}
class MyBinder extends Binder?{
public void startDownload()?{
Log.i("TAG","startDownload()?executed");
}
}
}
在activity里與service關聯上,首先創建一個ServiceConnection的匿名類,在里面重寫了onServiceConnected()方法和onServiceDisconnected()方法,這兩個方法會在Activity與Service建立關聯和解除關聯的時候調用。在onServiceConnected()方法中,通過向下轉型得到了MyBinder的實例,即我們在service里返回的Binder的子類的實例。在Activity中根據具體的場景來調用Binder的子類中的任何public方法,即Activitys可以指揮Service干什么Service就去干什么。代碼:
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
myBinder = (MyService.MyBinder) service;
myBinder.startDownload();
}
};
Activity和Service的關聯由bindService()方法來完成。構建出一個Intent對象,然后調用bindService()方法將Activity和Service進行關聯。bindService()方法接收三個參數,第一個參數是Intent對象,第二個參數ServiceConnection的實例,第三個參數是一個標志位,這里傳入BIND_AUTO_CREATE表示在Activity和Service建立關聯后自動創建Service,這會使得MyService中的onCreate()方法得到執行,但onStartCommand()方法不會執行。
關聯:
Intent bindIntent =newIntent(this, MyService.class);
bindService(bindIntent,?connection,?BIND_AUTO_CREATE);
解除關聯:
unbindService(connection);
注意,任何一個Service在整個應用程序范圍內都是通用的,即MyService不僅可以和MainActivity建立關聯,還可以和任何一個Activity建立關聯,而且在建立關聯時它們都可以獲取到相同的MyBinder實例。
三、銷毀Service
stopService()只會讓Service停止,unbindService只會讓Service和Activity解除關聯,一個Service必須要在既沒有和任何Activity關聯又處理停止狀態的時候才會被銷毀。
四、Service和Thread的關系
Service和Thread之間沒有任何關系!Thread是用來開啟一個子線程,在Thread里執行一些耗時操作就不會阻塞主線程的運行。實際上,Service是運行在主線程里的。可以這樣檢驗:
在activity里打印當前線程編號:
Log.i("MyService","MainActivity thread id is "+ Thread.currentThread().getId());
在service里打印當前service的線程編號:
Log.i("MyService","MyService thread id is "+ Thread.currentThread().getId());
Android的后臺就是指,它的運行是完全不依賴UI的。即使Activity被銷毀,或者程序被關閉,只要進程還在,Service就可以繼續運行。比如說一些應用程序,始終需要與服務器之間始終保持著心跳連接,就可以使用Service來實現。你可能又會問,前面不是剛剛驗證過Service是運行在主線程里的么?在這里一直執行著心跳連接,難道就不會阻塞主線程的運行嗎?當然會,但是我們可以在Service中再創建一個子線程,然后在這里去處理耗時邏輯就沒問題了。
一個比較標準的Service就可以寫成:
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
new Thread(new Runnable() {
@Override
public void run() {
// 開始執行后臺任務
}
}).start();
return super.onStartCommand(intent, flags, startId);
}
class MyBinder extends Binder {
public void startDownload() {
new Thread(new Runnable() {
@Override
public void run() {
// 執行具體的下載任務
}
}).start();
}
}
五、創建前臺Service
Service幾乎都是在后臺運行的。因為Service的系統優先級是比較低的,當系統出現內存不足時,就有可能會回收掉正在后臺運行的Service。如果希望Service一直保持運行狀態,而不會由于系統內存不足的原因導致被回收,可以考慮使用前臺Service。前臺Service和普通Service最大的區別就在于,它會一直有一個正在運行的圖標在系統的狀態欄顯示,下拉狀態欄后可以看到更加詳細的信息,非常類似于通知的效果。方法就是在service里設置一下:
public class MyService extends Service {
public static final String TAG = "MyService";
private MyBinder mBinder = new MyBinder();
@Override
public void onCreate() {
super.onCreate();
// 在API11之后構建Notification的方式
Notification.Builder builder = new Notification.Builder
(this.getApplicationContext()); //獲取一個Notification構造器
Intent nfIntent = new Intent(this, MainActivity.class);
builder.setContentIntent(PendingIntent.
getActivity(this, 0, nfIntent, 0)) // 設置PendingIntent
.setLargeIcon(BitmapFactory.decodeResource(this.getResources(),
R.mipmap.ic_large)) // 設置下拉列表中的圖標(大圖標)
.setContentTitle("下拉列表中的Title") // 設置下拉列表里的標題
.setSmallIcon(R.mipmap.ic_launcher) // 設置狀態欄內的小圖標
.setContentText("要顯示的內容") // 設置上下文內容
.setWhen(System.currentTimeMillis()); // 設置該通知發生的時間
Notification notification = builder.build(); // 獲取構建好的Notification
notification.defaults = Notification.DEFAULT_SOUND; //設置為默認的聲音
startForeground(1, notification);
Log.d(TAG, "onCreate() executed");
}
.........
}
首先在MyService的onCreate()方法中創建了一個Notification對象,然后調用了它的setLatestEventInfo()方法來為通知初始化布局和數據,并在這里設置了點擊通知后就打開MainActivity。然后調用startForeground()方法就可以讓MyService變成一個前臺Service,并會將通知的圖片顯示出來。