Service 學習

A Service is an application component representing either an application's desire to perform a longer-running operation while not interacting with the user or to supply functionality for other applications to use. Each service class must have a corresponding <service> declaration in its package'sAndroidManifest.xml. Services can be started with Context.startService() and Context.bindService().
Note that services, like other application objects, run in the main thread of their hosting process. This means that, if your service is going to do any CPU intensive (such as MP3 playback) or blocking (such as networking) operations, it should spawn its own thread in which to do that work. More information on this can be found in Processes and Threads. The IntentService class is available as a standard implementation of Service that has its own thread where it schedules its work to be done.

從官方文檔上可以看出,Service服務是不與用戶交互,長時間在后臺運行的一個組件。但是所在線程是在主線程,如果要處理耗時操作如播放音樂,處理網絡請求的話需要啟動一個子線程來處理。


一、啟動與銷毀

  • 啟動Service
    啟動Service有兩種方法:
    ①startService()
Intent intent = new Intent(MainActivity.this,TestService.class);
startService(intent);

生命流程:onCreate() → onStartCommand() → onDestory()
注意: onCreate()方法只會執行一次,多次調用StartService()方法只會在第一次調用onCreate(),而onStartCommand()會多次調用。
當服務被啟動后,Service與Activity就沒有了關系,Activity退出后Service還會執行。
②bindService()

bindService(intent,mcon,BIND_AUTO_CREATE);
public MCon mcon = new MCon();
public class MCon implements ServiceConnection{     
      @Override    
      public void onServiceConnected(ComponentName name, IBinder service) {        
            myBinder = (TestService.MyBinder)service;    
      }    
      @Override    
      public void onServiceDisconnected(ComponentName name) {

      }
}
onServiceConnected()
系統調用這個來傳送在service的onBind()中返回的IBinder.
OnServiceDisconnected()
Android系統在同service的連接意外丟失時調用這個.比如當service崩潰了或被強殺了.
當客戶端解除綁定時,這個方法不會被調用. 類ServiceConnection中的onServiceDisconnected()方法在正常情況下是不被調用的,
它的調用時機是當Service服務被異外銷毀時,例如內存的資源不足時這個方法才被自動調用。

生命流程: onCreate() → onBind() → onUnbind() → onDestory()
多次調用bindService()并不會再次調用onCreate() 和onBind()。
當服務被啟動后,Service與Activity就綁定在了一起,當Activity退出之后Service也退出。

  • 銷毀service
    使用startService()啟動之后調用stopService()來銷毀服務。
    使用bindService()綁定之后調用unbindService()來銷毀服務。
    如果同時調用startService()與bindService(),銷毀時需要同時調用stopService()與unbindService()來銷毀。
           
    1.如果先bindService,再startService:
    在bind的Activity退出的時候,Service會執行unBind方法而不執行onDestory方法,因為有startService方法調用過,所以Activity與Service解除綁定后會有一個與調用者沒有關連的Service存在
    2.如果先bindService,再startService,再調用Context.stopService
    Service的onDestory方法不會立刻執行,因為有一個與Service綁定的Activity,但是在Activity退出的時候,會執行onDestory,如果要立刻執行stopService,就得先解除綁定 
     
    要注意合理中斷Service中的線程,最好設置變量檢測中斷(先挖個坑)。

  • onStartCommand返回值
    START_STICKY:如果service進程被kill掉,保留service的狀態為開始狀態,但不保留遞送的intent對象。隨后系統會嘗試重新創建service,由于服務狀態為開始狀態,所以創建服務后一定會調用onStartCommand(Intent,int,int)方法。如果在此期間沒有任何啟動命令被傳遞到service,那么參數Intent將為null。
    START_NOT_STICKY:“非粘性的”。使用這個返回值時,如果在執行完onStartCommand后,服務被異常kill掉,系統不會自動重啟該服務
    START_REDELIVER_INTENT:重傳Intent。使用這個返回值時,如果在執行完onStartCommand后,服務被異常kill掉,系統會自動重啟該服務,并將Intent的值傳入。
    START_STICKY_COMPATIBILITY:START_STICKY的兼容版本,但不保證服務被kill后一定能重啟。

  • **bindService參數 **
    BIND_AUTO_CREATE:一般為這個,代表Service不存在時自動創建該Service。

二、Service與Activity通信

  • 采用onBind與接口方式雙向通信
public interface BinderInterface {    
      int getI();    
      void setI(int a);
}          
            
class MyBinder extends Binder implements BinderInterface{    
      @Override    
      public int getI() {        
      return i;    
      }    
      @Override    
      public void setI(int a) {        
      i = a;        
      mtThread.interrupt();    
      }
}
   
public MyBinder mBinder = new MyBinder();    
public IBinder onBind(Intent intent) { 
      return mBinder;    
}

利用obBind()將一個Binder對象傳遞給Activity。

public TestService.MyBinder myBinder;
public MCon mcon = new MCon();
public class MCon implements ServiceConnection{     
      @Override    
      public void onServiceConnected(ComponentName name, IBinder service) {        
            myBinder = (TestService.MyBinder)service;    
      }    
      @Override    
      public void onServiceDisconnected(ComponentName name) {

      }
}

轉化為MyBinder對象之后就可以使用MyBinder中的方法來相互通信了。

myBinder.setI(1000);
int i = myBinder.getI();
  • 使用Intent通信
    使用Intetnt在Activity啟動Service時,將數據放入Intent之中,之后在Service中的onStartCommand(Intent intent, int flags, int startId)方法中獲取到Intetnt并提取出數據。
Activity中:
Intent intent = new Intent(MainActivity.this,TestService.class)
intent.putExtra("name","li");
startService(intent);
    
Service中
public int onStartCommand(Intent intent, int flags, int startId) {    
      String name  = (intent.getExtras()).getString("name");        
      return START_STICKY;
}

三、前臺服務

  • 提升為前臺服務
    什么是前臺服務

A foreground service(前臺服務) is a service that's considered to be(被用戶所認可的) something the
user is actively aware of and thus not a candidate for(而不是一個候選的,可以在內存不足時,被系統殺死
的) the system to kill when low on memory. A foreground service must provide a notification for the status
bar(前臺服務必須提供一個顯示通知), which is placed under the "Ongoing" heading(它是不可以忽略的), >which means that the notification cannot be dismissed unless the service is either stopped or removed from >the foreground.(意思是通知信息不能被忽略,除非服務停止或主動移除,否則將一直顯示)

前臺服務是那些被認為用戶知道(用戶認可所認可)且在系統內存不足的時候不允許系統殺死的服務。 前臺服務必須給狀態欄提供一個通知,它被放到正在運行(Ongoing)標題之下——這就意味著通知只有在這個服務被終止或從前臺主動移除通知后才能被解除。
如果我們希望Service可以一直保持運行狀態且不會在內存不足的情況下被回收時,可以選擇將需要保持運行的Service設置為前臺服務
   
創建Notifition

private void bulidNotifition() {    
      notificationBuilder = new Notification.Builder(this);    
      Intent intent = new Intent(this,MainActivity.class);    
      notification = notificationBuilder.setContentIntent(PendingIntent.getActivity(this,0,intent,0))
              .setLargeIcon(BitmapFactory.decodeResource(this.getResources(),R.mipmap.ic_launcher))
              .setContentTitle("前臺Serviece")            
              .setContentText("內容")           
              .setWhen(System.currentTimeMillis()).build();
}

在onStartCommand中啟動前臺服務

public int onStartCommand(Intent intent, int flags, int startId) {    
      bulidNotifition();    
      startForeground(100,notification);    //100表示前臺服務的id  當使用的通知ID一致時,只會更新當前Notification
      return START_STICKY;
}

停止前臺服務

stopForeground(true);

啟動服務之后,就會看到提示框那里有個提示欄,說明我們的Service已經成為前臺服務了。

四、IntentService

開啟一個線程來處理用戶的intent請求,采用隊列等待,一個時間只有一個intent請求被執行。

主要方法:onHandleIntent(Intent intent)

此方法在具有請求的工作線程上被調用。 每次只處理一個Intent,但是處理發生在獨立于其他應用程序邏輯運行的工作線程上。 所以,如果這段代碼需要很長時間,它會阻止對同一IntentService的其他請求,但它不會阻止任何其他。 當所有請求都被處理后,IntentService停止自己,所以你不應該調用stopSelf()。

button1.setOnClickListener(new View.OnClickListener() {
    @Override    
    public void onClick(View v) {
        Intent intentservice1 = new Intent(MainActivity.this,MyIntentService.class);
        intentservice1.putExtra("action",1);
        startService(intentservice1);
    }
});

button2.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Intent intentservice2 = new Intent(MainActivity.this,MyIntentService.class);
        intentservice2.putExtra("action",2);
        startService(intentservice2);
    }
}
);
protected void onHandleIntent(Intent intent) {
    Log.d("intent",intent.getIntExtra("action",0)+"");
    try {
         Log.d("A1sleep","A1sleep");
         Thread.sleep(2000);
         Log.d("sleep down","sleep down");
    } catch (InterruptedException e) {
         e.printStackTrace();
    }
}
依次按下按鈕,intent依次執行

五、跨進程Service與通信

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容