Android AIDL开发

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)
  • String
  • CharSequence
  • List   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<String>). 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<String,Integer> 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<Address> 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<String> hobbies;
	private Address addr;

	public static final Parcelable.Creator<Student> CREATOR = new Parcelable.Creator<Student>() {

		@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<String> getHobbies() {
		return hobbies;
	}

	public void setHobbies(List<String> 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<Address> CREATOR = new Parcelable.Creator<Address>() {

		@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<number;i++){
				sum += i;
			}

			return sum;
		}

		@Override
		public void say(int time, String message) throws RemoteException {

			Logger.i(TAG, "call say,time="+time+"**message="+message);

			Logger.i(TAG, "say "+message+ " from "+time);
		}

		@Override
		public Student getStudent(int id) throws RemoteException {

			Student stu = new Student();
			stu.setId(id);
			stu.setName("Ricky");
			stu.setHobbies(Arrays.asList("篮球","code","Music"));

			Address addr = new Address();
			addr.setProvince("北京");
			addr.setCity("北京市");
			addr.setDistrict("朝阳区");
			addr.setStreet("静安中心");
			addr.setPostcode(100010);

			stu.setAddr(addr);

			return stu;
		}

		@Override
		public List<Address> getAddress(int id) throws RemoteException {

			List<Address> list = new ArrayList<Address>();

			Address addr = new Address();
			addr.setProvince("北京");
			addr.setCity("北京市");
			addr.setDistrict("朝阳区");
			addr.setStreet("香河园");
			addr.setPostcode(100010);

			list.add(addr);

			return list;
		}

	};
}

在Manifest清单文件中注册Service

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.ricky.android.aidl"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="15"
        android:targetSdkVersion="17" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.ricky.android.aidl.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service android:name="com.ricky.android.aidl.protocal.MyService">
            <intent-filter >
                <action android:name="com.ricky.android.aidl"/>
            </intent-filter>

        </service>

    </application>

</manifest>

到这里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<Address> 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

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="${relativePackage}.${activityClass}"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/tv_status"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="16sp"
        />

    <Button
        android:id="@+id/bt_bind"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/bind" />

    <Button
        android:id="@+id/bt_unbind"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/unbind" />

    <Button
        android:id="@+id/bt_call"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/call" />

</LinearLayout>

运行界面如下:

OK,到这里已经完成了AIDL完整流程了,如有写的不对的地方还请多多指教,谢谢!

参考资料:

Android Developer AIDL:http://developer.android.com/guide/components/aidl.html

源码下载:http://download.csdn.net/detail/fx_sky/8233633

时间: 2024-12-17 17:02:58

Android AIDL开发的相关文章

Android艺术开发探索——第二章:IPC机制(下)

Android艺术开发探索--第二章:IPC机制(下) 我们继续来讲IPC机制,在本篇中你将会学习到 ContentProvider Socket Binder连接池 一.使用ContentProvider ContentProvider是Android中提供的专门用来不同应用之间数据共享的方式,从这一点来看,他天生就是适合进程间通信,和Messenger一样,ContentProvider的底层实现同样也是Binder,由此可见,Binder在Android系统中是何等的重要,虽然Conten

Android软件安全开发实践(下)

Android开发是当前最火的话题之一,但很少有人讨论这个领域的安全问题.本系列将分两期,探讨Android开发中常见的安全隐患和解决方案.第一期将从数据存储.网络通信.密码和认证策略这三个角度,带你走上Android软件安全开发实践之旅. 过去两年,研究人员已发现Android上的流行软件普遍存在安全缺陷或安全漏洞.漏洞频发的原因可能有很多,例如以下几种. 与一切都是集中管理的iOS相比,Android提供了一种开放的环境,在获得了灵活性.可以满足各种定制需求的同时,也损失了部分安全性. 开发

本人讲课时录制的Android应用开发技术教学视频

网盘地址:http://yun.baidu.com/pcloud/album/info?query_uk=1963923831&album_id=3523786484935252365 本人讲课时录制的视频,采用webex录制,视频文件内容相对较小30-50兆左右,1个视频文件平均大概有1个小时左右的时间,每个例子基本上从建立项目开始边做边讲. 由于讲课范围是Android应用开发技术,视频没涉及搭建环境,基础控件的使用等基础内容. 主要内容包括: 后台服务. 服务的绑定.服务和线程.远程服务和

Android应用开发企业级最佳实践

一:家林的话: APK是AF(Application Framework)和应用开发工程师共同智慧的结晶,APK的运行是AF和应用开发工程师开发的Code相互作用. 本课程依据和Android之父以及Google.三星.HTC中的Android团队合作的经验,力求从设计者的角度带领大家彻底洞悉AF,先从AF的架构和移植讲起,然后详细的以AMS.PMS.WMS的Code细致验证和深度剖析,最后以ANR的彻底剖析结束. 二:这个课程能带给您什么价值? 1, 有Android App开发经验应用软件开

Android AIDL使用详解

1.什么是aidl:aidl是 Android Interface definition language的缩写,一看就明白,它是一种android内部进程通信接口的描述语言,通过它我们可以定义进程间的通信接口 icp:interprocess communication :内部进程通信 2.既然aidl可以定义并实现进程通信,那么我们怎么使用它呢?文档/android-sdk/docs/guide/developing/tools/aidl.html中对步骤作了详细描述: --1.Create

Android企业级应用程序开发完整训练:精通Android商业级开发最佳实践的24堂课

从企业级商业实战的角度入手,24小时内通过23个动手实战案例,循序渐进的对Android商业级别的应用程序开发要点各个击破,依托于在多年的Android(6款完整的硬件产品和超过20款应用软件)开发和企业级培训经验(超过150期的次Android的企业内训和公开课),旨在在实务的基础之上帮助你完成任何复杂程序的高质量Android应用程序开发,让Android开发跟上想象的速度.最后,通过ActivityManagerService揭秘Android应用程序一切行为背后的核心根源,让你从此开发应

android aidl摘要

/*在Android中, 每个应用程序都有自己的进程,当需要在不同的进程之间传递对象时,该如何实现呢? 显然, Java中是不支持跨进程内存共享的.因此要传递对象, 需要把对象解析成操作系统能够理解的数据格式, 以达到跨界对象访问的目的.在JavaEE中,采用RMI通过序列化传递对象.在Android中, 则采用AIDL(Android Interface Definition Language:接口描述语言)方式实现. AIDL是一种接口定义语言,用于约束两个进程间的通讯规则,供编译器生成代码

[书目20160624]Android应用开发从入门到精通

卢海东 著 第1章 揭开神秘面纱——Android系统简介 1   1.1 认识Android系统 2   1.1.1 Android成长历程 2   1.1.2 发行版本 3   1.1.3 得到大家的认可——Android系统的市场份额 3   1.2 Android架构解析 4   1.2.1 Android系统架构图 4   1.2.2 应用程序(Applications) 5   1.2.3 应用程序框架层(Framework) 6   1.2.4 系统运行库(Libraries) 7

Android安卓开发环境搭建应用游戏项目实战知识体系_极客学院

Java是Android开发的主要语言,所以掌握Java语言基础非常重要,本阶段讲解了Java的基本语法,要深入掌握Java语言,可以通过Java学习路径图学习. 17课程 10小时 19分钟 1.Java编程基础知识入门:变量与数据类型 本课学习变量的命名.定义和初始化及整数.浮点.字符数据类型. 25课时,141分钟 626人学习 2.Java语言Switch语句详解 本课学习switch语句,switch语句是分支语句的一组,适用于判断同一变量的多种状态,进行流程控制. 5课时,17分钟