Server side program instance based on Android

  • 2021-08-28 21:05:45
  • OfStack

In APP of iOS, each program runs in its own sandbox. Once the program is deleted, the application data will be cleared. Therefore, most programs that need to save data will use iCloud to back up the data. However, if it is an authoring APP, like notes, if it is exported to the computer, it must be transferred once, which is very troublesome. Therefore, many APP have built-in FTP servers. After starting, the computer only needs to access the data in APP through the FTP client link.

In fact, there are many similar APP in Android. In order to share the application data in APP with PC, there will also be FTP or WebDAV services running in APP. But Android doesn't have the same sandbox problem as iOS, although Android also has sandboxes. Usually, most mobile phones will not obtain root permission, and sensitive application data will be placed in the sandbox, that is, the internal data directory of APP, which is located in/data/data/com. xxx. xx/, and the path can be obtained through Context. getFilesDir (). If the mobile phone does not have root permission, no one can access it except APP itself. However, Android can choose to store the data in an external sandbox, that is, the external data directory of APP, which can be obtained through Context. getExternalFilesDir (), and even other crooked APP can create folders in external storage at will...

There are many APP built-in to exchange data with the outside in server-side operation mode, such as reading more, Documents5 and so on.

In the implementation, most of them start Socket to monitor a fixed port, and then deal with HTTP requests, but for most APP code farmers, dealing with HTTP is a very troublesome thing. To deal with Header, POST and GET, file upload and ordinary forms, etc., without the help of the third-party library, this function is very difficult to write well.

In the third-party implementation, there is AndroidAsync. Although I didn't look at the source code too much, it is estimated that 89 out of 10 also adopt this library.

However, it can also be used as a client, and it is very simple to run as a listening service:


AsyncHttpServer server = new AsyncHttpServer();
server.get("/", new HttpServerRequestCallback() {
  @Override
  public void onRequest(AsyncHttpServerRequest request, AsyncHttpServerResponse response) {
    response.send("Hello!!!");
  }
});
server.listen(5000);

For most students who have done WEB, they will definitely think of IIS, Tomcat and Apache when they mention server-side programs. However, IIS is based on Windows platform, and HTTP. SYS, which IIS depends on, is system-driven, so it is impossible to transplant it, and it is impossible to transplant it in my life. Tomcat is an JavaEE container running on JVM virtual machine. Although JAVA language is also used in Android, its virtual machine is ART (Dalvik before 4.4), and Apache is developed by C/C + +, so it is promising to transplant to Android. This reader can go online to find relevant tutorials, how to cross-compile Apache to ARM, and want to be a hand-reaching party, many of which have been compiled.

Let's take a chestnut to talk about how to run httpd for arm on Android. You can put the compiled httpd into the raw folder first, and judge whether it is in the specified position when MainActivity starts, and release it if it is not. I usually run it in a separate service, so that even if Activity is destroyed, the service will still run in the background, which is also a necessary feature of the server.


private File httpd;

@Override
public void onCreate() {
  super.onCreate();
  httpd = new File(getFilesDir(), "httpd");
  if (!httpd.exists()) {
    try {
      InputStream ins = getResources().openRawResource(R.raw.httpd);
      FileIOUtils.writeFileFromIS(httpd, ins);
      Runtime.getRuntime().exec("chmod 777 " + httpd.getAbsolutePath());
    } catch (Exception e) {
      Log.e(TAG, "onCreate: ", e);
    }
  }
}

There is an Runtime class in Android, which is mainly used to make Android application interact with its running environment. You can get an instance of this class directly by calling the static method of Runtime. getRuntime (), and then call exec to execute commands. Next, I created a binary execution class and made a simple encapsulation.


public class BinExecuter {

  /**
   *  Process  PID
   */
  private int pid;

  /**
   *  Executable 2 Binary file path 
   */
  private String bin;

  /**
   *  Startup parameters 
   */
  private String paras;

  /**
   *  Process instance 
   */
  private Process process;

  /**
   *  Get  PID
   * @return
   */
  public int getPid() {
    return pid;
  }

  /**
   *  Constructor 
   * @param bin  Executable 2 Binary file path 
   * @param paras  Startup parameters 
   */
  public BinExecuter(String bin, String paras) {

    this.bin = bin;
    this.paras = paras;

  }

  /**
   *  Start process 
   */
  public void start() {

    try {
      process = Runtime.getRuntime().exec(bin + " " + paras);
      Field f = process.getClass().getDeclaredField("pid");
      f.setAccessible(true);
      pid = f.getInt(process);
      f.setAccessible(false);
    } catch (Exception ex) {
      ex.printStackTrace();
    }
  }

  /**
   *  End a process 
   */
  public void stop() {
    if (pid > 0) {
      try {
        Runtime.getRuntime().exec("kill -9 " + pid);
      } catch (Exception ex) {
        ex.printStackTrace();
      }
    }
  }
}

However, this is not enough. Programs like httpd will have output from the console after starting. For example, if a client requests an url, or if an error occurs, it will be displayed on the console. There is no console window on Android, so how to capture the console output? Simple, redirect the output to the input stream.


InputStream outs = process.getInputStream();
InputStreamReader isrout = new InputStreamReader(outs);
BufferedReader brout = new BufferedReader(isrout);
String line;
try {
  while ((line = brout.readLine()) != null) {
    log.d(line);
  }
} catch (Exception ex) {
  ex.printStackTrace();
}

Note that there is a big crooked goose (while), the main thread will be blocked, start another thread on the line, transform this class, increase the console output listening, can make it slightly stronger 1 point.


/** author:yahch**/
public interface BinExecuteCallback {
  void onConsoleResponse(String text);
}

private BinExecuteCallback binExecuteCallback;

public void setBinExecuteCallback(BinExecuteCallback binExecuteCallback) {
  this.binExecuteCallback = binExecuteCallback;
}

The pairing method in an Aria2 server I developed some time ago is as follows:


@Override
public int onStartCommand(Intent intent, int flags, int startId) {
  if (intent != null) {
    ariaConfig = (AriaConfig) intent.getSerializableExtra("config");
    if (ariaConfig != null) {
      Log.d(TAG, ariaConfig.toString());
      binExecuter = new BinExecuter(fileAria2c.getAbsolutePath(), ariaConfig.toString());
      binExecuter.setBinExecuteCallback(new BinExecuter.BinExecuteCallback() {
        @Override
        public void onConsoleResponse(String text) {
          sendMessage(ARIA2_SERVICE_BIN_CONSOLE, text);
        }
      });
    }
  } else {
    stopSelf();
  }
  return super.onStartCommand(intent, flags, startId);
}

private void sendMessage(String name, String message) {
  MessageEvent genericEvent = new MessageEvent(name, message);
  EventBus.getDefault().post(genericEvent);
}

Through EventBus, the console messages intercepted in the service are thrown into Activity. Of course, broadcasting can also be used. I think EventBus is better to use.

Now GO language is in full bloom. GO is born for the server, and its cross-platform ability is particularly powerful. There are many programs compiled for ARM version on Github, such as frp, caddy and filebrowser, which can be transplanted to Android. What we have to do is to give him a shell, control it to run and stop, and configure some parameters.


Related articles: