How to Keep Your Android App Running Continuously

How to Keep Your Android App Running Continuously

How to keep your app running continuously?

By default, if no cross-process deployment configuration is made, each Android app runs in a separate virtual machine, with each virtual machine corresponding to a process. When the app is reclaimed by the system or actively killed by the user (through app management software), the process exits completely.

In some scenarios, when the app’s process exits, we still want to perform some operations. For example, after the app is uninstalled (the uninstallation first exits the operation), we want to redirect to a browser for a survey on the reasons for uninstallation; or to optimize the experience and improve the app’s hot start speed. For some monitoring apps, we want them to run continuously; otherwise, the data may be inaccurate, which requires the app’s process to be able to self-start after exiting.

To monitor the app’s stopped state and execute callbacks, a typical solution is mutual protection between multiple processes.

Go check your phone, Settings – Application Management – Running, have you noticed that apps like Alipay, QQ, WeChat, etc., all have two processes running at the same time?

How to Keep Your Android App Running Continuously

multiprocess

Mutual Protection Between Two Processes

Process A and B mutually protect each other. When process A detects that process B has exited, process A restarts process B. Similarly, when process A exits, B restarts A. Monitoring the running state of the processes is the key point of process protection. Some easy-to-think solutions include:

  1. Polling to check the status of the remote process

  2. Sending heartbeat packets to see if a response can be received; no response means the remote process may have exited

  3. Sending a broadcast notification to the other party that it is about to perish in the onTerminate method when the Application lifecycle terminates

By polling, as long as you use the ps command to query the status, there is no need for A and B to communicate between processes; the heartbeat packet response detection method requires communication through socket or other IPC mechanisms.

The guardian process can be a process running the Android app (Dalvik process) or a native Linux process. If it is a Dalvik process, when the app is uninstalled, the process will exit, and the bytecode will be removed, unable to run any logic such as redirecting to a browser page.

If the guardian process forks a new process in the Linux system and is not in the same process space as the app, when the app is closed, killed, or uninstalled, it will not affect the guardian’s running state, meaning the guardian remains operational and can perform corresponding actions. Therefore, the guardian can monitor the app’s running state and, upon discovering that the app has stopped running, can send commands to start the app process, ensuring the app’s survival, which is crucial for certain system monitoring apps. Moreover, processes on Linux cannot be obtained through Android’s PackageManager and will not appear in the app management software’s running software list, making them unlikely to be killed; the guardian itself can survive relatively reliably.

This article introduces how to fork a native process on Linux to guard the Dalvik app’s process.

Native Process Guardian on Linux

Introducing several APIs under Linux

  • int daemon (int nochdir, int noclose);

This function runs the program in the background, becoming a daemon program, and most services under Linux run this way.

  • int getopt_long(int argc, char * const argv[],

    const char *optstring,   const struct option *longopts, int *longindex);

    This is a helper function for parameter parsing. When there are multiple command parameters and some optional parameters, if you only accept parameters in order, it can become confusing. If you match by parameter name, it becomes much more convenient.

  • FILE popen(const char command , const char *type );

popen() creates a pipe, forks a new process to execute a shell command. The return value of popen() is a standard I/O stream, which must be terminated by pclose. This stream is unidirectional (can only be read or written). Writing to this stream is equivalent to writing to the standard input of that command; conversely, reading data from the stream is equivalent to reading the command’s standard output.

  • int system(const char * string);

The system() function will call fork() to create a child process, which will call /bin/sh-c string to execute the command represented by the parameter string. This command will return to the original calling process immediately after it completes. During the execution of system(), the SIGCHLD signal will be temporarily postponed, while the SIGINT and SIGQUIT signals will be ignored.

Guardian Flowchart

How to Keep Your Android App Running Continuously

Guardian Flowchart

Guardian Program Code

int main( int argc, char* argv[]  )  {   signal(SIGTERM, SIG_IGN);   const char *process_name = NULL;   const char *package_name = NULL;   const char *activity_name = NULL;   int interval_sec = 30;   struct option options[] = {   { "process_name", required_argument, 0, 'p' },   { "package_name", required_argument, 0, 'a' },   { "activity_name", required_argument, 0, 'c' },   { "interval_sec", required_argument, 0, 'i' },   { 0, 0, 0, 0 }   };   int c;   for (;;) {   c = getopt_long(argc, argv, "p:a:c:i:", options, NULL);    if (c == -1) { break; }   switch (c) {   case 'p': process_name = optarg; break; case 'a': package_name = optarg; break; case 'c': activity_name = optarg; break; case 'i': interval_sec = atoi(optarg); break; default: exit(EXIT_FAILURE); } } if (process_name == NULL || package_name == NULL || activity_name == NULL) exit(EXIT_FAILURE); daemon(1, 1); run_service(process_name, package_name, activity_name, 10); return 0; }

Implementation of run_service

int chk_process(const char *process_name){ FILE   *stream;  char   *line = NULL;  size_t len = 0;  ssize_t read_len; stream = popen( "ps", "r" );  if (stream == NULL) return -1; int exists = 0; while ( ( read_len = getline(&line, &len,  stream)) != -1) { int len = strlen(line); char *cmd = line + len; while ( len >0 ) { len--; if ( *cmd == ' ') { cmd++; break; } cmd--; } if( strncmp(cmd, process_name, strlen(process_name)) == 0 ) { exists = 1; break; } } pclose( stream ); if ( line != NULL ) free(line); return exists; } void run_service(const char *process_name, const char *package_name, const char *activity_name, int interval_sec){ while (1) { if ( chk_process(process_name) == 0) { char *pkg_activity_name = NULL; asprintf(&pkg_activity_name, "/system/bin/am start --user 0 -n %s/%s", package_name, activity_name); system(pkg_activity_name); free(pkg_activity_name); } sleep(interval_sec); } return; }

After successful compilation, generate xxx, rename it to xxx.so, and copy the file to libs. After installation, this file will be copied to the data/data/app_package directory along with the dynamic library. Write the code related to copying and chmod, the general process is as follows:

  1. path = “/data/data/” +packageName; // Path after installation

  1. Execute shell command: dd if= path+lib/xxx.so of=path/xxx; // Copy to app path, rename to xxx

  1. Assign executable permissions chmod 777 path/xxx;

  1. Run executable file path/xxx -p process_name -a pkgname ..(other parameters)

One point to note: All operations here are completed by executing shell commands, and you need to cd to the app path first to have read and write permissions.

public static boolean execCommand(String command, String packageName) { Process process = null; try { process = Runtime.getRuntime().exec("sh"); DataInputStream inputStream = new DataInputStream(process.getInputStream()); DataOutputStream outputStream = new DataOutputStream(process.getOutputStream()); outputStream.writeBytes("cd /data/data/" + packageName + "\n"); outputStream.writeBytes(command + " \n"); outputStream.writeBytes("exit\n"); outputStream.flush(); process.waitFor(); byte[] buffer = new byte[inputStream.available()]; inputStream.read(buffer); String s = new String(buffer); } catch (Exception e) { return false; } return true; }

When packaging and testing the code, stop the app’s operation through the app management interface to see if the app will be restarted.

References

http://www.cnblogs.com/caosiyang/archive/2012/06/25/2560976.html

http://blog.csdn.net/cashey1991/article/details/7942809

Written by /aimfaraway Original link: http://www.jianshu.com/p/a4a6222654a0

Also refer to:Everything You Need to Know About Android Process Survival

WeChat Android Client Background Survival Experience Sharing

About Java and AndroidChannel for Experts

The Java and Android Expert Channel is a public account with thousands of followers discussing Java and Android development, sharing and creating the most valuable articles, to make you an expert in this field!

We discuss the cutting-edge technologies in Android and Java development: Android performance optimization, pluginization, cross-platform, dynamicization, strengthening and anti-cracking, etc., and also discuss design patterns/software architecture by a team composed of engineers from BAT.

Follow us to receive red packets, reply: “Baidu”, “Alibaba”, “Tencent” for surprises!!! After following, you can join the WeChat group. The group consists of experts from Baidu, Alibaba, and Tencent.

Welcome to follow us, let’s discuss technology together. Scan and long press the QR code below to quickly follow us. Or search for the public WeChat account: JANiubility.

How to Keep Your Android App Running Continuously

Public account:JANiubility

How to Keep Your Android App Running Continuously

Leave a Comment

Your email address will not be published. Required fields are marked *