一 Service簡介html
Service是Context的子類android
Service是四大組件之一 用來在後臺處理一些比較耗時的操做或者去執行某些須要長期運行的任務app
二 注意ide
Service裏面不能直接執行耗時的操做 由於Service裏面全部方法執行都是在主線程動畫
若是要執行耗時的操做 開啓子線程ui
三 Service特色this
1. 沒有界面spa
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去啓動進程裏面的主線程
服務: 服務不是線程 服務也不是進程 服務運行在進程裏面(https://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()方法啓動一個服務 再把服務進程提高爲前臺進程