1. 程式人生 > >Android Studio下使用AIDL建立和使用遠端service

Android Studio下使用AIDL建立和使用遠端service

各位讀者朋友們大家好,好久沒來更新部落格了,今天心血來潮來寫一篇關於如何在AS下結合AIDL建立和使用遠端service。在此之前我先跟大家解釋下什麼是遠端service?以及為什麼要使用遠端service?相信不管對於安卓新手還是老手而言,對於service並不感到陌生,所以這裡就不跟討論其基礎概念和一些知識點了。所謂的遠端service意思其實就是提供一個獨立於某個app程序而建立的服務,這個服務可以提供給多個app共同使用,當然了,這些app必須擁有訪問這個遠端service的某種協議或者說介面,而這正是我們今天博文的另一個重點,就是AIDL,所謂的AIDL其實就是安卓介面定義語言,在本篇當中,它將用於提供一個我們訪問遠端service的一個介面。如果我的表述不是很清楚或者你完全聽不懂,那麼沒關係,我們通過程式碼和截圖一步步幫你搞懂我在本篇所要表達的內容,相信你能明白,因為本篇的內容很簡單。

一、"服務端"專案

按照國際慣例,我們先來開啟Android Studio並建立一個專案,專案名稱隨你定,這裡我就不演示了。然後在專案中建立一個service。下面我會貼出這個service的截圖和程式碼:


public class MyService extends Service {

    @Override
    public void onCreate() {
        super.onCreate();
        Log.i("mylog", "onCreate");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i("mylog","onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.i("mylog","onTestBind");
        return null;
    }


    @Override
    public boolean onUnbind(Intent intent) {
        Log.i("mylog","unBind");
        return super.onUnbind(intent);
    }

    @Override
    public void onDestroy() {
        Log.i("mylog","onDestroy");
        super.onDestroy();
    }

可以發現,這個自定義的service類中並沒有什麼特別的東西,都是些重寫的方法,當我們呼叫bindService方法時,就會呼叫onBind方法了,我們後面會通過logcat來跟蹤。我們往下繼續。。。。

接下來,我們為了讓這個service變為遠端的service,需要在AndroidManifest.xml檔案中註冊並加一個:reomote標識,具體程式碼如下:

 <service android:name=".service.MyService"
        android:process=":remote">

    </service>


然後,我們來開始本篇的重點,我們那就是使用AIDL來實現我們遠端service的使用,我們首先在AS下來建立一個AIDL檔案,右鍵app,然後new一個aidl檔案,如下圖:


然後輸入檔名,點選finish完成建立。請注意這裡的AIDL檔案所在的路徑(包名)要跟專案的包名保持一致。


下面我把我創建出來的AIDL檔案程式碼也貼出來,其實很簡單,因為我只寫了一個sayHelloWorld方法而已:

// IMyAidlService.aidl
package com.example.marktrace003.test;

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

interface IMyAidlService {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);


             void sayHelloWorld();
}


然後點選Bulid->Make project生成我們的java檔案。


這時你會發現我們已經可以呼叫這個介面並實現它的方法了。接下請注意,我們為了讓其他app能夠找到我們共享的遠端service,需要在服務中加入intent-filter標識,並指定我們的AIDL檔案路徑。也就是說AndroidManifest.xml檔案中的註冊程式碼部分將變為如下:

  <service android:name=".service.MyService"
        android:process=":remote">

        <intent-filter>
            <action android:name="com.example.marktrace003.test.IMyAidlService"/>
        </intent-filter>

    </service>

最後,我們完善下MyService這個類,實現sayHelloWorld方法,並在onBind方法中返回實現了這個方法的binder:

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

import com.example.marktrace003.test.IMyAidlService;

/**
 * Created by Marktrace003 on 2016/7/4.
 */
public class MyService extends Service {

    @Override
    public void onCreate() {
        super.onCreate();
        Log.i("mylog", "onCreate");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i("mylog","onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.i("mylog","onTestBind");
        return binder;
    }

    IMyAidlService.Stub binder = new IMyAidlService.Stub() {
        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

        }

        @Override
        public void sayHelloWorld() throws RemoteException {
            Log.i("mylog","Hello World");
        }
    };

    @Override
    public boolean onUnbind(Intent intent) {
        Log.i("mylog","unBind");
        return super.onUnbind(intent);
    }

    @Override
    public void onDestroy() {
        Log.i("mylog","onDestroy");
        super.onDestroy();
    }
}


二、“客戶端”專案

接下來,我們就可以來新建一個專案了,在這個專案中我們將訪問我們上面“服務端”專案的MyService,也就是實現遠端service的訪問。

同樣的,專案名由你來定,然後我們接下來要做的就是把我們編譯生成的那個AIDL java檔案拷貝到我們的這個新專案中,如何找到這個檔案呢?其實不難,在“服務端”專案中,我們切換到package模式下就能找到。


接著將我們的IMyAidlService檔案拷貝到我們的新專案即“客戶端”專案中,注意這裡檔案的存放路徑要跟“服務端”專案的存放路徑一致,也就是“客戶端”IMyAidlService的包名要跟服務端存放的包名相同,如下圖:


好了,接下來我們“客戶端”專案中新建一個Activity,在MainActivity中,我們將演示遠端訪問“服務端”專案的service,下面直接貼出這個MainActivity的程式碼:

public class MainActivity extends AppCompatActivity implements View.OnClickListener{

    private Button bind_btn;
    private IMyAidlService iMyAidlService;
    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
              iMyAidlService = IMyAidlService.Stub.asInterface(iBinder);

            try {

                iMyAidlService.sayHelloWorld();

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

        @Override
        public void onServiceDisconnected(ComponentName componentName) {

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

        bind_btn = (Button)findViewById(R.id.bind_btn);
        bind_btn.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.bind_btn:
                Intent intent = new Intent("com.example.marktrace003.test.IMyAidlService");
                intent.setPackage("com.example.marktrace003.test"); //Android5.0之後需要指定共享Service所在應用的應用包名,否則會拋異常
                bindService(intent,connection,BIND_AUTO_CREATE);
                break;
        }
    }
}
說明一下,由於我們已經匯入了AIDL檔案,所以我們直接可以使用這個介面,然後我們一開始定義ServiceConnection,當bindService時,我們傳入這個connection,觸發onServiceConnected方法,然後呼叫sayHelloWorld方法。請注意,我們這裡的Intent所傳入的這個action正是我們在前面“服務端”專案中的AndroidManifest.xml檔案中註冊service時加入的那個action,也正是因為這樣我們才能順利的找到這個遠端的service。那麼這裡還有一點需要說明的是,Google認為隱式呼叫這種呼叫方式不安全,由於Android5.0中已經禁用Intent隱式呼叫,所以採用隱式呼叫會丟擲異常,那麼為了保證我們的程式不會丟擲異常,需要在這裡指定一下包名,而包名自然就是我們“服務端”專案的那個包名了。

好了,接下來我們執行一下我們這個新建的“客戶端”專案。

點選一下繫結按鈕,按鈕佈局我沒給出,自己拖一個,我們主要在logcat中看下執行效果圖。


看看上圖,你會發現不僅打印出了Hello World,而且還呼叫了onBind方法,也就是說已經訪問到了我們那個遠端service了。哈哈,是不是很神奇。那麼我們本篇的內容到此為止也就結束了,有錯誤或遺漏之處還請廣大讀者指出,謝謝大家的閱讀!咱們下期見!