51

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

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

那么有可用的API吗?

谢谢。

4

11 回答 11

32

完整代码是

    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;
        }
    }
于 2013-10-28T10:28:34.233 回答
29

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

于 2014-01-27T19:03:09.363 回答
27

从 ActivityThread 获取

在API 28+ 中,您可以调用Application.getProcessName(),这只是ActivityThread.currentProcessName().

在旧平台上,直接调用ActivityThread.currentProcessName()即可。

请注意,在 API 18 之前,该方法被错误地调用ActivityThread.currentPackageName(),但实际上仍然返回了进程名称

示例代码

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
    • 索尼 Xperia M 运行库存 Android 7.1.1
    • 运行 Sony Android 4.1.2 的 Sony Xperia M
于 2019-04-25T05:44:43.147 回答
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);
  }
}
于 2017-02-27T15:18:18.280 回答
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在进程启动时已经将进程信息发送给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 对象。这样我们就可以轻松高效地获取当前进程名;

于 2017-08-30T12:24:00.677 回答
3

总结使用 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
    }
于 2019-04-08T21:40:55.703 回答
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;
}
于 2016-04-25T02:34:08.747 回答
2

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

public static String getProcessName ()

查看文档

于 2019-04-06T13:30:48.993 回答
1

如果我正确理解了您的问题,您应该可以使用ActivityManager,按照这个线程

于 2013-10-28T10:11:34.827 回答
1

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

    val activityThreadClass = XposedHelpers.findClass("android.app.ActivityThread", param.classLoader)
    val activityThread = XposedHelpers.callStaticMethod(activityThreadClass, "currentActivityThread")
    val processName = XposedHelpers.callStaticMethod(activityThreadClass, "currentProcessName")
于 2017-09-06T09:12:23.303 回答
0

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

  1. 首先从 /proc/pid/cmdline 判断进程的名称应该等于包名
  2. 判断进程的父进程是否是Zygote(为什么要这样做?因为有些APP有同名的不同进程)</li>
于 2018-09-05T04:14:47.537 回答