關於Service你所需要知道內容(一)

weixin_34019929發表於2018-07-19

根據下面的目錄來介紹和理解Service中的知識點:

一.Service的兩種生命週期

service啟動有兩種方式:啟動服務startService,繫結服務bindService。有不同是生命週期,如下所示:


4264145-55b1aae4f751ed3d
image
1.若一個Service被多次startService啟動,onCreate被呼叫一次,只有onStartCommand被呼叫多次。
2.onStartCommand必須放回一個整數=描述系統在殺死服務後應該如何繼續執行:

a.START_NOT_STICKY;不會重建服務,除非還存在未傳送的intent。當服務不再是必需的,並且應用程式能夠簡單地重啟那些未完成的工作時,這是避免服務執行的最安全的選項。

b.START_STICKY;如果系統在onStartCommand()返回後殺死了這個service,會重新建立這個service並且呼叫onStartCommand(),但是不再重新傳送上次最後一個intent,而是使用一個nullintent呼叫onStartCommand(),除非有一些掛起的intent,在此情況下,這些掛起的intent被派送。(適用媒體播放器類似服務,它們不執行命令,但需要一直執行並隨時待命)

c.START_REDELIVER_INTENT;如果系統在onStartCommand()返回後殺死了service,重新建立這個service並且使用上次最後一個intent呼叫onStartCommand().任何掛起的intent都順序地被派送。(這適合於活躍地執行一個工作並且應被立即恢復的服務,比如下載一個檔案)

3.啟動並繫結一個service,如果沒有解綁,呼叫stopService無法殺死服務。
4.unBindService()解除繫結服務,內部呼叫服務的生命週期方法onUnbind(),然後呼叫onDestory()銷燬服務。服務只能被解除繫結一次,如果unBindService方法被呼叫多次,就會出錯。
5.startService啟動服務,呼叫者退出,service依舊還在;bindService繫結服務,呼叫者退出,service也就退出。

二.Service的兩種啟動方式

啟動服務startService程式碼
 Intent intent = new Intent(TestActivity.this,TestService.class);
 startService(intent);
繫結服務bindService程式碼
private ServiceConnection mTestServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            myBinder = (TestService.MyBinder) iBinder;
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    };
  Intent intent = new Intent(TestActivity.this,TestService.class);
  bindService(intent,mTestServiceConnection,BIND_AUTO_CREATE);
  1. 繫結服務,首先要做的事情就是先用Map記錄當前繫結服務所需的一些資訊。 然後啟動服務。
  2. 解綁服務,先從早前的Map集合中移除記錄,然後停止服務。
  3. 如果再次解綁,無非就是再到這個map集合中找找有沒有這條記錄,沒有就丟擲服務沒有註冊的異常,也就是早前根本沒有註冊過任何服務。

三.遠端服務

下圖是根據不同方式對服務的分類,這裡重點介紹實現遠端服務


4264145-b00ae176dd3f0c7d.png
服務分類.png

實現遠端服務的程式碼:

AIDL程式碼
// IRemoteService.aidl
package com.example.chenpeng.julyapplication.service;

// Declare any non-default types here with import statements

import com.example.chenpeng.julyapplication.IParticipateCallback;

interface IRemoteService {
    int add(int a , int b);
    void join(IBinder token,String userName);
    void leave(IBinder token);
    List<String> getParticipators();
    void registerParticipateCallback(IParticipateCallback cb);
    void unregisterParticipateCallback(IParticipateCallback cb);
}
建立完AIDL檔案以後,點選Build->Rebuild Project,在app\build\generated\source\aidl\debug中會有對應的ADIL的Java介面檔案。生成如下程式碼:
package com.example.chenpeng.julyapplication.service;
public interface IRemoteService extends android.os.IInterface
{
    /** 內部類Stub,繼承Binder */
    public static abstract class Stub extends android.os.Binder implements com.example.chenpeng.julyapplication.service.IRemoteService
    {
        /**Binder的唯一標識,一般用當前的類名錶示*/
        private static final java.lang.String DESCRIPTOR = "com.example.chenpeng.julyapplication.service.IRemoteService";

        public Stub()
        {
            this.attachInterface(this, DESCRIPTOR);
        }
        /**
         * 用於將服務端的Binder物件轉換成客戶端所需的AIDL介面型別的物件,這種轉換過程區分程式
         * 如果客戶端和服務端位於同一個程式,返回服務端的Stub物件本身;否則返回系統封裝後的Stub.proxy物件
         */
        public static com.example.chenpeng.julyapplication.service.IRemoteService asInterface(android.os.IBinder obj)
        {
            if ((obj==null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin!=null)&&(iin instanceof com.example.chenpeng.julyapplication.service.IRemoteService))) {
                return ((com.example.chenpeng.julyapplication.service.IRemoteService)iin);
            }
            return new com.example.chenpeng.julyapplication.service.IRemoteService.Stub.Proxy(obj);
        }
        /**返回當前Binder物件*/
        @Override public android.os.IBinder asBinder()
        {
            return this;
        }
        /**
         * 這個方法執行在服務端中的Binder執行緒池中,當客戶端發起跨程式請求時,遠端請求會通過系統底層封裝後交由此方法處理。
         * 通過code可以確定呼叫哪個方法,如有入參,從data中獲取,方法完成後,如需要返回引數,將引數存入result中。
         * 若果,onTransact返回false,則客戶端的請求失敗,因此可以利用這個特徵來做許可權驗證。
         * */
        @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
        {
            switch (code)
            {
                case INTERFACE_TRANSACTION:
                {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_add:
                {
                    data.enforceInterface(DESCRIPTOR);
                    int _arg0;
                    _arg0 = data.readInt();
                    int _arg1;
                    _arg1 = data.readInt();
                    int _result = this.add(_arg0, _arg1);
                    reply.writeNoException();
                    reply.writeInt(_result);
                    return true;
                }
                case TRANSACTION_join:
                {
                    data.enforceInterface(DESCRIPTOR);
                    android.os.IBinder _arg0;
                    _arg0 = data.readStrongBinder();
                    java.lang.String _arg1;
                    _arg1 = data.readString();
                    this.join(_arg0, _arg1);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_leave:
                {
                    data.enforceInterface(DESCRIPTOR);
                    android.os.IBinder _arg0;
                    _arg0 = data.readStrongBinder();
                    this.leave(_arg0);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_getParticipators:
                {
                    data.enforceInterface(DESCRIPTOR);
                    java.util.List<java.lang.String> _result = this.getParticipators();
                    reply.writeNoException();
                    reply.writeStringList(_result);
                    return true;
                }
                case TRANSACTION_registerParticipateCallback:
                {
                    data.enforceInterface(DESCRIPTOR);
                    com.example.chenpeng.julyapplication.IParticipateCallback _arg0;
                    _arg0 = com.example.chenpeng.julyapplication.IParticipateCallback.Stub.asInterface(data.readStrongBinder());
                    this.registerParticipateCallback(_arg0);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_unregisterParticipateCallback:
                {
                    data.enforceInterface(DESCRIPTOR);
                    com.example.chenpeng.julyapplication.IParticipateCallback _arg0;
                    _arg0 = com.example.chenpeng.julyapplication.IParticipateCallback.Stub.asInterface(data.readStrongBinder());
                    this.unregisterParticipateCallback(_arg0);
                    reply.writeNoException();
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }
        /**
         * 這個方法執行在客戶端
         * 先把引數寫入_data中,接著呼叫transact方法發起RPC(遠端過程呼叫)請求,同時當前執行緒掛起;然後服務端的onTransact方法被呼叫,
         * 直到RPC過程返回後,當前執行緒繼續執行,並從_reply中取出RPC過程的返回結果,最後返回_reply中的資料
         * */
        private static class Proxy implements com.example.chenpeng.julyapplication.service.IRemoteService
        {
            private android.os.IBinder mRemote;
            Proxy(android.os.IBinder remote)
            {
                mRemote = remote;
            }
            @Override public android.os.IBinder asBinder()
            {
                return mRemote;
            }
            public java.lang.String getInterfaceDescriptor()
            {
                return DESCRIPTOR;
            }
            @Override public int add(int a, int b) throws android.os.RemoteException
            {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                int _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeInt(a);
                    _data.writeInt(b);
                    mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readInt();
                }
                finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
            @Override public void join(android.os.IBinder token, java.lang.String userName) throws android.os.RemoteException
            {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeStrongBinder(token);
                    _data.writeString(userName);
                    mRemote.transact(Stub.TRANSACTION_join, _data, _reply, 0);
                    _reply.readException();
                }
                finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
            @Override public void leave(android.os.IBinder token) throws android.os.RemoteException
            {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeStrongBinder(token);
                    mRemote.transact(Stub.TRANSACTION_leave, _data, _reply, 0);
                    _reply.readException();
                }
                finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
            @Override public java.util.List<java.lang.String> getParticipators() throws android.os.RemoteException
            {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.util.List<java.lang.String> _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getParticipators, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.createStringArrayList();
                }
                finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
            @Override public void registerParticipateCallback(com.example.chenpeng.julyapplication.IParticipateCallback cb) throws android.os.RemoteException
            {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeStrongBinder((((cb!=null))?(cb.asBinder()):(null)));
                    mRemote.transact(Stub.TRANSACTION_registerParticipateCallback, _data, _reply, 0);
                    _reply.readException();
                }
                finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
            @Override public void unregisterParticipateCallback(com.example.chenpeng.julyapplication.IParticipateCallback cb) throws android.os.RemoteException
            {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeStrongBinder((((cb!=null))?(cb.asBinder()):(null)));
                    mRemote.transact(Stub.TRANSACTION_unregisterParticipateCallback, _data, _reply, 0);
                    _reply.readException();
                }
                finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }
        static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_join = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
        static final int TRANSACTION_leave = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
        static final int TRANSACTION_getParticipators = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
        static final int TRANSACTION_registerParticipateCallback = (android.os.IBinder.FIRST_CALL_TRANSACTION + 4);
        static final int TRANSACTION_unregisterParticipateCallback = (android.os.IBinder.FIRST_CALL_TRANSACTION + 5);
    }
    public int add(int a, int b) throws android.os.RemoteException;
    public void join(android.os.IBinder token, java.lang.String userName) throws android.os.RemoteException;
    public void leave(android.os.IBinder token) throws android.os.RemoteException;
    public java.util.List<java.lang.String> getParticipators() throws android.os.RemoteException;
    public void registerParticipateCallback(com.example.chenpeng.julyapplication.IParticipateCallback cb) throws android.os.RemoteException;
    public void unregisterParticipateCallback(com.example.chenpeng.julyapplication.IParticipateCallback cb) throws android.os.RemoteException;
}

建立在服務端的Service程式碼
public class RemoteService extends Service {

    private static final String TAG = "RemoteService";
    private List<Client> mClient = new ArrayList<>();
    private List<String> mList = new ArrayList<>();
    //RemoteCallbackList,幫我自動處理了Link-To-Death的問題
    private RemoteCallbackList<IParticipateCallback> mRemoteCallbackList = new RemoteCallbackList<>();
    private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
        @Override
        public int add(int a, int b) throws RemoteException {
            return a+b;
        }

        @Override
        public void join(IBinder token, String userName) throws RemoteException {
            if(findClient(token) < 0){
                Client client = new Client(token,userName);
                //註冊客戶端死掉的通知
                mClient.add(client);
                client.mToken.linkToDeath( client,0);
                Log.i(TAG, "join: client token = " + token + " 加入client佇列");
                notifyParticipate(client.mName,true);
            }else{
                Log.i(TAG, "join: client成員已存在");
            }
        }

        @Override
        public void leave(IBinder token) throws RemoteException {
            int index = findClient(token);
            if(index >= 0 ){
                Client client = mClient.get(index);
                //取消註冊
                client.mToken.unlinkToDeath( client,0);
                mClient.remove(client);
                Log.i(TAG, "leave: 刪除token = " + token);
                notifyParticipate(client.mName,false);
            }
        }

        @Override
        public List<String> getParticipators() throws RemoteException {
            int size = mClient.size();
            for(int i = 0 ; i < size ; i++){
                mList.add(mClient.get(i).mName);
            }
            return mList;
        }

        @Override
        public void registerParticipateCallback(IParticipateCallback cb) throws RemoteException {
            mRemoteCallbackList.register(cb);
        }

        @Override
        public void unregisterParticipateCallback(IParticipateCallback cb) throws RemoteException {
            mRemoteCallbackList.unregister(cb);
        }

    };

    //更新通知
    private void notifyParticipate(String mName, boolean joinOrLeave) {
        int length = mRemoteCallbackList.beginBroadcast();
        for(int i = 0 ; i < length ; i++){
            try {
                //通知回撥
                mRemoteCallbackList.getBroadcastItem(i).onParticipate(mName,joinOrLeave);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        mRemoteCallbackList.finishBroadcast();
    }

    private int findClient(IBinder token){
        int index = -1;
        int size = mClient.size();
        for(int i = 0 ; i < size ; i++){
            if(mClient.get(i).mToken == token){
                index = i;
            }
        }
        return index;
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.i(TAG, "onBind: 繫結服務" );
        return mBinder;
    }


    @Override
    public void onDestroy() {
        super.onDestroy();
        mRemoteCallbackList.kill();
    }

    private class Client implements Binder.DeathRecipient{

        IBinder mToken;
        String mName;

        public Client(IBinder token , String name){
            mToken = token;
            mName = name;
        }

        @Override
        public void binderDied() {
            //客戶端的異常退出
            int index = mClient.indexOf(this);
            if(index < 0){
                return;
            }
            Log.i(TAG, "binderDied: 異常退出的客戶端名字 = " + mName);
            mClient.remove(index);
        }
    }
}
在服務端的AndroidMinfest.xml中做一下配置
<!-- 將本地服務設定成遠端服務,設定可被其他程式呼叫-->
        <service
            android:name=".service.RemoteService"
            android:process=":remote"
            android:exported="true"
            >
            <intent-filter>
                 //此處Intent的action最好寫成“伺服器端包名.aidl檔名”的形式
                <action android:name="com.example.chenpeng.julyapplication.service.IRemoteService"/>
            </intent-filter>
        </service>
建立客戶端,一個不同的應用,首先將aidl檔案複製到工程中,位置內容必須原封不動,下面就是client的程式碼
public class MainActivity extends AppCompatActivity {

    private Button mBindServiceBtn,mMethodBtn;
    private IRemoteService mRemoteService;
    private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            mRemoteService = IRemoteService.Stub.asInterface(iBinder);
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mBindServiceBtn = findViewById(R.id.bindServiceBtn);
        mMethodBtn = findViewById(R.id.methodBtn);
        mBindServiceBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //通過Intent指定服務端的服務名稱和所在包,與遠端Service進行繫結
                //引數與伺服器端的action要一致,即"伺服器包名.aidl介面檔名"
                Intent intent = new Intent("com.example.chenpeng.julyapplication.service.IRemoteService");

                //Android5.0後無法只通過隱式Intent繫結遠端Service
                //需要通過setPackage()方法指定包名,否則啟動失敗
                intent.setPackage("com.example.chenpeng.julyapplication");

                bindService(intent,mServiceConnection,BIND_AUTO_CREATE);
            }
        });
        mMethodBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if(mRemoteService != null){
                    try {
                       int sum =  mRemoteService.add(2,4);
                        Toast.makeText(getApplicationContext(),String.valueOf(sum),Toast.LENGTH_SHORT).show();
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
    }
}

四.IntentService原理

參考這篇IntentService原理

相關文章