Service基礎(chǔ)使用

Service基礎(chǔ)使用

之前的文章一直介紹Activity的使用,很多知識和用法單一的配合Activity使用,這次將總結(jié)Android四大組件之二——Service.
本文將要介紹以下內(nèi)容:

  1. Service是什么
  2. 兩種Service啟動
  3. Service 前臺服務(wù)與Notification
  4. 后臺定時(shí)服務(wù)
  5. IntentService

Service是什么

Service 是一個(gè)可以在后臺執(zhí)行長時(shí)間運(yùn)行操作而不使用用戶界面的應(yīng)用組件。服務(wù)可由其他應(yīng)用組件啟動,而且即使用戶切換到其他應(yīng)用,服務(wù)仍將在后臺繼續(xù)運(yùn)行。 此外,組件可以綁定到服務(wù),以與之進(jìn)行交互,甚至是執(zhí)行進(jìn)程間通信 (IPC)。 例如,服務(wù)可以處理網(wǎng)絡(luò)事務(wù)、播放音樂,執(zhí)行文件 I/O 或與內(nèi)容提供程序交互,而所有這一切均可在后臺進(jìn)行。

上面說了這么多可以做的耗時(shí)操作,但不要真的認(rèn)為Service默認(rèn)會運(yùn)行在子線程中,他也不運(yùn)行在一個(gè)獨(dú)立的進(jìn)程中,他同樣執(zhí)行在UI線程中,因此,不要在Service中執(zhí)行耗時(shí)操作,除非你在Service中創(chuàng)建了子線程來完成耗時(shí)操作。
Service不因程序切換到后臺或者用戶切換到另一個(gè)APP而停止,但如果應(yīng)用程序被殺死,他當(dāng)然就也消失了。

兩種Service的啟動

服務(wù)啟動分為兩種形式,一種是通過StartService()啟動Service,另一種是通過BindService(),至于先啟動在綁定這個(gè)我們最后再說。本文主要介紹的就是這兩種,這兩種有什么區(qū)別呢?我們先看一張他們的生命周期圖片,然后在慢慢細(xì)說。

Service啟動流程圖.jpg

StartService()

當(dāng)應(yīng)用組件(如 Activity)通過調(diào)用 startService() 啟動服務(wù)時(shí),服務(wù)即處于“啟動”狀態(tài)。一旦啟動,服務(wù)即可在后臺無限期運(yùn)行,即使啟動服務(wù)的組件已被銷毀也不受影響。 已啟動的服務(wù)通常是執(zhí)行單一操作,而且不會將結(jié)果返回給調(diào)用方。例如,它可能通過網(wǎng)絡(luò)下載或上傳文件。 操作完成后,服務(wù)會自行停止運(yùn)行。

通過上面的生命周期圖也可以看出來首次啟動會創(chuàng)建一個(gè)Service的實(shí)例,然后依次調(diào)用onCreate()和onStartCommand(),此時(shí)Service進(jìn)入運(yùn)行狀態(tài),即使你多次調(diào)用StartService()啟動Service,也不會創(chuàng)建新的Service對象,而是直接復(fù)用前面創(chuàng)建的Service對象,調(diào)用它的startCommand()方法。下面介紹一下startService各個(gè)回調(diào)方法,我們該做什么

onCreate():首次創(chuàng)建服務(wù),系統(tǒng)將調(diào)用此方法來執(zhí)行一次性設(shè)置程序,如果服務(wù)已經(jīng)運(yùn)行,則不會調(diào)用此方法
onStartCommand():啟動服務(wù)的時(shí)候,系統(tǒng)將會調(diào)用此方法。如果實(shí)現(xiàn)了此方法,則在服務(wù)工作完成后,需要主動調(diào)用stopSelf()或者stopService()來停止服務(wù)。
onDestroy(),當(dāng)服務(wù)不再使用且將被銷毀時(shí),系統(tǒng)將會調(diào)用此方法,服務(wù)應(yīng)該實(shí)現(xiàn)此方法來清理所有資源。這是服務(wù)接受的最后一個(gè)調(diào)用。

下面我將通過一個(gè)實(shí)例展示這個(gè)service的實(shí)現(xiàn),以及方法調(diào)用的順序。注意,通過AS新建一個(gè)Service則不需要配置mainfest文件,否則需要加入service聲明,大概如下面這樣。且注意始終通過顯示Intent啟動或者綁定Service,不要為他設(shè)置intent-filter

 <application ... >
      <service android:name=".ExampleService" />

  </application>

Service代碼如下:

public class TestService extends Service {
    public TestService() {
    }

    @Override
    public void onCreate() {
        super.onCreate();
        //處理一次性設(shè)置
        Log.i("TestService","onCreate被調(diào)用");
    }
    
    //必須要有這個(gè)方法
    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public void onDestroy() {
        Log.i("TestService","onDestroy被調(diào)用");
        super.onDestroy();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        //處理Service邏輯
        Log.i("TestService","onStartCommand被調(diào)用");
        return super.onStartCommand(intent, flags, startId);
    }
}

Activity中布局文件放置了一個(gè)啟動按鈕,一個(gè)停止按鈕,在onCreate()方法,實(shí)現(xiàn)startService,代碼如下:

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        stop = (Button)findViewById(R.id.stop);
        start = (Button) findViewById(R.id.start);
        final Intent intent = new Intent(this, TestService.class);
        start.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startService(intent);
            }
        });
        stop.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                stopService(intent);
            }
        });

    }

調(diào)用的結(jié)果如下:

點(diǎn)擊啟動service按鈕后
I/TestService: onCreate被調(diào)用
I/TestService: onStartCommand被調(diào)用
點(diǎn)擊停止service按鈕后
I/TestService: onDestroy被調(diào)用

bindService()

當(dāng)應(yīng)用組件通過調(diào)用 bindService() 綁定到服務(wù)時(shí),服務(wù)即處于“綁定”狀態(tài)。綁定服務(wù)提供了一個(gè)客戶端-服務(wù)器接口,允許組件與服務(wù)進(jìn)行交互、發(fā)送請求、獲取結(jié)果,甚至是利用進(jìn)程間通信 (IPC) 跨進(jìn)程執(zhí)行這些操作。 僅當(dāng)與另一個(gè)應(yīng)用組件綁定時(shí),綁定服務(wù)才會運(yùn)行。 多個(gè)組件可以同時(shí)綁定到該服務(wù),但全部取消綁定后,該服務(wù)即會被銷毀。

startService()一樣,首次使用bindService時(shí)綁定一個(gè)Service時(shí),系統(tǒng)會實(shí)例化一個(gè)Service實(shí)例,并調(diào)用onCreate()和onBind()方法,此后如果再次使用bindService綁定Service,系統(tǒng)不會創(chuàng)建新的Service實(shí)例,也不會再調(diào)用onBInd()方法,只是會直接把IBinder對象傳遞給其他后來增加的客戶端。 如果我們解除與服務(wù)的綁定,只需調(diào)用unbindService(),此時(shí)onUnbind和onDestory方法將會被調(diào)用!這是一個(gè)客戶端的情況,假如是多個(gè)客戶端綁定同一個(gè)Service的話,情況如下 當(dāng)一個(gè)客戶完成和service之間的互動后,它調(diào)用 unbindService() 方法來解除綁定。當(dāng)所有的客戶端都和service解除綁定后,系統(tǒng)會銷毀service。(除非service也被startService()方法開啟)。但一旦調(diào)用者銷毀,Service也立即終止。下面主要說一下onBInd這個(gè)回調(diào)方法。
第一種啟動方式并沒有直接提供Service與Activity的交互方式。第二種啟動方式即用bindService提供了Service與Activity交互方式,可以很方便的利用Ibinder實(shí)現(xiàn)Service與Activity的通信。關(guān)鍵在onBind方法
onBind()方法返回值是一個(gè)IBinder 對象,這個(gè)方法必須實(shí)現(xiàn),通過startService返回為null,表示不調(diào)用,bindService則返回一個(gè)擴(kuò)展Binder類的對象。
步驟如下:

  1. 定義一個(gè)繼承Binder類的類,在類里面編寫想要Activity調(diào)用的方法.
  2. 生成一個(gè)該類的對象,放到onBInd()方法中返回。
    代碼例子如下:
public class TestService2 extends Service {
    public TestService2() {
    }

    public Mybinder mybinder = new Mybinder();

    public int getInfo() {
        return 123;
    }

    public class Mybinder extends Binder {
        public TestService2 getService() {
            return TestService2.this;
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        Log.i("TS","onbind被調(diào)用");
        return mybinder;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.i("TS","onDestory被調(diào)用");
    }

    @Override
    public void onRebind(Intent intent) {
        super.onRebind(intent);
        Log.i("TS","onRebind被調(diào)用");
    }
}

這個(gè)例子Mybinder對象中g(shù)etService方法,得到這個(gè)Service對象,從而調(diào)用Service中所有方法。
在Activity中的代碼,要實(shí)現(xiàn)ServiceConnection類中的onServiceDisconnected和onServiceConnected方法,并且通過bindService來綁定服務(wù),unbindService來解綁服務(wù)。
主要代碼如下:

 TestService2.Mybinder binder;
    private ServiceConnection conn = new ServiceConnection() {

        //Activity與Service斷開連接時(shí)回調(diào)該方法
        @Override
        public void onServiceDisconnected(ComponentName name) {
            System.out.println("------Service DisConnected-------");
        }

        //Activity與Service連接成功時(shí)回調(diào)該方法
        @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
            //將Service強(qiáng)制轉(zhuǎn)換類型為Binder,這種用法在回調(diào)中很常見。
            System.out.println("------Service Connected-------");
            binder = (TestService2.Mybinder) service;
        }
    };
    
   ...
   
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
         start.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                bindService(intent, conn, Service.BIND_AUTO_CREATE);
            }
        });

        stop.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                unbindService(conn);
            }
        });
        //通過binder對象就可以對我們綁定的Service進(jìn)行各種操作
        status.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(MainActivity.this,binder.getService().getInfo() + "",Toast.LENGTH_SHORT).show();
            }
        });
    }

至于調(diào)用順序,還是和流程圖一樣,就不一一詳說了。這里已經(jīng)說明了2種啟動Service的方式和區(qū)別。下面將介紹一個(gè)很流行的Service應(yīng)用,前臺服務(wù)。

Service 前臺服務(wù)與Notification

我們在用很多應(yīng)用的時(shí)候,發(fā)現(xiàn)他們啟動的時(shí)候,會在通知欄生成一個(gè)和該App的通知,來繼續(xù)執(zhí)行Service,比如墨跡天氣,很多音樂App.這種叫前臺服務(wù),其實(shí)這種Service有一個(gè)很好的一點(diǎn),就是不會因?yàn)镾ervice自身的優(yōu)先級低,而被系統(tǒng)KILL,而前臺服務(wù)就不會。
前臺服務(wù)的寫法很容易,只需要在onCreate()中,建立一個(gè)通知,然后用startForeground()設(shè)置為前臺服務(wù)即可。
下面直接放出代碼,結(jié)合代碼注釋看看就好了,關(guān)于通知更多的內(nèi)容可以看看Notification詳解
這里只列出Service的onCreate()部分代碼

@Override
    public void onCreate() {
        super.onCreate();
        //設(shè)定一個(gè)PendingIntent,來表示點(diǎn)擊通知欄時(shí)跳轉(zhuǎn)到哪里
        PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, MainActivity.class), 0);
        Notification.Builder builder = new Notification.Builder(this);
        //建立一個(gè)notificationManager來管理通知的出現(xiàn)
        notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        //構(gòu)造通知的樣式,包括圖片,標(biāo)題,內(nèi)容,時(shí)間。
        builder.setSmallIcon(R.mipmap.ic_launcher).
                setWhen(System.currentTimeMillis()).
                setContentTitle("我是標(biāo)題").
                setContentText("我是內(nèi)容").
                setTicker("在啟動時(shí)彈出一個(gè)消息").//這個(gè)Android5.0以上可能會失效
                setWhen(System.currentTimeMillis()).
                setContentIntent(contentIntent);
                //最后通過build建立好通知
        Notification notification = builder.build();
        //通過manager來顯示通知,這個(gè)1為notification的id
        notificationManager.notify(1,notification);
        //啟動為前臺服務(wù),這個(gè)1為notification的id
        startForeground(1,notification);
    }

后臺定時(shí)服務(wù)

后臺定時(shí)服務(wù)其實(shí)并不是特殊的Service,只是Service的常見的一種應(yīng)用,放到后臺做定時(shí)更新,輪詢等。這次的Service要配合Alarm以及簡單的廣播機(jī)制來實(shí)現(xiàn)。
步驟主要如下:

  1. 獲得Service AlarmManager manager = (AlarmManager) getSystemService(ALARM_SERVICE);
  2. 通過set方法設(shè)置定時(shí)任務(wù)
int time = 1000;
        long triggerAtTime = SystemClock.elapsedRealtime() + time;
        Intent i = new Intent(this,AlarmReceiver.class);
        PendingIntent pi = PendingIntent.getBroadcast(this, 0, i, 0);
        manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pi);
  1. 定義一個(gè)Service,在onStartCommand中開辟一個(gè)事務(wù)線程,用于處理一些定時(shí)邏輯
@Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        new Thread(new Runnable() {
            @Override
            public void run() {
               //執(zhí)行想做的操作,比如輸出時(shí)間
            }
        }).start();
          //步驟二里面的代碼
        return super.onStartCommand(intent, flags, startId);
    }
  1. 定義一個(gè)Broadcast,用于啟動Service。
public class AlarmReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        Intent i = new Intent(context,LongRunningService.class);
        context.startService(i);
    }
}

這樣我們就能執(zhí)行后臺定時(shí)服務(wù)

IntentService

這是 Service 的子類,它使用工作線程逐一處理所有啟動請求。如果您不要求服務(wù)同時(shí)處理多個(gè)請求,這是最好的選擇。 您只需實(shí)現(xiàn) onHandleIntent() 方法即可,該方法會接收每個(gè)啟動請求的 Intent,使您能夠執(zhí)行后臺工作。

由于大多數(shù)啟動服務(wù)都不必須同時(shí)處理多個(gè)請求,因此使用IntentService類實(shí)現(xiàn)服務(wù)也許是最好的選擇。
IntentService執(zhí)行以下操作:

  • 創(chuàng)建默認(rèn)的工作線程,用于在應(yīng)用的主線程外執(zhí)行傳遞給onStartCommand()的所有Intent
  • 創(chuàng)建工作隊(duì)列,用于講一個(gè)Intent逐一傳遞給onHandleIntent()實(shí)現(xiàn),不要擔(dān)心多線程問題
  • 在處理完所有啟動后停止服務(wù),永遠(yuǎn)不用調(diào)用stopSelf()
  • 提供onBind()的默認(rèn)實(shí)現(xiàn)
  • 提供onstartCommand()的默認(rèn)實(shí)現(xiàn),可將Intent依次發(fā)送給工作隊(duì)列和onHandleIntent()
    下面給一個(gè)IntentService的例子
public class MyIntentService extends IntentService {

    private final String TAG = MyIntentService.class.getName();

    public MyIntentService() {
        
          //此構(gòu)造函數(shù)一定要實(shí)現(xiàn)  super(MyIntentService.class.getName());
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        String action = intent.getExtras().getString("param");
        if("s1".equals(action)) Log.i(TAG, "啟動s1");
        if("s2".equals(action)) Log.i(TAG, "啟動s2");

        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onCreate() {
        Log.i(TAG,"onCreate");
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i(TAG,"onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }


    @Override
    public void setIntentRedelivery(boolean enabled) {
        super.setIntentRedelivery(enabled);
        Log.i(TAG,"setIntentRedelivery");
    }

    @Override
    public void onDestroy() {
        Log.i(TAG,"onDestroy");
        super.onDestroy();
    }

}

在Activity中的onCreate()方法中加入如下代碼

 Intent it1 = new Intent(this,MyIntentService.class);
        Bundle b1 = new Bundle();
        b1.putString("param", "s1");
        it1.putExtras(b1);
        Intent it2 = new Intent(this,MyIntentService.class);
        Bundle b2 = new Bundle();
        b2.putString("param", "s2");
        it2.putExtras(b2);
        startService(it1);
        startService(it2);

運(yùn)行結(jié)果如下

I/com.shiqifeng.servicetest.MyIntentService: onCreate
I/com.shiqifeng.servicetest.MyIntentService: onStartCommand
I/com.shiqifeng.servicetest.MyIntentService: 啟動s1
I/com.shiqifeng.servicetest.MyIntentService: onStartCommand
I/com.shiqifeng.servicetest.MyIntentService: 啟動s2
I/com.shiqifeng.servicetest.MyIntentService: onDestroy

至此Service的基本使用全部結(jié)束

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 前言:本文所寫的是博主的個(gè)人見解,如有錯誤或者不恰當(dāng)之處,歡迎私信博主,加以改正!原文鏈接,demo鏈接 Serv...
    PassersHowe閱讀 1,514評論 0 5
  • 1.什么是Activity?問的不太多,說點(diǎn)有深度的 四大組件之一,一般的,一個(gè)用戶交互界面對應(yīng)一個(gè)activit...
    JoonyLee閱讀 5,858評論 2 51
  • 2.1 Activity 2.1.1 Activity的生命周期全面分析 典型情況下的生命周期:在用戶參與的情況下...
    AndroidMaster閱讀 3,279評論 0 8
  • 服務(wù)基本上分為兩種形式 啟動 當(dāng)應(yīng)用組件(如 Activity)通過調(diào)用 startService() 啟動服務(wù)時(shí)...
    pifoo閱讀 1,331評論 0 8
  • [文章內(nèi)容來自Developers] Service是一個(gè)可以在后臺執(zhí)行長時(shí)間運(yùn)行操作而不提供用戶界面的應(yīng)用組件。...
    岳小川閱讀 941評論 0 7

友情鏈接更多精彩內(nèi)容