如果一个进程占用内存超过了这个内存限制,就会报OOM的问题,很多涉及到大图片的频繁操作或者需要读取一大段数据在内存中使用时,很容易报OOM的问题。为了彻底地解决应用内存的问题,Android引入了多进程的概念,它允许在同一个应用内,为了分担主进程的压力,将占用内存的某些页面单独开一个进程,比如Flash、视频播放页面,频繁绘制的页面等。
android应用实现多进程需要依赖于android:process这个属性
适用元素:Application, Activity, BroadcastReceiver, Service, ContentProvider。
通常情况下,这个属性的值应该是”:“开头。表示这个进程是应用私有的。无法在在跨应用之间共用。
如果该属性值以小写字母开头,表示这个进程为全局进程。可以被多个应用共用。
为activity声明进程。其他组件同理。
<activity android:name=".MusicPlayerActivity" android:process=":music"/>
<activity android:name=".AnotherActivity" android:process="droidyue.com"/>
应用多进程有什么好处
增加App可用内存
在Android中,默认情况下系统会为每个App分配一定大小的内存。比如从最早的16M到后面的32M或者48M等。具体的内存大小取决于硬件和系统版本。
这些有限的内存对于普通的App还算是够用,但是对于展示大量图片或者模块较多的应用来说,显得实在是捉襟见肘,常见会比较卡顿。
使用多进程需要注意的一些问题就是:
- 静态成员和单例失效:每个进程保持各自的静态成员和单例,相互独立。
- 线程同步机制失效:每个进程有自己的线程锁。
- SharedPreferences可靠性下降:不支持并发写,会出现脏数据。
- Application多次创建:不同进程跑在不同虚拟机,每个虚拟机启动会创建自己的Application,自定义Application时生命周期会混乱。
既然有了这些问题,自然会有解决的方法。
1、使用Bundle
2、使用文件共享
3、使用Messenger
4、使用AIDL
5、使用ContentProvider
6、使用Socket
但是也有不同的使用场景,本文就讲到Bundle,文件共享,messager和AIDL。
名称 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
Bundle | 简单易用 | 只能传输Bundle支持的数据类型 | 四大组件的进程间通信 |
文件共享 | 简单易用 | 不适合高并发场景,并且无法做到进程间即时通信 | 无并发访问清醒,交换简单的数据,实时性不高的场景 |
AIDL | 功能强大,支持一对多并发通信,支持实时通信 | 使用稍复杂,需要处理好线程同步 | 一对多通信且有RPC需求 |
Messenger | 功能一般,支持一对多串行通信,支持实时通信 | 不能很好的处理高并发情形,不支持RPC,数据通过Message进行传输,因此只能传输Bundle支持的数据类型 | 低并发的一对多即时通信,无RPC需求 |
ContentProvider | 在数据源访问方面功能强大,支持一对多并发数据共享,可通过Call方法扩展其他操作 | 可以理解为受约束的AICL,主要提供数据的CRUD数据 | 一对多的进程间数据共享 |
Socket | 功能强大,可以通过网络传输字节流,支持一对多并发实时通信 | 实现细节稍微繁琐,不支持直接的RPC | 网络数据交换 |
一,Bundle实现
下面是一个简单的示例,在一个应用中的MainActivity和BundleActivity(分属不同进程)之间使用Bundle传输数据。首先在MainActivity中使用Bundle包装我们的字符串数据。
Bundle bundle = new Bundle();
bundle.putString("ipc", "Bundle test");
Intent intent=new Intent(MainActivity.this,BundleActivity.class);
intent.putExtras(bundle);
startActivity(intent);
并在BundleActivity中进行数据接收,其中“ipc”作为某个Bundle数据单元的标识。
Bundle bundle=getIntent().getExtras();
//获取Bundle的信息
String info=bundle.getString("ipc");
二,使用文件共享
两个进程通过读写同一个文件来交换数据,比如A进程把数据写入文件,B进程通过读取这个文件来获取数据。
Android系统基于Linux,使得并发读写文件可以没有限制的进行,甚至两个线程同时对文件读写操作都是允许的,尽管可能出问题,因此文件共享方式适合在对数据同步要求不高的进程间进行通信。
SharedPreferences也属于文件的一种,但是由于系统对它的读写有一定的缓存策略,即在内存中会有一份SharedPreferences文件的缓存;因此在多进程模式下,系统对它的读写就变得不可靠,会有很大几率丢失数据,不建议在进程间通信中使用SharedPreferences。
三,使用Messager
如需让服务与远程进程通信,则可使用Messenger为服务提供接口。
定义一个MessengerService继承自Service,并在AndroidManifest.xml中声明并给一个进程名,使该服务成为一个单独的进程。代码如下:
MessengerService.java
public class MessengerService extends Service{
class IncomingHandler extends Handler{
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case 0:
Toast.makeText(getApplicationContext(), "hello, xiaozhi", Toast.LENGTH_SHORT).show();
break;
}
}
}
Messenger mMessenger = new Messenger(new IncomingHandler());
@Nullable
@Override
public IBinder onBind(Intent intent) {
Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();
return mMessenger.getBinder();
}
}
AndroidManifest.xml文件的配置如下:
<service android:name=".MessengerService"
android:process="com.xiaozhi.messenger.service"/>
TestMessengerActivity.java
public class TestMessengerActivityextends Activity{
private boolean mBound;
private Messenger mMessenger;
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mMessenger = new Messenger(service);
mBound = true;
}
@Override
public void onServiceDisconnected(ComponentName name) {
mMessenger = null;
mBound = false;
}
};
public void sayHello(View v){
if(!mBound){
return;
}
Message msg = Message.obtain(null, 0 , 0, 0);
try {
mMessenger.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
protected void onStart() {
super.onStart();
Intent intent = new Intent(MessengerActivity.this, MessengerService.class);
bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
if(mBound){
unbindService(mServiceConnection);
mBound = false;
}
}
}
四,使用AIDL
AIDL是一种接口描述语言,通常用于进程间通信。
使用AIDL的步骤:
- 创建AIDL,在main下新建一个文件夹aidl,然后在aidl下新建AIDL文件,这时系统会自动为该文件创建一个包名。
aidl文件中会有一个默认的basicType方法,我们为它增加一个getName方法。
interface ITestAidlInterface {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
String getName(String nickName);
}
以上是我们自己创建的aidl文件,系统还会自动生成aidl代码,所在位置为:build/generated/source/aidl下debug和release,但是此时debug下没有任何东西,可以rebuild或运行一下程序,再次打开debug,发现生成了一个包和一个aidl文件。
然后在java下新建一个类AIDLService继承自Service
public class AIDLService extends Service {
ITestAidlInterface.Stub mStub = new ITestAidlInterface.Stub() {
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
@Override
public String getName(String nickName) throws RemoteException {
return "aidl " + nickName;
}
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mStub;
}
}
接着在AndroidManifest.xml中注册,并给一个进程名,是该服务成为一个独立的进程。
<service android:name=".AIDLService"
android:process="com.xiaozhi.test.service"/>
在MainActivity中进行与AIDLService之间的进程间通信。
public class MainActivity extends AppCompatActivity {
private Button mBtnAidl;
private ITestAidlInterface mITestAidlInterface;
ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mITestAidlInterface= ITestAidlInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mBtnAidl = (Button) findViewById(R.id.btn_aidl);
bindService(new Intent(MainActivity.this, AIDLService.class), mServiceConnection, BIND_AUTO_CREATE);
mBtnAidl.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(mIMyAidlInterface != null){
try {
String name = mIMyAidlInterface.getName("I'm nick");
Toast.makeText(MainActivity.this, "name = " + name, Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
});
}
}
在Activity中利用bindService与AIDLService进行连接,通过IMyAidlInterface实例与AIDLService进程进行通信.