一 Service簡介
Service是Context的子類
Service是四大組件之一 用來在幕後處理一些比較耗時的操作或者去執行某些需要長期啟動並執行任務
二 注意
Service裡面不能直接執行耗時的操作 因為Service裡面所有方法執行都是在主線程
如果要執行耗時的操作 開啟子線程
三 Service特點
1. 沒有介面
2. 在後台長時間的運行
3. 無法自己啟動
4. 單例模式
四 建立一個Service
1. 繼承Service
public class MyService extends Service { @Override public void onCreate() { super.onCreate(); Log.i("HUANG", "onCreate"); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.i("HUANG", "onStartCommand"); return super.onStartCommand(intent, flags, startId); } @Nullable @Override public IBinder onBind(Intent intent) { Log.i("HUANG", "onBind"); return null; } @Override public boolean onUnbind(Intent intent) { Log.i("HUANG", "onUnbind"); return super.onUnbind(intent); } @Override public void onDestroy() { super.onDestroy(); Log.i("HUANG", "onDestroy"); }}
2. AndroidManifest.xml application節點裡面配置service name屬性必須配置 其餘可選
<service android:name=".service.MyService" />
3. 啟動Service startService()或者bindService()
public class MainActivity extends AppCompatActivity implements View.OnClickListener { ServiceConnection mConnection; //服務的連線物件 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); findViewById(R.id.start).setOnClickListener(this); findViewById(R.id.stop).setOnClickListener(this); findViewById(R.id.bind).setOnClickListener(this); findViewById(R.id.unbind).setOnClickListener(this); } @Override public void onClick(View view) { switch (view.getId()) { case R.id.start: startService(new Intent(this, MyService.class)); break; case R.id.stop: stopService(new Intent(this, MyService.class)); break; case R.id.bind: Intent service = new Intent(this, MyService.class); mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) {} @Override public void onServiceDisconnected(ComponentName name) {} }; bindService(service, mConnection, Context.BIND_AUTO_CREATE); break; case R.id.unbind: unbindService(mConnection); break; } }}
五 Service生命週期
Service生命週期會因為啟動方式不一樣而不同 停止Service的方式也不同
當採用startService()方法啟動 與之有關的生命週期方法
onCreate() -> onStartCommand() -> onDestroy()
onCreate() 該方法在Service被建立時調用 只會被調用一次 無論調用多少次startService()方法和bindService()方法 Service也只被建立一次
onStartCommand() 只有採用startService()方法啟動時才會回調該方法 該方法在Service開始運行時被調用 多次調用startService()方法儘管不會多次建立Service 但onStartCommand()方法會被多次調用
onDestroy() 該方法在Service被銷毀時調用
只要調用一次stopService()方法便可以停止Service 無論之前它被調用了多少次的啟動Service方法
當採用bindService()方法啟動 與之有關的生命週期方法
onCreate() -> onBind() -> onUnbind() -> onDestroy()
onCreate() 該方法在Service被建立時調用 只會被調用一次 無論調用多少次startService()方法和bindService()方法 Service也只被建立一次
onBind() 只有採用bindService()方法啟動時才會回調該方法 該方法在調用者與Service綁定時被調用 當調用者與Service已經綁定 多次調用bindService()方法並不會導致該方法被多次調用
onUnbind() 只有採用bindService()方法啟動時才會回調該方法 該方法在調用者與Service解除綁定時被調用
onDestroy() 該方法在Service被銷毀時調用
只要調用一次unbindService()方法便可以停止Service 多次調用會報錯
如果一個Activity已經bindService() 那麼Activity退出一定要unbindService() 否則報錯
多個用戶端可以綁定同一個Service 如果Service還未被啟動 bindService()方法可以啟動Service
如果先採用startService()方法啟動 接著又採用bindService()方法啟動(此處startService()和bindService()沒有先後順序)
這個時候單獨執行 stopService()或者unbindService() Service都不會被銷毀
只有先執行stopService() 接著又執行unbindService() Service才會被銷毀(此處stopService()和unbindService()沒有先後順序)
也就是說 一個Service必須要在兩種啟動方式都停止的情況下才會被銷毀
六 startService()和bindService()區別
1. 導致Service的生命週期不同
2. 停止Service的方式不同
3. startService()啟動的Service 在系統正在啟動並執行服務中可以找到 bindService()啟動的Service 在系統正在啟動並執行服務中找不到
4. startService()調用者和Service是沒有關係的 即使調用者退出了 Service仍然運行 bindService()調用者和Service是綁定在一起的 調用者一旦退出 Service也就終止 "同生共死"
七 startService()和bindService()應用情境
通過startService()和stopService()啟動關閉Service 適用於調用者和Service之間沒有互動的情況
通過bindService()和unbindService()啟動關閉Service 適用於調用者和Service之間需要方法調用或者傳遞參數
八 線程 進程 服務
線程: 是CPU執行的一個最小單元
進程: 是系統裡面的一個最小單元 一個應用可以理解為一個進程 使用者啟動一個應用程式 作業系統就會為其分配一塊記憶體空間 CPU去啟動進程裡面的主線程
服務: 服務不是線程 服務也不是進程 服務運行在進程裡面(developer.android.com/reference/android/app/Service#WhatIsAService)
九 進程的優先順序別
Android系統試圖儘可能長的保持一個應用程式進程 只有記憶體不足的時候 系統才會殺死進程
殺死進程是根據進程的優先順序別 先殺層級低的
進程的優先順序別排序 前台進程 > 可視進程 > 服務進程 > 後台進程 > 空進程
1. 前台進程(使用者當前工作所需要的 一個進程如果滿足下列任何條件被認為是前台進程)
1.1 運行著一個正在與使用者互動的Activity(onResume()方法已經被調用)
1.2 寄宿著一個Service 該Service綁定到了一個與使用者互動的Activity
1.3 有一個Service對象正在執行生命週期方法
1.4 有一個BroadcastReceiver對象正在執行生命週期方法
2. 可視進程(沒有任何前台組件 但是仍然能影響使用者在螢幕上看到東西 一個進程如果滿足下列任何條件被認為是可視進程)
2.1 寄宿著一個不是前台的Activity 但是它對使用者仍可見(onPause()方法已經被調用)
2.2 寄宿著一個Service 該Service綁定到了一個可視的Activity
3. 服務進程(在後台 應用裡面有一個用startService()方法啟動的服務在運行)
4. 後台進程(在後台 任務棧裡面有Activity)
5. 空進程(在後台 任務棧裡面沒有Activity)
前台進程和可視進程難以被殺 服務進程要保活 後台進程和空進程被殺了影響不大
把服務進程提升為前台進程
public class MyService extends Service { @Override public void onCreate() { super.onCreate(); /** * Intent未指定Activity 無跳轉 * * Intent已指定Activity * 應用在後台 跳轉到指定Activity 當指定Activity退出時 不管點擊通知前是在哪個頁面 都會回到本應用棧頂頁面 * 應用不在後台 跳轉到指定Activity 當指定Activity退出時 回到點擊通知前的那個頁面 * 注意: 指定Activity也受啟動模式限制 * */ Intent intent = new Intent(this, MainActivity.class); PendingIntent pendingIntent = PendingIntent.getActivity(this, 99, //requestCode intent, PendingIntent.FLAG_UPDATE_CURRENT); //更新 同一個id並且同一個requestCode的PendingIntent NotificationCompat.Builder builder = new NotificationCompat.Builder(this); builder.setSmallIcon(R.mipmap.ic_launcher) //狀態列顯示的小表徵圖 .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher)) //下拉顯示的大表徵圖 .setTicker("滴滴") //首次出現在通知欄 帶上升動畫效果 .setContentTitle("ContentTitle") //標題 .setContentText("ContentText") //內容 .setWhen(System.currentTimeMillis()) //通知產生的時間 會在通知資訊裡顯示 .setContentInfo("ContentInfo") //顯示資訊 .setPriority(NotificationCompat.FLAG_FOREGROUND_SERVICE) //設定通知優先順序 .setDefaults(Notification.DEFAULT_ALL) //設定通知預設的聲音 震動 呼吸燈 .setAutoCancel(false) //點擊和清理可以去掉通知 .setOngoing(true) //設定為一個進行中的通知 通常是用來表示一個背景工作(如播放音樂 檔案下載) .setContentIntent(pendingIntent); // 把服務進程提升為前台進程 startForeground(100, builder.build()); } @Nullable @Override public IBinder onBind(Intent intent) { return null; }}
如果要保證一個應用在後台不被殺死 可以先在應用裡面採用startService()方法啟動一個服務 再把服務進程提升為前台進程
Android Service(下)