Android AIDL进程间通信(IPC)

简介:

IPC是Inter-Process Communication的缩写,意思是进程间通信。Android中IPC的方式有很多种,今天先说最灵活也是最常用的一种,即AIDL(Android Interface Definition Language)方式。

创建多进程的方式有两种,一,给四大组件指定android:process属性,二,通过JNI在native层fork一个新的进程。今天Demo以第一种为例,因为第二种我不会。为了方便查看此Demo没有写两个工程,而是在同一个应用内给SecondActivity 指定

android:process="com.dyk.aidltest.SecondActivity" 

这样就相当于SecondActivity是另一个应用的Activity。下面开始讲解Demo。



Demo下载


工程大纲:

首先新建一个实现了Parcelable的Book类,用作测试Bean。

package com.example.aidl;

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

public class Book implements Parcelable{

	public int bookId;
	public String bookName;

	public Book(int bookId, String bookName) {
		super();
		this.bookId = bookId;
		this.bookName = bookName;
	}

	@Override
	public int describeContents() {
		return 0;
	}

	/**序列化*/
	@Override
	public void writeToParcel(Parcel out, int flags) {
		out.writeInt(bookId);
		out.writeString(bookName);
	}
	
	private Book(Parcel source){
		bookId = source.readInt();
		bookName = source.readString();
	}
	
	/**反序列化*/
	public static final Creator<Book> CREATOR = new Creator<Book>() {

		@Override
		public Book createFromParcel(Parcel source) {
			
			return new Book(source);
		}

		@Override
		public Book[] newArray(int size) {
			return new Book[size];
		}
	};

}

接着新建需要的aidl文件

package com.example.aidl;

parcelable Book;

package com.example.aidl;

import com.example.aidl.Book;

interface IBookManager{
	List<Book> getBookList();
	void addBook(in Book book);
	
}

注意:Book必须实现Parcelable接口(序列化,反序列化),参数in代表输入型参数,out代表输出型参数,inout代表输入输出型参数,否则aidl不支持。就算Book和aidl文件在同一包下,也需要导包。如果AIDL用到了自定义的parcelable对象,则必须建立一个和它同名的aidl文件,并在其中声明它为parcelable类型。AIDL接口中只支持方法,不支持静态常量,这一点区别于传统的接口。

新建上述文件夹后可以在gen/package name目录下可以查找到系统自动为我们生成的IBookManager。乍一看这个类有点乱,format后可以发现这个类并没有想象的那么复杂。在此简单分析下这个类,不感兴趣的可以略过这一阶段,直接进入下一分割线。

--------------------------------------------------------------华丽丽的分割线--------------------------------------------

首先这个IBookManager继承了IInterface接口,里面有一个抽象类Stub和两个抽象方法。

public static abstract class Stub extends android.os.Binder implements com.example.aidl.IBookManager

<span style="white-space:pre">	</span>public java.util.List<com.example.aidl.Book> getBookList() throws android.os.RemoteException;

	public void addBook(com.example.aidl.Book book) throws android.os.RemoteException;
S tub里面有几个比较重要的方法:asInterface,asBinder,onTransact和一个代理类
private static class Proxy implements com.example.aidl.IBookManager 
asInterface:将服务端的Binder对象转换成客户端所需的AIDL接口类型的对象,这种对象区分进程,若在统一进程,返回服务端的Stub本身,否则返回封装后的Stub.proxy对象。

	if (((iin != null) && (iin instanceof com.example.aidl.IBookManager))) {
			return ((com.example.aidl.IBookManager) iin);
		}
	return new com.example.aidl.IBookManager.Stub.Proxy(obj);
asBinder:返回当前的Binder对象

onTransact:服务端通过参数code确定客户端请求的目标方法,接着从data中取出目标方法所需的参数,然后执行目标方法,最后向reply中写入返回值

<span style="white-space:pre">			</span>public boolean onTransact(int code, android.os.Parcel data,
				android.os.Parcel reply, int flags)
				throws android.os.RemoteException {
			switch (code) {
			case INTERFACE_TRANSACTION: {
				reply.writeString(DESCRIPTOR);
				return true;
			}
			case TRANSACTION_getBookList: {
				data.enforceInterface(DESCRIPTOR);
				java.util.List<com.example.aidl.Book> _result = this
						.getBookList();
				reply.writeNoException();
				reply.writeTypedList(_result);
				return true;
			}
			case TRANSACTION_addBook: {
				data.enforceInterface(DESCRIPTOR);
				com.example.aidl.Book _arg0;
				if ((0 != data.readInt())) {
					_arg0 = com.example.aidl.Book.CREATOR
							.createFromParcel(data);
				} else {
					_arg0 = null;
				}
				this.addBook(_arg0);
				reply.writeNoException();
				return true;
			}
			case TRANSACTION_registerListener: {
				data.enforceInterface(DESCRIPTOR);
				com.example.aidl.IOnNewBookArrivedListener _arg0;
				_arg0 = com.example.aidl.IOnNewBookArrivedListener.Stub
						.asInterface(data.readStrongBinder());
				this.registerListener(_arg0);
				reply.writeNoException();
				return true;
			}
			case TRANSACTION_unregisterListener: {
				data.enforceInterface(DESCRIPTOR);
				com.example.aidl.IOnNewBookArrivedListener _arg0;
				_arg0 = com.example.aidl.IOnNewBookArrivedListener.Stub
						.asInterface(data.readStrongBinder());
				this.unregisterListener(_arg0);
				reply.writeNoException();
				return true;
			}
			}
			return super.onTransact(code, data, reply, flags);
		}

-------------------------------------------------------------------华丽丽的分割线-------------------------------------------------------------------------

接下来就可以写我们的服务端了,这里使用一个service充当服务端。先上代码

package com.example.service;


import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import android.app.Service;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Log;
import com.example.aidl.Book;
import com.example.aidl.IBookManager.Stub;
import com.example.aidl.IOnNewBookArrivedListener;


public class MyService extends Service {


public static final String TAG = "MyService";

// CopyWriteArrayList支持并发读/写
// AIDL方法是在服务端的Binder线程池中执行的,
// 因此当多个客户端同时连接时,会存在多个线程同时访问的情形
// 类似的还有ConcurrentHashMap
/** 书籍列表 */
private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<Book>();
/** Stub继承Binder类实现了IBookManager接口 */
public class MyServiceImpl extends Stub {

@Override
public List<Book> getBookList() throws RemoteException {
for (int i = 0; i < 10; i++) {
Book mBook = new Book(i, "平凡的世界");
mBookList.add(mBook);
}
return mBookList;
}


@Override
public void addBook(Book book) throws RemoteException {
mBookList.add(book);
@Override
public IBinder onBind(Intent intent) {
return new MyServiceImpl();
}
}
在onBind中返回继承Stub的内部类MyServiceImpl。上文也提到过Stub继承Binder类实现了IBookManager接口并且本身是一个抽象类。所以非抽象子类需要重写getBookList()和addBook(Book book)方法。

服务器端暂时到此结束,接下来就到客户端了。

package com.example.aidltest;

import com.example.aidl.Book;
import com.example.aidl.IBookManager;
import com.example.aidl.IOnNewBookArrivedListener;

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.util.Log;
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 {

	public static final String TAG = "MainActivity";

	private Button bindService;
	private Button startSecondActivity;
	private TextView tvShow;

	private IBookManager mIBookManager;
	private ServiceConnection mServiceConnection = new ServiceConnection() {

		@Override
		public void onServiceDisconnected(ComponentName name) {
			//<span style="font-family: Arial, Helvetica, sans-serif;">onServiceDisconnected</span><span style="font-family: Arial, Helvetica, sans-serif;">和onServiceConnected方法都在UI线程中运行</span>
			//所以不要在它们里面直接调用服务器端的耗时方法
		}

		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			// service就是com.example.service.MyService$MyServiceImpl
			// Log.i(TAG,TAG+"******"+service.getClass().getName());
			
			// 面向接口编程
			mIBookManager = IBookManager.Stub.asInterface(service);
			//为了避免ANR下面的代码最好新开线程中运行,这里为了方便就不麻烦了
			try {
				// 父类调用子类方法
				tvShow.setText(mIBookManager.getBookList().toString());
				Book newBook = new Book(888,"监听测试***书籍");
				mIBookManager.addBook(newBook);
			} catch (RemoteException e) {
				e.printStackTrace();
			}
		}
	};
	
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		initView();
	}
	
	@Override
	protected void onDestroy() {
		
		//解除绑定
		unbindService(mServiceConnection);
		super.onDestroy();
	}

	private void initView() {
		bindService = (Button) findViewById(R.id.bindService);
		startSecondActivity = (Button) findViewById(R.id.startSecondActivity);
		tvShow = (TextView) findViewById(R.id.tvShow);
		bindService.setOnClickListener(this);
		startSecondActivity.setOnClickListener(this);
	}

	@Override
	public void onClick(View v) {
		switch (v.getId()) {
		case R.id.bindService:
			Intent intent = new Intent("com.example.service.MyService");
			// MyService类的onBind方法返回的是MyService的内部类MyServiceImpl
			boolean isBind = bindService(intent, mServiceConnection,Context.BIND_AUTO_CREATE);
			if (isBind) {
				Log.i(TAG, TAG + "********绑定成功***********");
			} else {
				Log.i(TAG, TAG + "********绑定失败***********");
			}
			break;
		case R.id.startSecondActivity:
			Intent intent2 = new Intent(MainActivity.this, SecondActivity.class);
			startActivity(intent2);
			break;
		default:
			break;
		}
	}

}
流程:A.bindService()--->S.onCreate--->S.onBind---->>A.onServiceConnected绑定成功,并获得Service对象

结束绑定按钮的监听事件-->>unbindService(conn)关闭连接对象-->>S.destory()销毁该service

PS:SecondActivity和Maintivity代码完全一样,只是在AndroidMainfest多了一个属性,可自行创建。

 android:process="com.dyk.aidltest.SecondActivity" 

点击“绑定服务”后效果如图:

可以看到已经获取到Book的信息了。


get。


客户端到调用服务器端的方法到此结束。如果有这么一个需求:服务端有数据变动需要立即通知客户端,那么应该怎么做呢?没错,接口。需要说明的是这里的接口不是普通的接口。因为AIDL中无法使用普通接口,所以我们需要写一个aidl接口。

package com.example.aidl;

import com.example.aidl.Book;

interface IOnNewBookArrivedListener{
	void onNewBookArrived(in Book newBook);
}

在IBookManaget里增加两个抽象方法
<span style="font-size:14px;">void registerListener(IOnNewBookArrivedListener listener);
	void unregisterListener(IOnNewBookArrivedListener listener);</span>
由于IBookManager增加两个抽象方法,所以相应的MyServiceImpl需要重写这两个方法。

<span style="white-space:pre">		</span>@Override
		public void registerListener(IOnNewBookArrivedListener listener)
				throws RemoteException {
			mListenerList.register(listener);
			Log.i(TAG, "register listener:" + listener);
		}

		@Override
		public void unregisterListener(IOnNewBookArrivedListener listener)
				throws RemoteException {
			// 其实就是从Map中移除相应的键值对(listener是key)
			mListenerList.unregister(listener);
			Log.i(TAG, "unregister listener:" + listener);
		}
对象是不能跨进程直接传输的,对象跨进程传输本质都是反序列化的过程。使用普通的ArrayList后Binder会把客户端传输过来的对象重新转化并生成一个对象,虽然我们注册和反注册使用的同一个客户端对象,但是这样通过Binder传递到服务器端后会产生两个全新的对象。使用RemoteCallbackList可以解决这个问题,具体优点如下:
<span style="white-space:pre">	</span>// RemoteCallbackList是系统专门提供的用于删除跨进程listener的接口
	// 当客户端进程终止后,它能自动移除客户端所注册的listener
	// 内部自动实现了线程同步的功能
	// 它不是list 内部采用Map接口保存AIDL的回调
	/** 监听器列表 */
	private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList = new RemoteCallbackList<IOnNewBookArrivedListener>();

在addBook中调用这个监听方法

			// 遍历通知每个监听者
			int len = mListenerList.beginBroadcast();
			for (int i = 0; i < len; i++) {
				IOnNewBookArrivedListener listener = mListenerList
						.getBroadcastItem(i);
				// 如果listener.onNewBookArrived(book)比较耗时,最好新开线程,防止ANR
				listener.onNewBookArrived(book);
			}
			mListenerList.finishBroadcast();
接下来在MainActivity或SecondActivity中实现这个接口,并重写两个方法registerListener和unregisterListener。

		IOnNewBookArrivedListener mIOnNewBookArrivedListener = new IOnNewBookArrivedListener.Stub() {
		
		@Override
		public void onNewBookArrived(Book newBook) throws RemoteException {
			//书籍增加后的做法
			Log.i(TAG, "newBook="+newBook.bookId+"\t"+newBook.bookName);
		}
	};
MainActivity或SecondActivity的onServiceConnected中注册监听

			//注册为监听者
			mIBookManager.registerListener(mIOnNewBookArrivedListener);

MainActivity或SecondActivity的onDestroy中反注册监听

		if (mIBookManager!=null&&mIBookManager.asBinder().isBinderAlive()) {
			try {
				//解除监听
				mIBookManager.unregisterListener(mIOnNewBookArrivedListener);
			} catch (RemoteException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

到这里基本算大功告成了,最后的最后还有一个小缺陷需要我们修复。默认情况下我们的服务器端谁都可以连接,这可不一定是我们想要的。没错,加入限权。

首先在AndroidMainfest中声明所需的限权

 <!-- 自定义MyService的限权,在OnBind中验证限权 -->
    <permission
        android:name="com.example.service.permission.MyService"
        android:protectionLevel="normal" >
    </permission>

<span style="white-space:pre">	</span><service
            android:name="com.example.service.MyService"
            android:permission="com.example.service.permission.MyService" >
            <intent-filter>
                <action android:name="com.example.service.MyService" />
            </intent-filter>
        </service>

<span style="white-space:pre">	</span><!-- 使用限权 -->
    <uses-permission android:name="com.example.service.permission.MyService" />

最后在onBinder中验证限权,没有限权返回空,无法绑定

    <span style="white-space:pre">	</span>@Override
	public IBinder onBind(Intent intent) {
		//限权验证 ,没有限权返回空,无法绑定
		int check  = checkCallingOrSelfPermission("com.example.service.permission.MyService");
		if (check==PackageManager.PERMISSION_DENIED) {
			return null;
		}
		return new MyServiceImpl();
	}

缕清思路加写作一共花了3个多小时,终于完成了!这一大套流程下来可真不容易,希望对你有所帮助。


Demo下载地址:http://download.csdn.net/detail/qq_17250009/9252577

相关文章
|
17天前
|
消息中间件 存储 Linux
|
1月前
|
消息中间件 Linux API
Linux c/c++之IPC进程间通信
这篇文章详细介绍了Linux下C/C++进程间通信(IPC)的三种主要技术:共享内存、消息队列和信号量,包括它们的编程模型、API函数原型、优势与缺点,并通过示例代码展示了它们的创建、使用和管理方法。
30 0
Linux c/c++之IPC进程间通信
|
1月前
|
消息中间件 存储 网络协议
操作系统的心脏:深入理解进程间通信(IPC)机制
在现代计算机系统中,操作系统扮演着至关重要的角色,而进程间通信(IPC)作为操作系统的核心功能之一,极大地影响着系统的性能和稳定性。本文将通过浅显易懂的语言,详细探讨进程间通信的基本原理、主要类型及其实际应用,旨在为读者提供一个清晰且全面的理解和认识。 ##
117 1
|
2月前
|
消息中间件 安全 Kafka
Python IPC机制全攻略:让进程间通信变得像呼吸一样自然
【9月更文挑战第12天】在编程领域,进程间通信(IPC)是连接独立执行单元的关键技术。Python凭借简洁的语法和丰富的库支持,提供了多种IPC方案。本文将对比探讨Python的IPC机制,包括管道与消息队列、套接字与共享内存。管道适用于简单场景,而消息队列更灵活,适合高并发环境。套接字广泛用于网络通信,共享内存则在本地高效传输数据。通过示例代码展示`multiprocessing.Queue`的使用,帮助读者理解IPC的实际应用。希望本文能让你更熟练地选择和运用IPC机制。
57 10
|
2月前
|
消息中间件 Python
深入理解操作系统的进程间通信(IPC)机制
本文将探讨操作系统中的核心概念——进程间通信(IPC),揭示其在系统运作中的重要性及实现方式。通过分析不同类型的IPC手段,如管道、信号、共享内存等,帮助读者更好地理解操作系统的内部工作原理及其在实际应用中的表现。
132 1
|
2月前
|
Java Android开发 数据安全/隐私保护
Android中多进程通信有几种方式?需要注意哪些问题?
本文介绍了Android中的多进程通信(IPC),探讨了IPC的重要性及其实现方式,如Intent、Binder、AIDL等,并通过一个使用Binder机制的示例详细说明了其实现过程。
312 4
|
2月前
|
安全 开发者 Python
Python IPC大揭秘:解锁进程间通信新姿势,让你的应用无界连接
【9月更文挑战第11天】在编程世界中,进程间通信(IPC)如同一座无形的桥梁,连接不同进程的信息孤岛,使应用无界而广阔。Python凭借其丰富的IPC机制,让开发者轻松实现进程间的无缝交流。本文将揭开Python IPC的神秘面纱,介绍几种关键的IPC技术:管道提供简单的单向数据传输,适合父子进程间通信;队列则是线程和进程安全的数据共享结构,支持多进程访问;共享内存允许快速读写大量数据,需配合锁机制确保一致性;套接字则能实现跨网络的通信,构建分布式系统。掌握这些技术,你的应用将不再受限于单个进程,实现更强大的功能。
66 6
|
2月前
|
Python
惊!Python进程间通信IPC,让你的程序秒变社交达人,信息畅通无阻
【9月更文挑战第13天】在编程的世界中,进程间通信(IPC)如同一场精彩的社交舞会,每个进程通过优雅的IPC机制交换信息,协同工作。本文将带你探索Python中的IPC奥秘,了解它是如何让程序实现无缝信息交流的。IPC如同隐形桥梁,连接各进程,使其跨越边界自由沟通。Python提供了多种IPC机制,如管道、队列、共享内存及套接字,适用于不同场景。通过一个简单的队列示例,我们将展示如何使用`multiprocessing.Queue`实现进程间通信,使程序如同社交达人般高效互动。掌握IPC,让你的程序在编程舞台上大放异彩。
23 3
|
2月前
|
消息中间件 Kafka 数据安全/隐私保护
Python IPC实战指南:构建高效稳定的进程间通信桥梁
【9月更文挑战第11天】在软件开发中,随着应用复杂度的提升,进程间通信(IPC)成为构建高效系统的关键。本文通过一个分布式日志处理系统的案例,介绍如何使用Python和套接字实现可靠的IPC。案例涉及定义通信协议、实现日志发送与接收,并提供示例代码。通过本教程,你将学会构建高效的IPC桥梁,并了解如何根据需求选择合适的IPC机制,确保系统的稳定性和安全性。
53 5
|
2月前
|
消息中间件 Unix
操作系统的心脏:深入理解进程间通信(IPC)
在现代计算中,操作系统扮演着至关重要的角色,它不仅管理着硬件资源,还负责协调和优化应用程序之间的交互。本文将深入探讨操作系统中的一个核心概念——进程间通信(IPC),揭示其背后的机制以及在实际应用中的重要性。通过通俗易懂的语言和条理清晰的解释,本文旨在为读者提供一个关于IPC的全面了解,从基本定义到高级应用,带领大家走进操作系统的神秘世界。

相关实验场景

更多