android IPC 机制 (开发艺术探索)
一、IPC 机制介绍
IPC是Inter-Process Communication的缩写,含义就是进程间通信或者跨进程通信,是指两个进程之间进行数据交换的过程。那么什么是进程,什么是线程,进程和线程是两个截然不同的概念。在操作系统中,线程是CPU调度的最小单元,同时线程是一种有限的系统资源。而进程指的一个执行单元,在PC和移动设备上指的是一个程序或者一个应用。一个进程可以包含多个线程,因此进程和线程是包含被包含的关系,最简单情况下,一个进程可以只有一个线程,即主线程,在Android里面也叫UI线程,在UI线程里才能操作界面元素。
1.1 Android中为什么要开启多进程
(1)分担主进程的内存压力。
当应用越做越大,内存越来越多,将一些独立的组件放到不同的进程,它就不占用主进程的内存空间了。比如在启动一个不可见的轻量级私有进程,在后台收发消息,或者做一些耗时的事情,或者开机启动这个进程等。
(2)防止主进程被杀守护进程,守护进程和主进程之间相互监视,有一方被杀就重新启动它。
1.2 如何开启多进程
四大组件在AndroidManifest文件中注册的时候,有个属性android:process这里可以指定组件的所处的进程。默认情况下的进程名就是包名。指定为别的进程之后,系统在启动这个组件的时候,就先创建(如果还没创建的话)这个进程,然后再创建该组件。
获取进程id和进程名:
String processName = "";
ActivityManager manager = (ActivityManager) getApplicationContext()
.getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningAppProcessInfo process: manager.getRunningAppProcesses()) {
if(process.pid == Process.myPid())
{
processName = process.processName;
}
}
Log.d(TAG, "Main: "+ Process.myTid()+" "+processName);
需要注意的是 运行在不同进程中的组件是属于不同的虚拟机和application。当某个应用有三个进程时则它的Application的oncreate会执行三次。
开启多进程如下:
进程名以“:”开头的进程属于当前应用的私有进程,其他应用的组件不可以和它跑在同一个进程中。而进程名不以“:”开头的进程属于全局进程,其他应用可以通过某些方式和它跑在同一个进程中。
1.3 Android中开启多进程有哪些弊端
(1)多占了系统的内存空间,很容易沾满而导致卡顿,同时也消耗用户的电量。同时在启动单独进程时,进程的创建会影响继承Application的实例,onCreate()会再次执行一遍。
(2)不同进程之间内存不能共享,最大的弊端是他们之间通信麻烦,不能将公用数据放在Application中,堆栈信息、文件操作也是独立的,如果他们之间传递的数据不大并且是可序列化的,可以考虑通过Bundle传递, 如果数据量较大,则需要通过AIDL或者文件操作来实现。
一般在安卓中使用多进程需要注意以下问题:
- 静态成员和单例模式完全失效
- 线程同步机制完全失效
- SharedPreferences的可靠性下降
- Application会多次创建
为了解决这个问题,系统提供了很多跨进程通信方法,虽然说不能直接地共享内存,但是通过跨进程通信我们还是可以实现数据交互。实现跨进程通信的方式有很多,比如通过Intent来传递数据,共享文件SharedPreference,基于Binder的Messenger和AIDL以及Socket等。
二、IPC 基础概念
主要介绍IPC中的基础概念,主要包括三个方面,Serializable 及 Parcelable 接口以及Binder。
2.1 Serializable 及 Parcelable 接口
参考:http://www.cnblogs.com/renqingping/archive/2012/10/25/Parcelable.html
2.2 IDAL( Android Interface definition language)
IDAL 是一种android内部进程通信接口的描述语言,通过它我们可以定义进程间的通信接口
2.3 Binder
三、Android中的IPC方式
AIDL 方式实现进程间通信
在server端和client端都需要添加aidl文件作为进程通信的协议。
距离每个文件里面的代码如下:


package com.lypeer.ipcclient; import android.os.Parcel;
import android.os.Parcelable; /**
* Created by lypeer on 2016/7/16.
*/
public class Book implements Parcelable{
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;
} private String name; private int price; public Book(){} 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;
}
}
Book


// Book.aidl
package com.lypeer.ipcclient; parcelable Book;
Book.aidl


package com.lypeer.ipcclient;
import com.lypeer.ipcclient.Book; interface BookManager { List<Book> getBooks();
void addBook(inout Book book);
}
BookManager.aidl
server端
定义并注册AIDLServcie,并实现onBind() 接口,具体实现如下:
package com.lypeer.ipcserver.service; import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.util.Log; import com.lypeer.ipcclient.Book;
import com.lypeer.ipcclient.BookManager; import java.util.ArrayList;
import java.util.List; /**
* 服务端的AIDLService.java
* <p/>
* Created by lypeer on 2016/7/17.
*/
public class AIDLService extends Service { public final String TAG = this.getClass().getSimpleName(); //包含Book对象的list
private List<Book> mBooks = new ArrayList<>(); //由AIDL文件生成的BookManager
private final BookManager.Stub mBookManager = new BookManager.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的参数,主要是为了观察其到客户端的反馈
book.setPrice(2333);
if (!mBooks.contains(book)) {
mBooks.add(book);
}
//打印mBooks列表,观察客户端传过来的值
Log.e(TAG, "invoking addBooks() method , now the list is : " + mBooks.toString());
}
}
}; @Override
public void onCreate() {
Book book = new Book();
book.setName("Android开发艺术探索");
book.setPrice(28);
mBooks.add(book);
Log.d(TAG, "AIDLService onCreate: ");
super.onCreate();
} @Nullable
@Override
public IBinder onBind(Intent intent) {
Log.e(getClass().getSimpleName(), String.format("on bind,intent = %s", intent.toString()));
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return mBookManager;
}
}
当Client端调用bindService绑定该服务时,AIDLService会被创建。
client端
client端定义一个Activity,并在Activity的onstart()方法中绑定AIDLService,即可以获取到AIDLService中定义的BookManager对象。通过一个按钮调用addBook方法就能调用Service端的addBook接口
package com.lypeer.ipcclient; 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.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Toast; import java.util.List; /**
* 客户端的AIDLActivity.java
* 由于测试机的无用debug信息太多,故log都是用的e
* <p/>
* Created by lypeer on 2016/7/17.
*/
public class AIDLActivity extends AppCompatActivity { //由AIDL文件生成的Java类
private BookManager 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);
} /**
* 按钮的点击事件,点击之后调用服务端的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);
Log.e(getLocalClassName(), book.toString());
} catch (RemoteException e) {
e.printStackTrace();
}
} /**
* 尝试与服务端建立连接
*/
private void attemptToBindService() {
Intent intent = new Intent();
intent.setAction("com.lypeer.aidl");
intent.setPackage("com.lypeer.ipcserver");
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 = BookManager.Stub.asInterface(service);
mBound = true; if (mBookManager != null) {
try {
mBooks = mBookManager.getBooks();
Log.e(getLocalClassName(), mBooks.toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}
} @Override
public void onServiceDisconnected(ComponentName name) {
Log.e(getLocalClassName(), "service disconnected");
mBound = false;
}
};
}
注: 1. 当AIDLActivity onStart()函数调用时会绑定服务,此时AIDLService的onCreate函数会被调用,同时mServiceConnection 的onServiceConnected接口会被调用,会解析出BookManager对象。
2. 当addBook被调用(按钮点击事件)时,AIDLService里面的addBook方法会被调用。从而实现进程间的通信。
AIDL为什么能实现进程间通信?
?
实际使用场景:
1. 智能设备中,有多个服务负责不同的职责:定位服务/配置服务/主功能服务/日志服务 等这些服务都是独立的app,通过aidl接口对外提供能力。
其它app如果希望获取位置信息/配置信息/执行某个功能/记一条日志等,都可以通过aidl调用相应服务。
注意事项:
1. 绑定服务的过程可能存在失败(对于十分重要的接口可以采用重试机制,但是会一定程度上增加逻辑复杂性)
2. 业务需要处理绑定失败的场景(onServiceDisconnected/onBindingDied)
3. 可能会存在绑定的ServiceConnection没有任何回调,可以通过定时器保证有结果(超时没有回调则回调绑定失败)
参考:
https://blog.csdn.net/lmj623565791/article/details/38461079
https://blog.csdn.net/luoyanglizi/article/details/51980630
通过守护进程service相互拉起(AIDL): https://blog.csdn.net/returnnull0/article/details/53750483
Android_常驻进程(杀不死的进程):https://blog.csdn.net/two_water/article/details/52126855
android IPC 机制 (开发艺术探索)的更多相关文章
- 《android开发艺术探索》读书笔记(二)--IPC机制
接上篇<android开发艺术探索>读书笔记(一) No1: 在android中使用多进程只有一种方法,那就是给四大组件在AndroidMenifest中指定android:process ...
- Android开发艺术探索——第二章:IPC机制(中)
Android开发艺术探索--第二章:IPC机制(中) 好的,我们继续来了解IPC机制,在上篇我们可能就是把理论的知识写完了,然后现在基本上是可以实战了. 一.Android中的IPC方式 本节我们开 ...
- Android开发艺术探索——第二章:IPC机制(上)
Android开发艺术探索--第二章:IPC机制(上) 本章主要讲解Android的IPC机制,首先介绍Android中的多进程概念以及多进程开发模式中常见的注意事项,接着介绍Android中的序列化 ...
- 《android开发艺术探索》读书笔记(十)--Android的消息机制
接上篇<android开发艺术探索>读书笔记(九)--四大组件 No1: 消息队列MessageQueue的内部存储结构并不是真正的队列,而是采用单链表的数据结构来存储消息列表,因为单链表 ...
- 《android开发艺术探索》读书笔记(三)--分发机制和滑动冲突
接上篇<android开发艺术探索>读书笔记(二) No1: 通过MotionEvent对象可以得到点击事件发生的x和y坐标,getX/getY返回的是相对于当前View左上角的x和y坐标 ...
- Android开发艺术探索第五章——理解RemoteViews
Android开发艺术探索第五章--理解RemoteViews 这门课的重心在于RemoteViews,RemoteViews可以理解为一种远程的View,其实他和远程的Service是一样的,Rem ...
- Android开发艺术探索——新的征程,程序人生路漫漫!
Android开发艺术探索--新的征程,程序人生路漫漫! 偶尔写点东西分享,但是我还是比较喜欢写笔记,看书,群英传看完了,是学到了点东西,开始看这本更加深入Android的书籍了,不知道适不适合自己, ...
- Android开发艺术探索读书笔记——进程间通信
1. 多进程使用场景 1) 应用某些模块由于特殊需求须要执行在单独进程中. 如消息推送,使消息推送进程与应用进程能单独存活,消息推送进程不会由于应用程序进程crash而受影响. 2) 为加大一个应用可 ...
- Android开发艺术探索笔记——View(二)
Android开发艺术探索笔记--View(二) View的事件分发机制 学习资料: 1.Understanding Android Input Touch Events System Framewo ...
随机推荐
- hdu-1069(dp)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1069 题意:一群猴子,给出n块砖的长x宽y高z,用这些砖拼起的高度最高是多少, 要求底下的砖的长宽都要 ...
- 实现WIFI MAC认证与漫游
前言 单位里有10来个网件的AP(WNAP210),需要对接入端(主要是手机)进行MAC认证,原来采用AP本地MAC认证,但是人员经常变动(离职),另外人员的岗位(流水线)也经常调整,这样就需在变动后 ...
- 8.7 使用索引-notes
七 正确使用索引 一 索引未命中 并不是说我们创建了索引就一定会加快查询速度,若想利用索引达到预想的提高查询速度的效果,我们在添加索引时,必须遵循以下问题 1 范围问题,或者说条件不明确,条件中出现这 ...
- 进度条ProgressBar
在本节中,作者只写出了进度条的各种样式,包括圆形.条形,还有自定义的条形,我想如果能让条形进度条走满后再继续从零开始,于是我加入了一个条件语句.作者的代码中需要学习的是handler在主线程和子线程中 ...
- 批量 truncate 表
如果某个用户下所有表或指定表中所有的数据已确定不再需要,此时可以进行批量 truncate declare cursor cur_trunc is select table_name from use ...
- Swift:在Safari中打开App
打开之前会发生什么呢,先看看这个图: 我这里只是简单模拟了一下.当你输入一个特殊的“url”之后,Safari弹出一个提示,问你是否继续打开这个App.如果你这个时候confirm的话.那么这个App ...
- Win & Mac 系统之间U盘传递的U盘文件格式选取问题
Win & Mac 系统之间U盘传递的U盘文件格式选取问题 1. Win系统与Mac系统之间可以通过 exFat U盘文件系统传递 exFAT(Extended File Allocation ...
- hdu 5036 概率+bitset
http://acm.hdu.edu.cn/showproblem.php?pid=5036 n个房间每个房间里面有一把或多把钥匙可以打开其他的门.如果手上没有钥匙可以选择等概率随机选择一个门炸开,求 ...
- 成员函数指针与高性能C++委托
1 引子 标准C++中没有真正的面向对象的函数指针.这一点对C++来说是不幸的,因为面向对象的指针(也叫做“闭包(closure)”或“委托(delegate)”)在一些语言中已经证明了它宝贵的价值. ...
- Android SQLiteOpenHelper Sqlite数据库升级onUpgrade
Android Sqlite数据库升级,在Android APP开发之中,非常常见: 在确定原来的数据库版本号之后,在原来数据库版本号+1,就会执行onUpgrade方法,进行数据库升级操作: 在on ...