1、今天我們來分析Service中的一個小技巧:前臺服務(Forground Service) 【問題】:我們都知道服務是運行在後臺的,如果系統出現記憶體不足的情況,那麼此時,系統就可能回收後代的服務,那麼我們如何保證服務可以一直運行? 【解決】:在服務中,有一個前臺服務的概念,調用startForg ...
1、今天我們來分析Service中的一個小技巧:前臺服務(Forground Service)
----------------------------------------------------------------------------------------------------------------------------------------
【問題】:我們都知道服務是運行在後臺的,如果系統出現記憶體不足的情況,那麼此時,系統就可能回收後代的服務,那麼我們如何保證服務可以一直運行?
【解決】:在服務中,有一個前臺服務的概念,調用startForground()方法。
我們看看官網對前臺服務及startForeground()的描述:
看了官方的解釋後,我們再來看看如何使用,上代碼:
public class MyService extends Service{ ......
@Override
public void onCreate() {
super.onCreate();
Intent intent = new Intent(getBaseContext(), MainActivity.class);
PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0 , intent, PendingIntent.FLAG_CANCEL_CURRENT);
Notification no = new Notification.Builder(getBaseContext()) // 啟動服務後,在前臺添加一個Notification
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.mipmap.ic_launcher)
.setTicker("Create a front desk Service!")
.setContentTitle("This is a front desk service")
.setContentText("A service skill!!!")
.setContentIntent(pi)
.setAutoCancel(true)
.setDefaults(Notification.DEFAULT_ALL)
.build();
startForeground(1, no);
Log.d(TAG, "onCreate executed");
}
}
我們再看一下官方文檔:
以上的代碼是在Service的創建中添加了一個Notification,調用startForground()就可以保證:只要服務一直存在,那麼在前臺就會一直顯示這個Notification。
如果我們在onDestroy()中調用stopForground()方法,會銷毀這個Notification,但是Service還是存活的,此時Service就會面臨被System幹掉的風險。
如果直接STOP SERVICE,那麼Notification和Service都會銷毀。
----------------------------------------------------------------------------------------------------------------------------------------
2、接下來,我們再來看一個Service的另外一個小技巧:IntentService
【問題】:我們知道服務的代碼邏輯是在主線程中執行的,如果我們在主線程中需要執行一些耗時的操作,那麼很有可能出現ANR(程式暫無響應)的狀況。
這個時候,我們可以採用Android的多線程編程(小編在之前的 AsyncTask 貼中講解過多線程,可以回顧)的方式,我們來看一段代碼:
public class MyService extends Service{ @Nullable @Override public IBinder onBind(Intent intent) { return null; } @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); }
}
現在,服務可以啟動起來了,但是如果不調用StopService()或stopSelf()方法,服務會一直運行,現在我們修改一下代碼:
public class MyService extends Service{ @Nullable @Override public IBinder onBind(Intent intent) { return null; } @Override public int onStartCommand(Intent intent, int flags, int startId) { new Thread(new Runnable() { @Override public void run() { // 處理具體的邏輯 // 開啟一個線程處理耗時操作
stopSelf(); // 讓服務執行完邏輯後自行停止 } }).start(); return super.onStartCommand(intent, flags, startId); } }
上面的代碼就是一個標準的Service的書寫形式,主要包含兩個知識點:Thread子線程的創建和stopSelf()方法的調用。
其實,在一般的使用過程中,一部分程式員很容易忘記以上兩個要點,存在遺忘,那麼有沒有更好的辦法能夠實現上面兩個需求呢?
【解決】:在Android中,專門提供了一個IntentService類(android.app.IntentService),這個類就能很好的滿足我們的需求!我們直接通過代碼來看:
(1)新建一個MyIntentService類繼承自IntentService,代碼:
public class MyIntentService extends IntentService{ public MyIntentService() { super("MyIntentService"); } @Override protected void onHandleIntent(Intent intent) { Log.d("MyIntentService", "MyIntentServiceThread id is " + Thread.currentThread().getId()); } @Override public void onDestroy() { super.onDestroy(); Log.d("MyIntentService", "onDestroy executed"); } }
以上代碼做了幾件事:
1、提供了一個無參的構造方法,並且調用了父類的有參構造函數(這個就不需要我說為什麼了吧);
2、子類實現父類的onHandleIntent()抽象方法,這個方法好就好在,它是一個已經運行在子線程中的方法。也就是說,服務調用了它,那麼執行的邏輯就如同Thread子線程。
onHandleIntent = Thread().start() + stopSelf()
3、onHandleIntent()執行完後會銷毀服務?會selfStop()?接著往下看代碼。
----------------------------------------------------------------------------------------------------------------------------------------
(2)在xml文件中,創建一個MyIntentService服務按鈕:
<Button android:id="@+id/start_intent_service" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/intent_service"/>
(3)接下來,修改MainActivity中的代碼:
public class MainActivity extends Activity {
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button startIntentService = (Button) super.findViewById(R.id.start_intent_service); startIntentService.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) { Log.d("MyIntentService", "MainActivity Thread id is " + Thread.currentThread().getId()); // 查看主線程的id Intent intentService = new Intent(getBaseContext(), MyIntentService.class); startService(intentService); } }); } }
(4)最後,在AndroidMainfest中註冊服務:
<service android:name=".MyIntentService" />
【結果】:直接看一下代碼執行的效果。
從打出的LOG可以看出:
(1)MyIntentService和MainActivity所在進程的id是不一樣的;
(2)onHandleIntent()方法在執行完邏輯後確實銷毀了服務,效果等同於stopSelf()。
從上面的分析可以看出onHandleIntent()方法確實相當的好用!