欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  科技

3章 性能平台GodEye源码分析-内存模块

程序员文章站 2022-03-11 15:37:31
3. 内存模块3.1 HeapHeap指的是监控应用的堆内存信息,主要是通过系统提供的Api获取,用法简单源码分析1、启动Heap的监控Heap的监控通过启动定时器,按xml配置的时间进行采集,通过MemoryUtil.getAppHeapInfo()获取堆内存信息public class HeapEngine implements Engine { private Producer mProducer; private long mInterva...

3. 内存模块

3.1 Heap

3章 性能平台GodEye源码分析-内存模块

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

3章 性能平台GodEye源码分析-内存模块

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

3章 性能平台GodEye源码分析-内存模块

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

3章 性能平台GodEye源码分析-内存模块

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