Android应用内多进程的使用及注意事项


 原文 http://zmywly8866.github.io/2015/06/14/android-multi-process-use-and-notice.html


Android应用内多进程介绍及使用

一个应用默认只有一个进程,这个进程(主进程)的名称就是应用的包名,进程是系统分配资源和调度的基本单位,每个进程都有自己独立的资源和内存空间,其它进程不能任意访问当前进程的内存和资源,系统给每个进程分配的内存会有限制。

如果一个进程占用内存超过了这个内存限制,就会报OOM的问题,很多涉及到大图片的频繁操作或者需要读取一大段数据在内存中使用时,很容易报OOM的问题,如果此时在程序中人为地使用GC会严重影响程序运行的流畅性,并且有时候并没有什么卵用,多数时候我们可以在android:minSdkVersion=”11”及以上的应用中,给AndroidManifest.xml的Application标签增加”android:largeHeap=”true”“这句话,请求系统给该应用分配更多可申请的内存:

但是这种做法的弊端有:

  • 有时候并不能彻底解决问题,比如API Level小于11时,或者是应用需要的内存比largeHeap分配的更大的时候;当该应用在后台时,仍然占用着的内存,系统总的内存就那么多,如果每个应用都向系统申请更多的内存,会影响整机运行效率。 

  • 为了彻底地解决应用内存的问题,Android引入了多进程的概念,它允许在同一个应用内,为了分担主进程的压力,将占用内存的某些页面单独开一个进程,比如Flash、视频播放页面,频繁绘制的页面等。Android多进程使用很简单,只需要在AndroidManifest.xml的声明四大组件的标签中增加”android:process”属性即可,process分私有进程和全局进程,私有进程的名称前面有冒号,全局进程没有,like this:

为了节省系统内存,在退出该Activity的时候可以将其杀掉(如果没有人为杀掉该进程,在程序完全退出时该进程会被系统杀掉),like this:

使用多进程会遇到的一些问题:

1.断点调试

调试就是跟踪程序运行过程中的堆栈信息,正如前面所讲,每个进程都有自己独立的资源和内存空间,每个进程的堆栈信息也是独立的,如果要在不同的进程间调试,是实现不了的,不过可以通过如下两种方式进行调试:

调试的时候去掉AndroidManifest.xml文件中Activity的android:process标签,这样保证调试状态下是在同一进程中,堆栈信息是连贯的,在调试完成后记得复原该属性;通过打印进行调试,但这种效率比较低。 

Activity管理:

通常我们为了完全退出一个应用,会在Application里面实现ActivityLifecycleCallbacks接口,监听Activity的生命周期,通过LinkedList来管理所有的Activity:

public class TestApplication extends Application implements ActivityLifecycleCallbacks{
	@Override
	public void onCreate() {
		super.onCreate();
		registerActivityLifecycleCallbacks(this);
	}
	@Override
	public void onActivityCreated(Activity activity, Bundle arg1) {
		if (null != mExistedActivitys && null != activity) {
			// 把新的 activity 添加到最前面,和系统的 activity 堆栈保持一致
			mExistedActivitys.offerFirst(new ActivityInfo(activity,ActivityInfo.STATE_CREATE));
		}
	}
	@Override
	public void onActivityDestroyed(Activity activity) {
		if (null != mExistedActivitys && null != activity) {
			ActivityInfo info = findActivityInfo(activity);
			if (null != info) {
				mExistedActivitys.remove(info);
			}
		}
	}
	@Override
	public void onActivityStarted(Activity activity) {
	}
	@Override
	public void onActivityResumed(Activity activity) {
	}
	@Override
	public void onActivityPaused(Activity activity) {
	}
	@Override
	public void onActivityStopped(Activity activity) {
	}
	@Override
	public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
	}
	public void exitAllActivity() {
		if (null != mExistedActivitys) {
			// 先暂停监听(省得同时在2个地方操作列表)
			unregisterActivityLifecycleCallbacks( this );
			// 弹出的时候从头开始弹,和系统的 activity 堆栈保持一致
			for (ActivityInfo info : mExistedActivitys) {
				if (null == info || null == info.mActivity) {
					continue;
				}
				try {
					info.mActivity.finish();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			mExistedActivitys.clear();
			// 退出完之后再添加监听
			registerActivityLifecycleCallbacks( this );
		}
	}
	private ActivityInfo findActivityInfo(Activity activity) {
		if (null == activity || null == mExistedActivitys) {
			return null;
		}
		for (ActivityInfo info : mExistedActivitys) {
			if (null == info) {
				continue;
			}
			if (activity.equals(info.mActivity)) {
				return info;
			}
		}
		return null;
	}
	class ActivityInfo {
		private final static int STATE_NONE = 0;
		private final static int STATE_CREATE = 1;
		Activity mActivity;
		int mState;
		ActivityInfo() {
			mActivity = null;
			mState = STATE_NONE;
		}
		ActivityInfo(Activity activity, int state) {
			mActivity = activity;
			mState = state;
		}
	}
	private LinkedList<ActivityInfo> mExistedActivitys = new LinkedList<ActivityInfo>();
}

但是如果应用内有多个进程,每创建一个进程就会跑一次Application的onCreate方法,每个进程内存都是独立的,所以通过这种方式无法实现将应用的Activity放在同一个LinkedList中,不能实现完全退出一个应用。

2.Application的多次重建


们发现MyApplication的onCreate方法调用了两次,打印出来的pid也不相同。由于通常会在Application的onCreate方法中做一些全局的初始化操作,它被初始化多次是完全没有必要的。出现这种情况,是由于即使是通过指定process属性启动新进程的情况下,系统也会新建一个独立的虚拟机,自然需要重新初始化一遍Application。那么怎么来解决这个问题呢?

       我们可以通过在自定义的Application中通过进程名来区分当前是哪个进程,然后单独进行相应的逻辑处理。

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public class MyApplication extends Application {  
  2.     public static final String TAG = "viclee";  
  3.   
  4.     @Override  
  5.     public void onCreate() {  
  6.         super.onCreate();  
  7.         int pid = android.os.Process.myPid();  
  8.         Log.d(TAG, "MyApplication onCreate");  
  9.         Log.d(TAG, "MyApplication pid is " + pid);  
  10.   
  11.         ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);  
  12.         List<ActivityManager.RunningAppProcessInfo> runningApps = am.getRunningAppProcesses();  
  13.         if (runningApps != null && !runningApps.isEmpty()) {  
  14.             for (ActivityManager.RunningAppProcessInfo procInfo : runningApps) {  
  15.                 if (procInfo.pid == pid) {  
  16.                      if (procInfo.processName.equals("com.example.processtest")) {  
  17.                          Log.d(TAG, "process name is " + procInfo.processName);  
  18.                      } else if (procInfo.processName.equals("com.example.processtest:remote")) {  
  19.                          Log.d(TAG, "process name is " + procInfo.processName);  
  20.                      }  
  21.                 }  
  22.             }  
  23.         }  
  24.     }  
  25. }  

3.静态成员不会共享:

按照正常的逻辑,静态变量是可以在应用的所有地方共享的,但是设置了process属性后,产生了两个隔离的内存空间,一个内存空间里值的修改并不会影响到另外一个内存空间。

4.数据文件无法共享:

不同进程之间内存不能共享,最大的弊端是他们之间通信麻烦,不能将公用数据放在Application中,堆栈信息、文件操作也是独立的。解决办法就是多进程的时候不并发访问同一个文件,比如子进程涉及到操作数据库,就可以考虑调用主进程进行数据库的操作。如果他们之间传递的数据不大并且是可序列化的,可以考虑通过Bundle传递, 如果数据量较大,则需要通过AIDL或者文件操作来实现。

结语

通过多进程可以分担应用内主进程的压力,但这是下下策,最好的解决方案还是要做好性能优化。











评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值