1) 应用某些模块因为特殊需求需要执行在单独进程中。java
如消息推送,使消息推送进程与应用进程能单独存活,消息推送进程不会因为应用程序进程crash而受影响。
2) 为加大一个应用可以使用的内存。需要多进程来获取多分内存空间。android
给四大组件(Activity、Service、Receiver、ContentProvider)在AndroidMainfest中指定android:process属性指定。数组
(1) 静态成员和单例失效,数据同步失败;
Android会为每一个应用/每一个进程分配一个独立的虚拟机。不一样虚拟机在内存分配上有不一样的地址空间,这就致使不一样虚拟机中訪问同一个类对象会产生多个副本。缓存
(2) 线程同步机制失效。
因为不一样进程不是同一块内存,不一样进程锁的不是同一对象。
(3) SharedPreferences可靠性降低;
SharedPreferences底层是经过读/写XML文件实现的。并发写可能会出问题。因此它不支持多个进程同一时候去执行写操做。不然会致使必定概率的数据丢失。
(4) Application会建立屡次。
当一个组件跑在一个新进程中,系统会给它又一次分配独立虚拟机,这事实上就是启动一个应用的过程,故执行在不一样进程中的组件属于不一样的虚拟机和不一样的Application。服务器
Intent和Binder数据传输时。或是对象持久化转存/经过网络传输给其它client时,需要使用Parcelable或Serializable将对象转换成可以传输的形式。markdown
(1) Serializable接口
Serializable是Java提供的一个序列化接口,它是一个空接口。想要某个类实现序列化,仅仅需要对应类实现Serializable接口就能够。Serializable是借助ObjectOutputStream和ObjectInputStream实现对象的序列化和反序列化
通常在对应类实现Serializable类中还会定义一个final long型的serialVersionUID,不用它也能实现对象的序列化。它是用来辅助反序列化的。网络
序列化时会把当前类的serialVersionUID写入序列化文件里。当反序列化系统会检測文件里的serialVersionUID,看它是否和当前类的serialVersionUID一致,假设一致说明序列化的类的版本号和当前类的版本号一样,这时可反序化成功;不然说明当前类和序列化的类相比发生了变换,如添加或下降某个成员变量,这时没法正常反序列化。
(2) Parcelable接口
在序列化过程当中需要实现的功能有:
1) 序列化:由writeToParcel方法完毕,终于经过Parcel中的一系列的write方法完毕。
2) 反序列化:由CREATOR完毕。内部标识了怎样建立序列化对象和数组,终于经过Parcel的一系列read方法来完毕反序列化;
3) 内容描写叙述符:由describeContents方法来完毕。差点儿所有状况下该方法都返回0,仅当当前对象中存在文件描写叙述符时返回1。
(3)二者差异
Serializable是Java中序列化接口,使用简单但开销大,序列和反序列化需要大量I/O操做。
Parcelable是Android中特有的序列化接口,使用复杂但开销小,效率高,是Android推荐的序列化方法;
Parcelable主要用在内存序列化上,假设将对象序列化到存储设备或将对象序列化后经过网络传输。过程会比較复杂。建议使用Serializable。并发
Binder是什么?socket
在一个进程中启动还有一个进程的Activity, Service, Receiver组件时,可以使用Bundle附加上需要传递的消息给远程进程。并经过Intent发送出去。ide
两个进程经过读/写同一个文件来交换数据,还可以序列化一个对象到文件系统中。从还有一个进程中恢复这个对象。它的实现原理是经过ObjectOutputStream将文件写入文件里。再经过ObjectInputSteam将文件恢复。
经过文件共享的方式有必定局限性。如并发读/写,读出的内容可能不是最新的。并发写就可能致使数据混乱。所以尽可能避免并发写这样的操做,或考虑用线程同步来限制多个线程的写操做。文件共享适合在对数据要求不高的进程之间通讯。
SharedPreferences是Android提供的一种轻量级存储方案,它经过键值对方式存储数据,底层它是採用XML来存储键值对。
因为系统对SharedPreferences的读写有必定的缓存策略。即在内存中会有一份SharedPreferences文件的缓存,在多进程模式下,系统对它的读/写就变得不可靠,当面对高并发读/写訪问时。SharedPreferences会有很是大概率会丢失数据。所以不建议在进程间通讯中使用SharedPreferences。
Messenager可以在不一样进程中传递Message对象。在Message中放入咱们要传递的数据。
它的底层实现是AIDL,如下是Messenger的两个构造方法:
public Messenger(Handler target) {
mTarget = target.getIMessenger();
}
public Messenger(IBinder target) {
mTarget = IMessenger.Stub.asInterface(target);
}
不管是IMessenger仍是Stub.asInterface,这样的用法都代表它的底层是AIDL,它一次仅仅处理一个请求,不存在线程同步问题。
实现步骤:
(1) 服务端进程
1) 定义一个Service用于client的绑定,创建一个Handler,在handleMessage里处理client发送过来的消息。
//用ServiceHandler接收并处理来自于client的消息
private class ServiceHandler extends Handler {
@Override
public void handleMessage(Message msg) {
if(msg.what == RECEIVE_MESSAGE_CODE){
Bundle data = msg.getData();
if(data != null){
String str = data.getString("msg");
}
//经过Message的replyTo获取到client自身的Messenger,
//Service可以经过它向client发送消息
clientMessenger = msg.replyTo;
if(clientMessenger != null){
Message msgToClient = Message.obtain();
msgToClient.what = SEND_MESSAGE_CODE;
//可以经过Bundle发送跨进程的信息
Bundle bundle = new Bundle();
bundle.putString("msg", "你好,client,我是MyService");
msgToClient.setData(bundle);
try{
clientMessenger.send(msgToClient);
}catch (RemoteException e){
e.printStackTrace();
Log.e("DemoLog", "向client发送信息失败: " + e.getMessage());
}
}
}
}
}
2) 经过Handler建立一个Messenger对象
//serviceMessenger是Service自身的Messenger,其内部指向了ServiceHandler的实例
//client可以经过IBinder构建Service端的Messenger,从而向Service发送消息,
//并由ServiceHandler接收并处理来自于client的消息
private Messenger serviceMessenger = new Messenger(new ServiceHandler());
3) 在Service的onBind中经过Messenger.getBinder()返回底层的Binder对象。
@Override
public IBinder onBind(Intent intent) {
Log.i("DemoLog", "MyServivce -> onBind");
//获取Service自身Messenger所对应的IBinder。并将其发送共享给所有client
return serviceMessenger.getBinder();
}
4) 注冊服务,让其执行在单独进程中
<service
android:name=".MyService"
android:enabled="true"
android:process=":remote" >
</service>
(2) client进程
1) 绑定服务端的Service
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder binder) {
//client与Service创建链接
Log.i("DemoLog", "client onServiceConnected");
//咱们可以经过从Service的onBind方法中返回的IBinder初始化一个指向Service端的Messenger
serviceMessenger = new Messenger(binder);
isBound = true;
Message msg = Message.obtain();
msg.what = SEND_MESSAGE_CODE;
//此处跨进程Message通讯不能将msg.obj设置为non-Parcelable的对象,应该使用Bundle
//msg.obj = "你好。MyService,我是client";
Bundle data = new Bundle();
data.putString("msg", "你好,MyService,我是client");
msg.setData(data);
//需要将Message的replyTo设置为client的clientMessenger,
//以便Service可以经过它向client发送消息
msg.replyTo = clientMessenger;
try {
Log.i("DemoLog", "client向service发送信息");
serviceMessenger.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
Log.i("DemoLog", "client向service发送消息失败: " + e.getMessage());
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
//client与Service失去链接
serviceMessenger = null;
isBound = false;
Log.i("DemoLog", "client onServiceDisconnected");
}
};
Intent intent = new Intent();
ComponentName componentName = new ComponentName(packageName, serviceNmae);
intent.setComponent(componentName);
try{
Log.i("DemoLog", "client调用bindService方法");
bindService(intent, conn, BIND_AUTO_CREATE);
}catch(Exception e){
e.printStackTrace();
Log.e("DemoLog", e.getMessage());
}
}
绑定成功后用服务端返回的IBinder对象建立一个Messenger,经过它向服务端发送Message消息。
假设需要服务端给client发送消息。需要在Handler的handleMessage方法里,依据client发送过来的Message.replyTo获取到client的Messenger对象,就可以向client发送消息了。同一时候client需要在服务链接的onServiceConnected方法中,将client的Messenger对象经过Message.replyTo给收到服务端发送过来的Message对象。这样就实现两方通讯。
(1)先新建一个Book.java实现Parcelable接口,表示一个图书的信息类;
import android.os.Parcel;
import android.os.Parcelable;
/** * Book.java */
public class Book implements Parcelable{
private String name;
private int price;
public Book(){}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public Book(Parcel in) {
name = in.readString();
price = in.readInt();
}
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 dest, int flags) {
dest.writeString(name);
dest.writeInt(price);
}
/** * 參数是一个Parcel,用它来存储与数据传输 * @param dest */
public void readFromParcel(Parcel dest) {
//注意。此处的读值顺序应当是和writeToParcel()方法中一致的
name = dest.readString();
price = dest.readInt();
}
//方便打印数据
@Override
public String toString() {
return "name : " + name + " , price : " + price;
}
}
(2) 新建一个Book.aidl。表示Book类在AIDL中的声明;
// Book.aidl
//这个文件的做用是引入了一个序列化对象 Book 供其它的AIDL文件使用
//注意:Book.aidl与Book.java的包名应当是同样的
//注意parcelable是小写
parcelable Book;
(3) 新建一个IBookManger.aidl接口,里面包括对应的方法;
// IBookManger.aidl
//导入所需要使用的非默认支持数据类型的包
import com.lypeer.ipcclient.Book;
interface IBookManger {
//所有的返回值前都不需要加不论什么东西。不管是什么数据类型
List<Book> getBooks();
Book getBook();
}
该方法保存后,会在gen文件夹下生成一个IBookManger.java类,它是系统为BookManger.aidl生成的Binder类,继承android.os.IInterface。它本身也仍是个接口。
它包括如下内容:
1) 定义了一个String型的DESCRIPTOR。Binder的惟一标识;
2) asInterface(android.os.IBinder obj)方法,将服务端的Binder对象转换成client所需的AIDL接口类型对象,当client和服务端处于同一进程,直接返回服务端的Stub对象自己。不然返回系统封装后的Stub.proxy对象。
3) asBinder方法:返回当前Binder对象。
4) onTransact方法:执行在服务端的Binder线程池中,当client发起远程请求,远程请求会经过系统封装后交由此方法处理,该方法原型为public Boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags),服务端经过code可以肯定client请求的是哪个方法,从data中取出目标方法所需的參数,执行对应方法后,将返回值写入reply中。假设此方法返回false,client会请求失败。
5) IBookManger.aidl接口中声明的方法的代理实现,此方法执行在client,当client调用此方法时,首先需要建立此方法需要的输入类型Parcel对象_data, 输出类型Parcel对象_reply和返回值对象。接着将參数信息写入_data中(如有參数的话),再调用transact方法发起RPC(远程过程调用)。当前线程挂起,服务端的onTransact方法会被调用,直到RPC过程返回后,当前线程继续执行。并从_reply中取出RPC过程的返回结果;最后返回_reply中的数据。
6) 声明了IBookManger.aidl中声明的方法。
7) 声明了对应的整型id分别用于标识声明的方法。该id用于标识在transact过程当中推断client请求的到底是哪一个方法。
8) 声明了一个类部类Stub,继承android.os.Binder实现IBookManager.aidl中类接口。当client和服务端处于同一进程,方法调用不会走跨进程的transact过程。若位于不一样进程。则由Stub的内部代理Proxy,调用跨进程transact过程。
需要注意的是:client发起请求时。当前线程会挂起直至服务端返回数据,若方法是一个耗时操做,不能在UI线程中发起此远程请求。服务端的Binder方法执行在Binder线程池中。因此不管操做是否耗时,都应採用同步的方法去实现。
(4)linkToDeath unlinkToDeath
因为Binder执行在服务端进程中。假设服务端进程因为某种缘由终止。这时client服务端的Binder链接断裂,会致使远程调用失败。但client很是可能不知道,解决此问题的办法,利用Binder提供的两个配对方法linkToDeath和unlinkToDeath,经过linkToDeath可以给Binder设置一个死亡代理,当Binder被终止时。咱们会接收到通知。这时可经过又一次发起请求恢复链接。
实现方法:
1) 先声明一个DeathRecipient对象,它是一个接口。内部仅仅有一个方法binderDied,咱们需要实现该方法,当Binder死亡时,系统回调binderDied方法,咱们可以移出以前binder代理并又一次创建远程服务。
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
if (mBookManager == null) {
return;
}
mBookManger.asBinder().unlinkToDeath(mDeathRecipient, 0);
mBookManger = null;
//又一次绑定远程服务
}
};
2) 在client绑定远程服务成功后,给binder设置死亡代理;
mService = IBookManager.Stub.asInterface(binder);
binder.linkToDeath(mDeathRecipient, 0);
(1)Serivce创建
咱们需要新建一个Service,称为AIDLService,代码例如如下:
/** 1. 服务端的AIDLService.java */
public class AIDLService extends Service {
public final String TAG = this.getClass().getSimpleName();
//包括Book对象的list
private List<Book> mBooks = new ArrayList<>();
//由AIDL文件生成的IBookManager
private final IBookManager.Stub mBookManager = new IBookManager.Stub() {
@Override
public List<Book> getBooks() throws RemoteException {
synchronized (this) {
Log.e(TAG, "invoking getBooks() method , now the list is : " + mBooks.toString());
if (mBooks != null) {
return mBooks;
}
return new ArrayList<>();
}
}
@Override
public void addBook(Book book) throws RemoteException {
synchronized (this) {
if (mBooks == null) {
mBooks = new ArrayList<>();
}
if (book == null) {
Log.e(TAG, "Book is null in In");
book = new Book();
}
//尝试改动book的參数,主要是为了观察其到client的反馈
book.setPrice(2333);
if (!mBooks.contains(book)) {
mBooks.add(book);
}
//打印mBooks列表,观察client传过来的值
Log.e(TAG, "invoking addBooks() method , now the list is : " + mBooks.toString());
}
}
};
@Override
public void onCreate() {
super.onCreate();
Book book = new Book();
book.setName("Android开发艺术探索");
book.setPrice(28);
mBooks.add(book);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.e(getClass().getSimpleName(), String.format("on bind,intent = %s", intent.toString()));
return mBookManager;//在onBinder中返回服务端的Binder对象
}
}
(2)注冊服务
<service android:name="aidl.AIDLService"
android:process=":remote">
</service>
/** 2. client的AIDLActivity.java */
public class AIDLActivity extends AppCompatActivity {
//由AIDL文件生成的Java类
private IBookManager mBookManager = null;
//标志当前与服务端链接情况的布尔值,false为未链接。true为链接中
private boolean mBound = false;
//包括Book对象的list
private List<Book> mBooks;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_aidl);
}
/** * button的点击事件。点击以后调用服务端的addBookIn方法 * * @param view */
public void addBook(View view) {
//假设与服务端的链接处于未链接状态,则尝试链接
if (!mBound) {
attemptToBindService();
Toast.makeText(this, "当前与服务端处于未链接状态,正在尝试重连,请稍后再试", Toast.LENGTH_SHORT).show();
return;
}
if (mBookManager == null) return;
Book book = new Book();
book.setName("APP研发录In");
book.setPrice(30);
try {
mBookManager.addBook(book);//经过服务端的Binder对象调服务端方法
Log.e(getLocalClassName(), book.toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}
/** * 尝试与服务端创建链接 */
private void attemptToBindService() {
Intent intent = new Intent();
intent.setAction("aidl.AIDLService");
intent.setPackage("com.xx.packagename");
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);//绑定服务
}
@Override
protected void onStart() {
super.onStart();
if (!mBound) {
attemptToBindService();
}
}
@Override
protected void onStop() {
super.onStop();
if (mBound) {
unbindService(mServiceConnection);
mBound = false;
}
}
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e(getLocalClassName(), "service connected");
mBookManager = IBookManager.Stub.asInterface(service);
mBound = true;
if (mBookManager != null) {
try {
mBooks = mBookManager.getBooks();//调用服务端的getBooks方法
Log.e(getLocalClassName(), mBooks.toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.e(getLocalClassName(), "service disconnected");
mBound = false;
}
};
}
经过获取服务端的Binder对象,就可以和服务端进行通讯了。
(1)跨进程listener接口
假设在服务端定声明了接口,client进行注冊和反注冊。会抛出异常,没法反注冊。因为在多进程中,Binder会把client传过来的对象又一次转化成一个新对象,这样尽管client注冊和反注冊使用的是同一个对象,但是经过Binder传递到服务端却变成两个对象。对象的跨进程传输本质都是反序列化过程。
可以使用RemoteCallbackList,它是系统专门用来删除跨进程listener接口。它实现原理利用底层Binder对象是同一个,仅仅要遍历服务端所有listener,找出和解注冊listener具备一样Binder对象的服务端listener,并把它删除掉。
同一时候RemoteCallbackList还有一个做用,当client进程终止。它会移除client所注冊的所有listener.
(2)权限验证
在注冊服务时加入permission权限验证,或是在onTransact方法中进行权限验证;
(3) AIDL訪问流程总结:
先建立一个Service和一个AIDL接口。再建立一个类继承自AIDL接口中的Stub类并实现Stub中的抽象方法;在Service的onBind方法中返回这个对象,再在client就可以绑定服务端service。创建链接后就可以訪问远程服务端的方法了。
和Messenger同样,ContentProvider底层一样是Binder.系统预置了很是多ContentProvider,如通信录、日程表等。仅仅需经过ContentResolver的query/update/insert/delete就可以跨进程訪问这些信息。详细实现过程例如如下:
1) 新建一个继承自系统ContentProvider的Provider,并重写onCreate, query, getType, insert, delete, update六个方法。
这六个进程执行在ContentProvider的进程中,除了onCreate由系统回调执行在主线程中,其它五个方法执行在Binder线程池中。
2) 注冊Provider
<provider android:name=".provider.XXProvider"
android:authorities="com.xx.xx.provider"
android:permission="com.xx.PROVIDER"
android:process=":provider">
</provider>
3) 在还有一个进程中訪问咱们定义的ContentProvider
Uri uri = Uri.parse("content://com.xx.xx.provider");
getContentResolver().query(uri, null, null, null, null);
getContentResolver().query(uri,)
4) ContentProvider数据源发生变化时。可经过ContentResolver的notifyChange方法来通知外界数据发生改变。外界可经过ContentResolver的registerContentObserver方法来注冊观察者。经过unregisterContentObserver方法来解除观察者。
Socket分为流式套接字和用户数据报套接字。各自是对应于网络的传输控制层中的TCP/UDP
TCP:面向链接的协议,稳定的双向通讯功能,链接需要“三次握手”。提供了超时重传机制,具备很是高的稳定性;
UDP:面向无链接协议。不稳定的单向通讯功能,也可提供双向通讯功能。效率高,不能保证数据必定能正确传输。
实现步骤:
1)服务端在新建一个Service,并创建TCP服务
@Override
public void onCreate() {
new Thread(new TcpServer()).start();//堵塞监听client链接
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
throw null;
}
private class TcpServer implements Runnable {
@Override
public void run() {
ServerSocket serverSocket;
try {
//监听8688端口
serverSocket = new ServerSocket(8688);
} catch (IOException e) {
return;
}
while (!isServiceDestroyed) {
try {
// 接受client请求。并且堵塞直到接收到消息
final Socket client = serverSocket.accept();
new Thread() {
@Override
public void run() {
try {
//有消息接收到。新开一个线程进行处理消息
responseClient(client);
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private void responseClient(Socket client) throws IOException {
// 用于接收client消息,将client的二进制数据流格式转成文本格式
BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
// 用于向client发送消息
PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(client.getOutputStream())), true);
out.println("您好。我是服务端");
while (!isServiceDestroyed) {
String str = in.readLine();//经过数据流的readLine。可直接变成文本格式
Log.i("moon", "收到client发来的信息" + str);
if (TextUtils.isEmpty(str)) {
//client断开了链接
Log.i("moon", "client断开链接");
break;
}
String message = "收到了client的信息为:" + str;
// 从client收到的消息加工再发送给client
out.println(message);
}
out.close();//关闭流
in.close();
client.close();
}
2)注冊服务
<service
android:name=".SocketServerService"
android:process=":remote" />
3) client先创建链接服务端socket
Socket socket = null;
while (socket == null) {
try {
//选择和服务器一样的端口8688
socket = new Socket("localhost", 8688);
mClientSocket = socket;
mPrintWriter = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
} catch (IOException e) {
SystemClock.sleep(1000);
}
}
4) client和服务端通讯。经过while循环不断去读取服务器发送过来的消息
// 接收服务器端的消息
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
while (!isFinishing()) {
final String msg = br.readLine();
if (msg != null) {
runOnUiThread(new Runnable() {
@Override
public void run() {
tv_message.setText(tv_message.getText() + "\n" + "服务端:" + msg);
}
}
);
}
}
5) client在onCreate中单开线程启动链接服务
Intent service = new Intent(this, SocketServerService.class);
startService(service);
new Thread() {
@Override
public void run() {
connectSocketServer();
}
}.start();