Binder與AIDL服務


Binder與AIDL服務

服務(Service)是Android系統中4個應用程序組件之一。服務主要用於兩個目的:后台運行和跨進程訪問。通過啟動一個服務,可以在不顯示界面的前提下在后台運行指定的任務,這樣可以不影響用戶做其他事情。通過AIDL服務可以實現不同進程之間的通信,這也是服務的重要用途之一。

跨進程訪問(AIDL服務)

Android系統中的進程之間不能共享內存,因此,需要提供一些機制在不同進程之間進行數據通信。Activity和Broadcast都可以跨進程通信,除此之外,還可以使用Content Provider進行跨進程通信。現在我們已經了解了4個Android應用程序組件中的3個(Activity、Broadcast和Content Provider)都可以進行跨進程訪問,另外一個Android應用程序組件Service同樣可以。這就是本節要介紹的AIDL服務。

什么是AIDL服務

本章前面的部分介紹了開發人員如何定制自己的服務(startService,bindService),但這些服務並不能被其他的應用程序訪問。為了使其他的應用程序也可以訪問本應用程序提供的服務,Android系統采用了遠程過程調用(Remote Procedure Call,RPC)方式來實現。與很多其他的基於RPC的解決方案一樣,Android使用一種接口定義語言(Interface Definition Language,IDL)來公開服務的接口。因此,可以將這種可以跨進程訪問的服務稱為AIDL(Android Interface Definition Language)服務。

建立AIDL服務的步驟:

建立AIDL服務要比建立普通的服務復雜一些,具體步驟如下:

(1)在Eclipse Android工程的Java包目錄中建立一個擴展名為aidl的文件。該文件的語法類似於Java代碼,但會稍有不同。詳細介紹見后面實例的內容。

(2)如果aidl文件的內容是正確的,ADT會自動生成一個Java接口文件(*.java)。

(3)建立一個服務類(Service的子類)。

(4)實現由aidl文件生成的Java接口。

(5)在AndroidManifest.xml文件中配置AIDL服務,尤其要注意的是,<action>標簽中android:name的屬性值就是客戶端要引用該服務的ID,也就是Intent類的參數值。

本例中將建立一個簡單的AIDL服務。這個AIDL服務只有一個getValue方法,該方法返回一個String類型的值。在安裝完服務后,會在客戶端調用這個getValue方法,並將返回值在TextView組件中輸出。建立這個AIDL服務的步驟如下:

(1)建立一個aidl文件。在Java包目錄中建立一個IMyService.aidl文件。IMyService.aidl文件的位置如圖所示。

clip_image002

IMyService.aidl文件的內容如下:

package com.anjoyo.aidl.remote;

interface IMyService 

{ 

    String getValue(); 

}

(2)編寫一個MyService類。MyService是Service的子類,在MyService類中定義了一個內嵌類(MyServiceImpl),該類是IMyService.Stub的子類。MyService類的代碼如下:

public class MyService extends Service  {   

    public class MyServiceImpl extends IMyService.Stub{  

        @Override  

        public String getValue() throws RemoteException{  

            return "AIDL遠程訪問";  

        }  

    }  

    @Override  

    public IBinder onBind(Intent intent) {          

        return new MyServiceImpl();  

    }  

}

在編寫上面代碼時要注意如下兩點:

IMyService.Stub是根據IMyService.aidl文件自動生成的,一般並不需要管這個類的內容,只需要編寫一個繼承於IMyService.Stub類的子類(MyServiceImpl類)即可。

onBind方法必須返回MyServiceImpl類的對象實例,否則客戶端無法獲得服務對象。

(3)在AndroidManifest.xml文件中配置MyService類,代碼如下:

<service android:name=".MyService" > 

    <intent-filter>   

        <action android:name="com.anjoyo.aidl.IMyService" /> 

    </intent-filter> 

</service>

其中"com.anjoyo.aidl.IMyService"是客戶端用於訪問AIDL服務的ID。

服務端寫完!!!

下面來編寫客戶端的調用代碼。首先新建一個Eclipse Android工程,並將自動生成的IMyService.java文件連同包目錄一起復制到工程的src目錄中,如圖所示。

調用AIDL服務首先要綁定服務,然后才能獲得服務對象,代碼如下:

傳遞復雜數據的AIDL服務

AIDL服務只支持有限的數據類型,因此,如果用AIDL服務傳遞一些復雜的數據就需要做更一步處理。AIDL服務支持的數據類型如下:

Java的簡單類型(int、char、boolean等)。不需要導入(import)。

String和CharSequence。不需要導入(import)。

List和Map。但要注意,List和Map對象的元素類型必須是AIDL服務支持的數據類型。不需要導入(import)。

AIDL自動生成的接口。需要導入(import)。

實現android.os.Parcelable接口的類。需要導入(import)。

其中后兩種數據類型需要使用import進行導入,將在本章的后面詳細介紹。

傳遞不需要import的數據類型的值的方式相同。傳遞一個需要import的數據類型的值(例如,實現android.os.Parcelable接口的類)的步驟略顯復雜。除了要建立一個實現android.os.Parcelable接口的類外,還需要為這個類單獨建立一個aidl文件,並使用parcelable關鍵字進行定義。具體的實現步驟如下:

(1)建立一個IMyService.aidl文件,並輸入如下代碼:

package com.example.day0108_aidl;

import com.example.day0108_aidl.Product;
interface IMyService
{
    String getValue(in Product p); 
    Product getProduct(); 
}

在編寫上面代碼時要注意如下兩點:

Product是一個實現android.os.Parcelable接口的類,需要使用import導入這個類。

如果方法的類型是非簡單類型,例如,String、List或自定義的類,需要使用in、out或inout修飾。其中in表示這個值被客戶端設置;out表示這個值被服務端設置;inout表示這個值既被客戶端設置,又被服務端設置。

(2)編寫Product類。該類是用於傳遞的數據類型,代碼如下:

package com.example.day0108_aidl;

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

public class Product implements Parcelable {

    private int id;
    private String name;
    @Override
    public int describeContents() {
        return 0;
    }
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(id);
        dest.writeString(name);
    }

    public Product(){

    } 
    public Product(Parcel in){
        this.id=in.readInt();
        this.name=in.readString();
    }
    
    public static final Parcelable.Creator<Product> CREATOR=new Creator<Product>() {
        
        @Override
        public Product[] newArray(int size) {
            return new Product[size];
        }
        
        @Override
        public Product createFromParcel(Parcel source) {
            return new Product(source);
        }
    };
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "Product [id=" + id + ", name=" + name + "]";
    }



}

在編寫Product類時應注意如下3點:

Product類必須實現android.os.Parcelable接口。該接口用於序列化對象。在Android中之所以使用Pacelable接口序列化,而不java.io.Serializable接口,是因為Google在開發Android時發現Serializable序列化的效率並不高,因此,特意提供了一個Parcelable接口來序列化對象。

在Product類中必須有一個靜態常量,常量名必須是CREATOR,而且CREATOR常量的數據類型必須是Parcelable.Creator。

在writeToParcel方法中需要將要序列化的值寫入Parcel對象。

(3)建立一個Product.aidl文件,並輸入如下內容:

parcelable Product;

建立AIDL服務的步驟(3)

(4)編寫一個MyService類,代碼如下:

package com.anjoyo.remote.aidl; 

//  AIDL服務類

package com.example.day0108_aidl;


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

public class MyService extends Service {
    class MyBinder extends IMyService.Stub{

    
        @Override
        public Product getProduct() throws RemoteException {
            Product p=new Product();
            p.setId(1);
            p.setName("測試");
            return p;
        }

        @Override
        public String getValue(Product p) throws RemoteException {
            p.setName(p.getName()+"我又回來了");
            return p.toString();
        }
        
    }
    @Override
    public IBinder onBind(Intent intent) {
        return new MyBinder();
    }

}

(5)在AndroidManifest.xml文件中配置MyService類,代碼如下:

<service android:name=".MyService" > 

    <intent-filter>   

        <action android:name="com.anjoyo.remote.aidl.IMyService" /> 

    </intent-filter> 

</service>

在客戶端調用AIDL服務的方法與第一個實例介紹的方法相同,首先將IMyService.java和Product.java文件復制到客戶端工程,然后綁定AIDL服務,並獲得AIDL服務對象,最后調用AIDL服務中的方法。完整的客戶端代碼如下:

package com.example.day0108_test;

import com.example.day0108_aidl.IMyService;
import com.example.day0108_aidl.Product;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
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.TextView;

public class MainActivity extends Activity {
    private TextView textView;
    private Button button;
    IMyService myService;
    private ServiceConnection conn = new ServiceConnection() {
        
        @Override
        public void onServiceDisconnected(ComponentName name) {
            
        }
        
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            myService=IMyService.Stub.asInterface(service);
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView=((TextView) findViewById(R.id.textView1));
        button=(Button) findViewById(R.id.button1);
        button.setOnClickListener(new OnClickListener() {
            
            @Override
            public void onClick(View v) {
                try {
                    Product p=new Product();
                    p.setId(2);
                    p.setName("測試");
                    textView.setText(myService.getProduct().toString()+myService.getValue(p).toString());
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });
        bindService(new Intent("com.yjy.service"), conn, Context.BIND_AUTO_CREATE);
    }



}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM