【问题标题】:Detecting if you're in the main process or the remote service process in Application检测您是在应用程序中的主进程还是远程服务进程
【发布时间】:2011-10-20 16:59:06
【问题描述】:

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

<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,但这对我没有多大帮助。

【问题讨论】:

    标签: java android service process java-threads


    【解决方案1】:

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

    在各个启动方法中,设置一个系统属性:

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

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

    【讨论】:

      【解决方案2】:

      我遇到了类似的问题,这就是我所做的。

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

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

      活动和服务的另一点不同是通过侦听器完成的:MyEngine 定义接口 MyEngine.ListenerMyServiceMyActivity 为引擎提供该接口的不同实现。

      所以,如果你想传递一个布尔值,有两种方法:

      // 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 并检查它是Activity 还是Service 的子进程,或者获取当前进程的名称,但这些都是Android 特定的实现细节,除非绝对必要,否则最好不要依赖它们。

      【讨论】:

        【解决方案3】:

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

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

        【讨论】:

        • 不幸的是,除非您在应用程序中使用进程属性,否则您认为主进程必须与您的包同名的假设是正确的。
        【解决方案4】:

        我正在使用一段代码,该代码取自 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());
            }
        

        【讨论】:

          猜你喜欢
          • 2020-10-10
          • 1970-01-01
          • 2014-08-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-04-03
          • 1970-01-01
          • 2016-12-23
          相关资源
          最近更新 更多