当前位置: 首页 > news >正文

【Android】支持在线打开的文件浏览服务器开发流程讲解

想要把闲置手机设备改造成像NAS,或Web文件服务器一样的功能,方便在家庭WIFI局域网内随时随地访问备份文件,或者学习资源,如何开发呢,接下来讲一讲实现过程。

首先,请看一段有趣的故事。


当我在街头偶遇那位心仪已久的女孩,她正站在一个二手手机摊前,手里握着一部略显陈旧的手机,眼神中带着一丝不舍与疑惑,向摊贩询问着:

“请问我这个闲置手机能卖多少钱?”

摊贩接过手机,尝试开机后,给出了一个略显吝啬的报价:

“50块。”

她闻言,眉头微蹙,显然对这个价格不太满意:

“这可是我以前花几千块买的,一直用得好好的,没想到现在只值这么点。”

我见状,心中暗自窃喜,这或许是我展现技术魅力的大好时机。我走近她,轻声建议道:

“其实,这部手机还有很多潜在的价值,没必要急着卖掉。”

她抬头看向我,眼中闪过一丝好奇:

“哦?还有什么用呢?”

我微笑着解释:

“你可以为它装上一些实用的APP,比如时钟APP,既省电又能当电子闹钟用;或者,利用家里的局域网,把它变成一个简易的文件服务器,存点视频、照片、电子书,随时随地都能访问,比买NAS服务器划算多了,还能省电费呢。再比如,还有答题服务器,对学习很有帮助,是个刷题的好工具。”

她听得入了神,眼中闪烁着兴奋的光芒:

“真的吗?你能帮我试试吗?如果成功了,我请你吃大餐!”

我欣然答应,心中早已盘算着如何大展身手。跟随她来到家中,我见到了她的母亲,并做了简单的自我介绍。在温馨的氛围中,我开始了我的“技术表演”。

我接过她递来的手机,仔细检查后发现,虽然系统版本较旧(Android 5.0),但硬件状况尚可,只需装上APP便能焕发新生。我向她解释道:

“这个手机系统有点旧了,装不了现代的App,不过别担心,我是程序员,可以自己开发APP给它装上。”

她闻言,眼中闪过一丝惊讶与敬佩:

“没想到你还是个程序员,技术一定很棒吧!”

我谦虚地笑了笑,随即向她借用电脑,准备现场开发。她迅速取来笔记本电脑,我迅速下载开发工具,搭建起基本环境,开始了我的“魔法”表演。

首先,我为她打造了一款简洁实用的时钟APP,借鉴了之前看过作者TA远方发布的一篇文章中的代码思路,很快便大功告成。我将APP安装到手机上,递给她体验。她试用后,满意地点点头:

“不错哦,挺实用的!”

接着,我趁热打铁,开始为她开发答题系统APP。虽然这次难度有所提升,但在我丰富的经验与不懈的努力下,参考作者TA远方的另一篇文章,最终还是成功将其呈现在她面前。她试用后,兴奋不已:

“这个太棒了,对我学习帮助很大!”

随着夜幕的降临,我们共进晚餐,聊得愈发投机。她突然提起了文件服务器的事情:

“你之前说的文件服务器,能实现吗?我想把学习资料都存进去,使用网盘不用VIP会员,那下载太慢了。”

我笑着点点头,信心满满地答应道:

“当然可以,这个对我来说只是小菜一碟。不过,具体实现还需要一些时间。等下次见面,我给你一个完整的解决方案。”

就这样,我们在愉快的氛围中结束了这次难忘的会面。

一个人走在回家的路上,心中暗自庆幸着,自言自语道:

”还好没出丑,总算出来了,之前做出来的都是参考TA远方作者发布的技术文章做出来的,不知道接下来的文件服务器要怎么做,回去得好好学习,争取早点做出来。“

然后,给自己鼓励道:

”为了心仪的女孩,加油吧,少年!“

而我们的故事,也因这次技术交流而悄然有了交情…


好了,故事告一段落,

接下来,让本作者讲解如何开发这个文件服务器,一起感受故事里学习生活中带来更多的便利。

文章目录

    • 页面布局
    • 安装模块
      • 服务器
      • 二维码
    • 实现功能
      • 更改
      • 开启服务器
      • 关闭服务器
      • 打开浏览器
      • 清空数据
      • 获取权限
    • 运行测试
    • 项目源码

图片描述

准备好闲置Android 安卓手机,电脑,

还需要你会用Android Studio开发工具,会用Java语言写代码,

想试试却没有安装,可参考以下文章开始安装

  • 【Android】安装2025版AndroidStudio开发工具开发老安卓旧版App

文章要讲的项目,是以Web服务器项目开发的基础上调整而来,之后的更多细节可参考以下文章

  • 【Android】答题系统Web服务器APP应用开发流程详解

注意:
开发此项目编译的APP安装包适配在安卓手机系统Android 5.0以上

打开已安装好的Android Studio开发工具,新建一个项目,例如 FileBrowser

在新建项目的窗口上,注意选择 No Activity,只有这一项,才能开发旧系统的App,

接下来,选择对应的

  • Language 选择 Java,
  • Minimum SDK 选择 API21,也就是最小适配 Android 5.0 以上的App,
  • Build configuration language 选择 Groovy DSL(build.gradle)

最后,等开发工具Build 创建项目完成。

完成后,

要新建一个页面,点中项目文件夹,点鼠标右键,按以下步骤选择

New → Activity → Empty Views Activity

由于第一个页面是应用程序入口,要默认勾选Launcher Activity,

这样,一个页面就自动建好了,看看项目路径app/src/main下的文件,

会发现多了两个文件:

  • 一个 MainActivity.java - 写页面逻辑
  • 一个 main_layou.xml - 写页面布局

页面布局

打开项目下的布局文件main_layout.xml,做好一个主页面,修改后如下图
图1

在页面逻辑下,写好初始化代码,如下:

public class MainActivity extends AppCompatActivity {//...@Override  protected void onCreate(Bundle savedInstanceState) {  super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//...//获取按钮tvAddress = findViewById(R.id.textViewServerAddress);  btnStart = findViewById(R.id.buttonStartServer);  btnStop = findViewById(R.id.buttonStopServer);  btnOpen = findViewById(R.id.buttonOpenBrowser);  btnUpdate = findViewById(R.id.buttonUpload);  btnClear = findViewById(R.id.buttonClear);  tvSourcePath = findViewById(R.id.textView5);  //按钮点击事件btnStart.setOnClickListener(s->startServer());  btnStop.setOnClickListener(s->stopServer());  btnOpen.setOnClickListener(s->openBrowser());  btnUpdate.setOnClickListener(s->openFolderPicker());  btnClear.setOnClickListener(s->clearOrderData());//...// 初始化显示  updateUI();  // 检查权限  checkStorageAccess();  checkPermissions();}//...
}

接下来,主要是讲功能的实现,先准备两个模块,

模块就是功能,就好比轮子
只是想告诉你,轮子有人造好了,
就这样,你可以下载来用,
自己最好不要,重复重复造轮子,
若没有,令自己满意满意的轮子,
你可以,尝试造属于自己的轮子,
可是可是你不懂,写好轮子的感觉。

现在告诉你这两个轮子,也就是叫两个模块,

安装模块

这两个模块,分别是服务器和二维码,

服务器

这个服务器的模块名叫AndServer,安装这个模块还需要几个步骤,

打开app模块文件build.gradle,添加以下内容

plugins {  //...id 'com.yanzhenjie.andserver'  
}dependencies {  //...implementation 'com.yanzhenjie.andserver:api:2.1.12'  annotationProcessor 'com.yanzhenjie.andserver:processor:2.1.12'  
}

还有一个文件build.gradle,跟上面的文件一样,但内容不一样,是放在项目的根目录下,

将其打开,添加以下内容

buildscript {  dependencies {  classpath 'com.yanzhenjie.andserver:plugin:2.1.12'  }  
}

注意这个要在里面的内容plugins { ... } 前面的位置插入,否则编译会报错。

看过之前开发答题服务器文章的读者也许会问,为什么不用之前的模块插件NanoHTTPD做服务器?

作者用过,只是没有这个的好用吧,有时间自己试一下,觉得哪个好用就用哪个吧。

二维码

还有个二维码的模块名叫zxing,安装这个模块只要一个步骤,

打开app模块文件build.gradle,添加以下内容

dependencies {  //...implementation 'com.google.zxing:core:3.5.2'  implementation 'com.journeyapps:zxing-android-embedded:4.3.0'  
}

实现功能

点击编辑器右上角的Sync Now,开发工具就会开始联网安装相关的模块,

等模块安装好了,接下来就实现功能

看看界面上按钮有什么,每个按钮都用到了几个功能,

需要一个个去实现:

更改

点击更改按钮,就会打开文件夹浏览窗口,用于选择文件夹位置,

点击按钮会调用一个方法,代码如下

private void openFolderPicker() {    //...FolderPickerUtils.openFolderPicker(folderPickerLauncher);  
}

其中方法openFolderPicker()会打开文件夹浏览窗口,这个窗口就用系统内置的,不用再单独做一个页面,

选择好文件夹后,窗口会调用folderPickerLauncher的一个方法,代码如下

private String selectedFolderPath = "";private final ActivityResultLauncher<Intent> folderPickerLauncher = registerForActivityResult(  new ActivityResultContracts.StartActivityForResult(),  new ActivityResultCallback<ActivityResult>() {  @Override  public void onActivityResult(ActivityResult result) {  String path = FolderPickerUtils.handleFolderPickerResult(  MainActivity.this, result.getResultCode(), result.getData());  setSelectedFolderPath(path);  }  }  
);

将文件夹路径通过自定义方法setSelectedFolderPath(path)设置到selectedFolderPath,这样服务器就能获取到管理资源文件的文件夹路径。

开启服务器

点击开启服务器,就会开启一个后台服务,假设有个服务类WebServerService,代码如下

private WebServerService webServerService;
private final ServiceConnection serviceConnection = new ServiceConnection() {  @Override  public void onServiceConnected(ComponentName name, IBinder service) {WebServerService.LocalBinder binder = (WebServerService.LocalBinder) service;  webServerService = binder.getService();  //...}  @Override  public void onServiceDisconnected(ComponentName name) {  //...}  
};

在之后的服务器开启时,会触发它里面这个方法onServiceConnected(),通过getService()返回实例化webServerService类,

这个服务类WebServerService需要自己实现,继承了服务,代码如下

public class WebServerService extends Service {private AndroidWebServer webServer;@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {return super.onStartCommand(intent, flags, startId);}//...
}

然后,重写它的方法onStartCommand(),这里面去调用模块实现webServerstart()方法,开启服务,

try {  webServer = new AndroidWebServer(SERVER_PORT, customFolderPath, this); webServer.start();  // 启动成功  String ipAddress = NetworkUtils.getLocalIpAddress(this);  String serverUrl = "http://" + ipAddress + ":" + SERVER_PORT;  sendStatusUpdate(true, serverUrl, null);  
} catch (Exception e) {stopSelf();  // 启动失败  sendStatusUpdate(false, null, "启动失败: " + e.getMessage());  
}

其中方法sendStatusUpdate就是发送通知的,告诉用户当前服务器的状态

最后,别忘了在文件AndroidManifest.xml里添加service,内容如下

<?xml version="1.0" encoding="utf-8"?>  
<manifest><!-- ... --><application><activity>  <!-- ... --></activity>  <!-- 前台服务 -->  <service  android:name=".server.WebServerService"  android:enabled="true"  android:exported="false" />  </application>  
</manifest>

当开启服务的按钮点击后,会调用一个方法,代码如下

private void startServer(){  //...// 启动服务(会创建前台通知)  Intent serviceIntent = new Intent(this, WebServerService.class);  // 我们可以通过Intent传递文件夹路径  serviceIntent.putExtra("folder_path", selectedFolderPath);  startService(serviceIntent);  PreferenceUtils.setString(this, SET_SOURCE_PATH, selectedFolderPath);  showToast( "服务器开启");  
}

可见,开启服务是这样的,通过Intent传递,并带个参数selectedFolderPath,就是资源文件夹位置

当服务器成功开启,就要更新下一些按钮状态为可以点击,

还有展示二维码,这样可以扫码访问,操作方便。

之前讲的答题服务器的文章内有类似的实现,这里就不展开讲二维码怎么弄了,

这也是用到了模块名zxing里面的方法,实现生成二维码图片。

关闭服务器

点击关闭服务器,就会把之前开启的后台服务给关闭了,

点击的按钮会调用一个方法,代码如下

private void stopServer(){  if (isServiceBound && webServerService != null) {  // 停止服务  Intent serviceIntent = new Intent(this, WebServerService.class);  stopService(serviceIntent);  // 解绑unbindService(serviceConnection);  isServiceBound = false;  showToast( "服务器关闭");  }  
}

当解绑后,系统会调用实例webServerService的一个onDestroy()方法,代码如下

public void onDestroy() {// 停止Web服务器  if (webServer != null) {  webServer.stop();  webServer = null;  sendStatusUpdate(false, null, null);  }  //...
}

从上面看出,调用了实例webServerstop()方法,就停止了服务器

聪明的你也许留意到了,服务器是webServer,是AndroidWebServer类,还需要自己实现,

这个实现就用到了AndServer模块,代码如下

public class AndroidWebServer {private final Server server;public AndroidWebServer(int port, String resourcePath, Context context){//...server = AndServer.webServer(context)  .port(port)  .timeout(10, TimeUnit.SECONDS)  .listener(new Server.ServerListener() {...})  .build();}public void start(){  if(!server.isRunning()) server.startup();  }  public void stop(){  if(server.isRunning()) server.shutdown();  }  public boolean isAlive(){  return  server.isRunning();  }
}

这个没有继承模块AndServer,而是在里面实例化了模块对象Server,可控制它的开启和停止,

聪明的你也许会发现问题,服务器的请求控制器逻辑没有写,当然不是写在listener()里面,

它是没有写到一起的,这就写一个控制器ApiController类,代码如下

@RestController  
@RequestMapping("/api")  
public class ApiController {@GetMapping(value = "/list", produces = MediaType.APPLICATION_JSON_VALUE)  @ResponseBody()  public String toMenuList(){  List<String> menus = new ArrayList<>();  menus.add(FolderName_Image);  menus.add(FolderName_Book);  menus.add(FolderName_Audio);  menus.add(FolderName_Video);  menus.add(FolderName_Other);  return String.format("[\"%s\"]", String.join("\",\"", menus));  }@RequestMapping(method= RequestMethod.POST, path="/filelist/{dirname}", produces = MediaType.APPLICATION_JSON_VALUE)  @ResponseBody  public String toDirFileList(@PathVariable("dirname") String dirName, @RequestBody String jsonBody) throws Exception {  List<String> dirs = getRequestBodyList(jsonBody);  String path = dirs.isEmpty() ? "" : String.join("/", dirs);  // 安全验证  if (path.contains("..")) throw new Exception("error path "+path);return toFileList(dirName, path);  }//部分省略...
}

从代码中看出,这是处理两个请求:

  • /api/list - 返回菜单列表,每个菜单项对应一个资源根目录文件夹;
  • /api/filelist/{dirname} - 返回dirname目录下的文件以及文件夹列表;

学会Java开发,创建过RESTful风格的控制器的同学,应该会发现它使用@RequestMapping()类似SpringMVC的注解;

这就是处理页面GET请求和POST请求的后台逻辑,

聪明的你又发现了问题,没有看到哪个代码会调用这个类ApiController,这个不用管,开发工具会自动调用它,这就是AndServer模块专为懒人开发者设计的;

对了,还有服务器配置要写下,再写一个类WebServerConfig,代码如下:

public class WebServerConfig implements WebConfig {  @Override  public void onConfig(Context context, Delegate configurator) {  // 配置静态资源路径  configurator.addWebsite(new CustomAssetsWebsite(context, "/wwwroot/"));}}

可见,这又是一个懒人设计,没见到哪个代码在调用它,

其中wwwroot是一个文件夹,类似前端托管,这里存放uniapp项目生成的H5页面文件(单独放一个自己写的index.html文件也行),

这个文件夹就放在项目目录位置下app/src/main/assets,内置在APP里面,

如果你要研究和修改作者开发的uniapp项目,可参考 Web文件在线浏览播放器-uniapp-项目源码

打开浏览器

点击打开浏览器,就会打开系统自带的浏览器,直接访问文件服务器的H5页面,

点击的按钮会调用一个方法,代码如下

private void openBrowser(){  String url = webServerService.getServerUrl();  if (url==null || url.isEmpty()) {  showToast( "无法获取网络IP地址");  } else {  // 打开浏览器访问Web服务器  LinkUtils.openUrl(this, url);  }  
}

由于H5页面是用uniapp项目做来编译出来的,旧手机(包括Android 5.0以下)的自带浏览器无法正常加载H5页面,用电脑或者最新的Android 8以上的系统浏览器就能正常打开访问

清空数据

点击清空数据这个不是那么重要的功能,它是用于清空H5页面POST请求服务器后台处理插入的数据,

实现过程是怎样的呢,

看看按钮点击后,会调用一个方法clearOrderData(),代码如下

private void clearOrderData() {  SQLiteDataHelper helper = new SQLiteDataHelper(this);  SQLiteDatabase db = helper.getWritableDatabase();  helper.onUpgrade(db, 1, 1);  db.close();  showToast("已清空订单数据");  
}

这是用到了数据库操作基础的功能,开发Android应用的程序员经常会用到它存取数据;

有一个类SQLiteDataHelper.class文件要自己去写,继承SQLiteOpenHelper

然后重写它的一些方法,代码如下

public class SQLiteDataHelper extends SQLiteOpenHelper {public SQLiteDataHelper(Context context) {super(context, "data.db", null, 1);}@Override  public void onCreate(SQLiteDatabase db) {//这里创建表...}@Override  public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {  //这里更新表...}//自己实现其它的操作数据库方法...
}

清除数据的话,只需调用它的方法onUpgrade就可,

在这个方法里面,只执行清理表的数据,代码如下

db.execSQL("DROP TABLE IF EXISTS star_order");

是不是有点熟悉,DROP TABLE IF EXISTS table_namesqlite的操作数据语句,
对做过数据处理的人来说,这是基本的工作,跟操作数据表格文档很相似。

获取权限

运行的App首次获取权限是必不可少的:

  • 需要访问网络状态,连接的WIFI;
  • 需要通知,服务器工作的状态通知;
  • 需要保持在后台运行的权限;
  • 访问手机内部存储的权限,读取一些保存的文件;

在第一个页面MainActivity代码中处理初始化的时候,调用了两个方法,代码如下

// 检查权限  
checkStorageAccess();  
checkPermissions();

这就是检查存储和通知两个权限的,检查没有权限就去授权,自己能实现吧,

除了写授权逻辑代码,还要…

在配置文件AndroidManifest.xml下添加uses-permission ...,内容如下

<?xml version="1.0" encoding="utf-8"?>  
<manifest xmlns:android="http://schemas.android.com/apk/res/android"  xmlns:tools="http://schemas.android.com/tools"><uses-permission android:name="android.permission.INTERNET" />  <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />  <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />  <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />  <uses-permission android:name="android.permission.WAKE_LOCK" />  <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />  <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /><!-- 对于Android 10+,需要添加这个权限来访问公共目录 -->  <uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION" />  <!-- 对于Android 11+,需要添加这个来管理所有文件 -->  <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" tools:ignore="ScopedStorage" /><application  ... android:requestLegacyExternalStorage="true"><!-- ... --></application>  </manifest>

运行测试

编译顺利的话,就可以安装到手机上,运行时效果如下
动画12.gif

动图里面展示的二维码没毛病,只是在这里不便展示罢了~

看了动图的操作步骤,首次运行需要设置好资源文件夹位置,该位置下文件夹分别解释如下:

  • image - 存放图片的文件夹
  • audio - 存放音乐的文件夹
  • video - 存放视频的文件夹
  • book - 存放电子书的文件夹
  • other - 其它,不知道如何分类的文件就暂时放在这里

必须有这些文件夹,然后放一些资源文件存在里面,

当用手机扫码访问,或打开浏览器输入IP地址访问时,效果如下:
图片描述

动图时长超了,不得不压缩降低画质才能上传成功;

看完动图,在线浏览的文件不用下载就可以打开并播放,没有像某些网盘一样有限速烦恼,现在是不是有点心动了呢。

文章到此结束,感谢您的耐心阅读。

项目源码

项目源码已整理完毕,可直接在下方入口获取,你可根据实际需求决定是否下载使用。

  • 相关项目源码 点此查看,
  • 更多的源码 点此查看,
  • 更多的资源 点此查看,

当你亲自测试时,浏览文件打开时可能会又有响应慢的情况,

  • 这可能与手机性能或网络环境有关,手机的运行速度和处理能力直接影响文件打开的响应时间;
  • 连接的WIFI路由器网速不足可能导致文件加载缓慢。路由器性能决定了局域网内的数据传输效率,与外部宽带网络无直接关联;
  • 建议更换更高性能的手机,可以提升文件处理速度,更换好的路由器设备能够优化局域网内的网络传输质量。

图片

http://www.dtcms.com/a/447580.html

相关文章:

  • 太原做企业网站的简洁企业网站
  • 中国邮政做特产的网站建一个网站怎么赚钱
  • 广州网站建设知名乐云seoseo关键词排名软件流量词
  • 网站备案 图标郑州做网站的论坛
  • 【Java核心技术/基础】30道Java集合框架面试题及答案
  • 福州市建设局网站 动态网站更改
  • 北京移动网站建设公司排名网站优化效果
  • ip子域名二级域名解析企业网站优化分为哪两个方向
  • 做网贷网站宁波公司建站模板
  • 怎么搭建自己的网站平台cms模板
  • 营销网站建设制作电商网站对比 京东商城 淘宝网 阿里巴巴
  • AI时代工作与学习的秘密武器:如何最大化利用大模型
  • 携程网站 建设平台分析logo在线设计图片
  • 网站建设案例企业网站怎样优化seo
  • 网站建设合同书品牌公关案例
  • wamp做的网站标签图标案例中优衣库所采用的网络营销方式
  • 河南省建设厅网站资质平移办法佛山服务类网站建设
  • wordpress安装tomcat江门排名优化怎么做
  • 英山建设银行网站重庆网站快速排名优化
  • 南充建设企业网站wordpress 微博客
  • wap网站建设免费广东建设监理协会网站
  • 构建AI智能体:五十四、智能投资顾问的两种实现:反应式与深思式实践策略对比
  • 甘肃做网站哪家专业深圳互联时空网站优化怎么样
  • 天蝎网站建设公司祥云县住房和城乡建设网站
  • 保定百度网站建设国外网页设计分享网站
  • 河北网站建设推广公司长兴县网站建设
  • 做网站的知识wordpress密码重置密码
  • 如何用ps做网站项目建设管理
  • 美容视频视频网站建设做网站接活全流程
  • Leetcode刷题