3章 性能平台GodEye源码分析-内存模块
3. 内存模块
3.1 Heap
Heap指的是监控应用的堆内存信息,主要是通过系统提供的Api获取,用法简单
源码分析
1、启动Heap的监控
Heap的监控通过启动定时器,按xml配置的时间进行采集,通过MemoryUtil.getAppHeapInfo()
获取堆内存信息
public class HeapEngine implements Engine {
private Producer<HeapInfo> mProducer;
private long mIntervalMillis;
private CompositeDisposable mCompositeDisposable;
HeapEngine(Producer<HeapInfo> producer, long intervalMillis) {
mProducer = producer;
mIntervalMillis = intervalMillis;
mCompositeDisposable = new CompositeDisposable();
}
@Override
public void work() {
mCompositeDisposable.add(Observable.interval(mIntervalMillis, TimeUnit.MILLISECONDS).map(new Function<Long, HeapInfo>() {
@Override
public HeapInfo apply(Long aLong) throws Exception {
ThreadUtil.ensureWorkThread("HeapEngine apply");
return MemoryUtil.getAppHeapInfo();
}
}).subscribeOn(ThreadUtil.computationScheduler())
.observeOn(ThreadUtil.computationScheduler())
.subscribe(new Consumer<HeapInfo>() {
@Override
public void accept(HeapInfo food) throws Exception {
ThreadUtil.ensureWorkThread("HeapEngine accept");
mProducer.produce(food);
}
}));
}
}
2、采集Heap信息
获取参数
- freeMemKb:可分配内存
- maxMemKb:最大内存
- allocatedKb:已分配内存
public static HeapInfo getAppHeapInfo() {
Runtime runtime = Runtime.getRuntime();
HeapInfo heapInfo = new HeapInfo();
heapInfo.freeMemKb = runtime.freeMemory() / 1024;
heapInfo.maxMemKb = Runtime.getRuntime().maxMemory() / 1024;
heapInfo.allocatedKb = (Runtime.getRuntime().totalMemory() - runtime.freeMemory()) / 1024;
return heapInfo;
}
3、总结
堆内存获取比较简单,以系统提供Api为准
3.2 Pss
PSS指的是监控应用的物理内存,PSS = 进程实际使用物理内存(USS) + 按比例分配共享库占用的内存
,最早的时候官方就推荐使用PSS曲线图来衡量App的物理内存占用,而Android 4.4之后才加入USS。但是PSS,有个很大的问题,就是”共享内存“,考虑一种情况,如果A进程与B进程都会使用一个共享so库,那么so库中初始化所用掉的那部分内存就会平分到A与B的头上。但是A是在B之后启动的,那么对于B的PSS曲线而言,在A启动的那一刻,即使B没有做任何事情,也会出现一个比较大的阶梯状下滑
源码分析
1、启动PSS的监控
PSS的监控通过启动定时器,按xml配置的时间进行采集,通过MemoryUtil.getAppPssInfo()
获取PSS信息
public class PssEngine implements Engine {
private Context mContext;
private Producer<PssInfo> mProducer;
private long mIntervalMillis;
private CompositeDisposable mCompositeDisposable;
PssEngine(Context context, Producer<PssInfo> producer, long intervalMillis) {
mContext = context;
mProducer = producer;
mIntervalMillis = intervalMillis;
mCompositeDisposable = new CompositeDisposable();
}
@Override
public void work() {
mCompositeDisposable.add(Observable.interval(mIntervalMillis, TimeUnit.MILLISECONDS).map(aLong -> {
ThreadUtil.ensureWorkThread("PssEngine accept");
return MemoryUtil.getAppPssInfo(mContext);
}).subscribeOn(ThreadUtil.computationScheduler())
.observeOn(ThreadUtil.computationScheduler())
.subscribe(food -> {
ThreadUtil.ensureWorkThread("PssEngine accept");
mProducer.produce(food);
}));
}
}
2、采集PSS信息
获取参数
- totalPssKb:当前进程的PSS
- dalvikPssKb:dalvik虚拟机(java层)的PSS
- nativePssKb:native层的PSS
- otherPssKb:native和dalvik部分之外的PSS
public static PssInfo getAppPssInfo(Context context) {
final int pid = ProcessUtils.getCurrentPid();
if (sActivityManager == null) {
sActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
}
Debug.MemoryInfo memoryInfo = sActivityManager.getProcessMemoryInfo(new int[]{pid})[0];
PssInfo pssInfo = new PssInfo();
pssInfo.totalPssKb = memoryInfo.getTotalPss();
pssInfo.dalvikPssKb = memoryInfo.dalvikPss;
pssInfo.nativePssKb = memoryInfo.nativePss;
pssInfo.otherPssKb = memoryInfo.otherPss;
return pssInfo;
}
public static int getCurrentPid() {
return android.os.Process.myPid();
}
3、总结
PSS的采集获取比较简单,以系统提供Api为准,但它在实际应用过程中比较难以派上用场
3.3 Ram
RAM为当前手机设备的运行内存,比如,360手机助手的悬浮窗,经常提示的运行内存超过80%之类,主要是通过系统提供的Api获取,用法简单
源码分析
1、启动Ram的监控
Ram的监控通过启动定时器,按xml配置的时间进行采集,通过MemoryUtil.getRamInfo(mContext)
获取Ram信息
public class RamEngine implements Engine {
private Context mContext;
private Producer<RamInfo> mProducer;
private long mIntervalMillis;
private CompositeDisposable mCompositeDisposable;
RamEngine(Context context, Producer<RamInfo> producer, long intervalMillis) {
mContext = context;
mProducer = producer;
mIntervalMillis = intervalMillis;
mCompositeDisposable = new CompositeDisposable();
}
@Override
public void work() {
mCompositeDisposable.add(Observable.interval(mIntervalMillis, TimeUnit.MILLISECONDS).map(new Function<Long, RamInfo>() {
@Override
public RamInfo apply(Long aLong) throws Exception {
ThreadUtil.ensureWorkThread("RamEngine apply");
return MemoryUtil.getRamInfo(mContext);
}
})
.subscribeOn(ThreadUtil.computationScheduler())
.observeOn(ThreadUtil.computationScheduler())
.subscribe(new Consumer<RamInfo>() {
@Override
public void accept(RamInfo food) throws Exception {
ThreadUtil.ensureWorkThread("RamEngine accept");
mProducer.produce(food);
}
}));
}
}
2、采集Ram信息
获取参数
- availMemKb:可用RAM
- totalMemKb:手机总RAM
- lowMemThresholdKb:内存占用满的阀值,超过即认为低内存运行状态,可能会Kill process
- isLowMemory:是否低内存状态运行
public static RamInfo getRamInfo(Context context) {
if (sActivityManager == null) {
sActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
}
final ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo();
sActivityManager.getMemoryInfo(mi);
final RamInfo ramMemoryInfo = new RamInfo();
ramMemoryInfo.availMemKb = mi.availMem / 1024;
ramMemoryInfo.isLowMemory = mi.lowMemory;
ramMemoryInfo.lowMemThresholdKb = mi.threshold / 1024;
ramMemoryInfo.totalMemKb = getRamTotalMem(sActivityManager);
return ramMemoryInfo;
}
/**
* 同步获取系统的总ram大小
*
* @param activityManager
* @return
*/
private static long getRamTotalMem(ActivityManager activityManager) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo();
activityManager.getMemoryInfo(mi);
return mi.totalMem / 1024;
} else if (sTotalMem.get() > 0L) {//如果已经从文件获取过值,则不需要再次获取
return sTotalMem.get();
} else {
final long tm = getRamTotalMemByFile();
sTotalMem.set(tm);
return tm;
}
}
/**
* 获取手机的RAM容量,其实和activityManager.getMemoryInfo(mi).totalMem效果一样,也就是说,在API16以上使用系统API获取,低版本采用这个文件读取方式
*
* @return 容量KB
*/
private static long getRamTotalMemByFile() {
final String dir = "/proc/meminfo";
try {
FileReader fr = new FileReader(dir);
BufferedReader br = new BufferedReader(fr, 2048);
String memoryLine = br.readLine();
String subMemoryLine = memoryLine.substring(memoryLine
.indexOf("MemTotal:"));
br.close();
return (long) Integer.parseInt(subMemoryLine.replaceAll(
"\\D+", ""));
} catch (IOException e) {
e.printStackTrace();
}
return 0L;
}
3、总结
目前Ram获取比较简单,都可以通过系统的APi获取
3.4 AppSize
AppSize指的是获取应用总大小,获取AppSize需要权限
<uses-permission android:name="android.permission.GET_PACKAGE_SIZE"></uses-permission>
源码分析
1、启动AppSize的监控
AppSize的监控通过启动定时器,按xml配置的时间进行采集,通过AppSizeUtil.getAppSize()
获取AppSize信息
public class AppSize extends ProduceableSubject<AppSizeInfo> implements Install<AppSizeConfig> {
private Disposable disposable;
private boolean mInstalled = false;
private AppSizeConfig mConfig;
@Override
public synchronized boolean install(AppSizeConfig config) {
if (mInstalled) {
L.d("AppSize already installed, ignore.");
return true;
}
mInstalled = true;
mConfig = config;
disposable = ThreadUtil.computationScheduler().scheduleDirect(() -> AppSizeUtil.getAppSize(GodEye.instance().getApplication(), new AppSizeUtil.OnGetSizeListener() {
@Override
public void onGetSize(AppSizeInfo appSizeInfo) {
L.d("AppSize onGetSize: cache size: %s, data size: %s, codeSize: %s", AppSizeUtil.formatSize(appSizeInfo.cacheSize),
AppSizeUtil.formatSize(appSizeInfo.dataSize), AppSizeUtil.formatSize(appSizeInfo.codeSize));
produce(appSizeInfo);
}
@Override
public void onError(Throwable t) {
L.d("AppSize onGetError: %s", String.valueOf(t));
produce(AppSizeInfo.INVALID);
}
}), config.delayMillis(), TimeUnit.MILLISECONDS);
L.d("AppSize installed.");
return true;
}
}
2、采集AppSize信息
AppSize获取需要按版本去获取
static void getAppSize(Context context, OnGetSizeListener listener) {
if (listener == null) {
return;
}
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
getAppSizeAboveO(context, listener);
} else {
getAppSizeLowerO(context, listener);
}
} catch (Exception e) {
listener.onError(e);
}
}
在AndroidO以上通过系统服务StorageManager
找到对应的应用获取
/**
* 获取应用的大小
*/
@RequiresApi(api = Build.VERSION_CODES.O)
private static void getAppSizeAboveO(Context context, @NonNull OnGetSizeListener listener) {
StorageStatsManager storageStatsManager = (StorageStatsManager) context
.getSystemService(Context.STORAGE_STATS_SERVICE);
StorageManager storageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
// 获取所有应用的StorageVolume列表
List<StorageVolume> storageVolumes = storageManager.getStorageVolumes();
for (StorageVolume item : storageVolumes) {
String uuidStr = item.getUuid();
UUID uuid;
if (uuidStr == null) {
uuid = StorageManager.UUID_DEFAULT;
} else {
uuid = UUID.fromString(uuidStr);
}
int uid = getUid(context, context.getPackageName());
// 通过包名获取uid
StorageStats storageStats;
try {
storageStats = storageStatsManager.queryStatsForUid(uuid, uid);
AppSizeInfo ctAppSizeInfo = new AppSizeInfo();
ctAppSizeInfo.cacheSize = storageStats.getCacheBytes();
ctAppSizeInfo.dataSize = storageStats.getDataBytes();
ctAppSizeInfo.codeSize = storageStats.getAppBytes();
listener.onGetSize(ctAppSizeInfo);
} catch (IOException e) {
listener.onError(e);
}
}
}
/**
* 根据应用包名获取对应uid
*/
private static int getUid(Context context, String pakName) {
try {
return context.getPackageManager().getApplicationInfo(pakName, PackageManager.GET_META_DATA).uid;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return -1;
}
在AndroidO以下,通过aidl方式调用getPackageSizeInfo
去获取大小,其中IPackageStatsObserver
由于参数需要个aidl的接口,我们创建IPackageStatsObserver.aidl
文件到aidl目录下
// IPackageStatsObserver.aidl
package android.content.pm;
// 包名必须在android.content.pm
import android.content.pm.PackageStats;
interface IPackageStatsObserver {
oneway void onGetStatsCompleted(in PackageStats pStats, boolean succeeded);
}
通过反射的形式去获取当前的应用大小
/**
* 获取应用大小8.0以下
*/
@SuppressWarnings("JavaReflectionMemberAccess")
private static void getAppSizeLowerO(Context context, @NonNull final OnGetSizeListener listener) {
try {
Method method = PackageManager.class.getMethod("getPackageSizeInfo", String.class,
IPackageStatsObserver.class);
// 调用 getPackageSizeInfo 方法,需要两个参数:1、需要检测的应用包名;2、回调
method.invoke(context.getPackageManager(), context.getPackageName(), new IPackageStatsObserver.Stub() {
@Override
public void onGetStatsCompleted(PackageStats pStats, boolean succeeded) {
AppSizeInfo ctAppSizeInfo = new AppSizeInfo();
ctAppSizeInfo.cacheSize = pStats.cacheSize;
ctAppSizeInfo.dataSize = pStats.dataSize;
ctAppSizeInfo.codeSize = pStats.codeSize;
listener.onGetSize(ctAppSizeInfo);
}
});
} catch (Throwable e) {
listener.onError(e);
}
}
获取参数
- cacheSize:应用的缓存大小,比如
/data/data/<app>/cache
- dataSize:应用内部数据的大小,比如
/data/data/<app>
- codeSize:应用代码的大小,即APK的大小
3、总结
AppSize的获取需要兼容各个版本的获取方式,获取起来会比较麻烦一点,在性能指标上,应用大小也算是一个点
本文地址:https://blog.csdn.net/qq_30379689/article/details/111870041