1. 程式人生 > >Android遠端服務AIDL的使用以及配合介面回撥使用

Android遠端服務AIDL的使用以及配合介面回撥使用

遠端服務的介面回撥

Android 開發中的一些場景需要用到程序間通訊,一般使用AIDL(Android Interface Definition

Language)。使用AIDL繫結一個遠端服務,遠端服務可以被其他應用繫結,繫結後可以使用定義在AIDL介面中的方法來進行一些操作,遠端服務會另外開啟一個程序。

1、簡單使用

  • 1.1 就像使用普通的服務一樣,首先要建立一個服務類的子類,繼承Service。

    RemoteService.java

package com.example.administrator.newpager.service;

import android.app.Service;
import
android.content.Intent; import android.os.IBinder; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.support.annotation.Nullable; import android.util.Log; import android.widget.Toast; /** * auther:wzy * date:2017/1/8 19 :52 * desc: */ public class RemoteService
extends Service {
private static final String TAG = "RemoteService"; private RemoteCallbackList<IMyAidlCallBack> mRemoteCallbackList = new RemoteCallbackList<>(); @Nullable @Override public IBinder onBind(Intent intent) { Log.i(TAG, "onBind: "); return
new LocalBinder(); } @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); } //Binder類 class LocalBinder extends IRemoteService.Stub { @Override public void refreshFromNet() throws RemoteException { doSomeThingHere(); } } }

這裡與普通服務子類建立的不同之處在於:

普通服務類中Activity需要與Service繫結時,會在onBind()方法裡返回一個IBinder的子類,這個子類只是 簡單方法,可以是Service子類中的方法,也可以繼承一個介面,實現需要重寫的方法。

這裡的IBinder類則需要繼承的是一個建立AIDL後自動生成的一個類,下面會說。

  • 1.2 建立AIDl ,在Android Studio 中直接有建立AIDL的選項

建立AIDL
編寫格式像這樣:

IRemoteService.java

package com.example.administrator.newpager.service;
import com.example.administrator.newpager.service.IMyAidlCallBack;

interface IRemoteService  {
    void refreshFromNet();
}

其中要注意,檔案中需要引用其他檔案的時候,一定要帶上如上的

import com.example.administrator.newpager.service.IMyAidlCallBack;

即使是在同一個包下

  • 1.3 建立好AIDL檔案後,執行IDE的ReBuild操作,這時候會生成一個Stub檔案,如樣例中則會生成IRemoteService.Stub檔案,這個檔案的內容細節先不去管,1.1中的Stub就是從這裡得到的。

Rebuild

  • 1.4 如1.1中程式碼所示,繼承IRemoteService.Stub 類,重寫裡面的方法,這些方法就是之前AIDL中定義的未實現的方法,實現這些方法很重要,其他應用或者Activity之類的繫結服務 ,就是為了呼叫這裡面的方法來執行一些必要操作。

  • 1.5 在 onBind()方法中返回 IRemoteService.Stub的子類物件。

  • 1.6 在需要呼叫服務中方法的地方,如一個應用的Activity,這個應用我們稱為客戶端,在Activity中,就像繫結普通服務一樣,執行mContext.bindService(intent,mServiceConnection,Context.BIND_AUTO_CREATE);

    這裡是得到服務物件的重要步驟,bindService是耗時的,因此非同步操作從回撥中獲取遠端服務物件。

    mServiceConnection :

    private ServiceConnection mServiceConnection = new ServiceConnection() {
          @Override
          public void onServiceConnected(ComponentName name, IBinder service) {
              mIRemoteService = IRemoteService.Stub.asInterface(service);
          }
    
          @Override
          public void onServiceDisconnected(ComponentName name) {
    
          }
      };
  • 1.7 繫結服務,bindService();

     private void bindService() {
    //        Intent intent = new Intent("com.example.administrator.newpager.REMOTESERVICE");
    //        intent.setComponent
    //                    (new ComponentName("com.example.administrator.newpager.service",
    //                        "com.example.administrator.newpager.service.RemoteService"));
          Intent intent = new Intent(mContext, RemoteService.class);
          mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
          showTextView();
      }

    繫結服務操作因Intent的不同,有多重繫結途徑。

  • 1.8 與普通服務開啟的操作一樣,這裡也需要 在manifest中需要進行配置。

    也由於使用場景不同,這裡可以配合Intent新增一些其他引數。

    <service
             <!-- 這裡可以新增一些Actioncategory-->
              android:name=".service.RemoteService">
    </service>

  • 1.9 在需要呼叫遠端服務中方法的地方呼叫。

2、遠端服務與介面回撥配合使用

​ 在有些場景中,呼叫方如其他應用,需要在程序在執行到某個階段執行呼叫方的一些操作,或者遠端服務在進行一些非同步操作的時候需要將結果回撥給呼叫方,這時候,使用介面回撥就能解決,Android中也提供了這樣的介面:RemoteCallbackList

  • 2.1 步驟與之前使用遠端服務類似,不同的是要多建立一個AIDL檔案,用於回撥的介面型別。

    // IMyAidlCallBack.aidl
    package com.example.administrator.newpager.service;
    
    interface IMyAidlCallBack {
       void callBack(int result);
    }
  • 2.2 在之前的AIDL檔案中新增這些

    package com.example.administrator.newpager.service;
    import com.example.administrator.newpager.service.IMyAidlCallBack;
    
    interface IRemoteService  {
        void refreshFromNet();
      //----新增----//
        void registerListener(IMyAidlCallBack callBack);
        void unRegisterListener(IMyAidlCallBack callBack);
        void doInBackGround();
       //-----------//
    }
  • 2.3 在之前的程式碼中

    package com.example.administrator.newpager.service;
    
    import android.app.Service;
    import android.content.Intent;
    import android.os.IBinder;
    import android.os.RemoteCallbackList;
    import android.os.RemoteException;
    import android.support.annotation.Nullable;
    import android.util.Log;
    import android.widget.Toast;
    
    /**
    * auther:wzy
    * date:2017/1/8 19 :52
    * desc: 
    */
    public class RemoteService extends Service {
     private static final String TAG = "RemoteService";
     private RemoteCallbackList<IMyAidlCallBack> mRemoteCallbackList = new RemoteCallbackList<>();
    
     @Nullable
     @Override
     public IBinder onBind(Intent intent) {
         Log.i(TAG, "onBind: ");
         return new LocalBinder();
     }
    
     @Override
     public void onCreate() {
         Log.i(TAG, "onCreate: ");
         super.onCreate();
     }
        //----------------新增------------------------//
      //執行耗時操作
     public void doInBackGround() {
         new Thread() {
             @Override
             public void run() {
                 long result = this.getId();
                 int count = 0;
                 Log.i(TAG, "mRemoteCallbackList: " +     mRemoteCallbackList+",mRemoteCallbackList.mCallBack:"+mRemoteCallbackList);
                 count = mRemoteCallbackList.beginBroadcast();
                 if (count == 0) {
                     return;
                 }
                 try {
                     for (int i = 0; i < count; i++) {
                         mRemoteCallbackList.getBroadcastItem(i).callBack((int) result);
                     }
                 } catch (RemoteException e) {
                     e.printStackTrace();
                 } finally {
                     mRemoteCallbackList.finishBroadcast();
                 }
             }
         }.start();
     }
        //------------------------------//
       @Override
     public int onStartCommand(Intent intent, int flags, int startId) {
         Log.i(TAG, "onStartCommand: ");
         return super.onStartCommand(intent, flags, startId);
     }
    //Binder類
     class LocalBinder extends IRemoteService.Stub {
    
         @Override
         public void refreshFromNet() throws RemoteException {
    
         }
    //-----------------新增-----------------------//
         @Override
         public void registerListener(IMyAidlCallBack callBack) throws RemoteException {
             Log.i(TAG, "callBack:" + callBack);
             Log.i(TAG, "mRemoteCallbackList:" + mRemoteCallbackList);
             if (mRemoteCallbackList == null) {
                 Toast.makeText(getApplicationContext(), "mRemoteCallbackList==null", Toast.LENGTH_SHORT).show();
                 Log.i(TAG, "mRemoteCallbackList==null");
                 return;
             }
             mRemoteCallbackList.register(callBack);
         }
    
         @Override
         public void unRegisterListener(IMyAidlCallBack callBack) throws RemoteException {
             mRemoteCallbackList.unregister(callBack);
         }
    
         @Override
         public void doInBackGround() throws RemoteException {
             RemoteService.this.doInBackGround();
         }
       //---------------------------------//
     }
    }

    上面新增的程式碼中比較關鍵的兩點有:註冊和反註冊遠端回撥介面,也就是將回調物件元素加入或移 除mRemoteCallbackList這個物件中的一個集合;執行回撥,將回調方法放在需要執行的地方,這裡模擬耗時操作,放在了執行緒執行結束的地方,mRemoteCallbackList.getBroadcastItem(i).callBack((int) result);

    需要注意的是:mRemoteCallbackList.finishBroadcast();這句必須執行,否則有可能會出現狀態不合法異常。

  • 2.3 使用它,在需要進行耗時操作,並且呼叫方需要獲取耗時操作的結果,再對其進行處理的地方;需要繼承IMyAidlCallBack.aidl自動生成的 IMyAidlCallBack.Stub類,並重寫裡面的方法。

  try {
              mIRemoteService.registerListener(new IMyAidlCallBack.Stub() {
                  @Override
                  public void callBack(final int result) throws RemoteException {
                      new Handler(Looper.getMainLooper()).post(new Runnable() {
                          @Override
                          public void run() {
                              textView.setText("result:" + result + "");
                          }
                      });
                  }
              });
          } catch (RemoteException e) {
              e.printStackTrace();
          }

執行結果:

這裡寫圖片描述