1.简介

前一段时间看了包建强老师的《App研发录》,决定将自己写的北京地铁换乘App重构一下,并更名为”帝都地铁”。

此版本将与业务无关的逻辑封装成subwaylib类库,并手写了”网络请求”和”图片加载”模块,优化了”线路搜索”的代码,个人觉得代码质量还是OK的。

Github地址:帝都地铁源码

2.网络请求

发起网络请求的代码示例(金山词霸的每日一句Api):

final AppRequestCallback callback = new AppRequestCallback() {@Overridepublic void onSuccess(String content) {Sentence sentence = JSON.parseObject(content, Sentence.class);if (sentence != null) {ImageLoader.getInstance().displayImage(sentence.getPicture(), ivPicture);tvContent.setText(sentence.getContent());tvNote.setText(sentence.getNote());}}
};
AppHttpRequest.getInstance().performRequest(this, "dsapi", null, callback);

项目中所有的网络请求的配置信息都写在本地xml文件中:

<?xml version="1.0" encoding="UTF-8"?>
<url><NodeKey="dsapi"Expires="21600"NetType="get"MockClass = ""Url="http://open.iciba.com/dsapi/"/>
</url>

AppHttpRequest执行请求时,会根据Key值取出网络请求的配置信息,包括:缓存时间(Expires),请求方式(NetType),模拟数据(MockClass),接口地址(Url)。

final URLData urlData = UrlConfigManager.findURL(activity, apiKey);

模拟数据不为空,则进行本地测试,为空则RequestManager创建Request,RequestThreadPool执行Request。

Request request = activity.getRequestManager().createRequest(urlData, params, callback);
RequestThreadPool.getInstance().execute(request);

Request是一个实现Runnable的抽象类,包含三个抽象方法:

protected abstract void doGet();
protected abstract void doPost();
protected abstract void abort();

分别以HttpClient和HttpURLConnection方式实现了Request,项目中默认使用HttpURLConnection方式获取网络请求数据。

public Request createRequest(final URLData urlData, final List<RequestParameter> parameters, final RequestCallback requestCallback) {final Request request = new HurlRequest(urlData, parameters, requestCallback);addRequest(request);return request;
}

当以Get方式请求数据时,如果参数不为空,则先拼接参数:

if ((mParameters != null) && (mParameters.size() > 0)) {mUrl = mUrl + HOST_PARAMS_SEPARATOR + formatRequestParams();
}

如果缓存时间大于0,则取缓存数据:

if (mExpires > 0) {strCacheContent = CacheManager.getInstance().getFileCache(mUrl);
}

如果缓存数据不为空,则返回缓存数据,为空则创建HttpURLConnection连接,获取接口数据,并将数据写入缓存。

if (urlConn.getResponseCode() == HttpURLConnection.HTTP_OK) {// 保存CoociestoreCookie();// 获取返回的数据is = urlConn.getInputStream();String response = BaseUtils.InputStream2String(is);is.close();// 把成功获取到的数据记录到缓存if (mExpires > 0) {CacheManager.getInstance().putFileCache(mUrl, response, mExpires);}// 处理返回信息doResponse(response);
}

以上即为执行一次网络请求的流程。

3.图片加载

加载网络图片的代码示例:

ImageLoader.getInstance().displayImage(sentence.getPicture(), ivPicture);

ImageLoader中会分别尝试从内存和硬盘中获取Bitmap,如果取不到则执行ImageRequest:

public void displayImage(final String imageUrl, final ImageViewWrapper imageViewWrapper, final ImageLoadingListener listener) {Bitmap bitmap;if (mImageCache != null) {// 从内存中获取Bitmapbitmap = mImageCache.getBitmapFromMemCache(imageUrl);if (bitmap != null) {imageViewWrapper.setImageBitmap(bitmap);return;}// 从硬盘中获取Bitmapbitmap = mImageCache.getBitmapFromDiskCache(imageUrl);[http://](http://)if (bitmap != null) {mImageCache.addBitmapToMemCache(imageUrl, bitmap);imageViewWrapper.setImageBitmap(bitmap);return;}}// 网络请求BitmapImageRequest request = new ImageRequest(imageUrl, imageViewWrapper, listener, mImageCache);ImageThreadPool.getInstance().submit(request);
}

ImageRequest实现了Runnable接口,使用HttpURLConnection下载网络图片,并对图片进行inSampleSize处理。开发过程中遇到了这样一个问题:HttpURLConnection获取的InputStream只能被BitmapFactory.decodeStream处理一次:

BitmapFactory.decodeStream returning null when options are set

解决方案是先将InputStream转换为ByteArrayOutputStream,当使用时在转回为InputStream:

// 将InputStream转换为ByteArrayOutputStream
baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) > -1) {baos.write(buffer, 0, len);
}
baos.flush();final BitmapFactory.Options options = BitmapDecoder.getBitmapFactoryOptions(new ByteArrayInputStream(baos.toByteArray()), mImageViewWrapper.getWidth(), mImageViewWrapper.getHeight()
);
final Bitmap bitmap = BitmapDecoder.decodeBitmapFromInputStream(new ByteArrayInputStream(baos.toByteArray()), options);

顺便再说一个小插曲,测试图片缓存的时候,需要断网来测试图片缓存和加载。有一次发现图片加载总是失败,debug一顿找,发现connect时总是异常,顿时不觉明历,最后恍然醒悟,wifi让我关了没开,哎,程序员真是苦啊。

4.线路搜索

本期将线路搜索的代码都写在SubwayMap类中,这样看起来也更直观。

首先,分别获取起点终点车站的车站ID集合:

// 起点车站名对应的车站ID集合
List<String> lstFromStationIds = mStationDao.getStationIdsByStationName(fromStationName);
// 终点车站名对应的车站ID集合
List<String> lstToStationIds = mStationDao.getStationIdsByStationName(toStationName);

这里要说明一下,比如说军事博物馆站是一个换乘车站,它有两个车站ID,分别为0109和0904,车站ID前两位表示车站所在线路,即军事博物馆站属于1号线和9号线,当以军事博物馆站为起点站查询时需要考虑分别从1号线和9号线为起点线路向其他线路换乘,因此需要取出车站名对应的车站ID集合,再根据车站ID集合获取起点终点线路集合。

接着,获取起点终点线路集合,比如从丰台科技园到北京站,则[09,02]:

List<String[]> lstFromToLineIds = getFromToLineIds(lstFromStationIds, lstToStationIds);

接着,获取起点到终点换乘路线详细信息,[09,01,02],[09,06,02],[09,04,02]:

List<String[]> lstTransferRouteLineIds = getFromToTransferRouteLineIds(lstFromToLineIds);

接着,遍历起点到终点换乘路线详细信息,以此加载换乘数据,并获取换乘详细信息:

TransferDetail transferDetail = new TransferDetail();
transferDetail.fromStationName = mFromStationName;
transferDetail.toStationName = mToStationName;
transferDetail.lstTransferRoute = new ArrayList<>();
for (String[] lids : lstTransferRouteLineIds) {// 构建临接表建图createSubwayMap(lids, lstFromStationIds.get(0), lstToStationIds.get(0));// 从图中搜索两点之间最短距离TransferRoute transferRoute = searchTransferRoute(lstFromStationIds.get(0), lstToStationIds.get(0));// 添加换乘路线updateTransferDetail(transferDetail, transferRoute);
}

这里也要说明一下,军事博物馆有两个车站ID,但构建地铁图时只能使用唯一的一个,因此选择车站ID的最小值为图中的车站ID。

最后,返回换乘详情。

transferDetail.ticketPrice = transferDetail.lstTransferRoute.get(0).ticketPrice;
return transferDetail;

其中搜索换乘路线详细信息时用到了深度优先搜索算法:

private void DFS(final int from, final int to) {if (SubwayData.LINE_TRANSFERS[from][to] == 1) {int i = 0;String[] lineIds = new String[mStack.size() + 2];for (int index : mStack) {lineIds[i++] = SubwayData.LINE_EDGES[index];}lineIds[i++] = SubwayData.LINE_EDGES[from];lineIds[i] = SubwayData.LINE_EDGES[to];mLstTransferRouteLineIds.add(lineIds);} else {mStack.push(from);isVisited[from] = true;for (int i = 0; i < SubwayData.LINE_EDGES.length; i++) {if (!isVisited[i] && SubwayData.LINE_TRANSFERS[from][i] == 1 && mStack.size() < mMinTransferTimes) {DFS(i, to);}}isVisited[from] = false;mStack.pop();}
}

搜索两点之间最短距离用到了迪杰斯特拉算法:

int cur = 0, min, tmp;
while (!visited[toStationIndex]) {// 寻找当前最小的路径min = Integer.MAX_VALUE;for (int i = 0; i < size; i++) {if (!visited[i] && distance[i] < min) {min = distance[i];cur = i;}}// 标记已获取到最短路径visited[cur] = true;// 修正当前最短路径和前驱结点for (int i = 0; i < size; i++) {tmp = getFromToDistanceByHeadIndex(cur, i);if (tmp != Integer.MAX_VALUE) {tmp += min;}if (!visited[i] && tmp < distance[i]) {distance[i] = tmp;previous[i] = cur;}}
}

以上即为整个项目的大致介绍,具体请看源码。

我的App-帝都地铁相关推荐

  1. 这些技术人棒棒哒!BingoDay2017获奖名单新鲜出炉~~~

    3月24日,在为来宾们带去详实的技术干货后,2017年的BingoDay圆满落幕了.但是,品高软件年度解决方案的竞选才刚刚开始!在经过数天紧张激烈的投票和评选后,今天,品高软件年度解决方案.Bingo ...

  2. 温暖守护美好生活,科技从不冷冰冰

    美好生活 从云知道. 从古至今,人类总是在不断追求更美好的生活.什么是美好的生活?不同年代的人有着不同的诠释,五六十年代,吃饱喝足便是人生至高的理想:到了七八十年代,赶上改革开放,实现现代化成为父辈们 ...

  3. [杂言] GoodBye,2016

    呼-- 2016年终于要过去了-- 就在这一年,我开始了-- 也就在这一年,我GG了-- 谨以此文纪念我过得不怎么好的2016. 一月 (我还是萌新求轻虐) 当时还在学习背包和深搜,突然dayu把yb ...

  4. 移动互联网下半场争夺战:逃不过娱乐大网,得年轻中产得天下

    如今,国内移动互联网已然开始进入到了下半场的争夺战中,消费升级大环境下也呈现出不同的发展趋势:移动互联网流量占比进一步提高,用户都是碎片化时间,但时长进一步增长,占据用户手机中的APP依然是娱乐类为主 ...

  5. 软件测试理论知识-基本概念

    先来看一下什么是软件测试的被测对象? 通俗的讲,就是我们日常见到的各类在电脑.手机.以及一些我们大多数接触的比较少的硬件设备上的相关软件,比如常见的12306购物网站,抖音.淘宝等app.地铁过安检的 ...

  6. 【校园卡】校园卡最近消息:2020校园卡还未停售,更新最新海报

    微信关注 "DLGG创客DIY" 设为"星标",重磅干货,第一时间送达. 大家都比较关心今年的停售时间,最近确认了联通和电信的校园卡都还没有停售,不过按往年经验 ...

  7. 200518更新校园卡最新小道消息

    200518最近收到了一些小道消息,和大家分享一下: 注意:是未经证实的小道消息,但可信度还是比较高的,消息是"舅舅"说的(wow典故,即内部知情人士) 一个好消息,一个坏消息. ...

  8. 【校园卡】2021年开学季三大运营商校园卡最近消息!

    微信关注 "DLGG创客DIY" 设为"星标",重磅干货,第一时间送达. 最近问我校园卡的朋友挺多, 毕竟快到开学季了(之前的卡快到期了),大家都懂. 这里汇总 ...

  9. 【校园卡】2020校园卡最近消息,联通推出500两年校园卡,更新校园卡对比表格...

    微信关注 "DLGG创客DIY" 设为"星标",重磅干货,第一时间送达. 最近联通也更新了500两年的校园卡,扫码进入后一共有4个可选,大家可能看着比较乱,这里 ...

最新文章

  1. GPT3 api接口调用
  2. 启动mysql提1067_win7系统启动mysql服务提升错误1067进程意外终止的解决方法
  3. [开源]Dapper Repository 一种实现方式
  4. SQL 行转列的两种做法
  5. 微信小程序获取二维码中URL中带的参数
  6. 2021-2025年中国点状插头装置行业市场供需与战略研究报告
  7. 混沌理论(Chaos Theory)
  8. Activiti6.0.0及以上版本集成Activiti Modeler
  9. 《Spring Cloud、Nginx高并发核心编程》读书笔记【END】
  10. Pixracer V1.0编译固件
  11. Ubuntu20.10系统FreeCAD 0.19编译安装
  12. 关于ITIL证书更新的重要通知
  13. 数据结构(C语言描述)——复数
  14. MongoDB 学习笔记
  15. Qt之QTextEdit
  16. 深入理解JVM中的栈和堆
  17. IDEA中展开包结构的方法
  18. Scrapy模拟登陆豆瓣抓取数据
  19. 虚拟机群晖找不到服务器,ESXI6.0安装群晖6.2,解决搜索不到IP问题。禁止转载
  20. 虹软-视觉算法-面经(2020届)

热门文章

  1. 微信小程序授权登陆,解密encryptedData出现乱码问题
  2. pywin32的一系列用法
  3. Flink运行时架构及各部署模式下作业提交流程
  4. [内网渗透学习]内网收集Fscan扫描探测工具
  5. 学习笔记-磁盘存储管理
  6. 六年Android生涯总结+展望:君子坐而论道,少年起而行之
  7. 常见的服务器操作系统和工作站操作系统
  8. html中如何设置动画效果,css3如何设置动画?
  9. ESP8266天猫精灵接入流程
  10. python3计算md5_python 计算文件的md5值实例