首页 > 系统 > Android > 正文

基于Android的服务器端程序实例

2019-10-22 18:14:10
字体:
来源:转载
供稿:网友

在 iOS 的 APP 中,每个程序都在自己的沙盒中运行,一旦程序删除了,应用的数据也就被清除了,所以大部分程序,需要保存数据的都会使用 iCloud 备份数据,但是如果是创作类的 APP,类似笔记之类的,如果要导出到电脑,就必须还要中转一次,非常麻烦。所以也有很多 APP 就开始内置了 FTP 服务器,一旦启动后,电脑只需要通过 FTP 客户端链接就可以访问 APP 内的数据了。

其实在Android中也有很多这些类似的 APP,为了方便和 PC 之间共享 APP 里的应用数据,也会有 FTP 或者WebDAV服务在 APP 里运行。但是Android不存在和 iOS 的那种沙盒问题,虽然 Android 也有沙盒。通常大部分的手机不会取得 root 权限,敏感的应用数据都会放在沙盒中,也就是 APP 内部数据目录,位于 /data/data/com.xxx.xx/ 中,可以通过 Context.getFilesDir() 获取到该路径,如果手机没有 root 权限,除了 APP 本身,谁也无法访问这里面的数据。但是 Android 可以选择将数据存放在外部沙盒中,也就是 APP 外部数据目录,可以通过Context.getExternalFilesDir() 获取到该路径,甚至还有其他歪门邪道的 APP 在外置存储里随便建立文件夹 ...

内置以服务器端运行方式和外部进行数据交换的 APP 有很多,比如多看阅读,Documents5 等等。

在实现上大部分都是启动 Socket 监听一个固定端口,然后处理 HTTP 请求,但是对于大部分 APP 码农,处理 HTTP 是一件非常麻烦的事情。要处理 Header,对 POST 和 GET 的处理,对文件上传和普通表单的处理等等,如果不借助第三方库,这个功能想要写好非常困难。

在第三方实现中有 AndroidAsync ,虽然没看过多看的源代码,但是估计十有八九也是采用了这个库。

不过它也可以作为客户端方式,作为监听服务方式运行使用方法非常简单:

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

对于大部分做过 WEB 的同学可能在提到服务器端程序时,肯定会想到 IIS 、Tomcat、Apache 这些。但是 IIS 是 Windows 平台的,IIS 所依赖的 HTTP.SYS 是系统驱动级别的,移植是不可能移植的,这辈子都不可能移植的。 Tomcat 是运行在 JVM 虚拟机上的 JavaEE 容器,Android 虽然也使用 JAVA 语言,但是其虚拟机是 ART(4.4以前是 Dalvik),Apache 是 C/C++ 开发的,移植到 Android 还是很有希望的。这个各位看官可以去网上找找相关的教程,Apache 如何交叉编译到 ARM,想做个伸手党也可以,很多已经编译好了的。

这里举个栗子说说如何在 Android 上运行 httpd for arm,可以先将编译好的 httpd 放入 raw 文件夹中,在 MainActivity 启动时判断是否在指定位置中,没有则释放。我通常是将其放在单独的服务中运行,这样就算 Activity 销毁了,服务还会在后台运行,这也是服务器必备的一个特性。

private File httpd;@Overridepublic 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);    }  }}

在 Android 中有一个 Runtime 类,这个类主要是用来让 Android 应用程序可以与它所在的运行环境进行交互,可以直接通过调用 Runtime.getRuntime() 的静态方法来得到这个类的实例,再调用 exec 就可以执行命令,接下来我创建了一个二进制执行类,对其做了一个简单的封装。

public class BinExecuter {  /**   * 进程 PID   */  private int pid;  /**   * 可执行二进制文件路径   */  private String bin;  /**   * 启动参数   */  private String paras;  /**   * 进程实例   */  private Process process;  /**   * 获取 PID   * @return   */  public int getPid() {    return pid;  }  /**   * 构造函数   * @param bin 可执行二进制文件路径   * @param paras 启动参数   */  public BinExecuter(String bin, String paras) {    this.bin = bin;    this.paras = paras;  }  /**   * 启动进程   */  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();    }  }  /**   * 结束进程   */  public void stop() {    if (pid > 0) {      try {        Runtime.getRuntime().exec("kill -9 " + pid);      } catch (Exception ex) {        ex.printStackTrace();      }    }  }}

但是这还是不够的,像 httpd 这类程序,启动后,控制台会有输出。例如有客户端请求了某个 url,或者出现什么错误,都会显示在控制台上。Android 上是没有控制台窗口的,那么如何捕捉控制台输出呢,简单,重定向输出到输入流中即可。

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

注意了,这里有个大歪鹅(while),主线程会被阻塞的,启动另外的线程就行了,改造这个类,增加控制台输出的监听,可以让它变稍微强大一点。

/** author:yahch**/public interface BinExecuteCallback {  void onConsoleResponse(String text);}private BinExecuteCallback binExecuteCallback;public void setBinExecuteCallback(BinExecuteCallback binExecuteCallback) {  this.binExecuteCallback = binExecuteCallback;}

在前段时间我开发的一个 Aria2 服务端中的对应用法如下:

@Overridepublic 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);}

通过 EventBus 把服务中截取的控制台消息抛到 Activity 中,当然也可以使用广播,我觉得 EventBus 还是要好用些。

现在 GO 语言也百花齐放,GO 天生就是为了服务端而生,而且跨平台能力特别强大,在 Github 上已经有很多程序编译为了 ARM 版本的,像 frp、caddy、filebrowser 这些,都可以移植在 Android 上,我们要做的,就是给他一个壳,控制它运行和停止,以及配置些参数。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持VEVB武林网。


注:相关教程知识阅读请移步到Android开发频道。
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表