Android基礎 -- Android Service

Service在Android開發的過程中,相信小夥伴們都沒有少與它互動,如此重要的組件,現在來一起在看看它吧

 

一、什麼是Service?

Service是Android的四大組件之一,屬於計算型的組件,它提供在後臺需要上時間運行的服務,例如音樂播放、文件下載等,有着無用戶界面、後臺運行、生命週期長等特點。

 

二、Service的生命週期是什麼樣子的?

Service的生命週期與Activity生命週期類似,需要關注兩組方法

一組是手動調用的方法

startService() 啓動服務,stopService() 關閉服務, bindService() 綁定服務, unbindService() 解綁服務

一組是自動調用的方法

onCreate() 創建服務, onStartCommand() 開始服務, onDestroy()  銷燬服務, onBind() 綁定服務, onUnbind() 解綁服務

常見的應用場景

1.啓動服務(只使用startService):手動調用startService --> oncreate --> onStartCommand --> Service運行 --> 手動調用stopService --> onDestory 需要注意的是:如果一個Service被startService多次啓動,那麼onCreate也只會調用一次,即Service實例只有1個;整個生命週期裏,只有onStartCommand可多次調用,其他只能調用1次;onStartCommand調用的次數等於startService調用次數。

2.綁定服務(只使用bindService):手動調用bindService --> onCreate --> onBind --> Service綁定後運行 --> 手動調用 unBIndService  --> onUnbind --> onDestory  需要注意的是:客戶端通過一個IBinder接口與服務進行通信;若一個Service被BIndService多次啓動,那麼onCreate也只會調用1次,即Service的實例只有一個;多個客戶端可以綁定在一個服務上,當所有的客戶端都解綁後,系統會銷燬服務,服務不需要手動終止。

特別注意:

關於操作Service

startService stopService 只能開啓&關閉Service,但無法操作Service;

bindService unBindService 除了綁定Service 還能操作Service;

 

關於Service何時銷燬

startService開啓的Service,調用者退出後,Service仍然存在

bindService開啓的Service,調用者退出後,Service隨着調用者的退出銷燬

若在無解綁的前提下調用stopService是無法停止服務的

 

關於onStartCommand返回值

onStartCommand必須返回一個整數,用來描述系統在殺死服務後應該如何運行

1)START_NOT_STICKY:不會重建服務,除非還存在未發送的Intent。當服務不再是必須的,並且應用程序能夠簡單的重啓那些未完成的工作時,這是避免服務運行的最安全的選項

2)START_STICKY:重建服務並且調用onStartCommond,但不會再次送入上一個intent,而是用nullintent來調用onStartCommand,除非還有啓動服務的intent未發送完,那麼這些剩下的intent會繼續發送(適用於媒體播放器類似的服務,它們不執行命令,但需要一直運行並隨時待命)

3)START_REDELIVER_INTENT:重建服務並且用上一個已經發送過的Intent調用onStartCommand,任何未發送完的intent也都會依次送入(適用於那些需要立即恢復工作的活躍服務,比如文件下載)

 

三、Service的分類

1.按運行地點分類可以分爲本地服務和遠程服務

本地服務(Local Service)

特點:運行在主線程,主線程被終止後,服務也會終止

優點:節約資源,通信方便,因爲在同1個進程不要IPC和AIDL

缺點:限制性大,主進程被終止後,服務也會終止

應用場景:需依附於某個進程的服務,也是最常用的服務類型,如音樂播放

遠程服務(Remote Service)

特點:運行在獨立的線程,服務常駐在後臺,不受其他Activity的影響

優點:靈活,服務常駐在後臺,不受其他Activity影響

缺點:資源消耗,單獨進程,使用IPC和AIDL編寫複雜

應用場景:系統級別的服務

 

按運行類型分類可以分爲前臺服務和後臺服務

前臺服務 

特點:在通知欄顯示通知,用戶可見

應用場景:服務使用時,需讓用戶知道操作的邏輯,如音樂播放服務,服務被終止時,通知欄的服務也會消失

後臺服務

特點:處於後臺的服務,用戶不可見

應用場景:服務使用時不需要讓用戶知道操作的邏輯,如天氣更新,日期同步等,服務被終止時,用戶沒有感知

 

按功能分類可以分爲可通信服務和不可通信服務

不可通信的後臺服務,調用者退出後Service仍然存在

特點:用startService

應用場景:服務不需要於Activity和Service通信

 

可通信的後臺服務

1)用bindService啓動

特點:調用者退出後,隨着調用者銷燬

應用場景:服務需要與Activity Service通信,需控制服務開始的時刻

用bindService啓動服務節約系統資源,第一次bindService時纔會創建服務的實例並且運行,特別當服務是遠程服務時,該效果更加明顯

服務只是一個遠程接口,供客戶端遠程調用執行方法

BoradcastReciever也可以完成這些需求,但是有以下缺點:若交互頻繁,容易造成性能問題,且BoradcastReciever本身執行時間非常短,很可能執行到一半,後面的代碼便不會執行了

2)使用startService,bindService啓動

特點:調用者退出後,隨着調用者的銷燬

應用場景:需與Activity和Service通信,不需要控制服務的開始時刻(服務一開始便運行)

 

四、具體使用

1.本地Service

-->新建子類繼承Service類(需重寫父類的onCreate onStartCommand onDestory和onBind方法)

-->構建用於Service的Intent對象

-->調用stratService啓動Service,調用stopService停止服務

-->在清單文件裏註冊Service

創建服務

public class MyService extends Service {


//啓動Service之後,就可以在onCreate()或onStartCommand()方法裏去執行一些具體的邏輯
//由於這裏作Demo用,所以只打印一些語句
    @Override
    public void onCreate() {
        super.onCreate();
        System.out.println("執行了onCreat()");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        System.out.println("執行了onStartCommand()");
        return super.onStartCommand(intent, flags, startId);


    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        System.out.println("執行了onDestory()");
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

啓停服務

//構建啓動服務的Intent對象
Intent startIntent = new Intent(this, MyService.class);
//調用startService()方法-傳入Intent對象,以此啓動服務
startService(startIntent);


//構建停止服務的Intent對象
Intent stopIntent = new Intent(this, MyService.class);
//調用stopService()方法-傳入Intent對象,以此停止服務
stopService(stopIntent);

註冊服務

//註冊Service服務
<service android:name=".MyService">
</service>

 

2.可通信的服務Service

上面介紹的代碼,只能用作單機使用,無法與Activity進行通信,接下來將增加與Activity通信的功能,即使用綁定Service服務(Binder類、bindService()、onBind()、unbindService()、onUnbind())

-->新建一個子類繼承Binder類,寫入與Activity關聯需要的方法,創建實例

public class MyService extends Service {

    private MyBinder mBinder = new MyBinder();

    @Override
    public void onCreate() {
        super.onCreate();
        System.out.println("執行了onCreat()");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        System.out.println("執行了onStartCommand()");
        return super.onStartCommand(intent, flags, startId);


    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        System.out.println("執行了onDestory()");
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        System.out.println("執行了onBind()");
        //返回實例
        return mBinder;
    }


    @Override
    public boolean onUnbind(Intent intent) {
        System.out.println("執行了onUnbind()");
        return super.onUnbind(intent);
    }

    //新建一個子類繼承自Binder類
    class MyBinder extends Binder {

        public void service_connect_Activity() {
            System.out.println("Service關聯了Activity,並在Activity執行了Service的方法");

        }
    }
}

-->綁定與解綁的方法

//構建綁定服務的Intent對象
Intent bindIntent = new Intent(this, MyService.class);
//調用bindService()方法,以此停止服務

bindService(bindIntent,connection,BIND_AUTO_CREATE);
//參數說明
//第一個參數:Intent對象
//第二個參數:Serviceconnection實例
//第三個參數:標誌位
//這裏傳入BIND_AUTO_CREATE表示在Activity和Service建立關聯後自動創建Service
//這會使得MyService中的onCreate()方法得到執行,但onStartCommand()方法不會執行
//調用unbindService()解綁服務
//參數是Serviceconnection實例
unbindService(connection);

-->ServiceConnection匿名類

    //創建ServiceConnection的匿名類
    private ServiceConnection connection = new ServiceConnection() {

        //重寫onServiceConnected()方法和onServiceDisconnected()方法
        //在Activity與Service建立關聯和解除關聯的時候調用
        @Override
        public void onServiceDisconnected(ComponentName name) {
        }

        //在Activity與Service解除關聯的時候調用
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //實例化Service的內部類myBinder
            //通過向下轉型得到了MyBinder的實例
            myBinder = (MyService.MyBinder) service;
            //在Activity調用Service類的方法
            myBinder.service_connect_Activity();
        }
    };

 

3.前臺Service

在狀態欄可見,擁有較高的優先級

對上面的代碼稍加修改

        //添加下列代碼將後臺Service變成前臺Service
        //構建"點擊通知後打開MainActivity"的Intent對象
        Intent notificationIntent = new Intent(this,MainActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(this,0,notificationIntent,0);

        //新建Builer對象
        Notification.Builder builer = new Notification.Builder(this);
        builer.setContentTitle("前臺服務通知的標題");//設置通知的標題
        builer.setContentText("前臺服務通知的內容");//設置通知的內容
        builer.setSmallIcon(R.mipmap.ic_launcher);//設置通知的圖標
        builer.setContentIntent(pendingIntent);//設置點擊通知後的操作

        Notification notification = builer.getNotification();//將Builder對象轉變成普通的notification
        startForeground(1, notification);//讓Service變成前臺Service,並在系統的狀態欄顯示出來

 

4.遠程Service

多個應用程序共享一個後臺服務(遠程服務),即一個遠程Service與多個應用程序組件(四大組件)進行跨進程通信

爲了讓Service與多個應用程序的組件進行跨進程通信(IPC),需要使用AIDL(Android Interface Definition Language,即Android接口定義語言;用於讓某個Service與多個應用程序組件之間進行跨進程通信,從而可以實現多個應用程序共享同一個Service的功能。)

在多進程通信中,最基礎的模型存在兩個進程角色:服務端和客戶端

服務端(Service)

-->新建AIDL文件,並聲明該服務需要向客戶端提供的接口

-->在Service子類中實現AIDL中定義的接口方法,並定義相關的生命週期方法

-->在清單文件中註冊服務,並且聲明服務爲遠程服務

客戶端(Client)

-->拷貝服務端的AIDL文件到目錄下

-->使用Stub.asInterface接口獲取服務端的Binder,根據需要調用服務端提供的接口方法

-->通過Intent指定服務端的服務名稱和所在包,綁定遠程Service

 

服務端代碼

注意這裏是新建AIDL文件而不是Java類,完成之後make project

// 在新建的AIDL_Service1.aidl裏聲明需要與Activity進行通信的方法

interface AIDL_Service1 {
    void AIDL_Service();
}
//AIDL中支持以下的數據類型
//1. 基本數據類型
//2. String 和CharSequence
//3. List 和 Map ,List和Map 對象的元素必須是AIDL支持的數據類型;
//4. AIDL自動生成的接口(需要導入-import)
//5. 實現android.os.Parcelable 接口的類(需要導入-import)

在Service子類中實現AIDL中定義的接口方法,並定義生命週期的方法(onCreate、onBind等)

public class MyService extends Service {
    
    // 實例化AIDL的Stub類(Binder的子類)
    AIDL_Service1.Stub mBinder = new AIDL_Service1.Stub() {

        //重寫接口裏定義的方法
        @Override
        public void AIDL_Service() throws RemoteException {
            System.out.println("客戶端通過AIDL與遠程後臺成功通信");
        }
    };

//重寫與Service生命週期的相關方法
    @Override
    public void onCreate() {
        super.onCreate();e

        System.out.println("執行了onCreate()");

    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        System.out.println("執行了onStartCommand()");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        System.out.println("執行了onDestory()");
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {

        System.out.println("執行了onBind()");
    //在onBind()返回繼承自Binder的Stub類型的Binder,非常重要
        return mBinder;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        System.out.println("執行了onUnbind()");
        return super.onUnbind(intent);
    }

}

在清單文件裏註冊

<service
            android:name=".MyService"
            android:process=":remote"  //將本地服務設置成遠程服務
            android:exported="true"      //設置可被其他進程調用
            >
            //此處Intent的action必須寫成“服務器端包名.aidl文件名”
            <intent-filter>
                <action android:name="你的包名.service_server.AIDL_Service1"/>
            </intent-filter>

        </service>

 

客戶端

--> 將服務端的AIDL文件所在的包複製到客戶端目錄下(Project/app/src/main),並進行編譯

最好是複製,什麼都不要改,否則容易出問題

-->使用Stub.asInterface接口獲取服務器的Binder

-->通過Intent指定服務端的服務名稱和所在包,進行Service綁定;

-->根據需要調用服務提供的接口方法。

public class MainActivity extends AppCompatActivity {

        private Button bindService;

        //定義aidl接口變量
        private AIDL_Service1 mAIDL_Service;

        //創建ServiceConnection的匿名類
        private ServiceConnection connection = new ServiceConnection() {

            //重寫onServiceConnected()方法和onServiceDisconnected()方法
            //在Activity與Service建立關聯和解除關聯的時候調用
            @Override
            public void onServiceDisconnected(ComponentName name) {
            }

            //在Activity與Service建立關聯時調用
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {

                //使用AIDLService1.Stub.asInterface()方法獲取服務器端返回的IBinder對象
                //將IBinder對象傳換成了mAIDL_Service接口對象
                mAIDL_Service = AIDL_Service1.Stub.asInterface(service);

                try {

                    //通過該對象調用在MyAIDLService.aidl文件中定義的接口方法,從而實現跨進程通信
                    mAIDL_Service.AIDL_Service();

                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        };


        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);

            bindService = (Button) findViewById(R.id.bind_service);

            //設置綁定服務的按鈕
            bindService.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    
                    //通過Intent指定服務端的服務名稱和所在包,與遠程Service進行綁定
                    //參數與服務器端的action要一致,即"服務器包名.aidl接口文件名"
                    Intent intent = new Intent("你的包名.service_server.AIDL_Service1");
                    
                    //Android5.0後無法只通過隱式Intent綁定遠程Service
                    //需要通過setPackage()方法指定包名
                    intent.setPackage("你的包名.service_server");
                    
                    //綁定服務,傳入intent和ServiceConnection對象
                    bindService(intent, connection, Context.BIND_AUTO_CREATE);

                }
            });
        }

    }

這樣便完成了簡單的客戶端和服務端的通信

 

通過這邊博客對Android Service的知識點進行了一個較爲全面的梳理,希望對小夥伴們有所幫助~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章