当您想要确定安卓App被系统杀死的原因时,可以通过以下步骤进行分析:
- 打开Android Studio中的Profiling工具
在您的项目中,打开Android Studio并进入Profiling工具。点击左上角的“Android Profiler”图标,选择“CPU”或“Memory”,使其显示应用程序的性能指标,在顶部的时间轴上可以看到应用程序的各个时间段的信息。
- 找到应用程序被杀死的时间段
在Profiling工具中,查找被杀死应用程序的具体时间。
从时间轴上找到应用程序被杀死的时间段。
- 分析系统报告
在找到被杀死应用程序的时间段后,可以通过Adb命令查看系统报告,以获取更多详细信息。例如,在终端中使用以下命令:
adb shell dumpsys meminfo <package_name>
其中 <package_name>
是您的应用程序包名称。这将输出您的应用程序在系统内存中的使用情况。从输出中查找应用程序被终止的时间段并分析内存使用情况。如果应用程序在被终止之前消耗了大量内存,可能会因内存不足而被系统终止。
- 检查日志
使用以下命令在Adb控制台中查看日志信息:
adb logcat -d *:E
这将打印所有的错误日志。查找您的应用程序被终止的时间段的日志。这样可以了解应用程序的哪些部分可能出现了问题。还可以在日志中查找对象分配和释放的详细信息,并确定是否有意外的长时间超时或阻止等问题。
如图,左侧是app运行被杀死前最后一条错误日志
2023-06-04 07:07:52.512 10156-10156/com.iot.listeninsoul
E/Toast: setGravity() shouldn't be called on text toasts, the values won't be used
右侧是系统在该app错误后的一条系统异常
06-04 07:07:52.512 10156 10156
E Toast : setGravity() shouldn't be called on text toasts, the values won't be used
06-04 07:07:52.564 428 13128
E BpBinder: Too many binder proxy objects sent to uid 1000 from uid 10127 (6000 proxies held)
至此我找到了app被系统杀死的原因
向系统进程(uid 1000)发送了超过6000个binder代理对象。
错误解决
在Android系统中,Binder是一种用于进程间通信(IPC)的机制。应用程序可以使用Binder在不同的进程之间进行通信。在某些情况下,由于使用不当,可能会导致系统输出错误日志"Too many binder proxy objects sent to uid xxx from uid yyy (zzz proxies held)",其中xxx和yyy为应用程序的UID和调用者的UID,zzz表示当前UID持有的Binder代理对象的数量。
这个错误日志的意思是,应用程序通过Binder机制向系统发送了太多的Binder代理对象,而这些代理对象导致系统的Binder进程(包括system_server等)消耗过多的内存和CPU资源。
造成这种错误日志的原因有很多,可能是应用程序在使用Binder时没有正确地释放它们,也可能是应用程序在使用Binder时使用了大量的回调和监听器,导致系统创建了大量的Binder代理对象并占用过多的系统资源。
解决这个问题的方法包括:
-
根据错误日志所提供的UID信息,定位和修复导致问题的应用程序。
-
在应用程序中使用Binder代理对象时,需要注意及时释放它们,以便避免过多的代理对象占用系统资源。可以使用try-finally块在释放代理对象时确保一定会被释放,例如:
IBinder binderProxy = null;
try {
binderProxy = getBinderProxyObject();
// 使用binderProxy对象
} finally {
if (binderProxy != null) {
binderProxy.unlinkToDeath(deathRecipient, 0);
binderProxy = null;
}
}
-
避免在应用程序中频繁使用回调和监听器,减少创建大量的Binder代理对象。
-
在应用程序的Manifest文件中,可以添加
android:process
属性,将服务或应用程序部分进程化,从而减少应用程序对系统资源的占用。
综上,遵循最佳实践和使用正确的代码约定可以帮助避免出现"Too many binder proxy objects sent to uid xxx from uid yyy (zzz proxies held)"的错误日志,并提高应用程序的性能和稳定性。
安卓UID 1000
在Android系统中,UID(User ID)是用于识别不同应用程序和进程之间的关键标识符。UID分为两类,系统UID和普通UID。系统中默认的UID为1000,是一个系统级别的UID,对应的是根用户(root)或系统管理员用户(system),它们可以执行一些非常重要的系统操作,并拥有系统内核和实现上的特殊权限。在Android系统中,UID为1000的进程因此是指系统进程,如:
-
System_server进程:是Android系统的核心进程,负责管理各种系统服务,如包管理、窗口管理、电源管理、软件更新等。这个进程应该是运行时间最久的进程之一,也是最重要的系统进程之一。
-
Mediaserver进程:是Android系统中负责处理音视频相关的服务的进程,例如解码、编码、格式转换等服务。在Android 5.0及之前的版本中,这个进程是由系统UID为1000的用户组管理的。
-
Surfaceflinger进程:是Android系统的显示层服务,负责在屏幕上显示图像、文字和其他类型的信息。它与其他组件,如Window Manager之间进行通信来显示和处理UI元素。
除了System_server、Mediaserver和Surfaceflinger进程之外,还有一些其他的系统进程也属于UID为1000的系统进程。这些系统进程都是非常重要的组件,它们控制着Android系统的核心部分,使用者无法直接操作,但却在Android系统中起着至关重要的作用。
Android 优化内存
在Android应用开发中,系统限制了每个应用程序在运行时所能够占用的内存大小。如果您的应用程序需要占用更多的内存,可以通过以下几种方法增加应用程序的可占用内存:
- 从Manifest中修改android:largeHeap属性
在应用程序的Manifest文件中,可以添加以下行来允许应用程序使用较大的堆空间:
<application
android:largeHeap="true"
...>
...
</application>
- 使用JNI和本地代码管理内存
通过使用JNI和本地代码进行内存管理,可以更有效地分配和释放内存。通过JNI,在本地代码中动态分配内存,可以更灵活地控制内存分配,同时避免了Java对象的垃圾回收机制,从而改善了应用程序的内存使用。
JNI(Java Native Interface)是一个允许Java代码调用本地代码(如C或C ++)的桥梁。JNI提供了一个接口,允许Java代码直接调用本地函数。在本地代码中,可以使用malloc()等函数来动态分配内存。由于这些内存不是由Java的垃圾回收机制管理,因此需要手动释放内存,以避免内存泄漏等问题。
以下是一个使用JNI和本地代码来管理内存的简单示例:
Java代码:
// 声明native方法,调用本地代码
public native int[] getArray(int size);
// 在getArray方法中调用本地方法
public int[] getArray(int size) {
return getArrayFromJNI(size);
}
// 加载本地库
static {
System.loadLibrary("native-lib");
}
// 定义本地方法,使用C/C++代码分配内存
private native int[] getArrayFromJNI(int size);
C/C++代码:
JNIEXPORT jintArray JNICALL Java_com_example_memorydemo_MainActivity_getArrayFromJNI(JNIEnv *env, jobject obj, jint size) {
// 调用C中动态分配内存的函数
int* arr = (int*)malloc(size * sizeof(int));
jintArray jArr = (*env)->NewIntArray(env, size);
// 将本地分配的内存拷贝到Java堆内存中
(*env)->SetIntArrayRegion(env, jArr, 0, size, arr);
return jArr;
}
在上述示例中,Java代码调用了本地代码中的getArrayFromJNI
方法,并传递了一个整数参数size
。在本地代码中,使用malloc
函数分配了一个整数数组,然后将整数数组拷贝到Java堆内存中,并使用NewIntArray
函数返回一个整数数组对象的引用。
需要注意的是,由于在本地代码中手动分配了内存,因此需要手动释放该内存以避免内存泄漏。可以在Java代码中将此任务交给本地代码,使其释放内存。例如:
Java代码:
public native void freeArray(int[] arr);
public void freeArray(int[] arr) {
freeArrayFromJNI(arr);
}
private native void freeArrayFromJNI(int[] arr);
C/C++代码:
JNIEXPORT void JNICALL Java_com_example_memorydemo_MainActivity_freeArrayFromJNI(JNIEnv *env, jobject obj, jintArray jArr) {
// 获取Java数组大小并释放本地代码中的内存
int size = (*env)->GetArrayLength(env, jArr);
int* arr = (int*) (*env)->GetIntArrayElements(env, jArr, NULL);
free(arr);
// 释放Java层的内存
(*env)->ReleaseIntArrayElements(env, jArr, arr, NULL);
}
在本示例中,释放本地代码中的内存和Java层的内存分别在本地代码中处理。在freeArray
方法中,Java代码调用freeArrayFromJNI
并传递整数数组的引用。本地代码中,使用GetIntArrayElements
和ReleaseIntArrayElements
函数分配和释放Java层的内存,然后使用free
函数释放本地代码中分配的内存。
通过使用JNI和本地代码管理内存,可以更精细地控制内存分配和释放,并避免Java的垃圾回收机制所带来的性能开销。
- 使用LRU缓存等缓存技术优化内存
通过使用LRU缓存等缓存技术,可以优化应用程序的内存使用。通过缓存技术,可以将常用的对象保存在内存中,减少对象的创建和销毁,从而降低内存使用。
使用缓存技术可以减少对象的创建和销毁,从而优化内存使用。其中,LRU(Least Recently Used)缓存是一种常用的缓存技术,它通过保存最近最少使用的对象,以优化内存使用。
以下是一个使用LRU缓存技术优化内存的示例:
public class LruCacheDemo {
private LruCache<String, Bitmap> cache;
public LruCacheDemo() {
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
final int cacheSize = maxMemory / 8; // 缓存大小为最大内存的1/8
// 初始化LRU缓存,并重写sizeOf方法来计算缓存项目的大小
cache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getByteCount() / 1024; // 像素数量转为KB
}
};
}
public void addBitmapToCache(String key, Bitmap bitmap) {
if (getBitmapFromCache(key) == null) {
cache.put(key, bitmap);
}
}
public Bitmap getBitmapFromCache(String key) {
return cache.get(key);
}
}
在上述示例中,使用了LruCache
类来实现LRU缓存。在构造函数中,根据最大内存大小计算出LRU缓存的大小,并重写了sizeOf
方法来计算缓存项目的大小。
在addBitmapToCache
方法中,如果缓存中不存在指定的key
,则将该图像添加到缓存中。在getBitmapFromCache
方法中,根据key
从缓存中获取相应的位图。
使用这种LRU缓存技术,可以避免重复加载已经存在的图片,减少了内存的使用,提高了应用程序的性能。
需要注意的是,LRU缓存技术并不适用于所有类型的应用程序,对于一些内存消耗很小的应用程序来说,LRU缓存技术的开销可能会超过收益。因此,在使用LRU缓存技术时,需要根据应用程序的实际情况进行调整。
- 优化应用程序的代码和资源
通过优化应用程序的代码和资源,可以减少应用程序的内存使用,从而达到增加可占用内存的目的。例如,移除不必要的资源、优化图片资源等。
需要注意的是,要合理使用内存的同时避免内存泄漏,否则会导致OOM(Out Of Memory)错误。应用程序的内存增加不表示应该浪费内存,而是应该在其使用场景中合理地利用内存。