Android遠程服務編寫和調用教程

Android遠程服務編寫和調用教程

說明

網上汗牛充棟的文章都是介紹Android遠程服務的,一個個將Binder機制、AIDL講得頭頭是道,然而沒有幾個人能夠給出清晰的範例說明如何用最快的方法學會編寫和調用一個Android遠程服務。若你僅僅是想如何編寫或者調用Android的遠程服務,而懶得去理解Binder機制是如何運行的,那麼本篇文章正好適合你。畢竟現在人人都會開車,但沒有幾個人明白髮動機到底是如何運作的。

源代碼

代碼包在此,包括一個服務端和一個客戶端,無需下載分:
http://download.csdn.net/detail/logicteamleader/9407694

預備知識

讀者應該有基本的java知識,和Android簡單app的開發經驗。

環境

代碼運行環境:
1.ADT2014版本;
2.android:minSdkVersion=”8”;android:targetSdkVersion=”20”
3.workspace中已經生成了appcompatv7,它的版本是android-22;

遠程服務開發教程

在開始開發之前,先弄清楚幾個概念:
1. IPC:進程間通信,你只需要知道Android是依賴這個東西來進行遠程服務調用的就可以了。
2. Binder機制:Android發明的一種IPC機制,據說非常非常的好,你就當它是個黑盒子,通過這個黑盒子就可以進行遠程服務調用了,而且Android中的很多機制都是通過它實現的。
3. AIDL語言:一種專門用來寫遠程接口的語言,看它的名字就知道了,Android Interface Definition
Language。AIDL語言可以被android提供的編譯器編譯爲Java源代碼,這個Java源代碼將會被服務的和客戶端使用,用來簡化遠程服務開發流程。如果你當初玩過CORBA,那就更能明白什麼是IDL語言了
4. IInterface接口、IBinder接口、IBinder類等等:都是用來實現Binder機制的接口和類,在本教程中,你就當它們是Binder黑盒子的一部分,不需要了解。
再說一點,其實Android提供的ApiDemos中就有一個遠程服務的標準範例,但是其一是它沒有將服務端和客戶端分開寫,其二是例子中摻雜了太多其他的功能,因此理解起來較爲困難。這個例子是com.example.android.apis.app.RemoteService,有興趣的可以在看完本文後再去詳細研究。

第一步,創建一個普通Android應用

應用名爲WxbRemoteService,這個應用可以刪掉其Activity類,但是爲了簡單,我們就保留所有自動創建的代碼。

第二步,編寫AIDL

AIDL語言的語法和Java其實很像,你甚至可以先編寫一個Java接口,然後刪掉public、protected、private這些權限限定詞即可。例子如下IWxbService.aidl:

package com.dumaisoft.wxbremoteservice;

interface IWxbService {
    void setName(String name);
    String getName();
}

注意幾點:
1.接口名和aidl文件名相同。
2.接口和方法前不用加訪問權限修飾符public,private,protected等,也不能用final,static。
3.Aidl默認支持的類型包話java基本類型(int、long、boolean等)和(String、List、Map、 CharSequence),使用這些類型時不需要import聲明。對於List和Map中的元素類型必須是Aidl支持的類型。如果使用自定義類型作 爲參數或返回值,自定義類型必須實現Parcelable接口。
4.自定義類型和AIDL生成的其它接口類型在aidl描述文件中,應該顯式import,即便在該類和定義的包在同一個包中。
5.在aidl文件中所有非Java基本類型參數必須加上in、out、inout標記,以指明參數是輸入參數、輸出參數還是輸入輸出參數。
6.Java原始類型默認的標記爲in,不能爲其它標記
IWxbService.aidl文件的位置是在com.dumaisoft.wxbremoteservice包中,只要語法正確,則會在ADT的gen目錄下的com.dumaisoft.wxbremoteservice包中生成java文件IWxbService.java。
IWxbService.aidl定義了一個遠程接口,它包含兩個方法getName和setName。

第三步,編寫服務類

添加一個WxbService類,它繼承了Service類,源代碼如下:

package com.dumaisoft.wxbremoteservice;

import com.dumaisoft.wxbremoteservice.IWxbService.Stub;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;

public class WxbService extends Service {

    private ServiceImpl serviceImpl;

    //繼承由IWxbService.aidl生成的com.dumaisoft.wxbremoteservice.IWxbService.Stub類
    class ServiceImpl extends Stub{
        private String _name;
        @Override
        public void setName(String name) throws RemoteException {
            _name = name;
        }

        @Override
        public String getName() throws RemoteException {
            return _name;
        }
    }

    //將ServiceImpl做一個簡單的單例模式
    private ServiceImpl getInstance(){
        if(serviceImpl == null){
            serviceImpl = new ServiceImpl();
        }
        return serviceImpl;
    }

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

通過研究代碼可知,和普通的服務類相比,遠程服務類最大的區別就是它擁有一個名爲ServiceImpl的成員變量,這個成員變量繼承了Stub類,並實現了Stub類的getName和setName方法。這個Stub類就是由 IWxbService.aidl生成的IWxbService.java提供的。我們不用研究其源代碼,只用知道它的用法:
第一:讓Service的一個成員變量繼承Stub,並實現遠程接口的方法;
第二:在Service的onBind方法中返回一個Stub子類的實例。

第四步,配置AndroidManifest.xml

加上如下代碼:

        <service android:name="WxbService">
            <intent-filter>
                <action android:name="com.dumaisoft.wxbremoteservice.REMOTE_SREVICE"/>
            </intent-filter>
        </service>

注意action的name爲”com.dumaisoft.wxbremoteservice.REMOTE_SREVICE”,這個由開發者保證不重名即可。

第五步,安裝app到手機上

安裝完成後,你的遠程服務就被註冊到Binder黑盒子中了,任何客戶端只要知道你的遠程服務action名稱和接口,就可以bind服務,並調用接口。

遠程服務調用教程

第一步,創建一個android應用

應用名爲WxbRemoteServiceClient,src包中自動生成了com.dumaisoft.wxbremoteserviceclient包。

第二步,引入遠程服務的AIDL文件

在src包中創建com.dumaisoft.wxbremoteservice包(爲了與服務端的包名相同),然後將上面編寫的IWxbService.aidl文件拷貝入此目錄。顯然,在本工程的gen目錄中也生成了IWxbService.java文件。

第三步,編寫調用遠程服務的代碼

代碼如下:

package com.dumaisoft.wxbremoteserviceclient;

import com.dumaisoft.wxbremoteservice.IWxbService;

import android.app.Activity;
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.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends Activity {
    private Button btnBind;
    private Button btnSetName;
    private Button btnGetName;
    private IWxbService serviceProxy;   //遠程服務的代理
    private ServiceConnection conn = new ServiceConnection() {

        @Override
        public void onServiceDisconnected(ComponentName name) {     
        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //獲取遠程服務代理
            serviceProxy = IWxbService.Stub.asInterface(service);
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btnBind = (Button) this.findViewById(R.id.btnBind);
        btnSetName = (Button) this.findViewById(R.id.btnSetName);
        btnGetName = (Button) this.findViewById(R.id.btnGetName);
        btnBind.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent service = new Intent();
                //Remote Service Action name
                service.setAction("com.dumaisoft.wxbremoteservice.REMOTE_SREVICE");
                bindService(service, conn, Service.BIND_AUTO_CREATE);
            }
        });
        btnSetName.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    serviceProxy.setName("MyName");
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });
        btnGetName.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    String name = serviceProxy.getName();
                    Toast.makeText(getApplicationContext(), name, Toast.LENGTH_LONG).show();
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });      
    }
}

注意幾點:
一、創建一個ServiceConnection的匿名子類,在其onServiceConnected方法中獲取遠程服務代理對象serviceProxy。事實上,onServiceConnected方法會在bindService方法調用時被調用,因此能確保一定可以獲得遠程服務的代理對象;
二、IWxbService.Stub.asInterface(service)方法也是由IWxbService.java文件提供的,其內部機制不用研究,只需要知道它會返回一個IWxbService接口的對象,該對象可以通過Binder黑盒子調用遠程服務的setName和getName方法;
三、使用Intent指定action爲”com.dumaisoft.wxbremoteservice.REMOTE_SREVICE”,即可正確的bind到遠程服務。
四、bind成功後,就可以通過遠程服務的代理對象,使用遠程服務的功能了。

小結

至此,讀者應該能比較快速的開發出一個遠程服務,並能編寫客戶端輕鬆的調用它了。還有一點需要說明的是,除了使用AIDL來進行遠程服務的編寫和調用外,還可以直接使用IBinder、Binder等接口和類來進行遠程服務編寫調用。畢竟AIDL只是一種編寫binder框架的工具。
若真的想詳細瞭解binder機制,可以去詳細閱讀aidl生成的代碼,並閱讀其他大神的文章。

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