有没有办法在Android中获取当前进程名称

时间:2013-10-28 10:06:08

标签: android

我为我的特定活动设置了一个Android:process =“:XX”,使其在一个单独的进程中运行。 但是当新的活动/进程init时,它将调用我的Application:onCreate(),其中包含一些应用程序级初始化。

我正在考虑通过检查当前进程名称来避免重复初始化。

那么有API吗?

感谢。

11 个答案:

答案 0 :(得分:27)

完整代码

    String currentProcName = "";
    int pid = android.os.Process.myPid();
    ActivityManager manager = (ActivityManager) this.getSystemService(Context.ACTIVITY_SERVICE);
    for (RunningAppProcessInfo processInfo : manager.getRunningAppProcesses())
    {
        if (processInfo.pid == pid)
        {
            currentProcName = processInfo.processName;
            return;
        }
    }

答案 1 :(得分:25)

ActivityManager解决方案包含一个偷偷摸摸的错误,特别是如果您从Application对象中检查自己的进程名称。有时,从getRunningAppProcesses返回的列表根本不包含您自己的进程,从而引发了一个特殊的存在问题。

我解决这个问题的方法是

    BufferedReader cmdlineReader = null;
    try {
        cmdlineReader = new BufferedReader(new InputStreamReader(
            new FileInputStream(
                "/proc/" + android.os.Process.myPid() + "/cmdline"),
            "iso-8859-1"));
        int c;
        StringBuilder processName = new StringBuilder();
        while ((c = cmdlineReader.read()) > 0) {
            processName.append((char) c);
        }
        return processName.toString();
    } finally {
        if (cmdlineReader != null) {
            cmdlineReader.close();
        }
    }

编辑:请注意,此解决方案比通过ActivityManager快得多,但如果用户正在运行Xposed或类似操作,则无效。在这种情况下,您可能希望将ActivityManager解决方案作为后备策略。

答案 2 :(得分:5)

这是DavidBurström回答的更新。这可以更简洁地写成:

public String get() {
  final File cmdline = new File("/proc/" + android.os.Process.myPid() + "/cmdline");
  try (BufferedReader reader = new BufferedReader(new FileReader(cmdline))) {
    return reader.readLine();
  } catch (IOException e) {
    throw new RuntimeException(e);
  }
}

答案 3 :(得分:4)

我有更有效的方法,你不需要IPC到ActivityManagerService并轮询正在运行的进程,或者读取文件。你可以从你的自定义Application类调用这个方法;

 private String getProcessName(Application app) {
    String processName = null;
    try {
        Field loadedApkField = app.getClass().getField("mLoadedApk");
        loadedApkField.setAccessible(true);
        Object loadedApk = loadedApkField.get(app);

        Field activityThreadField = loadedApk.getClass().getDeclaredField("mActivityThread");
        activityThreadField.setAccessible(true);
        Object activityThread = activityThreadField.get(loadedApk);

        Method getProcessName = activityThread.getClass().getDeclaredMethod("getProcessName", null);
        processName = (String) getProcessName.invoke(activityThread, null);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return processName;
}

当进程开始时,ActivityManagerService已经将进程infor发送到ActivityThread。(ActivityThread.main - > attach() - > IActivityManager.attachApplication - IPC - > ActivityManagerService - > ApplicationThread.bindApplication )

ApplicationThread:
public final void bindApplication(String processName,***) {
    //***
    AppBindData data = new AppBindData();
    data.processName = processName;
    //**
}

当我们调用getProcessName时,它最终将传递给AppBindData对象。 因此,我们可以轻松高效地获取当前进程名称;

答案 4 :(得分:2)

如果我已正确理解您的问题,您应该可以按照ActivityManager使用this thread

答案 5 :(得分:2)

首先,获取当前进程pid。其次,列出所有运行过程。最后,如果它有相同的pid,那没关系,或者它是假的。

public static String getProcessName(Context context) {
    int pid = android.os.Process.myPid();
    ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    List<ActivityManager.RunningAppProcessInfo> infos = manager.getRunningAppProcesses();
    if (infos != null) {
        for (ActivityManager.RunningAppProcessInfo processInfo : infos) {
            if (processInfo.pid == pid) {
                return processInfo.processName;
            }
        }
    }
    return null;
}

答案 6 :(得分:2)

从ActivityThread获取它

在API 28+中,您可以调用Application.getProcessName()is just a public wrapper之间的ActivityThread.currentProcessName()

在较旧的平台上,只需直接致电ActivityThread.currentProcessName()

请注意,在API 18之前,the method was incorrectly called ActivityThread.currentPackageName() but still in fact returned the process name

示例代码

public static String getNameFromActivityThread() {
    if (Build.VERSION.SDK_INT >= 28)
        return Application.getProcessName();
    else {
        // Using the same technique as Application.getProcessName() for older devices
        // Using reflection since ActivityThread is an internal API

        try {
            @SuppressLint("PrivateApi")
            Class<?> activityThread = Class.forName("android.app.ActivityThread");

            // Before API 18, the method was incorrectly named "currentPackageName", but it still returned the process name
            // See https://github.com/aosp-mirror/platform_frameworks_base/commit/b57a50bd16ce25db441da5c1b63d48721bb90687
            String methodName = Build.VERSION.SDK_INT >= 18 ? "currentProcessName" : "currentPackageName";

            Method getProcessName = activityThread.getDeclaredMethod(methodName);
            return (String) getProcessName.invoke(null);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        }
    }
}

兼容性

经过测试并正在研究

  • 官方模拟器
    • 16
    • 17
    • 18
    • 19
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • Q beta 1
  • 真实设备
    • 运行Android 8.1.0的摩托罗拉Moto G5 Plus
    • 运行Android 6.0.1的三星Galaxy S5
    • 索尼Xperia M跑步机Android 7.1.1
    • 运行Sony Android 4.1.2的Sony Xperia M

答案 7 :(得分:1)

ActivityThread class中有一个方法,您可以使用反射来获取当前的processName。你不需要任何循环或技巧。与其他解决方案相比,性能最佳。限制是您只能获得自己的进程名称。这不是什么大问题,因为它涵盖了大多数使用案例。

    val activityThreadClass = XposedHelpers.findClass("android.app.ActivityThread", param.classLoader)
    val activityThread = XposedHelpers.callStaticMethod(activityThreadClass, "currentActivityThread")
    val processName = XposedHelpers.callStaticMethod(activityThreadClass, "currentProcessName")

答案 8 :(得分:1)

自Android Pie(SDK v28)起, Application 类中实际上有一个官方方法:

public static String getProcessName ()

See the docs

答案 9 :(得分:0)

主进程的父进程应该是合子,这应该是准确的解决方案

  1. 首先从/ proc / pid / cmdline判断进程名称,该名称应与程序包名称相同
  2. 判断流程的父亲是否Zygote(为什么这样做?因为某些APP具有相同名称的不同进程)

答案 10 :(得分:0)

总结使用Kotlin获取进程名称的不同方法:

基于https://stackoverflow.com/a/21389402/3256989 / proc / pid / cmdline

    fun getProcessName(): String? =
        try {
            FileInputStream("/proc/${Process.myPid()}/cmdline")
                .buffered()
                .readBytes()
                .filter { it > 0 }
                .toByteArray()
                .inputStream()
                .reader(Charsets.ISO_8859_1)
                .use { it.readText() }
        } catch (e: Throwable) {
            null
        }

基于https://stackoverflow.com/a/55549556/3256989(来自SDK版本28(Android P)的

  fun getProcessName(): String? = 
    if (VERSION.SDK_INT >= VERSION_CODES.P) Application.getProcessName() else null

基于https://stackoverflow.com/a/45960344/3256989反射

    fun getProcessName(): String? =
        try {
            val loadedApkField = application.javaClass.getField("mLoadedApk")
            loadedApkField.isAccessible = true
            val loadedApk = loadedApkField.get(application)

            val activityThreadField = loadedApk.javaClass.getDeclaredField("mActivityThread")
            activityThreadField.isAccessible = true
            val activityThread = activityThreadField.get(loadedApk)

            val getProcessName = activityThread.javaClass.getDeclaredMethod("getProcessName")
            getProcessName.invoke(activityThread) as String
        } catch (e: Throwable) {
            null
        }

基于https://stackoverflow.com/a/19632382/3256989 ActivityManager

    fun getProcessName(): String? {
        val pid = Process.myPid()
        val manager = appContext.getSystemService(Context.ACTIVITY_SERVICE) as? ActivityManager
        return manager?.runningAppProcesses?.filterNotNull()?.firstOrNull { it.pid == pid }?.processName
    }