写点什么

看我一波,Android 获取进程名函数,代码优化到极致的操作

用户头像
Android架构
关注
发布于: 3 小时前

但是,大叔要说,这个方法不是最优解。

二、通过 ActivityManager 获取当前进程名的弊端

  1. ActivityManager.getRunningAppProcesses() 方法需要跨进程通信,效率不高


需要 和 系统进程的 ActivityManagerService 通信。必然会导致该方法调用耗时。


  1. 拿到 RunningAppProcessInfo 的列表之后,还需要遍历一遍找到与当前进程的信息。


显然额外的循环也会增加耗时;



当然这个耗时影响很小。


  1. 最恐怖的是 ActivityManager.getRunningAppProcesses() 有可能调用失败,返回 null,也可能 AIDL 调用失败。


当然 ActivityManager.getRunningAppProcesses()调用失败是极低的概率。



当你的 APP 用户量达到一定的数量级别时,一定会有用户遇到 ActivityManager.getRunningAppProcesses()调用失败的情况。



在我们开头描述的使用场景中,出现进程名获取失败的情况,将会是非常恐怖。



一旦导致进程中的某些组件没有初始化,整个进程大概率是要 gg 了。

三、寻求更优解

**方法一:大叔发现,在 android api28 的时候新增了一个方法:[Application.getProcessName()](


)**



Application.getProcessName()方法直接返回当前进程名。这不就是我们想要的 API 吗!


但是这个方法只有在 android9【也就是 aip28】之后的系统才能调用。


public class ProcessUtil {


/**


  • 通过 Application 新的 API 获取进程名,无需反射,无需 IPC,效率最高。


*/


public static String getCurrentProcessNameByApplication() {


if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {


return Application.getProcessName();


}


return null;


}


}


android9 以前的系统怎么办呢?


android9 以前的系统怎么办呢?


android9 以前的系统怎么办呢?


方法二:ActivityThread.currentProcessName() 方法


于是大叔好奇,看了看 Application.getProcessName()的源码,他是如何实现的?


public class Application extends ContextWrapper implements ComponentCallbacks2 {


public static String getProcessName() {


return ActivityThread.currentProcessName();


}


}


我们发现了 ActivityThread.currentProcessName()这个方法。


于是大叔继续翻了下源码:


/**


  • {@hide}


*/


public final class ActivityThread extends ClientTransactionHandler {


public static String currentProcessName() {


//....


}


}


奥利给,ActivityThread.currentProcessName()方法居然是 public static 的。


奥利给,ActivityThread.currentProcessName()方法居然是 public static 的。


奥利给,ActivityThread.currentProcessName()方法居然是 public static 的。


但是,马上就发现:


ActivityThread 类是 hide 的,app 无法直接调用。


ActivityThread 类是 hide 的,app 无法直接调用。


ActivityThread 类是 hide 的,app 无法直接调用。


于是大叔继续翻源码,看看这个方法是什么时候新增的。



大叔发现这个方法在 android4.3.1 上就已经有了这个方法了。


在 android4.0.4 上没有找到 currentProcessName()方法。


那么意味着,我们是不是可以反射调用 ActivityThread.currentProcessName() ?


答案当然是可以。


于是我们在 ProcessUtil 工具类中实现了这个方法:


public class ProcessUtil {


/**


  • 通过反射 ActivityThread 获取进程名,避免了 ipc


*/


public static String getCurrentProcessNameByActivityThread() {


String processName = null;


try {


final Method declaredMethod = Class.forName("android.app.ActivityThread", false, Application.class.getClassLoader())


.getDeclaredMethod("currentProcessName", (Class<?>[]) new Class[0]);


declaredMethod.setAccessible(true);


final Object invoke = declaredMethod.invoke(null, new Object[0]);


if (invoke instanceof String) {


processName = (String) invoke;


}


} catch (Throwable e) {


}


return processName;


}


}

四、将三

《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


种方法结合得到一个更优方案


于是我们将三个方法结合。


  1. 我们优先通过 Application.getProcessName() 方法获取进程名。

  2. 如果获取失败,我们再反射 ActivityThread.currentProcessName()获取进程名

  3. 如果失败,我们才通过常规方法 ActivityManager 来获取进程名


如下代码:


public class ProcessUtil {


private static String currentProcessName;


/**


  • @return 当前进程名


*/


@Nullable


public static String getCurrentProcessName(@NonNull Context context) {


if (!TextUtils.isEmpty(currentProcessName)) {


return currentProcessName;


}


//1)通过 Application 的 API 获取当前进程名


currentProcessName = getCurrentProcessNameByApplication();


if (!TextUtils.isEmpty(currentProcessName)) {


return currentProcessName;


}


//2)通过反射 ActivityThread 获取当前进程名


currentProcessName = getCurrentProcessNameByActivityThread();


if (!TextUtils.isEmpty(currentProcessName)) {


return currentProcessName;


}


//3)通过 ActivityManager 获取当前进程名


currentProcessName = getCurrentProcessNameByActivityManager(context);


return currentProcessName;


}


/**


  • 通过 Application 新的 API 获取进程名,无需反射,无需 IPC,效率最高。


*/


public static String getCurrentProcessNameByApplication() {


if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {


return Application.getProcessName();


}


return null;


}


/**


  • 通过反射 ActivityThread 获取进程名,避免了 ipc


*/


public static String getCurrentProcessNameByActivityThread() {


String processName = null;


try {


final Method declaredMethod = Class.forName("android.app.ActivityThread", false, Application.class.getClassLoader())


.getDeclaredMethod("currentProcessName", (Class<?>[]) new Class[0]);


declaredMethod.setAccessible(true);


final Object invoke = declaredMethod.invoke(null, new Object[0]);


if (invoke instanceof String) {


processName = (String) invoke;


}


} catch (Throwable e) {


e.printStackTrace();


}


return processName;


}


/**


  • 通过 ActivityManager 获取进程名,需要 IPC 通信


*/


public static String getCurrentProcessNameByActivityManager(@NonNull Context context) {


if (context == null) {


return null;


}


int pid = Process.myPid();

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
看我一波,Android获取进程名函数,代码优化到极致的操作