Android多进程:一般情况下,一个应用程序就是一个进程,进程名就是应用程序的包名。进程是系统分配资源的基本单位,每个进程都有自己独立的资源和内存空间。

1 Android开启多进程的原因

  • 单进程分配的内存不够,需要更多的内存。 早期的Android系统只为一个单进程的应用分配了16MB的可用内存,随着手机硬件的提升和Android系统的改进,虽然可分配的内存越来越多,但仍然可以通过开启多进程来获取更多内存来处理自己的APP业务。
  • 进程之间相互监视,如果有进程被杀或者崩溃,另外的进程可以重新启动它
  • 一个进程退出了,另外的进程仍然可以工作,比如说推送服务,只要负责推送消息的进程没有退出,仍然能推送消息

2 开启多进程

AndroidManifest.xml中配置android:process

  • 第一种:如android:process = ":remote",以:开始,后面的字符串是可以随意指定的。如果包名是com.cah.androidtest,所以实际进程名是com.cah.androidtest:remote这种设置形式表示该进程为当前应用的私有进程,其他应用的组件不可以和它跑在同一进程中
  • 第二种:如android:process = "com.cah.androidtest.remote",以小写字母开头,表示运行在一个以这个名字命名的全局进程中,其他应用的组件可以和它跑在同一进程中(使用SharedUID,且签名一致),从而减少资源的占用。

首先在Activity中启动一个服务:

public class MainActivity extends AppCompatActivity {private static final String TAG = "MainActivity";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Intent myServiceIntent = new Intent(MainActivity.this, MyService.class);startService(myServiceIntent);}
}public class MyService extends Service {private static final String TAG = "MyService";@Overridepublic void onCreate() {Log.e(TAG, "onCreate ");}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {Log.e(TAG, "onStartCommand: ");return START_STICKY;}@Overridepublic void onDestroy() {Log.e(TAG, "onDestroy: ");}@Nullable@Overridepublic IBinder onBind(Intent intent) {return null;}
}

然后在AndroidManifest.xml中配置android:process就可以了:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.cah.androidtest"><applicationandroid:name=".MyApplication"android:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true"android:theme="@style/Theme.AndroidTest"><activity android:name=".MainActivity"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity><serviceandroid:name=".MyService"android:process=":remote" /></application></manifest>

查看进程:

3 Android IPC通信中的UIDPID识别

3.1 UID

Android上,一个UID标识一个应用程序。应用程序在安装时被分配UID,应用程序在设备上存续期间,UID保持不变。在Linux中的UID是用户的ID,由于Android系统设计之初是单用户系统,UID被赋予新的使命,数据共享。 不同程序如果要相互访问,只能是UID相同才可以,这使得数据共享具有一定的安全性。(不同的程序,还需要拥有相同的签名)

Android系统在Android 4.2开始加入多用户的支持。通常,第一个在系统中注册的用户将默认成为系统管理员。不同用户的设置各不相同,并且不同用户安装的应用以及应用数据也不相同。但是系统中和硬件相关的设置则是共用的,例如,网络设置等。

用户切换后前面用户运行的后台进程还可以继续运行。这样,进行用户切换时无须中断一些后台进行的耗时操作。

3.2 PID

PID即进程ID,一个应用里可以有多个PID。在Android系统中一般不会把已经kill掉的进程ID重新分配给新的进程,新的进程号,一般比之前所有的进程号都要大。

进程com.cah.androidtest


进程com.cah.androidtest:remote

进程com.jiandan.jianeryou

3.3 sharedUserId

Android中每个应用都有唯一的一个UID该应用程序下的资源仅对应用自身可见,如果想要其他应用程序可见,就要使用sharedUserId,这样就可以使两个应用程序公用一个UID

以下是两个单独的应用程序:com.cah.androidtestcom.cah.kotlintext

com.cah.kotlintest

class MainActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)val sp = getSharedPreferences("user", 0)sp.edit().putString("name", "Eileen").apply()}
}

com.cah.androidtest

public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);try {Context ct = this.createPackageContext("com.cah.kotlintest", Context.CONTEXT_IGNORE_SECURITY);SharedPreferences sp = ct.getSharedPreferences("user", MODE_PRIVATE);String name = sp.getString("name", "not get name");Log.d("kotlin", "share preference-->" + name); boolean isCommit = sp.edit().putInt("age", 10).commit();Log.d("kotlin", "share preference-->" + isCommit);} catch (PackageManager.NameNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}// Failed to ensure /data/user/0/com.cah.kotlintest/shared_prefs: mkdir failed: EACCES (Permission denied)
// kotlin: share preference-->not get name
// SharedPreferencesImpl: Couldn't create directory for SharedPreferences file /data/user/0/com.cah.kotlintest/shared_prefs/user.xml
// kotlin: share preference-->false

为两个应用程序的AndroidManifest.xml添加sharedUserId

<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.cah.kotlintest"android:sharedUserId="com.cah.share">...
</manifest><manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.cah.androidtest"android:sharedUserId="com.cah.share">...
</manifest>

此时运行程序会有The application could not be installed: INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,这是由于使用了sharedUserId后,不同的签名造成的。卸载程序,重新安装。

运行:

// kotlin: share preference-->Eileen
// kotlin: share preference-->true

查看程序:



4 查看Android应用程序内存

为了维持多任务的功能环境,Android为每个进程设置了最大内存(在设备出厂的时候就确定了)。表示堆分配的初始大小,超过这个值就会OOM

可以通过代码获得每个进程可用的最大内存:

ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
int heapGrowthLimit = am.getMemoryClass();

以下是源码:

/**
* Return the approximate per-application memory class of the current
* device.  This gives you an idea of how hard a memory limit you should
* impose on your application to let the overall system work best.  The
* returned value is in megabytes; the baseline Android memory class is
* 16 (which happens to be the Java heap limit of those devices); some
* devices with more memory may return 24 or even higher numbers.
*/
public int getMemoryClass() {return staticGetMemoryClass();
}/** @hide */
@UnsupportedAppUsage
static public int staticGetMemoryClass() {// Really brain dead right now -- just take this from the configured// vm heap size, and assume it is in megabytes and thus ends with "m".String vmHeapSize = SystemProperties.get("dalvik.vm.heapgrowthlimit", "");if (vmHeapSize != null && !"".equals(vmHeapSize)) {return Integer.parseInt(vmHeapSize.substring(0, vmHeapSize.length()-1));}// 如果不存在dalvik.vm.heapgrowthlimit,则以dalvik.vm.heapsize为准return staticGetLargeMemoryClass();
}/**
* Return the approximate per-application memory class of the current
* device when an application is running with a large heap.  This is the
* space available for memory-intensive applications; most applications
* should not need this amount of memory, and should instead stay with the
* {@link #getMemoryClass()} limit.  The returned value is in megabytes.
* This may be the same size as {@link #getMemoryClass()} on memory
* constrained devices, or it may be significantly larger on devices with
* a large amount of available RAM.
*
* <p>This is the size of the application's Dalvik heap if it has
* specified <code>android:largeHeap="true"</code> in its manifest.
*/
public int getLargeMemoryClass() {return staticGetLargeMemoryClass();
}/** @hide */
static public int staticGetLargeMemoryClass() {// Really brain dead right now -- just take this from the configured// vm heap size, and assume it is in megabytes and thus ends with "m".String vmHeapSize = SystemProperties.get("dalvik.vm.heapsize", "16m");return Integer.parseInt(vmHeapSize.substring(0, vmHeapSize.length() - 1));
}

dalvik.vm.heapgrowthlimit,单个进程可用的最大内存,主要针对的是这个值;dalvik.vm.heapsize,表示不受控情况下的极限堆, 如果要使用这个极限堆,需要在AndroidMainfest中指定:

<applicationandroid:name=".MyApplication"android:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:largeHeap="true"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true"android:theme="@style/Theme.AndroidTest">....
</application>

或:

$adb shell getprop dalvik.vm.heapgrowthlimit

$adb shell getprop dalvik.vm.heapsize

$adb shell getprop dalvik.vm.heapstartsize

能够获取极限堆的本意是为了一小部分消耗大量内存的应用,最好不要轻易使用,因为额外的内存会影响系统整体的用户体验,并且会使得GC的运行时间更长。另外,一些要求严格的设备dalvik.vm.heapsizedalvik.vm.heapgrowthlimit可能是一样的。

查看内存使用情况:


Native Heap:给Native层分配的内存;Dalvik HeapJava对象在堆中分配的内存

Heap内存有3列,Heap Size—可用最大内存;Heap Alloc—已经分配的内存;Heap Free—剩余可用内存。Heap Size = Heap Alloc + Heap Free。如果Heap Free变得很小,就可能发生OOM

5 开启多进程引出的问题

5.1 使Application运行多次

public class MyApplication extends Application {private static final String TAG = "CAH";@Overridepublic void onCreate() {super.onCreate();int pid = android.os.Process.myPid();Log.e(TAG, "Application.onCreate ==== pid: " + pid);String processNameString = "";ActivityManager mActivityManager =(ActivityManager) this.getSystemService(getApplicationContext().ACTIVITY_SERVICE);for (ActivityManager.RunningAppProcessInfo appProcess : mActivityManager.getRunningAppProcesses()) {Log.e(TAG, "onCreate: appProcess.pid = " + appProcess.pid);if (appProcess.pid == pid) {processNameString = appProcess.processName;}}if ("com.cah.androidtest".equals(processNameString)) {Log.e(TAG, "onCreate: processName = " + processNameString + " ===work");} else {Log.e(TAG, "onCreate: processName = " + processNameString + " ===work");}}
}// Application.onCreate ==== pid: 20747
// onCreate: appProcess.pid = 20747
// onCreate: processName = com.cah.androidtest ===work// Application.onCreate ==== pid: 20792
// onCreate: appProcess.pid = 20747
// onCreate: appProcess.pid = 20792
// onCreate: processName = com.cah.androidtest:remote ===work

开启的两个进程都会执行onCreate方法。所以在Application中进行初始化操作以及数据的传递操作,都是不合适的,解决的方法就是得到每个进程的名称,如果进程的名称和应用的进程名相同则进行相应的操作,如果不相同则进行其他进程的操作。

5.2 静态成员失效

public class MainActivity extends AppCompatActivity {private final static String TAG = "MainActivity";public static boolean processFlag = false;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);processFlag = true;Intent service = new Intent(this, MyService.class);startService(service);}
}public class MyService extends Service {private static final String TAG = "MyService";@Overridepublic void onCreate() {new Thread(new TcpServer()).start();super.onCreate();Log.e(TAG, "onCreate: " + MainActivity.processFlag); // false}@Nullable@Overridepublic IBinder onBind(Intent intent) {throw null;}@Overridepublic void onDestroy() {super.onDestroy();}
}

设置了android:process属性之后,产生了两个隔离的内存空间,在一个进程的内存空间里修改一个值并不会影响到另外的内存空间。

5.3 文件共享问题

多进程情况下会出现两个进程在同一时刻访问同一文件的情况,这可能会造成资源的竞争。在多线程的情况下,有锁机制控制资源的共享,但是多进程比较难,虽然有文件锁、排队等机制,但是在Android中很难实现。解决方法就是多进程的时候不要同时访问一个文件,选择一个进程进行操作,其他的进程调用操作进程实现。

5.4 断点调试问题

调试就是跟踪程序运行过程中的堆栈消息,由于每个进程都有自己独立的内存空间和各自的对战,无法实现在不同的进程间调试。调试时可以先去掉android:process标签,这样可以保证调试状态下在同一进程中,堆栈信息时连贯的,调试完后,在恢复。

参考

https://www.cnblogs.com/weizhxa/p/8457987.html
https://www.cnblogs.com/helloTerry1987/p/13109971.html
https://blog.csdn.net/weixin_41987588/article/details/82694758
https://blog.csdn.net/c_z_w/article/details/85336283
https://blog.csdn.net/weixin_33762130/article/details/93196657?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_baidulandingword-4&spm=1001.2101.3001.4242
https://blog.csdn.net/skyxiaojt/article/details/80002913?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-5&spm=1001.2101.3001.4242
https://www.cnblogs.com/mythou/p/3258715.html
https://www.cnblogs.com/helloTerry1987/p/13109971.html
https://xuexuan.blog.csdn.net/article/details/52328928

Android多进程(一)—— 开启多进程相关推荐

  1. android开启多个service进程,Android开启多进程

    1. 为何要开启多进程 为何开启android应用要开启多进程,主要有以下几点: 单进程所分配的内存不够,需要更多的内存.在早期android系统只为一个单进程的应用分配了16M的可用内存,随着手机的 ...

  2. android 启动多个进程,Android开启多进程

    8种机械键盘轴体对比 本人程序员,要买一个写代码的键盘,请问红轴和茶轴怎么选? Android开启多进程的方式:给四大组建配置android:process=""属性: 通过JNI ...

  3. android 跨进程 android:process,Android跨进程通信技术-多进程模式的运行机制

    本文为个人学习笔记分享,没有任何商业化行为,对其他文章的引用都会标记.如有侵权行为,请及时提醒更正!如需转载请表明出处 本文主要来源是 任玉刚大神的<Android开发艺术探索> 如果说用 ...

  4. Android中设置组件多进程

    MultiProcessComponent 源码地址 github源码下载地址https://github.com/onlynight/MultiProcessComponent 概述 这个demo中 ...

  5. python启动多个进程_Python程序中的进程操作--—--开启多进程

    Python程序中的进程操作-----开启多进程 之前我们已经了解了很多进程相关的理论知识,了解进程是什么应该不再困难了,刚刚我们已经了解了,运行中的程序就是一个进程.所有的进程都是通过它的父进程来创 ...

  6. python多进程原理_python多进程的详细介绍(附示例)

    本篇文章给大家带来的内容是关于PHP中的SAPI是什么?如何实现?(图文),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. 进程 Python是运行在解释器中的语言,查找资料知道,py ...

  7. Android 拍照是开启(调用)闪光灯(原创)

    2019独角兽企业重金招聘Python工程师标准>>> ....//mCamera为Camera对象,下面的是拍照方法.mCamera.takePicture(null, null, ...

  8. Android零基础入门第7节:搞定Android模拟器,开启甜蜜之旅

    原文:Android零基础入门第7节:搞定Android模拟器,开启甜蜜之旅 在前几期中总结分享了Android的前世今生.Android 系统架构和应用组件那些事.带你一起来聊一聊Android开发 ...

  9. Android studio3.0开启抓包功能打包会使apk体积增大好几倍

    Android studio3.0开启抓包功能打包会使apk体积增大好几倍 Android studio3.0开启抓包功能打包会使apk体积增大好几倍: 今天尝试了下AS3.0抓包功能,还没怎么整明白 ...

最新文章

  1. szu cf集训Codeforces Round #631 (Div. 2)A ~ D[贪心,数据结构,思维,dp]
  2. 最不安全的网络管理员大盘点
  3. 运维基础(13)日志切割工具 Logrotate
  4. java23中设计模式——行为模式——Memento(备忘机制)
  5. c语言三个数从小到大排序/输出_我的c语言笔记(三)
  6. oracle中srv添加监听服务,Oracle 11g ORA-12514:TNS:监听程序当前无法识别连接描述符中请求的服务...
  7. 爬虫实现原理与实现技术
  8. 一、tars简单介绍 二、tars 安装部署资料准备
  9. 08.为什么要使用lombok,它解决了什么问题?
  10. python中qt有哪些控件_使用PyQt5调用Qt程序,基础PythonQt控件的使用方法
  11. Caffe + Ubuntu 14.04 64bit + CUDA6.5 + 无GPU 配置
  12. 在js中对HTML的radio标签和checkbox标签的选择项进行输出
  13. MySQL的函数-窗口函数
  14. Android黑白照片上色APP,黑白图片上色工具
  15. Linux 工具 | 第1篇:高级流控-TC+HTB+IFB+内核模块
  16. 【懒懒的Python学习笔记二】
  17. 未能打开这台计算机的策略组对象,Windows7 组策略错误:“未能打开这台计算机上的组策略对象。您可能没有合适的权限。”...
  18. 从零开始的Docker [ 7 ] --- 顶级 Volumes,数据卷, 系统限制sysctls
  19. 电脑断电后,突然无线有线网卡无法使用
  20. 转载:Vsphere 出现 “ XXX esx.problem.hyperthreading.unmitigated.formatOnHost not found XXX”的解决方案

热门文章

  1. 微信小程序-拼图动态验证
  2. java文件上传后如何删除_java中上传完文件删除不了的问题
  3. “everything over IP over everything”含义说明
  4. windows2008 r2,安装扫描仪驱动
  5. 2021年中国铝合金模板行业市场现状及企业格局分析:中国忠旺等行业龙头企业市场份额将不断提升[图]
  6. vb.net线程详细讲解
  7. R语言绘制布林带通道
  8. MSP432P401R学习笔记
  9. 计算机与经济学之关联刍议,工商管理本科毕业论文参考选题[1]-1
  10. 阿里云祝顺民:生而为云,连接增长