简介:
学了一段时间的安卓了,最近准备跟着 第一行代码 这本书做一个较为完整的App项目,巩固一下所学到的知识。现在 第一行代码 这本书已经出到第三版了,里面是使用Kotlin进行开发的。但我平时还是Java用的比较多点,因此,我就想使用Java 来实现第三版中的 SunnyWeather 天气预报APP。下面就是关于这次项目的一些学习笔记。
1.准备工作
实现的功能和所需的技术要点都在书 P601页上有详细描述,这里就不再哆嗦了。在工作开始之前,先得按照书上描述,申请开放的天气API 接口,提交申请后,大概需要3~4个工作日才能通过审核。
如果之前使用的是第二版进行学习的同学,可能还需要了解一些第二版中没有提到的一些新内容。主要就是网络请求框架Retrofit、Jetpack中的ViewModel和LiveData 这些内容。
虽然安卓应用同时支持Java和Kotlin进行开发,但使用Kotlin代码编写的部分操作,在使用Java进行重写的时候,还是会出现一些问题,下面就是对出现的部分问题的一些说明。
2.问题说明
2.1 网络请求同步的问题
这是一个比较棘手的问题。到最后,我也没有能够完美解决。在实际开发中,我们常常需要发起网络请求,并将请求到的数据赋给一个实例,然后再进行一系列,如数据处理,UI更新等操作,但我在开发过程中,却遇到了 还没等到网络请求操作,程序就先进行数据操作 这种问题。
下面是项目中的具体例子,方便我们理解这个问题。(相关代码可见项目Repository类和SunnyNetWork类)
//获取到对应的数据模型类的实例,返回的数据不为null
final PlaceResponse placeResponse = sunnyWeatherNetwork.searchPlaces(query);
if (placeResponse.getStatus().equals("ok")) {
places = placeResponse.getPlaces();// 获取到包含地区信息的list
Log.d("Repository","place response success " );
placesData.postValue(places);//将list传入Livedata内,并准备返回
} else {
//返回状态不是ok的情况
Log.d("Repository", "place status is" + placeResponse.getStatus() );
}
上面是项目中实现搜索地区数据信息功能的部分代码。首先, sunnnyWeatherNetwork .searchPlaces 这个方法中封装了网络请求操作以及空指针检测,返回获取到的数据并保证数据不为null。我们调用该方法,并把返回值赋给 placeResponse 。随后我们对placeResponse中的 status 值进行判断,如果为“ok”, 则进行一些数据处理操作,否则,则打印错误信息。
逻辑上来看,这段代码不难理解,而且在实现上应该没有什么问题。但实际上却存在空指针异常或是数据错误等问题。
下面是对项目进行调试时的记录日志:
我们从上面的调试记录中可以看到,仓库层(Repository)已经进行完数据处理了,网络请求才完成。这样导致的后果就是取得的 placeResponse 为空或者为null。我自己思考的结论是,问题出现的原因是:进行网络请求时,Restrofit 会 new 一个线程,在另一个线程中进行网络请求,这样的结果就是仓库层的线程和网络请求线程并发执行,导致出现:没等网络请求成功响应,程序就进一步执行的情况。
解决方案:
在Kotlin中,我们可以使用async函数和await()方法,来实现同步。但我在用Java编写时,由于水平不足,没有很好地解决同步问题,因此会出现这样的问题:进行网络请求时,第一次网络请求得到的结果,是错误的,需要丢弃。
在搜索地区信息功能模块中,该问题导致搜索框体验不佳,需要在输入 想要搜索的地区信息后,再按下其他键。(等于发起第二次请求)才能搜索到结果。
可以看到,输入北京后,还需要再输入任意一个按键,才能正确显示结果。
在天气信息显示功能中,为了解决这个问题,我就写了个循环,隔一小段时间就进行一次网络请求,直到返回的对象不为空时,再跳出循环。
do {//realtimeResponse 和 dailyResponse通过调用封装的网络请求方法获取
realtimeResponse = sunnyWeatherNetwork.getRealtimeWeather(lng, lat);
dailyResponse = sunnyWeatherNetwork.getDailyWeather(lng, lat);
Log.d("Repository","refresh Weather 数据申请中");
sleep(500);
}while (realtimeResponse == null || dailyResponse == null);//只有两个不为null时才跳出循环
这些方法只是暂时的解决,应该可以通过线程同步等方式的到更好的解决。
2.2 其他问题
1.项目中显示的文字均采用硬编码的写法。更合理的做法应该是定义在String.xml 中,然后在布局中引用。
2.编码不够规范。变量定义还不够规范(例如很多变量声明时未加上final),部分文档也没写清楚,代码自我感觉比较乱。
3.搜索界面中,状态栏与搜索框颜色不同,略显违和
4.部分机型兼容问题。在实现背景图与状态栏融合的效果时,在Android Studio 上的Pixel4上进行测试时,会发生图标折叠的情况。然而在实体机上,却不会出现这种情况。
5.天气api令牌值的引用存在问题。在PlaceService中可以通过代码 ${SunnyWeatherApplication.TOKEN} 来取得令牌值,但在WeatherService 中使用相同的引用方法,却提示令牌错误。最后我也没发现是什么原因导致的,只能在WeatherService中显式的填入令牌值才解决问题。
3.后续可扩展功能
1.修复上述提到的问题
2.实现后台更新天气信息
3.通过位置信息,自动选择城市
4.适配深色主题
4.源码链接
下面是这次实战项目的源码链接,希望能给有需要学习的同学提供一点帮助吧。