Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android AIDL開發

Android AIDL開發

編輯:關於Android編程

Introduction

在Android中, 每個應用程序都運行在自己的進程中,擁有獨立的內存空間。但是有些時候我們的應用程序需要跟其它的應用程序進行通信,這個時候該怎麼辦呢?顯然, Java中不允許跨進程內存共享.無法直接交換數據。Android中可以采用AIDL的方式實現進程間通信(interprocess communication(IPC))。

Android Developer原文介紹如下:AIDL (Android Interface Definition Language) is similar to other IDLs you might have worked with. It allows you to define the programming interface that both the client and service agree upon in order to communicate with each other using interprocess communication (IPC). On Android, one process cannot normally access the memory of another process. So to talk, they need to decompose their objects into primitives that the operating system can understand, and marshall the objects across that boundary for you. The code to do that marshalling is tedious to write, so Android handles it for you with AIDL.

 

Scenario (應用場景)

當且僅當你需要讓來自不同應用程序的客戶端通過你的Service實現IPC並且希望在你的Service中處理多線程問題,你才需要使用AIDL。

Android Developer原文介紹如下:

Note: Using AIDL is necessary only if you allow clients from different applications to access your service for IPC and want to handle multithreading in your service. If you do not need to perform concurrent IPC across different applications, you should create your interface by implementing a Binder or, if you want to perform IPC, but do not need to handle multithreading, implement your interface using a Messenger. Regardless, be sure that you understand Bound Services before implementing an AIDL.

 

線程阻塞與安全問題

Before you begin designing your AIDL interface, be aware that calls to an AIDL interface are direct function calls. You should not make assumptions about the thread in which the call occurs. What happens is different depending on whether the call is from a thread in the local process or a remote process. Specifically:

Calls made from the local process are executed in the same thread that is making the call. If this is your main UI thread, that thread continues to execute in the AIDL interface. If it is another thread, that is the one that executes your code in the service. Thus, if only local threads are accessing the service, you can completely control which threads are executing in it (but if that is the case, then you shouldn't be using AIDL at all, but should instead create the interface by implementing a Binder).Calls from a remote process are dispatched from a thread pool the platform maintains inside of your own process. You must be prepared for incoming calls from unknown threads, with multiple calls happening at the same time. In other words, an implementation of an AIDL interface must be completely thread-safe.The oneway keyword modifies the behavior of remote calls. When used, a remote call does not block; it simply sends the transaction data and immediately returns. The implementation of the interface eventually receives this as a regular call from the Binder thread pool as a normal remote call. If oneway is used with a local call, there is no impact and the call is still synchronous.

 

Defining an AIDL Interface

你需要按照Java編程語言的語法來定義你的AIDL接口並將其保存到後綴名為 .aidl 的文件中。.aidl 的文件必須保存在 主應用和Client應用的src目錄下。當你的應用程序中包含 .aidl時,Android SDK tools會根據 .aidl的定義在應用程序的gen/ 目錄下自動產生IBinder接口。主應用程序中的Service需要實現IBinder接口,然後Client應用就可以綁定到此Service並通過IBinder對象實現IPC。

 

AIDL實戰

充分了解上述知識之後,就可以開始動手實現AIDL接口並實現進程間通信了。

為了讓大家更好的理解先展示一下整個工程的目錄結構

\

 

1.創建 .aidl 文件

你必須按照Java的語法結構來構建.aidl 文件,每一個.aidl 文件只能定義一個接口。

AIDL默認支持一下數據類型:

 

All primitive types in the Java programming language (such as int, long, char, boolean, and so on)StringCharSequenceList All elements in the List must be one of the supported data types in this list or one of the other AIDL-generated interfaces or parcelables you've declared. A List may optionally be used as a generic class (for example, List). The actual concrete class that the other side receives is always an ArrayList, although the method is generated to use the List interface.Map All elements in the Map must be one of the supported data types in this list or one of the other AIDL-generated interfaces or parcelables you've declared. Generic maps, (such as those of the form Map are not supported. The actual concrete class that the other side receives is always a HashMap, although the method is generated to use the Map interface.

 

如果你要使用除了上述的其它類型對象,你必須通過 import語句來導入,即使它和你在.aidl 文件中定義的包名一致。

下面是.aidl 示例文件

 

// IRemoteService.aidl
package com.ricky.android.aidl.protocal;

import com.ricky.android.aidl.model.Student;
import com.ricky.android.aidl.model.Address;

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

/** Example service interface */
interface IRemoteService {
    /** Request the process ID of this service, to do evil things with it. */
    int getPid();

    /** Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    int sum(int number, String process);
    
    void say(int time, String message);
    
    Student getStudent(int id);
    
    List getAddress(int id);
}

 

AIDL接口需要傳遞Student對象和Address對象,二者都實現了Parcelable接口,代碼分別如下:

Student.java

 

package com.ricky.android.aidl.model;

import java.util.List;
import android.os.Parcel;
import android.os.Parcelable;

public class Student implements Parcelable {
	private int id;
	private String name;
	private List hobbies;
	private Address addr;
	
	public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {

		@Override
		public Student createFromParcel(Parcel in) {
			// TODO Auto-generated method stub
			return new Student(in);
		}

		@Override
		public Student[] newArray(int size) {
			// TODO Auto-generated method stub
			return new Student[size];
		}
		
	};
	
	public Student(){
		
	}
	
	protected Student(Parcel in){
		readFromParcel(in);
	}

	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;
	}

	public List getHobbies() {
		return hobbies;
	}

	public void setHobbies(List hobbies) {
		this.hobbies = hobbies;
	}

	public Address getAddr() {
		return addr;
	}

	public void setAddr(Address addr) {
		this.addr = addr;
	}

	@Override
	public int describeContents() {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public void writeToParcel(Parcel out, int flags) {
		out.writeInt(id);
		out.writeString(name);
		out.writeList(hobbies);
		out.writeParcelable(addr, 0);
	}
	
	@SuppressWarnings(unchecked)
	public void readFromParcel(Parcel in) {
        id = in.readInt();
        name = in.readString();
        hobbies = in.readArrayList(getClass().getClassLoader());
        addr = in.readParcelable(getClass().getClassLoader());
    }

	@Override
	public String toString() {
		return Student [id= + id + , name= + name + , hobbies= + hobbies
				+ , addr= + addr + ];
	}
	
}

Address.java

 

 

package com.ricky.android.aidl.model;

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

public class Address implements Parcelable {
	private String province;
	private String city;
	private String district;
	private String street ;
	private int postcode;
	
	public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {

		@Override
		public Address createFromParcel(Parcel in) {
			
			return new Address(in);
		}

		@Override
		public Address[] newArray(int size) {
			
			return new Address[size];
		}
		
	};
	
	public Address(){
		
	}
	
	public Address(Parcel in){
		readFromParcel(in);
	}
	
	public String getProvince() {
		return province;
	}
	public void setProvince(String province) {
		this.province = province;
	}
	public String getCity() {
		return city;
	}
	public void setCity(String city) {
		this.city = city;
	}
	public String getDistrict() {
		return district;
	}
	public void setDistrict(String district) {
		this.district = district;
	}
	public String getStreet() {
		return street;
	}
	public void setStreet(String street) {
		this.street = street;
	}
	public int getPostcode() {
		return postcode;
	}
	public void setPostcode(int postcode) {
		this.postcode = postcode;
	}
	
	@Override
	public String toString() {
		return Address [province= + province + , city= + city
				+ , district= + district + , street= + street
				+ , postcode= + postcode + ];
	}
	
	@Override
	public int describeContents() {
		// TODO Auto-generated method stub
		return 0;
	}
	
	@Override
	public void writeToParcel(Parcel out, int flags) {
		out.writeString(province);
		out.writeString(city);
		out.writeString(district);
		out.writeString(street);
		out.writeInt(postcode);
	}
	
	public void readFromParcel(Parcel in) {
		province = in.readString();
		city = in.readString();
		district = in.readString();
		street = in.readString();
		postcode = in.readInt();
    }
}


 

注意:.aidl 文件必須存放在.aidl 文件中定義的包名目錄下,例如本例中package com.ricky.android.aidl.protocal;

 

2.實現IBinder接口

 

 

package com.ricky.android.aidl.protocal;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import com.ricky.android.aidl.model.Address;
import com.ricky.android.aidl.model.Student;
import com.ricky.android.aidl.util.Logger;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.Process;
import android.os.RemoteException;

public class MyService extends Service {

	private static final String TAG = MyService.class.getSimpleName();

	@Override
	public void onCreate() {

		Logger.i(TAG, onCreate);

		super.onCreate();
	}

	@Override
	public IBinder onBind(Intent intent) {

		Logger.i(TAG, onBind);

		return mBinder;
	}

	private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {

		@Override
		public int getPid() throws RemoteException {
			
			return Process.myPid();
		}

		@Override
		public int sum(int number, String process) throws RemoteException {
			
			Logger.i(TAG, call sum,number=+number+**process=+process);
			
			int sum = 0;
			for(int i=0;i getAddress(int id) throws RemoteException {
			
			List list = new ArrayList();
			
			Address addr = new Address();
			addr.setProvince(北京);
			addr.setCity(北京市);
			addr.setDistrict(朝陽區);
			addr.setStreet(香河園);
			addr.setPostcode(100010);
			
			list.add(addr);
			
			return list;
		}

	};
}

在Manifest清單文件中注冊Service

 

 




    

    
        
            
                

                
            
        
        
        
            
                
            
            
        
        
    




 

到這裡AIDL的Server端已經完成了,Client通過綁定MyService獲取到IBinder對象就可以實現 IPC了。

 

There are a few rules you should be aware of when implementing your AIDL interface:

Incoming calls are not guaranteed to be executed on the main thread, so you need to think about multithreading from the start and properly build your service to be thread-safe.By default, RPC calls are synchronous. If you know that the service takes more than a few milliseconds to complete a request, you should not call it from the activity's main thread, because it might hang the application (Android might display an Application is Not Responding dialog)—you should usually call them from a separate thread in the client.No exceptions that you throw are sent back to the caller.

 

 

3.暴露接口給Client調用

3.1.新建客戶端Application,結構如下:

\

 

3.2 引入AIDL文件以及相關的類,直接從服務端裡代碼copy過來就OK。

 

3.3 綁定遠程Service

 

package com.ricky.android.aidlclient;

import java.util.List;

import com.ricky.android.aidl.model.Address;
import com.ricky.android.aidl.model.Student;
import com.ricky.android.aidl.protocal.IRemoteService;
import com.ricky.android.aidlclient.util.Logger;

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;
import android.widget.Toast;

public class MainActivity extends Activity implements OnClickListener {

	protected static final String TAG = null;
	private Button bt_bind;
	private IRemoteService mIRemoteService;
	private Button bt_unbind;
	private TextView tv_status;
	
	private boolean isBind;
	private Button bt_call;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		findViewById();
		setListener();
		processLogic();
	}

	private void findViewById() {
		
		bt_bind = (Button) findViewById(R.id.bt_bind);
		bt_unbind = (Button) findViewById(R.id.bt_unbind);
		bt_call = (Button) findViewById(R.id.bt_call);
		
		tv_status = (TextView) findViewById(R.id.tv_status);
	}

	private void setListener() {
		bt_bind.setOnClickListener(this);
		bt_unbind.setOnClickListener(this);
		bt_call.setOnClickListener(this);
	}
	
	private void processLogic() {
		
		tv_status.setText(Unbinding);
	}
	
	private ServiceConnection mConnection = new ServiceConnection() {

		// Called when the connection with the service is established
		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			
			Logger.i(TAG, onServiceConnected);
			
			// Following the example above for an AIDL interface,
	        // this gets an instance of the IRemoteInterface, which we can use to call on the service
	        mIRemoteService = IRemoteService.Stub.asInterface(service);
		}

		// Called when the connection with the service disconnects unexpectedly
		@Override
		public void onServiceDisconnected(ComponentName name) {
			
			Logger.i(TAG, onServiceDisconnected);
			
			mIRemoteService = null;
		}
	};

	@Override
	public void onClick(View v) {
		switch (v.getId()) {
		case R.id.bt_bind:
			
			bindService(new Intent(com.ricky.android.aidl),
                    mConnection, Context.BIND_AUTO_CREATE);
			
			isBind = true;
			tv_status.setText(Binding);
			
			break;

		case R.id.bt_unbind:
			if (isBind) {
				try {
					unbindService(mConnection);
					
					isBind = false;
					tv_status.setText(Unbinding);
					
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			
			break;
			
		case R.id.bt_call:
			if (isBind) {
				
				try {
					int pid = mIRemoteService.getPid();
					
					Logger.i(TAG, call mIRemoteService's getPid pid=+pid);
					
					mIRemoteService.say(1, hello world);
					
					int sum = mIRemoteService.sum(100, ricky);
					
					Logger.i(TAG, call mIRemoteService's sum result=+sum);
					
					Student stu = mIRemoteService.getStudent(3);
					
					Logger.i(TAG, call mIRemoteService's getStudent stu=+stu);
					
					List list = mIRemoteService.getAddress(2);
					
					Logger.i(TAG, call mIRemoteService's getAddress list=+list);
					
				} catch (RemoteException e) {
					e.printStackTrace();
				}
			}else{
				Logger.i(TAG, not bind remote service);
				Toast.makeText(getApplicationContext(), not bind remote service,Toast.LENGTH_SHORT).show();
			}
			break;
		default:
			break;
		}
	}
	
	@Override
	protected void onDestroy() {
		
		if (isBind) {
			try {
				unbindService(mConnection);
				
				isBind = false;
				tv_status.setText(Unbinding);
				
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		
		super.onDestroy();
	}
}


 

通過 Context.bindService(Intent service, ServiceConnection conn, int flags);方法綁定遠程Service,然後通過 IRemoteService com.ricky.android.aidl.protocal.IRemoteService.Stub.asInterface(IBinder obj);方法獲取到 IRemoteService ,此時Client 就可以通過獲取到的IRemoteService 對象進行IPC了。

 

activity_main.xml

 



    

 

 

 

運行界面如下:

\
 

 

 

\

 

 

 

 

 

 

 

 

 

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved