Android开发艺术探究(二):IPC机制

xiaoxiao2021-02-28  115

1. Android IPC简介

1.1. 简介

IPCInter-Process Communication,进程间通信或者跨进程通信。

进程:一般指一个执行单元,Android中一般指一个程序或者一个应用

线程:是CPU调度的最小单元。

一个进程可以有多个线程,也可以只有一个线程,也就是UI线程,即主线程。

Android中,只有在UI线程中才能操作界面元素,耗时操作不要放到UI线程中,很容易引起ANR

Android not responding),即程序无响应。

注:造成ANR的场景有哪些?

u Service Timeout:服务在20s内未执行完成。(前台服务:20s,后台服务200su BroadcastQueue Timeout:比如前台广播10秒内未执行完成u ContentProvider Timeout:内容提供者执行超时u InputDispatching Timeout:事件分发5秒内未完成。

 

1.2. 使用多进程的情况

1.2.1. 应用自身在某些情况下需要使用多进程

如:某些模块由于特殊原因需要运行在单独的进程中,或者为了加大程序可使用的内存(Android中对单个应用所使用的最大内存做了限制)

当然为了加大程序所有内存也可以通过设置android:largeheaptrue

1.2.2. 当前应用需要向其他应用调取数据

2. Android中的多进程模式

2.1. 开启多进程模式

正常情况下,Android中的多进程是指一个应用中使用多进程,因此,对于应用间的通信暂不做讨论。

开启多进程模式只有一种方法,即在四大组件ActivityServiceBroadcastReceiverContentProvider”在

AndroidManifest.xml中指定android:process属性。

默认情况下:android:process属性的属性值为当前应用的包名。

 

SecondActivity:指定的“:remote”省略了包名,全写为“com.ryg.chapter_2:remote”,它是当前应用的私有进程,其他应用的组件不可以跑到同一个进程中。

ThirdActivity:指定“com.ryg.chapter_2.remote,不以“:”开头,它是一个全局进程。其他应用可以通过ShareUID的方式跑到同一个进程中。

一个应用启动另一个应用的方式:

 

 

2.2. 多进程模式引发的问题

每一个进程,系统会为它分配一块独立的内存空间,或者说分配一个独立的虚拟机。不同的虚拟机访问同一个类的对象会产生多个副本。

l 静态成员和单例模式完全失效线程同步机制完全失效 - synchronizedl SharedPreferences的可靠性下降l Application会多次创建

 

3. IPC基础概念

3.1. Serializable接口

Java提供的实现序列化与反序列的空接口,只需让实体类实现Serializable这个接口,并声明一个serialVersionUID,如果不声明serialVersionUID会对反序列化造成影响。

3.2. Parcelable接口

Parcelable是由Android提供的序列化与反序列化接口,需要实现Parcelable接口,并重写writeToParcel

describeContents、构造函数(传入Parcel)、CREATOR方法。

 

package com.lance.artinquiry.aidl; import android.os.Parcel; import android.os.Parcelable; public class Book implements Parcelable{ private int boodId; private String bookName; public Book(int boodId, String bookName) { this.boodId = boodId; this.bookName = bookName; } protected Book(Parcel in) { boodId=in.readInt(); bookName=in.readString(); } public static final Creator<Book> CREATOR = new Creator<Book>() { @Override public Book createFromParcel(Parcel in) { return new Book(in); } @Override public Book[] newArray(int size) { return new Book[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel out, int flags) { out.writeInt(boodId); out.writeString(bookName); } }

Parcelable接口方法介绍:

 

3.3. SerializableParcelable的区别

两者都可以实现序列化并且都可以用于Intent之间的数据传递。SerializableJava的序列化接口,用起来简单但是开销大。ParcelableAndroid的序列化接口,用起来稍微麻烦,但是效率高。推荐使用。Parcelable主要用在内存序列化上,如果将对象序列化到存储设备或者通过网络传输的话,推荐使用Serializable

3.4. Binder

Binder是一个很深入的话题。

只管来说,BinderAndroid的一个类,它实现了IBinder接口。

IPC角度来说,BinderAndroid的一种跨线程通信方式,Binder还可以理解成一种虚拟的物理设备,它的设备驱动是/dev/binder,该通信设备在Linux上没有;

Android Framework角度来说,BinderServiceManager连接各种ManagerActivityManagerWindowManager等等)和相应ManagerService的桥梁;

Android应用层角度来说,Binder是客户端和服务器端进行通信的媒介,当bindService时,服务端会返回一个包含服务端业务调用的Binder对象,通过这个Binder对象,客户端可以获取服务端提供的服务或者数据,这里的服务包括普通服务和基于AIDL的服务。

4. Android中的IPC方式

4.1. 使用Bundle

四大组件中的三大组件(ActivityServiceReceiver)都支持在Intent中传递Bundle数据。由于Bundle实现了Parcelable接口,所以它可以很方便的实现跨进程传递数据

4.2. 使用文件共享

进程间通过读写同一个文件实现数据传递。使用该方式要避免并发操作。本质上SharedPreferences也是一种文件,但是系统对它的读写有一定的缓存策略,即在内存中也会有一份SharedPreferences文件的缓存,因此多进程模式下,系统对它的读/写就变得不可靠。

4.3. 使用Messenger

Messenger:信使,通过它可以在不同进程中传递Message对象,在Message中放入我们需要传递的数据,就可以轻松地实现数据的进程间通讯了。Messenger一次只处理一个请求,因此服务端不用考虑线程同步的问题。

先看下Messenger的构造函数:

 

4.3.1. 服务端实现

代码实现

package com.lance.artinquiry.chapter_2.messenger; import android.app.Service; import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Messenger; import android.os.RemoteException; import android.support.annotation.Nullable; import com.lance.artinquiry.util.LogUtil; public class MessengerService extends Service{ private static class MessengerHandler extends Handler{ @Override public void handleMessage(Message msg) { switch (msg.what){ case MyContants.MSG_FROM_CLIENT: LogUtil.e("receive msg from client:"+msg.getData().getString("msg")); Messenger messenger=msg.replyTo; Message message=Message.obtain(null,MyContants.MSG_FROM_SERVICE); Bundle data=new Bundle(); data.putString("msg","你好,我是服务端"); message.setData(data); try { messenger.send(message); } catch (RemoteException e) { e.printStackTrace(); } break; default: super.handleMessage(msg); } } } private final Messenger mMessenger=new Messenger(new MessengerHandler()); @Nullable @Override public IBinder onBind(Intent intent) { return mMessenger.getBinder(); } }

注册service

 

<service android:name=".chapter_2.messenger.MessengerService" android:process=":remote"> </service>

4.3.2. 客户端实现

 

package com.lance.artinquiry.chapter_2.messenger; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Messenger; import com.lance.artinquiry.R; import com.lance.artinquiry.ui.BaseActivity; import com.lance.artinquiry.util.LogUtil; public class MessengerActivity extends BaseActivity{ @Override protected int getLayoutId() { return R.layout.activity_messenger; } @Override protected void initData() { } @Override protected void initViews() { Intent intent=new Intent(this,MessengerService.class); bindService(intent,connection, Context.BIND_AUTO_CREATE); } private Messenger mMessenger; private ServiceConnection connection=new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { mMessenger=new Messenger(service); Message message=Message.obtain(null,MyContants.MSG_FROM_CLIENT); Bundle data=new Bundle(); data.putString("msg","你好,我是客户端."); message.replyTo=mGetReplyMessenger; message.setData(data); try { mMessenger.send(message); }catch (Exception e){ e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { } }; private Messenger mGetReplyMessenger=new Messenger(new MessageHandler()); private static class MessageHandler extends Handler{ @Override public void handleMessage(Message msg) { switch (msg.what){ case MyContants.MSG_FROM_SERVICE: LogUtil.e("服务端回复的消息:"+msg.getData().getString("msg")); break; default: super.handleMessage(msg); } } } @Override protected void onDestroy() { super.onDestroy(); unbindService(connection); } }

 

4.4. 使用AIDL

Messenger是一种轻量级的IPC方案,只能用来传递消息,但是无法跨进程调用服务端的方法。

这种需求可以通过aidl实现。

4.4.1. 服务端实现

服务端首先要创建一个Service用来监听客户端的连接请求,然后创建一个AIDl文件,将暴露给客户端的接口在这个AIDL中声明,最后在Service中实现这个AIDL接口。

BookManagerService.java

 

package com.lance.artinquiry.chapter_2.didl; import android.app.Service; import android.content.Intent; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.support.annotation.Nullable; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; public class BookManagerService extends Service{ @Nullable @Override public IBinder onBind(Intent intent) { return mBinder; } @Override public void onCreate() { super.onCreate(); mBookList.add(new Book(1,"Android开发艺术探究")); mBookList.add(new Book(2,"Thinking in Java")); } private CopyOnWriteArrayList<Book> mBookList=new CopyOnWriteArrayList<>(); 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); } }; }

Book.java:实现了Parcelable接口

package com.lance.artinquiry.chapter_2.didl; import android.os.Parcel; import android.os.Parcelable; public class Book implements Parcelable{ private int boodId; private String bookName; public Book(int boodId, String bookName) { this.boodId = boodId; this.bookName = bookName; } protected Book(Parcel in) { boodId=in.readInt(); bookName=in.readString(); } public static final Creator<Book> CREATOR = new Creator<Book>() { @Override public Book createFromParcel(Parcel in) { return new Book(in); } @Override public Book[] newArray(int size) { return new Book[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel out, int flags) { out.writeInt(boodId); out.writeString(bookName); } public int getBoodId() { return boodId; } public void setBoodId(int boodId) { this.boodId = boodId; } public String getBookName() { return bookName; } public void setBookName(String bookName) { this.bookName = bookName; } @Override public String toString() { return "Book{" + "boodId=" + boodId + ", bookName='" + bookName + '\'' + '}'; } }

Book.aidl文件:

 

package com.lance.artinquiry.chapter_2.didl; parcelable Book;

IBookManager.aidl

package com.lance.artinquiry.chapter_2.didl; import com.lance.artinquiry.chapter_2.didl.Book; interface IBookManager { List<Book> getBookList(); void addBook(in Book book); }

4.4.2. 客户端实现

首先需要绑定服务端的BookManagerService,绑定成功后,将返回的Binder对象转成AIDL接口所属的类型,接着就是调用AIDL的方法。

 

package com.lance.artinquiry.chapter_2.didl; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.IBinder; import android.os.RemoteException; import com.lance.artinquiry.R; import com.lance.artinquiry.ui.BaseActivity; import com.lance.artinquiry.util.LogUtil; import java.util.List; public class BookManagerActivity extends BaseActivity{ @Override protected int getLayoutId() { return R.layout.activity_aidl; } @Override protected void initViews() { } @Override protected void initData() { Intent intent=new Intent(this,BookManagerService.class); bindService(intent,connection, Context.BIND_AUTO_CREATE); } private ServiceConnection connection=new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { IBookManager bookManager=IBookManager.Stub.asInterface(service); try { List<Book> list1=bookManager.getBookList(); LogUtil.e(list1.toString()); Book book=new Book(3,"从你的生命中走过"); bookManager.addBook(book); List<Book> list2=bookManager.getBookList(); LogUtil.e(list2.toString()); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { } }; @Override protected void onDestroy() { super.onDestroy(); unbindService(connection); } }

结果打印为:

 

4.5. 使用ContentProvider

ContentProviderAndroid中提供的专门用于不同应用间进行数据共享的方式,从这一点来看,它天生就适合进程间通信。和Messenger一样,ContentProvider的底层实现也是Binder。虽然它的底层实现也是Binder,但是它的使用要比AIDL简单很多,这是因为系统已经为我们做了封装,使得我们无需关注底层细节就可以轻松实现IPC

系统预置了许多ContentProvider,比如通信录信息、日程表信息等,要跨进程访问这些信息,只需要通过

ContentResolverqueryupdateinsertdelete方法即可。

首先,创建一个BookContentProvider类:

package com.lance.perfect.ui.contentprovider; import android.content.ContentProvider; import android.content.ContentValues; import android.database.Cursor; import android.net.Uri; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.util.Log; import com.orhanobut.logger.Logger; public class BookContentProvider extends ContentProvider{ @Override public boolean onCreate() { Logger.e("%s---------------------onCreate",Thread.currentThread().getName()); return false; } @Nullable @Override public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) { Log.e("","=================query"); return null; } @Nullable @Override public String getType(@NonNull Uri uri) { Logger.e("%s---------------------getType",Thread.currentThread().getName()); return null; } @Nullable @Override public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) { Logger.e("%s---------------------insert",Thread.currentThread().getName()); return null; } @Override public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) { Logger.e("%s---------------------delete",Thread.currentThread().getName()); return 0; } @Override public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) { Logger.e("%s---------------------update",Thread.currentThread().getName()); return 0; } } 然后在AndroidManifest.xml中注册这个Provider

<!--注册内容提供者BookContentProvider 其中:android:authorities是ContentProvider的唯一标识 通过这个属性外部应用就可以访问这个ContentProvider了。 为了掩饰进程间通信,我们把BookContentProvider设在独立的进程中。 并为它设置权限 --> <provider android:authorities="com.lance.perfect.bookProvider" android:name=".ui.contentprovider.BookContentProvider" android:permission="com.lance.perfect.PROVIDER" android:process=":provider"> </provider>最后在Activity中进行使用

Uri uri=Uri.parse("content://com.lance.perfect.bookProvider"); getContentResolver().query(uri,null,null,null,null); getContentResolver().insert(uri,null); getContentResolver().delete(uri,null,null); 

4.6. 使用Socket

Socket也称为“套接字”,是网络通信中的概念,它分为流式套接字和用户数据报套接字,分别使用的网络传输协议为TCPUDP

TCP是面向连接的协议,提供稳定的双向通信协议,TCP的连接的建立需要经过“三次握手”才能完成,为了提供稳定的数据传输功能,其本身提供了超时重传机制,因此具有很高的稳定性;

UDP是无连接的,提供不稳定的单向通信功能,当然UDP也可以实现双向通信功能。

在性能上,UDP具有更好的效率,其缺点是不能保证数据一定能够正确传输,尤其是在网络堵塞的情况下。

5. 选用合适的IPC方式

 

 

 

转载请注明原文地址: https://www.6miu.com/read-25558.html

最新回复(0)