Android进程间通讯

  对于Android来说,它是基于Linux内核的移动操作系统,它的进程间通讯并不完全继承自Linux,相反,它有自己的进程间的通讯方式。
  IPC是Inter-Process Communication的缩写,含义为进程间通信或者跨进程通信,是指两个进程之间进行数据交换的过程。按照操作系统中的描述,线程是CPU执行的最小调度单元,同时线程是一种有限的系统资源。而进程一般指一个执行单元,在PC和移动设备上指一个程序或者一个应用。一个进程可以包含多个线程,且至少包含一个线程。在android里面,主线程也叫UI线程,在UI线程里才能操作界面元素。很多时候,一个进程中需要执行大量的耗时的任务,如果这些任务放在主线程中去执行就会造成界面无法响应,严重影响用户体验,在android中还有一个特殊化的名字叫做ANR,即应用无响应。
具体实现IPC的方式有很多种:

1. 使用Bundle

  我们知道,四大组件中的三大组件(Activity,Service,Receiver)都是支持在Intent中传递Bundle数据的,由于Bundle实现Parcelable接口,所以它可以方便地在不同的进程中传输。基于这一点,当我们在一个进程中启动了另一个进程的Activity、Service、Receiver,我们就可以在Bundle中附加我们需要传输给远程进程的信息并通过Intent发送出去。当然,我们传输的数据必须被序列化,比如基本数据类型、实现了Parcelable的对象,实现了Serializable的对象以及一些android支持的特殊对象,具体内容可以看Bundle这个类,就可以看到所有它支持的类型。Bundle不支持的类型我们无法通过进程间传递数据。

2. 使用文件共享

  共享文件也是一种不错的进程间的通信方式。两个进程通过读/写同一个文件来交换数据。比如A进程把数据写入文件,B进程通过读取这个文件来获取数据。我们在windows上,一个文件如果被加了排斥锁将会导致其他线程无法对其进行访问,包括读和写,而由于android系统基于Linux,使得其并发读/写可以没有限制的进行,甚至两个线程同时对一个文件进行写操作都是可以允许的,尽管这可能是出问题。通过文件交换数据很好使用,除了可以交换一些文本信息外,我们还可以序列化一个对象到文件系统的同时从另一个进程中恢复这个对象。反序列化得到的对象只是在内容上和序列化的对象是一样的,但它们本质上是两个对象。
  当然,SharePreferences是个特例,众所周知,SharePreferences是Android中提供的轻量级的存储方案,它通过键值对的方式来存储数据,在底层实现上它采用xml文件来存储键值对,每个应用的SharePreferences文件都可以在当前所在的data目录下查看到。一般来说,它的目录位于/data/data/package name/shared_prefs目录下,其中package name表示当前应用的包名。从本质上来说,SharePreferences也属于文件的缓存,因此在多进程模式下,系统对它的读/写就变的不可靠,当面对高并发的模式读/写访问,SharedPrefenerces有很大几率会丢失数据,因此,不建议在进程通信中使用SharedPreferences。

3. 使用Messenger

  Messager翻译为信使,顾名思义,通过它可以在不同进程间传递Message对象,在Message中放入我们需要传递的数据,就可以轻松地实现数据进程间传递了。Messenger是一种轻量级的IPC,它的底层实现是AIDL,从其两个构造方法中就可以明显看出AIDL的痕迹,不管是I Messenger还是Stub.asInterface,这种使用方法都表明它的底层是AIDL。

	public Messager(Handler target){
		mTarget = target.getIMessenger();
	}

	public Messenger(IBinder target){
		mTarget = IMessenger.Stub.asInterface(target);
	}

  Messenger的使用方法很简单,它对AIDL做了封装,使得我们可以更简单地进行进程间通信。同时,由于它一次处理一个请求,因此在服务端我们不用考虑线程同步的问题,这是因为在服务端中不存在并发执行的情形。实现一个Message 人有如下步骤,氛围客户端和服务端:

  1. 服务端进程
      首先,我们需要在服务端创建一个Service来处理客户端的链接请求,同时创建一个Handler ,并通过它来创建一个Messenger对象,然后在Service的onBind中返回这个Messenger对象底层的Binder即可。
  2. 客户端进程
      客户端进程中,首先要绑定服务端的Service,绑定成功后用服务端返回的IBinder对象创建一个Messenger,通过这个Messenger就可以向服务端发送消息了,发消息类型为Message对象。如果需要服务端能够回应客服端,就和服务端一样,我们还需要创建一个Handler并创建一个新的Messenger,并把这个Messenger对象通过Message的replyTo参数传递给服务端,服务端通过这个replyTo参数就可以回应客户端。
4. 使用AIDL

  Messager是以串行的方式来进行处理客户端发来的消息,如果大量的消息同时发送到服务器,服务端仍然只能一个个处理,如果有大量的并发请求,那么用Messenger显然就不太合适了。同时,Messenger的作用主要是为了传递消息,很多时候我们可能需要跨进程调用服务端的方法,这种情形下Messager就无法做到了,但是我们可以使用AIDL来实现跨进程的方法调用。AIDL也是Messenger的底层实现,因此Messenger本质上也是AIDL,只不过系统为我们做了封装,从而方便上层的调用而已。

  1. 服务端
      服务端首先要创建一个Service用来监听客户端的链接请求,然后创建一个AIDL文件,将暴露给客户的接口在这个AIDL文件中声明,最后在Service中实现这个AIDL接口即可。
  2. 客户端
      客户端所做的事情就要稍微简单一些,首先需要绑定服务端的Service,绑定成功后,将服务端返回的Binder对象转成AIDL接口所属的类型,接着就可以调用AIDL中的方法了。
  3. AIDL接口的创建
      首先先看AIDL接口的创建,我们创建了一个后缀名为AIDL的文件,在里面声明了一个接口和两个接口的方法
		//IBookManager.aidl
		package com.ryg.chapter_2.aidl;
		
		import com.ryg.chapter_2.aidl.Book;
		
		interface IBookManager{
			List<Book> getBookList();
			void addBook(in Book Book);
		}

  在AIDL并不是所有的数据都能使用的,AIDL支持一下数据类型的数据传输

  • 基本数据类型(int、long、char、boolean、double等)
  • String和CharSequence
  • List:只支持ArrayList,里面每个元素都必须能够被AIDL支持
  • MAP:只支持HashMap,里面每个元素都必须被AIDL支持,包括key和value
  • Parcelable:所有实现了Parcelable接口的对象
  • AIDL:所有实现接口本身也可以在AIDL文件中使用
      需要注意的是:如果AIDL文件中使用到了自定义Parcelable对象,则,必须要显示的用import进来,不管它们是否和当前的AIDL文件位于同一个包内。另一个需要注意的地方是,如果AIDL文件中用到自定义的Parcelable对象,那么必须新疆一个和它同名的AIDL文件,并在其中声明它为Parcelable类型。
    为了方便AIDL的开发,建议把所有的与AIDL相关的类和文件放在同一个包内,这样做的好处是当客户端是另一个应用时,我们可以直接把包复制到客户端工程中,避免文件丢失。
  1. 远程服务端Service的实现
      先创建一个Service,称为BookManagerService
	public class BookMangerService extends Service{
		private static final String TAG = "BMS";
		private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<Book>();
		private Binder mBinder = new IBOokManager.Stub(){
			@Override
			public List<Book> getBookList() throws RemoteException{
				return mBookList;
			}
			@Override
			public void addBook(Book book) throws RemoteException{
				mBookList.add(book);
			}
		}
		@Override
		public void onCreate(){
			super.onCreate();
			mBookList.add(new Book(1,"Android"));
			mBookList.add(new Book(2,"Java"));
		}
		@Override
		public IBinder onBind(Intent intent){
			return mBinder;
		}
	}

然后需要在XML中注册这个Service,如下所示:

	<service
		android:name=".aidl.BookManagerService"
		android:process=":remote"
	</service>

5.客户端的实现
  首先要绑定远程服务,绑定成功后将服务端返回的Binder对象转换成AIDL接口,然后通过这个接口就可以调用服务端的远程方法了

		public class BookManagerActivity extends Activity{
			private static final String TAG="BookManagerActivity";
			private ServiceConnection mConnetion = new ServiceConnection(){
				public void onServiceConnected(ComponectName className,IBinder service){
					IBookManager bookManager = IBookManager.Stub.asInterface(service);
					try{
						List<Book> list = bookManger.getBookList();
						getCanonicalName();
					}catch(RemoteException e){
						e.printStackTrace
					}
				}
				public void onServiceDisconneted(ComponentName className){
				}
			};
			@Override
			protected void onCreate(Bundle savedInstanceState){
				super.onCreate(savedInstanceState);
				setContentView(R.layout.activity_book_manager);
				Intent intent = new Intent(this,BookManagerService.class);
				bindService(intent,mConnection,Context.BIND_AUTO_CREATE);
			}
			@Override
			protected void onDestory(){
				unbindService(mConnection);
				super.onDestory();
			}
	}

以上就是一次初步完整的通过AIDL进行的IPC的过程。

5. 使用ContentProvider

  ContentProvider是android中专门用于不同应用间进行数据共享的方式。和Messager一样,底层同样使用了Binder,由此可见,Binder在Android系统中是何等重要。
  系统预制了很多ContentProvider,比如通讯录信息、日程表信息等,要跨进程访问这些信息,只需要通过ContentResolver的query、update、insert和delete方法即可。

6. 使用Socket

  这个更好理解,即通过远程网络链接进程间通信。

注意:

   一般来说,使用多进程会出现以下问题:

  1. 静态成员和单例模式完全失效。
  2. 线程同步机制完全失效。
  3. SharedPreferences的可靠性下降。
  4. Application会多次创建。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值