检测您是否在Android上的Application中的主进程或远程服务进程中

时间:2011-08-05 09:16:36

标签: android service process

我有一个应用程序,它在一个单独的进程中运行远程服务:

<service android:name=".MyService" android:process=":remote"/>

我也在使用Application类:

<application android:label="@string/app_name" android:name=".MyApplication" ...

我可以这样做吗?

public class MyApplication extends Application {

    public MyApplication() {
        if (isRemoteService()) {
            setupLog("remoteservice.log");
        } else {
            setupLog("application.log");
        }
    }

我想我可以获取进程名称并使用它来检测我是否在远程服务或主应用程序中,但我还没有找到如何获取进程名称。我可以从android.os.Process.myPID()获取PID,但这对我没有多大帮助。

4 个答案:

答案 0 :(得分:4)

例如,如果要检查是否在主进程中,可以在应用程序中编写代码,如下所示:

public class MyApplication extends Application {
@Override
public void onCreate() {
    //...code here will be execute in every process...
    if (isMainProcess()) {
        //...code here will be execute only in main process
    }
    super.onCreate();
}

// your package name is the same with your main process name
private boolean isMainProcess() {
    return getPackageName().equals(getProcessName());
}

// you can use this method to get current process name, you will get
// name like "com.package.name"(main process name) or "com.package.name:remote"
private String getProcessName() {
    int mypid = android.os.Process.myPid();
    ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
    List<RunningAppProcessInfo> infos = manager.getRunningAppProcesses();
    for(RunningAppProcessInfo info : infos) {
        if (info.pid == mypid) {
            return info.processName;
        }
    }
    // may never return null
    return null;
}

}

答案 1 :(得分:1)

我可以提供间接解决方案:

在每个相应的StartUp方法中,设置一个系统属性:

System.setProperty("PROCESS_TYPE","SERVICE");
System.setProperty("PROCESS_TYPE","RECEIVER");
System.setProperty("PROCESS_TYPE","ACTIVITY");

属性是静态的,与进程隔离,可以从任何地方访问。额外的好处是它们可以被Logback等日志框架直接使用。

答案 2 :(得分:1)

我有类似的问题,这就是我所做的。

MyServiceMyActivity有一个共同的部分MyEngine,但在这两种情况下,行为必须略有不同。

有一点不同的是设置,但此设置在类MyServiceMyActivity中完成。

活动和服务的另一个不同之处是通过监听器完成:MyEngine定义接口MyEngine.Listener,而MyServiceMyActivity为引擎提供不同的该接口的实现。

因此,如果要传递布尔值,可以使用两种方法:

// Method 1: different initialization
class MyEngine {
    MyEngine(boolean isService) { ... }
}
class MyActivity extends Activity {
    private MyEngine = new MyEngine(false);
    ...
}
class MyService extends Service {
    private MyEngine = new MyEngine(true);
    ...
}

// Method 2: callbacks
class MyEngine {
    interface Listener {
        boolean isService();
    }
    private Listener mListener;
    MyEngine(Listener listener) { mListener = listener; }
}
class MyActivity extends Activity {
    private mListener = new MyEngine.Listener() {
        boolean isService() { return false; }
    }
    private MyEngine = new MyEngine(mListener);
    ...
}
class MyService extends Service {
    private mListener = new MyEngine.Listener() {
        boolean isService() { return true; }
    }
    private MyEngine = new MyEngine(mListener);
    ...
}

注释

  1. 上面示例中使用的布尔值在现实世界中是无用的:如果您想使用不同的日志文件名,最好传递文件名而不是布尔值。如果你想执行两个不同的动作,最好有一个具有两个实现的侦听器功能。

  2. 当然,可以通过Context并检查它是ActivityService的孩子,还是获取当前流程的名称,但这些事情是特定于Android的实现细节,除非绝对必要,否则最好不要依赖它们。

答案 3 :(得分:0)

我正在使用一段代码,该代码取自Google的WorkManager库。撰写本文时,它位于GreedyScheduler.java中。这样定义了方法getProcessName()

    @Nullable
    private String getProcessName() {
        if (SDK_INT >= 28) {
            return Application.getProcessName();
        }

        // Try using ActivityThread to determine the current process name.
        try {
            Class<?> activityThread = Class.forName(
                    "android.app.ActivityThread",
                    false,
                    GreedyScheduler.class.getClassLoader());
            final Object packageName;
            if (SDK_INT >= 18) {
                Method currentProcessName = activityThread.getDeclaredMethod("currentProcessName");
                currentProcessName.setAccessible(true);
                packageName = currentProcessName.invoke(null);
            } else {
                Method getActivityThread = activityThread.getDeclaredMethod(
                        "currentActivityThread");
                getActivityThread.setAccessible(true);
                Method getProcessName = activityThread.getDeclaredMethod("getProcessName");
                getProcessName.setAccessible(true);
                packageName = getProcessName.invoke(getActivityThread.invoke(null));
            }
            if (packageName instanceof String) {
                return (String) packageName;
            }
        } catch (Throwable exception) {
            Log.d("TAG", "Unable to check ActivityThread for processName", exception);
        }

        // Fallback to the most expensive way
        int pid = Process.myPid();
        ActivityManager am =
                (ActivityManager) mContext.getSystemService(ACTIVITY_SERVICE);

        if (am != null) {
            List<ActivityManager.RunningAppProcessInfo> processes = am.getRunningAppProcesses();
            if (processes != null && !processes.isEmpty()) {
                for (ActivityManager.RunningAppProcessInfo process : processes) {
                    if (process.pid == pid) {
                        return process.processName;
                    }
                }
            }
        }

        return null;
    }

然后在主叫方:

    boolean isMainProcess() {
        return TextUtils.equals(mContext.getPackageName(), getProcessName());
    }