【问题标题】:Is there a way to get current process name in Android有没有办法在Android中获取当前进程名称
【发布时间】:2013-10-28 10:06:08
【问题描述】:

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

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

那么有可用的 API 吗?

谢谢。

【问题讨论】:

  • 您是否考虑将接受的答案更改为this one?它以与操作系统相同的方式获取进程名称,我认为它比其他答案更快、更可靠。谢谢

标签: android


【解决方案1】:

完整代码是

    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;
        }
    }

【讨论】:

  • 请注意 manager.getRunningAppProcesses() 可能会返回 null(而不是像好的设计所要求的空列表)。
  • 它也可能很慢。请注意。
【解决方案2】:

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 解决方案作为备用策略。

【讨论】:

  • 这可能是因为您的进程是远程服务。如果您调用 am.getRunningServices() 并遍历 RunningServiceInfo 对象,您将在 info 对象的 .process 字段的清单中找到您的服务进程以及您为进程提供的名称
  • 当用户运行 Xposed 时究竟发生了什么导致它无法工作?
  • 然后 /proc/your_pid/cmdline 就连你自己都无法阅读了。
  • 谢谢!你有没有找到更多关于活动管理器没有返回你的进程的信息? Mike 的上述怀疑是否正确 - 这与您的服务处于单独的进程中有关?
  • 是的,就是这样。
【解决方案3】:

从 ActivityThread 中获取

在 API 28+ 中,您可以调用 Application.getProcessName(),其中 is just a public wrapperActivityThread.currentProcessName() 周围。

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

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

示例代码

public static String getProcessName() {
    if (Build.VERSION.SDK_INT >= 28)
        return Application.getProcessName();

    // 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 测试版 1
  • 真实设备
    • 运行 Android 8.1.0 的摩托罗拉 Moto G5 Plus
    • 运行 Android 6.0.1 的三星 Galaxy S5
    • Sony Xperia M 运行库存 Android 7.1.1
    • 运行 Sony Android 4.1.2 的 Sony Xperia M

【讨论】:

    【解决方案4】:

    这是对 David Burströ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);
      }
    }
    

    【讨论】:

    【解决方案5】:

    我有更高效的方法,你不需要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在进程启动时已经将进程信息发送给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 对象。 这样我们就可以轻松高效地获取当前进程名;

    【讨论】:

      【解决方案6】:

      总结使用 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 v.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
          }
      

      【讨论】:

        【解决方案7】:

        首先,获取当前进程的 pid。其次,列出所有正在运行的进程。最后,如果它具有相等的pid,则可以,否则为false。

        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;
        }
        

        【讨论】:

        • 欢迎来到 StackOverflow!一般来说,如果答案包含对代码的用途的解释,以及为什么在不介绍其他人的情况下解决了问题,那么答案会更有帮助。
        【解决方案8】:

        从Android Pie(SDK v28)开始,在Application类中其实有一个官方的方法:

        public static String getProcessName ()

        See the docs

        【讨论】:

        • 不错的发现!我在my answer 中对这种方法进行了全面的撰写。
        【解决方案9】:

        如果我正确理解了您的问题,您应该可以使用ActivityManager,就像this thread一样。

        【讨论】:

        • 谢谢我得到了带有AcitivityManagerandroid.os.Process.myPid()的processName
        【解决方案10】:

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

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

        【讨论】:

        • 不错的发现!请注意,该方法在 API 18 之前称为 currentPackageName()。我在 my answer 中对这种方法进行了全面的描述。
        【解决方案11】:

        主进程的父进程应该是zygote,这应该是正确的解决方案

        1. 先从/proc/pid/cmdline判断进程名应该和包名一致
        2. 判断进程的父进程是否为Zygote(为什么要这样做?因为有些APP有同名的不同进程)

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-05-05
          • 2019-11-03
          • 1970-01-01
          • 2011-08-02
          相关资源
          最近更新 更多