Android四大組件(二):Service

Service也是一個(gè)單獨(dú)的Android組件。通常用于為其他組件提供后臺(tái)服務(wù)或者監(jiān)控其他組件的運(yùn)行狀態(tài)。

Activity與Service的對(duì)比

相似點(diǎn)

  • 都是單獨(dú)的Android組件
  • 都擁有獨(dú)立的生命周期
  • 都是Context的派生類,所以可以調(diào)用Context類定義的如getResources()、getContentResolver()等方法
  • 都擁有自己生命周期回調(diào)方法

不同點(diǎn)

  • Activity運(yùn)行于前臺(tái)有圖形用戶界面,負(fù)責(zé)與用戶交互;Service通常位于后臺(tái)運(yùn)行,不需要與用戶交互,也沒(méi)有圖形用戶界面。

應(yīng)用場(chǎng)景
如果某個(gè)程序組件需要在運(yùn)行時(shí)向用戶呈現(xiàn)界面,或者程序需要與用戶交互,就需要用Activity,否則就應(yīng)該考慮使用Service了。


創(chuàng)建Service

  • 定義一個(gè)繼承Service的子類;
package com.rave.simpledemo.service;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.support.annotation.Nullable;

/**
 * Created by Administrator on 2019/1/21.
 */
public class FirstService extends Service {
    /**
     * Service子類必須實(shí)現(xiàn)的方法。該方法返回一個(gè)IBinder對(duì)象,應(yīng)用程序可通過(guò)IBinder對(duì)象與Service組件通信
     * @param intent
     * @return
     */
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        System.out.println("===================onBind FirstService=================");
        return null;
    }

    /**
     * 當(dāng)Service上綁定的所有客戶端都斷開(kāi)連接時(shí)會(huì)回調(diào)該方法
     * @param intent
     * @return
     */
    @Override
    public boolean onUnbind(Intent intent) {
        System.out.println("===================onUnbind FirstService=================");
        return super.onUnbind(intent);
    }

    /**
     * Service第一次被創(chuàng)建后回調(diào)該方法
     */
    @Override
    public void onCreate() {
        super.onCreate();
        System.out.println("===================onCreate FirstService=================");
    }

    /**
     * Service被關(guān)閉之前回調(diào)該方法
     */
    @Override
    public void onDestroy() {
        super.onDestroy();
        System.out.println("===================onDestroy FirstService=================");
    }

    /**
     * 該方法的早期版本是onStart(Intent intent, int startId),
     * 當(dāng)客戶端調(diào)用startService(Intent)方法啟動(dòng)Service時(shí)都會(huì)回調(diào)該方法
     * @param intent
     * @param flags
     * @param startId
     * @return
     */
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        System.out.println("===================onStartCommand FirstService=================");
        return super.onStartCommand(intent, flags, startId);

    }
}


  • 在Manifest.xml文件中配置該Service。
        <service android:name="com.rave.simpledemo.service.FirstService">
            <!-- 配置intent-filter元素說(shuō)明該Service可被哪些Intent啟動(dòng) -->
            <intent-filter>
                <!-- 為intent-filter配置action -->
                <action android:name="com.rave.simpledemo.service.FIRST_SERVICE" />
            </intent-filter>
        </service>

啟動(dòng)和停止Service

啟動(dòng)Service
① 通過(guò)Context的startService()方法啟動(dòng)Service,訪問(wèn)者與Service之間沒(méi)有關(guān)聯(lián),Service和訪問(wèn)者之間也無(wú)法進(jìn)行通信、數(shù)據(jù)交換。即使訪問(wèn)者退出,Service依然運(yùn)行:

        Intent intentService = new Intent(this, FirstService.class);
        intentService.setAction("com.rave.simpledemo.service.FIRST_SERVICE");
        //啟動(dòng)Service
        startService(intentService);
        //停止Service
        //stopService(intentService);

特點(diǎn):每當(dāng)Service被創(chuàng)建時(shí)會(huì)回調(diào)onCreate方法,每次Service被啟動(dòng)時(shí)都會(huì)回調(diào)onStartCommand方法。多次啟動(dòng)一個(gè)已有的Service組件將不會(huì)再回調(diào)onCreate方法,但每次啟動(dòng)時(shí)都會(huì)回調(diào)onStartCommand方法。

② 通過(guò)Context的bindService()方法啟動(dòng)Service,訪問(wèn)者與Service綁定在一起。訪問(wèn)者與Service之間可以進(jìn)行方法調(diào)用或數(shù)據(jù)交換。訪問(wèn)者一旦退出,Service也就終止:

boolean bindService(Intent service, ServiceConnection conn,int flags);
unbindService(ServiceConnection conn)

①service:該參數(shù)通過(guò)Intent指定要啟動(dòng)的Service;
②conn:該參數(shù)是一個(gè)ServiceConnection對(duì)象,該對(duì)象用于監(jiān)聽(tīng)訪問(wèn)者與Service之間的連接情況。當(dāng)訪問(wèn)者與Service之間連接成功時(shí)將回調(diào)ServiceConnection對(duì)象的onServiceConnected(ComponentName name,IBinder service)方法;當(dāng)訪問(wèn)者與Service之間斷開(kāi)連接時(shí)將回調(diào)ServiceConnection對(duì)象的onServiceDisconnected(ComponentName name)方法;
③flags:指定綁定時(shí)是否自動(dòng)創(chuàng)建Service(如果Service還未創(chuàng)建)。該參數(shù)可指定為0(不自動(dòng)創(chuàng)建)或BIND_AUTO_CREATE(自動(dòng)創(chuàng)建)。

  • 如何與被綁定的Service進(jìn)行本地通信(遠(yuǎn)程的、跨進(jìn)程的通信會(huì)采用AIDL,下文會(huì)單獨(dú)介紹)

ServiceConnection對(duì)象的onServiceConnected方法中有一個(gè)IBinder對(duì)象,該對(duì)象可實(shí)現(xiàn)與被綁定Service之間的通信。在開(kāi)發(fā)Service類時(shí),該Service類必須提供一個(gè)IBinder onBind(Intent intent)方法,在綁定本地Service的情況下,onBind(Intent intent)方法所返回的IBinder對(duì)象將會(huì)傳給ServiceConnection對(duì)象里onServiceConnected(ComponentName name, IBinder service)方法的service參數(shù),訪問(wèn)者就可通過(guò)該IBinder對(duì)象與Service進(jìn)行通信。在實(shí)際開(kāi)發(fā)中通常會(huì)采用繼承Binder(IBinder的實(shí)現(xiàn)類)的方式實(shí)現(xiàn)自己的IBinder對(duì)象。

具體步驟:
①在Service里面創(chuàng)建一個(gè)IBinder對(duì)象(通過(guò)繼承Binder類來(lái)實(shí)現(xiàn)自定義的Binder);
②在onBind(Intent intent)方法里面返回這個(gè)Binder對(duì)象;
③客戶端在綁定Service時(shí)所需的ServiceConnection對(duì)象的onServiceConnected方法里面獲取到onBind方法返回的Binder對(duì)象;
④客戶端通過(guò)持有的Binder對(duì)象來(lái)訪問(wèn)Service。


通過(guò)Binder通信

對(duì)于Service的onBind()方法所返回的IBinder對(duì)象來(lái)說(shuō),它可被當(dāng)成該Service組件所返回的回調(diào)對(duì)象,Service允許客戶端通過(guò)該IBinder對(duì)象來(lái)訪問(wèn)Service內(nèi)部的數(shù)據(jù),這樣即可實(shí)現(xiàn)客戶端與Service之間的通信。

package com.rave.simpledemo.service;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.support.annotation.Nullable;

/**
 * Created by Administrator on 2019/1/22.
 */
public class BindService extends Service {
    private boolean quit = false;
    private int count = 0;
    //定義onBinder方法要返回的Binder對(duì)象
    private MyBinder binder = new MyBinder();
    // 通過(guò)繼承Binder來(lái)實(shí)現(xiàn)IBinder類
    public class MyBinder extends Binder {

        public int getCount() {
            return count;
        }
    }
    
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        System.out.println("==================BindService onBind====================");
        return binder;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        System.out.println("==================BindService onUnbind====================");
        return true;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        System.out.println("==================BindService onCreate====================");
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (!quit) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    count++;
                }
                stopSelf();
            }
        }).start();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        this.quit = true;
        System.out.println("==================BindService onDestroy====================");
    }

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

    }
}
package com.rave.simpledemo.service;

import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import com.rave.simpledemo.R;

public class BindServiceActivity extends AppCompatActivity {

    private Button btnStartService;
    private Button btnStopService;
    private Button btnGetStatus;

    //啟動(dòng)Service時(shí)返回的IBinder對(duì)象
    BindService.MyBinder binder;
    //定義一個(gè)ServiceConnection對(duì)象
    private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            System.out.println("===================BindService onServiceConnected=================");
            binder = (BindService.MyBinder) service;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            System.out.println("===================BindService onServiceDisconnected=================");
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_bind_service);
        btnStartService = (Button) findViewById(R.id.btn_startservice);
        btnStopService = (Button) findViewById(R.id.btn_stopservice);
        btnGetStatus = (Button) findViewById(R.id.btn_getstatus);

        final Intent intent = new Intent(BindServiceActivity.this, BindService.class);
        intent.setAction("com.rave.simpledemo.service.BIND_SERVICE");

        btnStartService.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //綁定指定的Service
                bindService(intent, conn, Service.BIND_AUTO_CREATE);
            }
        });

        btnStopService.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //解除綁定Service(這里的參數(shù)ServiceConnection對(duì)象)
                unbindService(conn);
            }
        });

        btnGetStatus.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int count = binder.getCount();
                Toast.makeText(BindServiceActivity.this, count + "", Toast.LENGTH_LONG).show();
            }
        });
    }

}

Service的生命周期

上文的啟動(dòng)Service的兩個(gè)示例中,我們都有對(duì)Service的生命周期方法進(jìn)行觀測(cè)。我們發(fā)現(xiàn)根據(jù)啟動(dòng)方式的不同Service回調(diào)的生命周期方法也不同。

Service的生命周期

總結(jié)一下Service的生命周期方法回調(diào):

startService()和bindService()是兩個(gè)獨(dú)立的操作,我們可以只啟動(dòng)不綁定;也可以通過(guò)bindService()方法的第三個(gè)參數(shù),在綁定時(shí)啟動(dòng);還可以先啟動(dòng)后綁定;
①被啟動(dòng)的服務(wù)的生命周期:如果一個(gè)Service被某個(gè)Activity調(diào)用Context.startService方法啟動(dòng)(Service也可以啟動(dòng)服務(wù)),那么不管是否有Activity使用bindService綁定或者unbindService解除綁定該Service,該Service都在后臺(tái)運(yùn)行。如果一個(gè)Service被startService方法多次啟動(dòng),那么onCreate方法只會(huì)調(diào)用一次,但是onStartCommand將會(huì)每次都被回調(diào),并且系統(tǒng)只會(huì)創(chuàng)建Service的一個(gè)實(shí)例(因此你只需要調(diào)用一次stopService來(lái)停止)。該Service將會(huì)一直在后臺(tái)運(yùn)行,而不管對(duì)應(yīng)程序的Activity是否在運(yùn)行,直到被調(diào)用stopService,或自身的stopSelf方法。當(dāng)然如果系統(tǒng)資源不足,Android系統(tǒng)也可能結(jié)束服務(wù)。

②被綁定的服務(wù)的生命周期:如果一個(gè)Service被某個(gè)Activity調(diào)用Context.bindService方法綁定啟動(dòng),不管調(diào)用bindService調(diào)用幾次,onCreate方法都只會(huì)調(diào)用一次,同時(shí)onStartCommand方法始終不會(huì)調(diào)用。當(dāng)鏈接建立之后,Service將會(huì)一直運(yùn)行,除非調(diào)用Context.unbindService斷開(kāi)連接或者之前調(diào)用bindService的Context不存在了(如Activity被finish的時(shí)候),系統(tǒng)將會(huì)自動(dòng)停止Service,對(duì)應(yīng)onDestroy將被調(diào)用。

③被啟動(dòng)又被綁定的服務(wù)的生命周期:如果一個(gè)Service先被啟動(dòng),后又被綁定,則該Service將會(huì)一直在后臺(tái)運(yùn)行。并且不管如何調(diào)用,onCreate始終只會(huì)調(diào)用一次,對(duì)應(yīng)startService調(diào)用多少次,Service的onStartCommand就會(huì)調(diào)用多少次。調(diào)用unbindService將不會(huì)停止Service,而必須調(diào)用stopService或Service自身的stopSelf來(lái)停止服務(wù)(在沒(méi)有解綁的前提下使用stopService是無(wú)法停止服務(wù)的)。

④當(dāng)服務(wù)被停止時(shí)的清除工作:當(dāng)一個(gè)Service被終止(1、調(diào)用stopService;2、調(diào)用stopSelf;3、不再有綁定的連接(通過(guò)bindService啟動(dòng)))時(shí),onDestroy方法將會(huì)被回調(diào),在這里應(yīng)當(dāng)做一些清除工作(如停止Service中創(chuàng)建并運(yùn)行的線程、注冊(cè)的偵聽(tīng)器、接收器等)。

使用Service時(shí)的注意事項(xiàng)

①在調(diào)用bindService綁定到Service的時(shí)候,就應(yīng)當(dāng)保證在某處調(diào)用unbindService解除綁定(盡管Activity被finish的時(shí)候會(huì)自動(dòng)解除綁定,并且會(huì)自動(dòng)停止Service);

②使用startService啟動(dòng)服務(wù)之后,一定要使用stopService停止服務(wù),不管你是否使用bindService;

③同時(shí)使用startService和bindService時(shí)要注意:Service的終止,需要unbindService與stopService同時(shí)調(diào)用,才能終止Service。關(guān)于調(diào)用順序,如果先調(diào)用unbindService此時(shí)服務(wù)不會(huì)自動(dòng)終止,再調(diào)用stopService服務(wù)才會(huì)停止;如果先調(diào)用stopService此時(shí)服務(wù)也不會(huì)終止,需要再調(diào)用unbindService(或者調(diào)用bindService的Context不存在了,如Activity被finish的時(shí)候)服務(wù)才會(huì)自動(dòng)停止。

④當(dāng)旋轉(zhuǎn)手機(jī)屏幕的時(shí)候,如果發(fā)生了Activity的重建(請(qǐng)參考Android四大組件(一)Activity),旋轉(zhuǎn)之前的使用bindService建立的連接便會(huì)斷開(kāi)(Context不存在了)。

⑤在sdk 2.0之前的版本,使用的onStart方法被onStartCommand方法替換了,但是onStart方法仍然有效。


跨進(jìn)程調(diào)用Service(AIDL服務(wù))

理解什么是AIDL(Android Interface Definition Language,即Android接口定義語(yǔ)言)
Android系統(tǒng)中,應(yīng)用程序都運(yùn)行在自己的進(jìn)程中,進(jìn)程之間一般無(wú)法直接進(jìn)行數(shù)據(jù)交換。在Java技術(shù)中,RMI可實(shí)現(xiàn)跨進(jìn)程調(diào)用;在Android的中,采用了相似的方式來(lái)實(shí)現(xiàn)跨進(jìn)程調(diào)用Service,這就是AIDL。與RMI相似,也是先定義一個(gè)遠(yuǎn)程調(diào)用接口,然后為該接口提供一個(gè)實(shí)現(xiàn)類;與RMI不同的是,客戶端訪問(wèn)Service時(shí),Android并不是直接返回Service對(duì)象給客戶端。這點(diǎn)在綁定本地Service時(shí)已經(jīng)看到,Service只是將一個(gè)回調(diào)對(duì)象(IBinder對(duì)象)通過(guò)onBind()方法返回給客戶端。因此Android的AIDL遠(yuǎn)程接口的實(shí)現(xiàn)類就是IBinder實(shí)現(xiàn)類。與綁定本地Service不同的是,本地Service的onBind()方法會(huì)直接把IBinder對(duì)象本身傳給客戶端的ServiceConnection的onServiceConnected方法的第二個(gè)參數(shù)。遠(yuǎn)程Service的onBind方法只是將IBinder對(duì)象的代理傳給客戶端的ServiceConnection的onServiceConnected方法的第二個(gè)參數(shù)。客戶端獲取了遠(yuǎn)程Service的IBinder對(duì)象的代理后,就可通過(guò)該IBinder對(duì)象去回調(diào)遠(yuǎn)程Service的屬性或方法了。

AIDL的操作步驟

Service端
1、新建AIDL文件

新建AIDL文件

2、按需求自己定義接口


自己定義IUserInfo接口

3、重新build工程,AS會(huì)在build目錄中生成aidl對(duì)應(yīng)的java實(shí)現(xiàn)


4、編寫(xiě)Service類

package com.rave.simpledemo.aidldemo;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;

import java.util.Timer;
import java.util.TimerTask;

/**
 * Created by Administrator on 2019/1/25.
 */
public class AidlUserService extends Service {
    private String name = "rave";
    private int age = 0;

    private int count = 0;
    private Timer timer = new Timer();

    /**
     * 這里跟本地Service不一樣,沒(méi)有直接繼承Binder類,而是繼承了ADT所生成的IUserInfo.Stub
     * 但是IUserInfo.Stub也是Binder的子類
     */
    private IBinder stub = new IUserInfo.Stub() {

        @Override
        public String getName() throws RemoteException {
            return name + count;
        }

        @Override
        public int getAge() throws RemoteException {
            return age + count;
        }
    };

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        /**返回catBinder對(duì)象
         * 在綁定本地Service的情況下,該catBinder對(duì)象會(huì)直接傳給客戶端的
         * ServiceConnection對(duì)象的onServiceConnected方法的第二個(gè)參數(shù);
         * 在綁定遠(yuǎn)程Service的情況下,只將catBinder對(duì)象的代理傳給客戶端的
         * ServiceConnection對(duì)象的onServiceConnected方法的第二個(gè)參數(shù)。
         */
        return stub;
    }

    @Override
    public void onCreate() {
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                count++;
            }
        }, 0, 1 * 1000);
    }

    @Override
    public void onDestroy() {
        /**
         * 在onDestroy里面做清理回收工作
         */
        timer.cancel();
    }
}

5、當(dāng)然不要忘記在manifest.xml里注冊(cè)該Service

        <service android:name=".aidldemo.AidlUserService">
            <intent-filter>
                <action android:name="com.rave.simpledemo.service.AIDL_USER_SERVICE" />
            </intent-filter>
        </service>

客戶端訪問(wèn)AIDL服務(wù)
1、將Service端的AIDL接口文件復(fù)制到客戶端應(yīng)用中,要連文件路徑一起復(fù)制

需要復(fù)制的部分

2、同Service端一樣,重新build工程,AS會(huì)調(diào)用ADT在build目錄中生成aidl對(duì)應(yīng)的java實(shí)現(xiàn)

3、創(chuàng)建ServiceConnection對(duì)象,并以ServiceConnection對(duì)象作為參數(shù),調(diào)用bindService()方法綁定遠(yuǎn)程Service

package com.richinfo.aidlclient;

import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import com.rave.simpledemo.aidldemo.IUserInfo;

public class UserInfoActivity extends AppCompatActivity {

    private TextView tvUserName, tvUserAge;
    private Button btnBind, btnUnbind, btnGet;
    private Intent intent;

    private IUserInfo userInfoService;
    private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            /**
             * 與本地綁定Service不同,綁定遠(yuǎn)程Service的ServiceConnection不能直接獲取Service的onBind方法所返回的對(duì)象;
             * 它只能返回onBind()方法所返回的對(duì)象的代理,因此需要在這里做asInterface的處理。
             */
            userInfoService = IUserInfo.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_user_info);
        tvUserName = (TextView) findViewById(R.id.tv_username);
        tvUserAge = (TextView) findViewById(R.id.tv_userage);
        btnBind = (Button) findViewById(R.id.btn_bind);
        btnUnbind = (Button) findViewById(R.id.btn_unbind);
        btnGet = (Button) findViewById(R.id.btn_get);

        intent = new Intent();
        // Android 5.0以后需要用顯示意圖來(lái)啟動(dòng)Service,否則會(huì)報(bào)異常
        // java.lang.IllegalArgumentException: Service Intent must be explicit
        // 這里由于是跨進(jìn)程所以用Component來(lái)解決這個(gè)問(wèn)題,試過(guò)用setPackage的方式,無(wú)法解決這個(gè)問(wèn)題
        intent.setComponent(new ComponentName("com.rave.simpledemo", "com.rave.simpledemo.aidldemo.AidlUserService"));
        //這種方式很多博客上說(shuō)可以,經(jīng)測(cè)試這種方式可以startService成功的啟動(dòng)遠(yuǎn)程service,但是在啟動(dòng)之后再綁定會(huì)出現(xiàn)綁定失敗的情況。
        //intent.setPackage("com.rave.simpledemo");

        intent.setAction("com.rave.simpledemo.service.AIDL_USER_SERVICE");
        btnBind.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startService(intent);
                bindService(intent, conn, Service.BIND_AUTO_CREATE);
            }
        });

        btnUnbind.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                unbindService(conn);
            }
        });

        btnGet.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    tvUserName.setText(userInfoService.getName());
                    tvUserAge.setText(userInfoService.getAge()+"");
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //記得解綁
        unbindService(conn);
        stopService(intent);
    }
}

實(shí)踐中遇到的問(wèn)題
1、Android 5.0以后系統(tǒng)禁止使用隱式意圖來(lái)啟動(dòng)Service,所以在跨進(jìn)程特別是跨APP的綁定Service的時(shí)候,可以采用如下方式:

        Intent intent = new Intent();
        intent.setAction("com.rave.simpledemo.service.AIDL_USER_SERVICE");
        // Android 5.0以后需要用顯示意圖來(lái)啟動(dòng)Service,否則會(huì)報(bào)異常
        // java.lang.IllegalArgumentException: Service Intent must be explicit
        // 這里由于是跨進(jìn)程所以用Component來(lái)解決這個(gè)問(wèn)題,試過(guò)用setPackage的方式,無(wú)法解決這個(gè)問(wèn)題
        intent.setComponent(new ComponentName("com.rave.simpledemo", "com.rave.simpledemo.aidldemo.AidlUserService"));

        //這種方式很多博客上說(shuō)可以,經(jīng)測(cè)試這種方式可以startService成功的啟動(dòng)遠(yuǎn)程service,但是在啟動(dòng)之后再綁定會(huì)出現(xiàn)綁定失敗的情況。
        //intent.setPackage("com.rave.simpledemo");

2、無(wú)法通過(guò)bindService設(shè)置BIND_AUTO_CREATE來(lái)創(chuàng)建服務(wù),只能先startService啟動(dòng),再調(diào)用bindService綁定:

        // 在使用AIDL的時(shí)候,必須要先用startService來(lái)啟動(dòng)服務(wù),再用bindService來(lái)綁定服務(wù),否則會(huì)綁定失敗。
        startService(intent);
        boolean flag = bindService(intent, conn, Service.BIND_AUTO_CREATE);

3、在Service的onDestroy里面一定要清理資源,避免內(nèi)存泄漏。已經(jīng)客戶端調(diào)用bindService之后要手動(dòng)解綁。

傳遞復(fù)雜數(shù)據(jù)的AIDL服務(wù)

上面我們所展示的都是通過(guò)AIDL傳輸基本數(shù)據(jù)類型。如果是自定義類的話,Android要求調(diào)用遠(yuǎn)程Service的參數(shù)和返回值都必須實(shí)現(xiàn)Parcelable接口。具體步驟如下:

服務(wù)端
1、使用AIDL代碼來(lái)定義這些自定義類型(包括參數(shù)類型和返回值類型):

使用AIDL定義參數(shù)類型
使用AIDL定義返回值類型

2、定義實(shí)現(xiàn)Parcelable接口的參數(shù)類和返回值類(返回值類一樣這里demo省略代碼)

package com.rave.simpledemo.aidldemo;

import android.os.Parcel;
import android.os.Parcelable;

/**
 * Created by Administrator on 2019/1/28.
 */
public class Person implements Parcelable {
    private Integer id;
    private String name;
    private String pass;

    public Person() {

    }

    public Person(Integer id, String name, String pass) {
        super();
        this.id = id;
        this.name = name;
        this.pass = pass;
    }

    protected Person(Parcel in) {
        name = in.readString();
        pass = in.readString();
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getPass() {
        return pass;
    }

    public void setPass(String pass) {
        this.pass = pass;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        result = prime * result + ((pass == null) ? 0 : pass.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }

        if (getClass() != obj.getClass()) {
            return false;
        }

        Person other = (Person) obj;
        if (this.name == null) {
            if (other.name != null) {
                return false;
            }
        } else if (!this.name.equals(other.name)) {
            return false;
        }

        if (this.pass == null) {
            if (other.pass != null) {
                return false;
            }
        } else if (!this.pass.equals(other.pass)) {
            return false;
        }

        return true;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        //把對(duì)象所包含的數(shù)據(jù)寫(xiě)到Parcel中
        dest.writeInt(id);
        dest.writeString(name);
        dest.writeString(pass);
    }

    public static final Creator<Person> CREATOR = new Creator<Person>() {
        @Override
        public Person createFromParcel(Parcel in) {
            return new Person(in.readInt(), in.readString(), in.readString());
        }

        @Override
        public Person[] newArray(int size) {
            return new Person[size];
        }
    };
}

3、使用AIDL定義通信接口

AIDL定義通信接口
  • 在AIDL接口中定義方法時(shí),需要指定形參的傳遞模式。對(duì)于java語(yǔ)言來(lái)說(shuō),一般都是采用傳入?yún)?shù)的方式,因此上面指定為in模式。具體的傳參方式有以下幾種:
    ① in:參數(shù)由客戶端設(shè)置,或者理解為客戶端傳入?yún)?shù)值;
    ② out:參數(shù)由服務(wù)器設(shè)置,或者理解成由服務(wù)端返回值;
    ③ inout:客戶端服務(wù)端都可以設(shè)置,或者理解成可以雙向通信。

  • 任何自定義類型都需要在這里顯示使用import引入(即便該類的AIDL文件和通信接口是在同一個(gè)包下),否則會(huì)報(bào)錯(cuò):

Error:Execution failed for task ':app:compileDebugAidl'.
com.android.ide.common.process.ProcessException: org.gradle.process.internal.ExecException: Process 'command 'G:\Android\sdk1\build-tools\23.0.1\aidl.exe'' finished with non-zero exit value 1

4、再次不要忘記在manifest.xml里注冊(cè)該Service

客戶端
1、跟傳輸基本類型一樣,將Service端的AIDL接口文件復(fù)制到客戶端應(yīng)用中(包括定義接口的aidl的文件IPet.aidl、定義的自定義類型的AIDL文件(包括參數(shù)類型和返回值類型)、定義的自定義類型的Java類文件(包括參數(shù)類型和返回值類型))。同樣要保證文件路徑和類的包名和跟Service端一致。

復(fù)制到客戶端的文件

2、后面的啟動(dòng)服務(wù)和綁定服務(wù)的操作跟傳輸基本數(shù)據(jù)類型一樣。下面給出本例的客戶端代碼:

package com.richinfo.aidlclient;

import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import com.rave.simpledemo.aidldemo.IPet;
import com.rave.simpledemo.aidldemo.Person;
import com.rave.simpledemo.aidldemo.Pet;

import java.util.List;

public class ComplexTypeActivity extends AppCompatActivity {
    private TextView tvContent;
    private Button btnBind;
    private Button btnUnbind;
    private Button btnGet;

    private Intent complexServiceIntent;
    private IPet iPetService;

    private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            iPetService = IPet.Stub.asInterface(service);
            System.out.println("onServiceConnected");
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

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

        tvContent = (TextView) findViewById(R.id.tv_content);
        btnBind = (Button) findViewById(R.id.btn_bind);
        btnGet = (Button) findViewById(R.id.btn_get);
        btnUnbind = (Button) findViewById(R.id.btn_unbind);

        complexServiceIntent = new Intent();
        complexServiceIntent.setAction("com.rave.simpledemo.service.COMPLEX_SERVICE");
        complexServiceIntent.setComponent(new ComponentName("com.rave.simpledemo", "com.rave.simpledemo.aidldemo.ComplexService"));

        btnBind.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startService(complexServiceIntent);
                boolean isSuccess = bindService(complexServiceIntent, conn, Service.BIND_AUTO_CREATE);
                System.out.println("bind successed:" + isSuccess);
            }
        });

        btnGet.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    List<Pet> petList = iPetService.getPets(new Person(1, "rava", "rave"));
                    int size = petList.size();
                    tvContent.setText("pet count:" + size + "");
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });

        btnUnbind.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                unbindService(conn);
            }
        });

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(conn);
        stopService(complexServiceIntent);
    }
}

獲取系統(tǒng)服務(wù)

1、 電話管理器(TelephonyManager):

//getSystemService(String name):根據(jù)服務(wù)名稱來(lái)獲取系統(tǒng)服務(wù)。
TelephonyManager telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
  • 獲取網(wǎng)絡(luò)狀態(tài)
  • 獲取SIM卡信息
  • 監(jiān)聽(tīng)通話狀態(tài)

2、短信管理器(SmsManager):

//獲取SmsManager
SmsManager smsManager = SmsManager.getDefault();
  • 短信發(fā)送

3、音頻管理器(AudioManager):

//獲取音頻管理器
AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
  • 調(diào)節(jié)系統(tǒng)音量:adjustStreamVolume(int streamType, int direction, int flags),streamType聲音類型;direction對(duì)聲音進(jìn)行增大還是減小;flags調(diào)整聲音時(shí)的標(biāo)志。
  • 設(shè)置麥克風(fēng)靜音:setMicrophoneMute(boolean on)。
  • 設(shè)置聲音模式:setMode(int mode),可設(shè)置的值有NORMAL、RINGTONE和IN_CALL。
  • 設(shè)置手機(jī)的電話鈴聲模式:setRingerMode(int ringerMode),可設(shè)置鈴聲模式、靜音模式、振動(dòng)模式。
  • 設(shè)置打卡擴(kuò)音器:setSpeakerphoneOn(boolean on)。
  • 將制定類型的聲音調(diào)整為靜音:setStreamMute(int streamType, boolean state),streamType聲音類型。
  • 設(shè)置手機(jī)指定類型的音量值:setStreamVolume(int streamType, int index, int flags),streamType聲音類型;index具體的音量值;flags調(diào)整聲音時(shí)的標(biāo)志。

4、振動(dòng)器(Vibrator):

Vibrator vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
  • 控制手機(jī)振動(dòng):vibrate(long milliseconds)、vibrate(long[] pattern, int repeat)振動(dòng);cancel()關(guān)閉振動(dòng)。

5、手機(jī)鬧鐘服務(wù)(AlarmManager):

AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
  • 開(kāi)發(fā)鬧鐘應(yīng)用
  • 全局定時(shí)器

特殊的Service——IntentService

通過(guò)上面的內(nèi)容,我們已經(jīng)發(fā)現(xiàn)了。服務(wù)的代碼默認(rèn)運(yùn)行在主線程里的。如果要在服務(wù)里面執(zhí)行耗時(shí)操作的代碼,就需要開(kāi)啟一個(gè)主線程去處理這些代碼。比如在啟動(dòng)和停止Service的例子中:

    @Override
    public void onCreate() {
        super.onCreate();
        System.out.println("==================BindService onCreate====================");
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (!quit) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    count++;
                }
                stopSelf();
            }
        }).start();
    }

我們知道服務(wù)一旦啟動(dòng)就會(huì)一直運(yùn)行下去,必須手動(dòng)調(diào)用stopService()或者stopSelf()方法才能讓服務(wù)停止下來(lái)。所以在上例中我們需要在run()方法的最后,調(diào)用stopSelf來(lái)結(jié)束服務(wù)。谷歌給我們提供IntentService,就是為了方便開(kāi)發(fā)者處理在Service中開(kāi)啟子線程的情況。它幫我們開(kāi)發(fā)者封裝了開(kāi)啟子線程、線程間通信(采用Handler)、以及自動(dòng)銷毀服務(wù)等操作。

IntentService的使用步驟
1、繼承IntentService創(chuàng)建自定義的IntentService類

package com.rave.simpledemo.service;

import android.app.IntentService;
import android.content.Intent;

/**
 * Created by Administrator on 2019/1/30.
 */
public class BindIntentService extends IntentService {
    private boolean quit = false;
    private int count = 0;

    public BindIntentService() {
        super("BindIntentService");
    }

    /**
     * 必須實(shí)現(xiàn)的抽象方法,我們的業(yè)務(wù)邏輯就是在這個(gè)方法里面去實(shí)現(xiàn)的
     * 方法在子線程運(yùn)行,我們不用去關(guān)心ANR的問(wèn)題
     * 在OnCreate方法里面創(chuàng)建并啟動(dòng)子線程,
     * 在OnStartCommand方法里面,將Intent封裝成Message并傳遞到子線程的handler,然后回調(diào)onHandleIntentonStart
     *
     * @param intent
     */
    @Override
    protected void onHandleIntent(Intent intent) {
        while (!quit) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            count++;
            System.out.println("==================Current count:" + count + "====================");
            if (count == 10)
                quit = true;
        }
    }

    /**
     * 為了驗(yàn)證onHandleIntent執(zhí)行后,服務(wù)會(huì)不會(huì)自動(dòng)銷毀,我們?cè)谶@里重寫(xiě)onDestroy方法
     * 如果會(huì)自動(dòng)銷毀,那么在"IntetnService Running"出現(xiàn)后,應(yīng)該會(huì)出現(xiàn)"IntetnService Stop"
     */
    @Override
    public void onDestroy() {
        System.out.println("==================BindIntentService onDestroy:" + count + "====================");
        //經(jīng)測(cè)試,如果onHandleIntent里面的代碼邏輯沒(méi)有走完,在服務(wù)外部調(diào)用stopService來(lái)停止服務(wù),并不會(huì)立即結(jié)束子線程
        quit = true;
        super.onDestroy();
    }
}

2、在Manifest里面注冊(cè)IntentService,注冊(cè)方式跟Service一樣

3、啟動(dòng)IntentService
我們推薦使用startService(intent)的方式來(lái)啟動(dòng)服務(wù)。

注意!!!也可以使用bindService的方式來(lái)啟動(dòng)服務(wù)。但是在使用bindService的啟動(dòng)的時(shí)候,即使onHandleIntent里面的邏輯執(zhí)行完畢,也不會(huì)自動(dòng)銷毀服務(wù)。原因應(yīng)該是Service還是被綁定狀態(tài),調(diào)用stopSelf無(wú)法停止。所以如果使用bindService啟動(dòng)服務(wù)將會(huì)失去IntentService的一大特點(diǎn),使用時(shí)請(qǐng)謹(jǐn)慎.

4、銷毀IntentService
IntentService的一大特點(diǎn)就是onHandleIntent里面的代碼邏輯執(zhí)行完之后,自動(dòng)銷毀Service。所以我們可以不用專門做停止IntentService的操作。

注意!!!我們也可以在客戶端手動(dòng)調(diào)用stopService來(lái)銷毀服務(wù),但是用這種方式不會(huì)停止IntentService里面啟動(dòng)的子線程。如果要采用這種方式銷毀服務(wù),一定要注意子線程無(wú)法停止,從而導(dǎo)致內(nèi)存泄漏的問(wèn)題。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容