四大組件-----Service詳解

什么是Service

先看一下google官方的介紹:

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

總結(jié)一下,Service是一個應(yīng)用組件,它有以下特點:
1.不需要提供用戶界面
2.可以在后臺長時間運行
默認情況下,服務(wù)運行在UI線程,執(zhí)行耗時操作需要開辟子線程,否則會引起ANR。

服務(wù)通常分為兩種形式:

1.普通服務(wù),通過startService啟動,一旦啟動,服務(wù)即可在后臺無限期運行,即使啟動服務(wù)的組件已被銷毀也不受影響。
直到stopService被調(diào)用,或者Service本身調(diào)用了stopSelf,才會停止。
2.Bound 服務(wù),當應(yīng)用組件通過調(diào)用 [bindService()](https://developer.android.google.cn/reference/android/content/Context.html#bindService(android.content.Intent, android.content.ServiceConnection, int))綁定到服務(wù)時,服務(wù)即處于“綁定”狀態(tài)。綁定服務(wù)提供了一個客戶端-服務(wù)器接口,允許組件與服務(wù)進行交互、發(fā)送請求、獲取結(jié)果,甚至是利用進程間通信 (IPC) 跨進程執(zhí)行這些操作。 僅當與另一個應(yīng)用組件綁定時,綁定服務(wù)才會運行。 多個組件可以同時綁定到該服務(wù),但全部取消綁定后,該服務(wù)即會被銷毀。

實際上,普通Service也可以與組件綁定,關(guān)鍵在于是否實現(xiàn)了[onStartCommand()](https://developer.android.google.cn/reference/android/app/Service.html#onStartCommand(android.content.Intent, int, int))(允許組件啟動服務(wù))和 onBind()(允許綁定服務(wù))這兩個回調(diào)方法。這樣的服務(wù)需要解除綁定并stop才會銷毀。

關(guān)鍵方法和生命周期

onCreate()
首次創(chuàng)建服務(wù)時,系統(tǒng)將調(diào)用此方法來執(zhí)行一次性設(shè)置程序(在調(diào)用 onStartCommand() 或 onBind() 之前)。如果服務(wù)已在運行,則不會調(diào)用此方法。

onStartCommand()
當另一個組件(如 Activity)通過調(diào)用 startService() 請求啟動服務(wù)時,系統(tǒng)將調(diào)用此方法。一旦執(zhí)行此方法,服務(wù)即會啟動并可在后臺無限期運行。 如果您實現(xiàn)此方法,則在服務(wù)工作完成后,需要由您通過調(diào)用 stopSelf() 或 stopService() 來停止服務(wù)。(如果您只想提供綁定,則無需實現(xiàn)此方法。)

onBind()
當另一個組件想通過調(diào)用 bindService() 與服務(wù)綁定時,系統(tǒng)將調(diào)用此方法。在此方法的實現(xiàn)中,必須通過返回 IBinder 提供一個接口,供客戶端用來與服務(wù)進行通信。請務(wù)必實現(xiàn)此方法,但如果您并不希望允許綁定,則應(yīng)返回 null。

onUnbind()
當組件通過unbindService與Service解綁時,系統(tǒng)會調(diào)用此方法。

onDestroy()
當服務(wù)不再使用且將被銷毀時,系統(tǒng)將調(diào)用此方法。服務(wù)應(yīng)該實現(xiàn)此方法來清理所有資源,如線程、注冊的偵聽器、接收器等。 這是服務(wù)接收的最后一個調(diào)用

如果組件通過調(diào)用 startService() 啟動服務(wù)(這會導(dǎo)致對 onStartCommand() 的調(diào)用),則服務(wù)將一直運行,直到服務(wù)使用 stopSelf() 自行停止運行,或由其他組件通過調(diào)用 stopService() 停止它為止。

如果組件是通過調(diào)用 bindService() 來創(chuàng)建服務(wù)(且未調(diào)用 onStartCommand(),則服務(wù)只會在該組件與其綁定時運行。一旦該服務(wù)與所有客戶端之間的綁定全部取消,系統(tǒng)便會銷毀它。

也就是說,startService與stopService,bindService與unbindService是對應(yīng)關(guān)系,startService必須通過stopService來停止,這時候調(diào)用的是onStartCommand()和onDestroy();bindService必須通過unbindService來停止,這時候調(diào)用的是onBind()和onDestroy()。
到這里,Service的生命周期已經(jīng)很清楚了:

onCreate()onStartCommand()/onBind()(→onUnbind())→onDestroy()
注:系統(tǒng)因內(nèi)存過低等原因,回收掉服務(wù)的時候,onDestroy是不會執(zhí)行的。 如果服務(wù)已經(jīng)運行,調(diào)用startService時,不會重新執(zhí)行onCreate,只會執(zhí)行onStartCommand();

Service實例:

1.普通Service:

首先定義一個Service,并重寫相應(yīng)方法

public class MyService  extends Service{
    @Override
    public void onCreate() {
        // TODO Auto-generated method stub
        super.onCreate();
        Log.d("Service", "----->onCreate");
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // TODO Auto-generated method stub
        Log.d("Service", "----->onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }
    @Override
    public void onDestroy() {
        // TODO Auto-generated method stub
        super.onDestroy();
        Log.d("Service", "----->onDestroy");
    }
}

然后在Activity中啟動/停止該服務(wù)

public class ServiceActivity  extends Activity implements OnClickListener{
    private Button startButton;
    private Button stopButton;
    Intent i;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.service_activity);
        startButton=(Button) findViewById(R.id.start_service);
        stopButton=(Button) findViewById(R.id.stop_service);
        startButton.setOnClickListener(this);
        stopButton.setOnClickListener(this);
    }   
    @Override
    public void onClick(View v) {
        // TODO Auto-generated method stub
        switch (v.getId()) {
        case R.id.start_service:
            i=new Intent(this,MyService.class);
            startService(i);
            break;
        case R.id.stop_service:
            i=new Intent(this,MyService.class);
            stopService(i);
            break;
        default:
            break;
        }   
    }
}

當然,也不要忘記在manifest文件中注冊,四大組件都是需要注冊的。
這樣,一個簡單的服務(wù)就完成了。

2.Bound服務(wù)

Bound服務(wù)是將啟動Service的組件(通常是Activity)與Service綁定起來,這樣Activity和Service可以非常簡單的進行通信。實例如下:

1.定義一個Binder類,并在onbind方法中返回它的實例:

public class MyService  extends Service{
    private MyBinder mBinder=new MyBinder();
    public class MyBinder extends Binder{
        public void startDownload(){
            Log.d("Service", "---->Start Download.....");
        }
    }
    @Override
    public IBinder onBind(Intent intent) {
        // TODO Auto-generated method stub
        Log.d("Service", "----->onBind");
        return mBinder;
    }
}

2.在Activity中啟動時,使用bindService方法。

該方法有三個參數(shù):
Intent,表示要啟動的Service;
connection:實現(xiàn)ServiceConnection接口的類,該接口中有兩個方法:onServiceDisconnected和onServiceConnected,onbind方法返回的Binder會傳入onServiceConnected,從而對服務(wù)進行操作;
Flag:通常選用BIND_AUTO_CREATE,bindService時會自動創(chuàng)建服務(wù)。
代碼如下:

private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.d("Service", "----->onServiceDisconnected");
        }
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.d("Service", "----->onServiceConnected");
            mBinder=(MyService.MyBinder) service;
            mBinder.startDownload();
        }
    };

Intent i=new Intent(this,MyService.class);
bindService(i, connection   , BIND_AUTO_CREATE);

注意:onServiceDisconnected函數(shù)并不是unbind時候調(diào)用的,正常情況下該函數(shù)不會被調(diào)用,只有意外斷開連接的時候,比如Service被系統(tǒng)回收等,才會調(diào)用。
同一個Service可以被許多組件同時綁定,返回的也是相同的Binder對象。只有當所有組件都解綁時,Service才會銷毀。

3.遠程Service:

遠程服務(wù)是指,單獨運行在一個進程中的服務(wù)。使用遠程Service也很簡單,只要設(shè)置manifest文件里Service的屬性:android:process=":remote"(意思是在當前應(yīng)用下新建一個進程,名字是包名+remote)
使用遠程Service不會產(chǎn)生ANR問題,它獨立運行在新進程中,會產(chǎn)生一個問題,如何與Activity進行通信?可以試一下,遠程Service是不能與Activity綁定的,bindService不能用在遠程Service上,所以這里就涉及到IPC的概念了。
介紹幾種比較常用的方法:

AIDL通信
首先新建一個AIDL文件,在文件中定義好Activity與Service通信的方法:
在名為ServiceAIDL.aidl的文件中,定義以下方法:

package com.training.service;
interface ServiceAIDL {
    int startDownload();
}

保存之后在gen文件夾下會生成對應(yīng)的.java文件。
然后在Service中實現(xiàn)該接口,代碼如下:

ServiceAIDL.Stub mBinder = new ServiceAIDL.Stub() {
        @Override
        public void startDownload() throws RemoteException {
            // TODO Auto-generated method stub
            Log.d("Service", "----->Remote Service Start download.....");
        }
    };
@Override
public IBinder onBind(Intent intent) {
        // TODO Auto-generated method stub
        Log.d("Service", "----->onBind");
        return mBinder;
    }

ServiceAIDL.Stub是一個實現(xiàn)了AIDL文件中定義的接口ServiceAIDL的類。它繼承自Binder類,Service中取得它的實例之后,在onBind方法中返回,就可以把該實例傳遞到Activity中了。
Activity中通過ServiceConnection來操作binder:

private ServiceConnection reoteConnection=new ServiceConnection() {
        @Override
        public void onServiceDisconnected(ComponentName name) {
            // TODO Auto-generated method stub
        }
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            // TODO Auto-generated method stub
            mServiceAIDL=ServiceAIDL.Stub.asInterface(service) ;
            try {
                int result=mServiceAIDL.plus(3, 5);
                Log.d("Service", "Result is "+ result);
                mServiceAIDL.startDownload();
            } catch (Exception e) {
                 e.printStackTrace();
            }
        }
    };
Intent i=new Intent(this,RemoteService.class);
bindService(i, reoteConnection  , BIND_AUTO_CREATE);

這樣,一個簡單的AIDL通信就完成了。
遠程Service是可以垮應(yīng)用共享的,可以通過隱式Intent從任何Activity啟動并操作他。

Messager通信

1:Service中定義兩個Messenger:
一個server端的messenger,該messenger會通過onbind方法傳遞給client端,在client端通過該messenge發(fā)送消息給server一個client端的messenger,用來接收client端的messenger,用該messenger發(fā)送消息回client
代碼如下:

    static final int MSG_CLIENT_TO_SERVER=1;
    static final int MSG_SERVER_TO_CLIENT=2;
    private Messenger clientMessenger;
    private Messenger serverMessenger = new Messenger(new ServerHandler());
    class ServerHandler extends Handler{
        @Override
        public void handleMessage(Message msg) {
            Log.d("Service", "ThreadName:"+Thread.currentThread().getName());
            switch (msg.what) {
            case MSG_CLIENT_TO_SERVER:
                Log.d("Service", "Get message from client to server!");
                //msg.replyTo用來攜帶一個Messenger,此處接收到消息是客戶端傳來的,所以攜帶的是client Messenger,這樣就獲取到了客戶端的messenger,然后可以通過此messenger發(fā)送msg給客戶端
                //注意,客戶端發(fā)送消息時,必須將自己的messenger賦值給replyTo
                clientMessenger = msg.replyTo;
                Message toClientMsg=Message.obtain(null, MSG_SERVER_TO_CLIENT);
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }
                try {
                    clientMessenger.send(toClientMsg);
                } catch (RemoteException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                break;
            default:
                break;
            }
        }

這段代碼的意思是,接收到客戶端發(fā)送的消息時,打印log,然后獲取到client的messenger,等待2秒之后,發(fā)送消息回client。

2:client端,通用也要定義兩個messenger。

    private Messenger serverMessenger;
    private Messenger clientMessenger=new Messenger(new Handler(){
        @Override
        public void handleMessage(Message msg) {
            // TODO Auto-generated method stub
            if(msg.what==MessagerService.MSG_SERVER_TO_CLIENT)
                Log.d("Service", "Receive message from server");
        }
    });

然后在connection中獲取到Service的messenger:

    private ServiceConnection messengerConnection=new ServiceConnection() {
        @Override
        public void onServiceDisconnected(ComponentName name) {
            // TODO Auto-generated method stub
        }
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            // TODO Auto-generated method stub
            serverMessenger=new Messenger(service);
        }
    };

定義了一個Button,點擊的時候,會發(fā)送消息給Service:

Message msg=Message.obtain(null, MessagerService.MSG_CLIENT_TO_SERVER);
            msg.replyTo=clientMessenger;
            try {
                serverMessenger.send(msg);
            } catch (RemoteException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

這樣,一個簡單的messenger通信就完成了。
當然也不要忘記 注冊Service,定義成remote模式讓Service運行在獨立進程中,還要將Service與Activity綁定起來。

IntentService

IntentService是系統(tǒng)提供給的一個已經(jīng)繼承自Service類的特殊類,用戶只需要覆寫onHandlerIntent()方法,該方法會將耗時任務(wù)自動運行在子線程當中,運行完畢后,系統(tǒng)會調(diào)用stopself來銷毀Service。

public class MyIntentService extends IntentService {
    public MyIntentService(String name) {
        super(name);
    }
    @Override
    protected void onHandleIntent(Intent intent) {
        Log.w("IntentService", "Start download in onHandleIntent......");
        Log.w("IntentService", "thread name:" + Thread.currentThread().getName());
    }
}

前臺Service

Service是默認運行在后臺的,優(yōu)先級相對比較低,容易被系統(tǒng)回收掉。想讓Service一直處于運行狀態(tài)的話,前臺服務(wù)會在通知欄顯示一條消息,一般不會被系統(tǒng)回收掉。
主要代碼如下:

public class ForegroundService extends Service{
    @Override
    public void onCreate() {
        // TODO Auto-generated method stub
        super.onCreate();
        showNotification();
    }
    private void showNotification(){
        NotificationCompat.Builder mBuilder=new NotificationCompat.Builder(this)
        .setContentTitle("前臺Service")
        .setContentText("Service is running")
        .setSmallIcon(R.drawable.ic_launcher);//設(shè)置通知內(nèi)容
        Intent intent=new Intent(this,ServiceActivity.class);
        PendingIntent pendingIntent=PendingIntent.getActivity(this  , 0 ,intent, 0);
        mBuilder.setContentIntent(pendingIntent);//設(shè)置點擊響應(yīng)
        
        Notification notification=mBuilder.build();//構(gòu)建通知
        NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        notificationManager.notify(1,notification);//顯示通知
        startForeground(1, notification);//啟動服務(wù)
    }
}

然后在Activity中啟動服務(wù)就可以看見了

關(guān)于Service的其他:

1:onStartCommand(Intent intent, int flags, int startId)方法:
intent:啟動Service時候傳遞進來的intent
flags:通常為 0,或者START_FLAG_REDELIVERY(1), START_FLAG_RETRY(2).表示Service的啟動方式
startid:一個唯一的整型,用于表示此次Client執(zhí)行startService(...)的請求請求標識,在多次startService(...)的情況下,呈現(xiàn)0,1,2....遞增
還有一個關(guān)鍵的返回值,通常有三個值可選:

START_NOT_STICKY:當Service因為內(nèi)存不足而被系統(tǒng)kill后,接下來未來的某個時間內(nèi),即使系統(tǒng)內(nèi)存足夠可用,系統(tǒng)也不會嘗試重新創(chuàng)建此Service。除非程序中Client明確再次調(diào)用startService(...)啟動此Service。

START_STICKY:當Service因為內(nèi)存不足而被系統(tǒng)kill后,接下來未來的某個時間內(nèi),當系統(tǒng)內(nèi)存足夠可用的情況下,系統(tǒng)將會嘗試重新創(chuàng)建此Service,一旦創(chuàng)建成功后將回調(diào)onStartCommand(...)方法,但其中的Intent將是null,pendingintent除外。

START_REDELIVER_INTENT:與START_STICKY唯一不同的是,回調(diào)onStartCommand(...)方法時,其中的Intent將是非空,將是最后一次調(diào)用startService(...)中的intent。

2:Broadcast Receiver由于生命周期非常短,只要幾秒鐘,所以不能作為 Bound Service的發(fā)起者。

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

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

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