OkHttp拦截器实现无网络时加载缓存,避免Unknown host异常
我们在创建OkHttp客户端时,可以添加接口数据缓存,真的很香:
123456File cacheDir = ... // 缓存目录,可以是内部存储也可以是外部存储的目录OkHttpClient okHttpClient = new OkHttpClient.Builder() .connectTimeout(10, TimeUnit.SECONDS) .cache(new Cache(cacheDir, 20 * 1024 * 1024)) // 这里给了20MB缓存目录容量,超过后会自动清理 .... .build();
然后我们会发现,先正常请求网络数据,然后断开网络连接,重新请求,并没有返回缓存。而是出现一些诸如“Unknown host…”解析不了域名这种异常,查看之前设置的缓存文件目录,也确实有文件,可怎么就不加载呢?哦,结果还要配置一下缓存策略,回到我们的主题:拦截器。我们可以在拦截器中实现网络连接判断并强制开起缓存:
12345678910111213141516private static class Cach ...
OkHttp拦截器获取GET/POST请求参数并添加公共参数
我们在创建OkHttp客户端时,可以添加各种拦截器,真的很香:
123456OkHttpClient okHttpClient = new OkHttpClient.Builder() .connectTimeout(10, TimeUnit.SECONDS) .addInterceptor(new XXXInterceptor()) .addInterceptor(new YYYInterceptor()) .... .build();
服务端经常需要我们做一些参数校验的工作,需要在本地先把请求参数封装起来再加密之类的,那么我们可以用拦截器来实现获取所有get或post参数,还可以添加一些公共参数,这样就不用在每个接口定义那去加了:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354private static class ParamsInterceptor impl ...
微信小程序任务栈实现原理
背景之前面试一些校招同学,聊到微信小程序是什么launchMode,其任务栈是如何实现的?很多同学只提到singleInstance,这是不合适的。今天我们就猜测并解析一下微信主程序与小程序的关系与大致实现,最后给出源码,可以给大家作一个简单参考。
初探既然要研究微信,那么我们就先打开几个小程序,再用adb命令看看任务栈信息。在终端使用 adb shell dumpsys activity activities 命令后,可以找到最近任务列表的Activity信息:
12345678910Running activities (most recent first): TaskRecord{caccd90 #3239 A=.AppBrandUI3 U=0 StackId=1 sz=1} Run #4: ActivityRecord{bb162b8 u0 com.tencent.mm/.plugin.appbrand.ui.AppBrandUI3 t3239} TaskRecord{d6c62d6 #3190 A=com.tence ...
NDK开发中为什么除以0程序不崩溃?
我们都知道不管是在Java还是在C++程序中,下面这段代码都会导致程序错误:
123int x = 10;int y = x / 0;...
但是我今天发现了一个比较神奇的事情,把这段代码写成native方法后在Java层调用,竟然没有导致App崩溃,代码是这样子的:
12345678910111213141516#include <jni.h>#include "logger.h"#ifdef __cplusplusextern "C" {#endifJNIEXPORT void JNICALL test_crash(JNIEnv *env, jobject /* this */) { int x = 10; int y = x / 0; LOGD("crash %d", y);}#ifdef __cplusplus}#endif
Java层很简单,我保证没有try-catch,而且Log打印出来y的值竟然是0,这完全颠覆我三观。怀疑人生的我把这两行关 ...
源码茶舍之android:externalService是什么属性?实现原理?
发现在AndroidManifest中声明Service时,偶然发现一个布尔类型的属性:android:externalService示例如下:
123<service android:externalService="true" ... />
如果minSDK小于24,会显示警告,很显然这是一个24以后的新东西。先顾名思义一下,external的service,外置(外挂)的服务?它和 android:exported 以及 android:isolatedProcess 属性是什么关系?
初探先谷歌一下,再百度一下,无果。竟然没有一个人解释这是什么东西,这更加激发了我的好奇心。马上去Android官网搜,搜到service标签的文档,心中窃喜:https://developer.android.com/guide/topics/manifest/service-element翻完整个文档,发现居然也没有 android:externalService 的说明,难道是太新了忘了补充文档吗?不过我们可以从中先复习一下 android:exp ...
MyBatis中插入(insert)后返回主键(key)的注解方式
一般我们插入数据后需要知道其自增的主键key是多少,有两种方式:
用@Options注解:比如这里有个订单(Order)相关的DAO:
12345public interface OrderDao { @Insert("INSERT INTO ...") @Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id") void insertOrder(Order order);}
这里的keyProperty表示对象中的成员变量,keyColumn表示数据库中的列名,因此我们这里数据库主键名称就是id ,其实此处不写keyColumn也是可以的,因为我们只是读不是写。此外,如果你Order实体中主键名称就叫”id”的话,keyProperty也不用写了。实体类则如下:
1234public class Order { private long id; ...}
最后通过 orde ...
MyBatis直接使用LocalDateTime时间类型以及MySQL时区问题排错
时间类型Java 8提供了新的时间API,相关介绍大家可以自行搜索或者直接参考这篇Java中的时间与时区,因此大家在写实体类时,可以放弃用以前的Date或者Timestamp类型了,直接用LocalDateTime类就行了,MyBatis从3.4.5版本开始就完全支持这种类型了,根本不用自己再去写什么类型转换,目前网上搜到的大部分文章还是让我们自己去实现,其实不用的。
我们来看看其官方文档( https://github.com/mybatis/mybatis-3/releases ):
mybatis-3.4.5@harawata harawata released this on 20 Aug 2017 · 472 commits to master since this releaseEnhancements:Make default enum type handler customizable. #971Make mapper method and its interface type accessible to SqlProvider. #1055Allow using c ...
授权后连接MySQL依然被拒绝访问(Access denied for user 'root'@'localhost')
我们在Spring Boot的应用配置中一般都会如下:
1jdbc:mysql://123.123.123.123:3306/db_name?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
123.123.123.123是MySQL数据库所在主机的IP地址,如果你想要远程访问数据库,就必须授权,一般这样操作:
12GRANT ALL PRIVILEGES ON *.* TO 'root'@'%'IDENTIFIED BY '12345' WITH GRANT OPTION;FLUSH PRIVILEGES;
12345是你设置的访问密码。但有时候我们轻量应用服务都是单机的,根本不用远程访问,也就是说上面的配置我会这样写:
1jdbc:mysql://localhost:3306/byd?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
然后查看 ...
源码茶舍之由一次简单的ANR分析深入了解Context
ANR是Android的老大难了,关于这方面的基础知识和深入好文都非常多,大家不妨谷歌一下。最近搭载骁龙855的小米9也发布了,移动平台的设备性能越来越强,许多App大多时候其实都吃不完那么多计算资源。说得可能不好听一点,很多烂代码要是在很多年前的手机上,本该导致卡顿(甚至是ANR)的,但由于如今强大的计算性能,卡顿几率大大减小了。从某方面来说增大了程序的容错,同时也掩盖了程序本身的缺陷。
今天的题目关键词是“简单分析”和“深入了解”,哈哈,可能对于大佬们来说这些内容并不深入,所以我措辞为“了解”,望轻喷。
分析traces文件前段时间,业务质量平台报上来很多ANR,我是一看就头疼呀!每次心里都犯嘀咕,我怎么就从来没遇到ANR呢?你们到底是怎么使用的。吐槽归吐槽,问题还是要解决的,Android的系统日志打包上来一般都会有traces.txt文件(还有event log等等,这里给大家硬广一下我另一篇使用可视化的ChkBugreport分析log文件),也是我们分析这类问题的入口,里面记录了各个应用进程和系统进程的函数堆栈信息。于是乎,抓一份来瞧瞧:
123456789101112 ...
Glide4.8集成现有OkHttpClient并加载https图片
转眼间谷歌的Glide图片加载库都4.8.0了,时间过得真的太快。今天解决两个问题(第1个是独立问题,第2个依赖第1个):1、Glide网络加载库(其默认为原生的API实现)集成为OkHttp,众所周知OkHttp可以帮助我们更方便地玩转网络请求;2、让Glide可以加载https前缀的图片链接(如果你的域名证书是服务端自己瞎签的,没有认证,就过不了安全检查,表现为你用Chrome浏览器打开这个链接会提示不安全的红色警告)。
问题1:集成OkHttp一般来说我们项目一开始会分别使用Glide和OkHttp库,没特殊需求时没想到过它们还要结合。首先,假设我们已经有OkHttpClient的初始化逻辑了:
12345678// 简化示意的初始化代码public static OkHttpClient getHttpClient() { OkHttpClient.Builder builder = new OkHttpClient.Builder() .connectTimeout(10, TimeUnit.SECONDS) .ad ...
RxJava2开发小记:先加载缓存再请求网络之简单实现
关于缓存用户角度:做客户端,大部分时候都在追求良好的用户体验,缓存,就要达到一个缓兵之计的效果。因为用户永远是“暴躁”的,页面加载要是有缓存先展示出来,用户就会安心很多。这是一种视觉上的舒服,大多数用户并不在乎这个数据从哪来。开发者角度:对于客户端程序来说,网络状况是未知而不稳定的,在耗时上面,一次网络请求可能大于本地数据读取好几个数量级。并且,某些及时性不高的数据,并不需要每次都从服务端请求,而是按过期时间来判断是否需要更新缓存,这样也能尽可能地减轻服务器压力。
总结一下上面的开篇废话就是,用户是暴躁的,服务器是脆弱的,唯一坚挺并且逆来顺受的就是客户端了哈哈哈!
正文目标望闻问切四部曲:UI层发起数据获取意愿(望),打听是否能加载缓存(闻),再访问网络进行远程请求(问),最后刷新缓存至本地存储并返回数据给UI层(切)。药材读缓存的Observable一个(localDataObservable)请求网络数据的Observable一个(remoteDataObservable)关键性的串联操作符contact一个上药
12345678910111213141516// 闻:本地缓存Ob ...
RxJava2开发小记:用CompositeDisposable来“安排”Retrofit网络请求
情景前不久项目遇到了偶现的OOM问题,看调用栈发现有RxJava相关,我们项目以RxJava2+RxAndroid+Retrofit2+OkHttp为基础设施的。上谷歌搜了一转,最终竟踏入了RxAndroid的GitHub issue区,发现有个老外和我情景类似,原帖链接在此:https://github.com/ReactiveX/RxAndroid/issues/387, 他说他只用Retrofit发起1500个请求没毛病,但是加上RxAndroid就炸了,他怀疑RxAndroid有Bug。这个问题被项目Owner(即JakeWharton大神)回复了,他给大家解释了这种框架组合的正确用法。下面是他的原话:
The problem is that Schedulers.io() uses a cached thread pool without a limit and thus is trying to create 1500 threads. You should consider using a Scheduler that has a fixed limit of thre ...
goAsync帮你在onReceive中简便地进行异步操作
广播回调onReceive是在主线程跑的,所以我们不能在里面搞耗时操作,不然秒秒钟ANR。
又因为onReceive中的代码在执行完后,BroadcastReceiver对象就无效了,生命周期结束。所以我们不能直接在里面起子线程,若应用进程被回收掉,线程的任务就可能无法完成。徒增不可控因素。
解决:普遍的处理方式是在onReceive中再起一个IntentService去执行异步操作。这样就有了Service组件的保障,进程不会被轻易杀掉,但同时这个操作也比较重,代码实现上还得再去写个处理特殊业务的IntentService。
比如说我在onReceive里面只做一些很简单的耗时操作,可能就一两行代码我也要去写个Service?显然谷歌爹已经想到了这一点。
在API 11以后,BroadcastReceiver新增了一个静态内部类PendingResult,我们可以通过调用goAsync()方法来获取这个PendingResult对象,然后就可以愉快地进行子线程操作了,最后通过调用它的finish方法来结束广播接收者的生命。一切就变得可控了(相当于强行给Receiver续命)。
123 ...
两种方法避免Android系统更改字体大小
当用户更改系统的字体大小后,App界面可能会变得面目全非,适配起来非常困难。有的时候我们可能不需要去适配,那么就需要限制部分页面或者控件受系统字体大小更改的影响。
目前,大家在网上搜,一般都是下面的这个办法,这也是今天介绍的第一个方法,可以直接使当前Activity的所有字体大小固定:
12345678910111213@Overridepublic Resources getResources() { Resources resources = super.getResources(); if (resources != null) { Configuration configuration = resources.getConfiguration(); // 系统设置的字体大小超过了我们接受的限制 if (configuration != null && configuration.fontScale > mLimitFontScale) { config ...
使用可视化的ChkBugreport分析log文件
ChkBugreport是一款专门分析Android Bugreport文件的可视化工具。
下载源码在GitHub上把代码down下来:https://github.com/sonyxperiadev/ChkBugReport
编译jar包在源码的core/目录下有一个createjar.sh脚本,执行它!此时其实已经可以使用了,直接用命令:
1java -jar chkbugreport.jar bugreport_xxx.log
会在同级目录下生成一个bugreport_xxx.log_out文件夹,打开里面的html文件即可查看可视化界面。
更方便地使用每次都运行上面的命令太繁琐了,我们可以把此工具加入系统环境。1、先下载脚本文件:http://sonyxperiadev.github.com/ChkBugReport/download/chkbugreport2、将脚本文件放到~/bin目录下,即$HOME/bin,给予此脚本执行权限:
1chmod +x chkbugreport
3、把刚才编译好的jar包改名为chkbugreport ...