我的App-帝都地铁
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-帝都地铁相关推荐
- 这些技术人棒棒哒!BingoDay2017获奖名单新鲜出炉~~~
3月24日,在为来宾们带去详实的技术干货后,2017年的BingoDay圆满落幕了.但是,品高软件年度解决方案的竞选才刚刚开始!在经过数天紧张激烈的投票和评选后,今天,品高软件年度解决方案.Bingo ...
- 温暖守护美好生活,科技从不冷冰冰
美好生活 从云知道. 从古至今,人类总是在不断追求更美好的生活.什么是美好的生活?不同年代的人有着不同的诠释,五六十年代,吃饱喝足便是人生至高的理想:到了七八十年代,赶上改革开放,实现现代化成为父辈们 ...
- [杂言] GoodBye,2016
呼-- 2016年终于要过去了-- 就在这一年,我开始了-- 也就在这一年,我GG了-- 谨以此文纪念我过得不怎么好的2016. 一月 (我还是萌新求轻虐) 当时还在学习背包和深搜,突然dayu把yb ...
- 移动互联网下半场争夺战:逃不过娱乐大网,得年轻中产得天下
如今,国内移动互联网已然开始进入到了下半场的争夺战中,消费升级大环境下也呈现出不同的发展趋势:移动互联网流量占比进一步提高,用户都是碎片化时间,但时长进一步增长,占据用户手机中的APP依然是娱乐类为主 ...
- 软件测试理论知识-基本概念
先来看一下什么是软件测试的被测对象? 通俗的讲,就是我们日常见到的各类在电脑.手机.以及一些我们大多数接触的比较少的硬件设备上的相关软件,比如常见的12306购物网站,抖音.淘宝等app.地铁过安检的 ...
- 【校园卡】校园卡最近消息:2020校园卡还未停售,更新最新海报
微信关注 "DLGG创客DIY" 设为"星标",重磅干货,第一时间送达. 大家都比较关心今年的停售时间,最近确认了联通和电信的校园卡都还没有停售,不过按往年经验 ...
- 200518更新校园卡最新小道消息
200518最近收到了一些小道消息,和大家分享一下: 注意:是未经证实的小道消息,但可信度还是比较高的,消息是"舅舅"说的(wow典故,即内部知情人士) 一个好消息,一个坏消息. ...
- 【校园卡】2021年开学季三大运营商校园卡最近消息!
微信关注 "DLGG创客DIY" 设为"星标",重磅干货,第一时间送达. 最近问我校园卡的朋友挺多, 毕竟快到开学季了(之前的卡快到期了),大家都懂. 这里汇总 ...
- 【校园卡】2020校园卡最近消息,联通推出500两年校园卡,更新校园卡对比表格...
微信关注 "DLGG创客DIY" 设为"星标",重磅干货,第一时间送达. 最近联通也更新了500两年的校园卡,扫码进入后一共有4个可选,大家可能看着比较乱,这里 ...
最新文章
- GPT3 api接口调用
- 启动mysql提1067_win7系统启动mysql服务提升错误1067进程意外终止的解决方法
- [开源]Dapper Repository 一种实现方式
- SQL 行转列的两种做法
- 微信小程序获取二维码中URL中带的参数
- 2021-2025年中国点状插头装置行业市场供需与战略研究报告
- 混沌理论(Chaos Theory)
- Activiti6.0.0及以上版本集成Activiti Modeler
- 《Spring Cloud、Nginx高并发核心编程》读书笔记【END】
- Pixracer V1.0编译固件
- Ubuntu20.10系统FreeCAD 0.19编译安装
- 关于ITIL证书更新的重要通知
- 数据结构(C语言描述)——复数
- MongoDB 学习笔记
- Qt之QTextEdit
- 深入理解JVM中的栈和堆
- IDEA中展开包结构的方法
- Scrapy模拟登陆豆瓣抓取数据
- 虚拟机群晖找不到服务器,ESXI6.0安装群晖6.2,解决搜索不到IP问题。禁止转载
- 虹软-视觉算法-面经(2020届)