Bookmark this article for your projects.
One, the general way to get the process name, through ActivityManager
In a multi-process APP, it is often necessary to know whether the current process is a main process or a background process. Or some process.
The following code is a common use to determine which process is the current process based on its name when the process is started:
public class MyApp extends Application {
private static final String TG = "MyApp";
@Override
public void onCreate(a) {
super.onCreate();
// If the current process is the main process, initialize the main process
if(isMainProcess()) { initMainProcess(); }}private boolean isMainProcess(a) {
// Get the name of the current process and compare it with the main process to determine whether it is the main process
String processName = ProcessUtil.getCurrentProcessName(this);
Log.e(TG, "isMainProcess processName=" + processName);
return BuildConfig.APPLICATION_ID.equals(processName);
}
private void initMainProcess(a) {
Log.e(TG, "initMainProcess"); }}Copy the code
Use ActivityManager to get the process name, which is also recommended by many web searches.
But, uncle Would say, this method is not optimal.
/** * Get the process name from ActivityManager, need IPC communication */
public static String getCurrentProcessNameByActivityManager(@NonNull Context context) {
int pid = Process.myPid();
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
if(am ! =null) {
List<ActivityManager.RunningAppProcessInfo> runningAppList = am.getRunningAppProcesses();
if(runningAppList ! =null) {
for (ActivityManager.RunningAppProcessInfo processInfo : runningAppList) {
if (processInfo.pid == pid) {
returnprocessInfo.processName; }}}}return null;
}
Copy the code
But, uncle Would say, this method is not optimal.
But, uncle Would say, this method is not optimal.
But, uncle Would say, this method is not optimal.
2. The downside of getting the current process name from ActivityManager
-
ActivityManager. GetRunningAppProcesses () method to cross-process communication, efficiency is not high
You need to communicate with the ActivityManagerService of the system process. The method call will inevitably be time consuming.
-
Once you have the list of RunningAppProcessInfo, you need to iterate to find information related to the current process.
Obviously the extra loop also adds time;
Of course, this time is of little consequence.
-
Is the most horrible ActivityManager. GetRunningAppProcesses () call fails, likely returns null, may also be AIDL call fails.
Of course ActivityManager. GetRunningAppProcesses () call the probability of failure is very low.
When your APP when the number of users to achieve a certain level, there must be a users encounter ActivityManager getRunningAppProcesses () call fails.
In the usage scenario we described at the beginning, the failure to obtain the process name would be terrifying.
If some components in the process are not initialized, the process is likely to be gg.
Third, seek a better solution
Method 1: Uncle found a new method in android API28:Application.getProcessName()
Application. GetProcessName () method returns the current process directly. This is the API we want!
However, this method can only be called on android9 (aka AIP28).
public class ProcessUtil {
/** * Obtain the process name from the Application API. No reflection, no IPC. * /
public static String getCurrentProcessNameByApplication(a) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
return Application.getProcessName();
}
return null; }}Copy the code
What about pre-Android 9 systems?
What about pre-Android 9 systems?
What about pre-Android 9 systems?
Method 2: ActivityThread currentProcessName () method
Uncle so curious and looked at the Application. The getProcessName source (), he is how to implement?
public class Application extends ContextWrapper implements ComponentCallbacks2 {
public static String getProcessName(a) {
returnActivityThread.currentProcessName(); }}Copy the code
We found ActivityThread. CurrentProcessName () this method.
So the uncle continues to read the source code:
/ * * * {@hide} * /
public final class ActivityThread extends ClientTransactionHandler {
public static String currentProcessName(a) {
//....}}Copy the code
Ollie, ActivityThread. CurrentProcessName () method is public static.
Ollie, ActivityThread. CurrentProcessName () method is public static.
Ollie, ActivityThread. CurrentProcessName () method is public static.
But then it became clear:
The ActivityThread class is hidden and cannot be called directly by the app.
The ActivityThread class is hidden and cannot be called directly by the app.
The ActivityThread class is hidden and cannot be called directly by the app.
So uncle continues to look at the source code to see when this method was added.
Uncle found that this method already has this method on android4.3.1.
CurrentProcessName () method not found on android4.0.4.
So mean, whether we can reflection calls ActivityThread. CurrentProcessName ()?
The answer, of course, is yes.
So we implement this method in the ProcessUtil utility class:
public class ProcessUtil {
/** * Get the process name by reflecting ActivityThread, avoiding IPC */
public static String getCurrentProcessNameByActivityThread(a) {
String processName = null;
try {
final Method declaredMethod = Class.forName("android.app.ActivityThread".false, Application.class.getClassLoader())
.getDeclaredMethod("currentProcessName", (Class<? > [])new Class[0]);
declaredMethod.setAccessible(true);
final Object invoke = declaredMethod.invoke(null.new Object[0]);
if (invoke instanceofString) { processName = (String) invoke; }}catch (Throwable e) {
}
returnprocessName; }}Copy the code
Fourth, combine the three methods to get a better scheme
So we combined these three approaches.
- We preferred by Application. GetProcessName () method to get the process name.
- If fail, we will reflect ActivityThread. CurrentProcessName () to obtain the process name
- If that fails, we get the process name through the normal method ActivityManager
The following code:
public class ProcessUtil {
private static String currentProcessName;
/ * * *@returnCurrent process name */
@Nullable
public static String getCurrentProcessName(@NonNull Context context) {
if(! TextUtils.isEmpty(currentProcessName)) {return currentProcessName;
}
//1) Obtain the current process name from the Application API
currentProcessName = getCurrentProcessNameByApplication();
if(! TextUtils.isEmpty(currentProcessName)) {return currentProcessName;
}
//2) Get the name of the current process by reflecting ActivityThread
currentProcessName = getCurrentProcessNameByActivityThread();
if(! TextUtils.isEmpty(currentProcessName)) {return currentProcessName;
}
//3) Get the current process name from ActivityManager
currentProcessName = getCurrentProcessNameByActivityManager(context);
return currentProcessName;
}
/** * Obtain the process name from the Application API. No reflection, no IPC. * /
public static String getCurrentProcessNameByApplication(a) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
return Application.getProcessName();
}
return null;
}
/** * Get the process name by reflecting ActivityThread, avoiding IPC */
public static String getCurrentProcessNameByActivityThread(a) {
String processName = null;
try {
final Method declaredMethod = Class.forName("android.app.ActivityThread".false, Application.class.getClassLoader())
.getDeclaredMethod("currentProcessName", (Class<? > [])new Class[0]);
declaredMethod.setAccessible(true);
final Object invoke = declaredMethod.invoke(null.new Object[0]);
if (invoke instanceofString) { processName = (String) invoke; }}catch (Throwable e) {
e.printStackTrace();
}
return processName;
}
/** * Get the process name from ActivityManager, need IPC communication */
public static String getCurrentProcessNameByActivityManager(@NonNull Context context) {
if (context == null) {
return null;
}
int pid = Process.myPid();
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
if(am ! =null) {
List<ActivityManager.RunningAppProcessInfo> runningAppList = am.getRunningAppProcesses();
if(runningAppList ! =null) {
for (ActivityManager.RunningAppProcessInfo processInfo : runningAppList) {
if (processInfo.pid == pid) {
returnprocessInfo.processName; }}}}return null; }}Copy the code
Five, a simple test of performance
Uncle did a simple test to test the duration of the next three method calls:
Tests are done on the simulator. The simulator configuration is as follows:
Test code is as follows:
private fun testGetCurrentProcessNameByApplication(a){
val beginTime = SystemClock.elapsedRealtimeNanos()
ProcessUtil.getCurrentProcessNameByApplication()
Log.i(TG, "getCurrentProcessNameByApplication duration=${SystemClock.elapsedRealtimeNanos() - beginTime}")}private fun testGetCurrentProcessNameByActivityThread(a){
val beginTime = SystemClock.elapsedRealtimeNanos()
ProcessUtil.getCurrentProcessNameByActivityThread()
Log.i(TG, "getCurrentProcessNameByActivityThread duration=${SystemClock.elapsedRealtimeNanos() - beginTime}")}private fun testGetCurrentProcessNameByActivityManager(a){
val beginTime = SystemClock.elapsedRealtimeNanos()
ProcessUtil.getCurrentProcessNameByActivityManager(this)
Log.i(TG, "getCurrentProcessNameByActivityManager duration=${SystemClock.elapsedRealtimeNanos() - beginTime}")}Copy the code
Before each function is called, the APP restarts and stands for 1 minute before being called:
The output log is as follows:
The 2020-09-27 18:30:03. 323, 14007-14007 / com. Android. Study I/vzProcessUtilAct: GetCurrentProcessNameByApplication duration = 654000 2020-09-27 18:31:02. 029, 14082-14082 / com. Android. Study I/vzProcessUtilAct: GetCurrentProcessNameByActivityThread duration = 1121000 2020-09-27 18:32:01. 669, 14150-14150 / com. Android. Study I/vzProcessUtilAct: getCurrentProcessNameByActivityManager duration=1661000Copy the code
You can see:
ProcessUtil. GetCurrentProcessNameByApplication () takes 654000 nanoseconds = 0.654 milliseconds
ProcessUtil. GetCurrentProcessNameByActivityThread () takes 1121000 nanoseconds = 1.121 milliseconds
ProcessUtil. GetCurrentProcessNameByActivityManager () takes 1661000 nanoseconds = 1.661 milliseconds
Six, summarized
Sometimes that’s what development is all about. Get down and figure it out. Sometimes you’ll be surprised.
This kind of surprise, even more than you chase after the introduction of all kinds of modern technology to the practical.
A quick introduction, a flutter introduction, a RxJava introduction, a Kotlin introduction… These are important.
However, the experience of digging into the details of the code and solving individual problems is much more valuable.
The process of solving these problems, the habits of mind that are formed, is essential for a programmer to survive.
If this article is of any help to you, please click “like” 👍 before you go. Thanks here
More highlights:
- How Does Kotlin solve Java development pain points to make programmers Happier
- Why did Google choose Kotlin? How does Kotlin solve the Java development pain point [continued]?
- Simple, kotin Any class
- One minute introduction to Kotiln coroutines, thread switching
- Break your perception that Java, divided by zero, must crash?
- Make your code more robust by making assertions more sophisticated
- Java dynamic proxy, easy from entry to mastery
- Timestamp summary: system.nanotime (), system.currentTimemillis (), SystemClock
- Breaking the limitations of Android O on Service
- Best explanation: Android O has Background Execution Limits on services.
- Tip 1 “Don’t go over the wall to view the Android Development Documentation online && View the Android Development Documentation offline”
- Tip 2 “ADB Root Android Simulator, assistance: Problem follow-up, Android system analysis, Competitive product analysis”
- Tip 3: Android Development: Write Unit Tests using Main
- Tip 4 “Android source code reading and download”