基于Android的服务器端程序实例
在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,虽然没看过多看的源代码,但是估计十有八九也是采用了这个库。
不过它也可以作为客户端方式,作为监听服务方式运行使用方法非常简单:
AsyncHttpServerserver=newAsyncHttpServer(); server.get("/",newHttpServerRequestCallback(){ @Override publicvoidonRequest(AsyncHttpServerRequestrequest,AsyncHttpServerResponseresponse){ 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上运行httpdforarm,可以先将编译好的httpd放入raw文件夹中,在MainActivity启动时判断是否在指定位置中,没有则释放。我通常是将其放在单独的服务中运行,这样就算Activity销毁了,服务还会在后台运行,这也是服务器必备的一个特性。
privateFilehttpd; @Override publicvoidonCreate(){ super.onCreate(); httpd=newFile(getFilesDir(),"httpd"); if(!httpd.exists()){ try{ InputStreamins=getResources().openRawResource(R.raw.httpd); FileIOUtils.writeFileFromIS(httpd,ins); Runtime.getRuntime().exec("chmod777"+httpd.getAbsolutePath()); }catch(Exceptione){ Log.e(TAG,"onCreate:",e); } } }
在Android中有一个Runtime类,这个类主要是用来让Android应用程序可以与它所在的运行环境进行交互,可以直接通过调用Runtime.getRuntime()的静态方法来得到这个类的实例,再调用exec就可以执行命令,接下来我创建了一个二进制执行类,对其做了一个简单的封装。
publicclassBinExecuter{ /** *进程PID */ privateintpid; /** *可执行二进制文件路径 */ privateStringbin; /** *启动参数 */ privateStringparas; /** *进程实例 */ privateProcessprocess; /** *获取PID *@return */ publicintgetPid(){ returnpid; } /** *构造函数 *@parambin可执行二进制文件路径 *@paramparas启动参数 */ publicBinExecuter(Stringbin,Stringparas){ this.bin=bin; this.paras=paras; } /** *启动进程 */ publicvoidstart(){ try{ process=Runtime.getRuntime().exec(bin+""+paras); Fieldf=process.getClass().getDeclaredField("pid"); f.setAccessible(true); pid=f.getInt(process); f.setAccessible(false); }catch(Exceptionex){ ex.printStackTrace(); } } /** *结束进程 */ publicvoidstop(){ if(pid>0){ try{ Runtime.getRuntime().exec("kill-9"+pid); }catch(Exceptionex){ ex.printStackTrace(); } } } }
但是这还是不够的,像httpd这类程序,启动后,控制台会有输出。例如有客户端请求了某个url,或者出现什么错误,都会显示在控制台上。Android上是没有控制台窗口的,那么如何捕捉控制台输出呢,简单,重定向输出到输入流中即可。
InputStreamouts=process.getInputStream(); InputStreamReaderisrout=newInputStreamReader(outs); BufferedReaderbrout=newBufferedReader(isrout); Stringline; try{ while((line=brout.readLine())!=null){ log.d(line); } }catch(Exceptionex){ ex.printStackTrace(); }
注意了,这里有个大歪鹅(while),主线程会被阻塞的,启动另外的线程就行了,改造这个类,增加控制台输出的监听,可以让它变稍微强大一点。
/**author:yahch**/ publicinterfaceBinExecuteCallback{ voidonConsoleResponse(Stringtext); } privateBinExecuteCallbackbinExecuteCallback; publicvoidsetBinExecuteCallback(BinExecuteCallbackbinExecuteCallback){ this.binExecuteCallback=binExecuteCallback; }
在前段时间我开发的一个Aria2服务端中的对应用法如下:
@Override publicintonStartCommand(Intentintent,intflags,intstartId){ if(intent!=null){ ariaConfig=(AriaConfig)intent.getSerializableExtra("config"); if(ariaConfig!=null){ Log.d(TAG,ariaConfig.toString()); binExecuter=newBinExecuter(fileAria2c.getAbsolutePath(),ariaConfig.toString()); binExecuter.setBinExecuteCallback(newBinExecuter.BinExecuteCallback(){ @Override publicvoidonConsoleResponse(Stringtext){ sendMessage(ARIA2_SERVICE_BIN_CONSOLE,text); } }); } }else{ stopSelf(); } returnsuper.onStartCommand(intent,flags,startId); } privatevoidsendMessage(Stringname,Stringmessage){ MessageEventgenericEvent=newMessageEvent(name,message); EventBus.getDefault().post(genericEvent); }
通过EventBus把服务中截取的控制台消息抛到Activity中,当然也可以使用广播,我觉得EventBus还是要好用些。
现在GO语言也百花齐放,GO天生就是为了服务端而生,而且跨平台能力特别强大,在Github上已经有很多程序编译为了ARM版本的,像frp、caddy、filebrowser这些,都可以移植在Android上,我们要做的,就是给他一个壳,控制它运行和停止,以及配置些参数。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。