上一篇博客传送门:Android常见问题总结(六)
如何判断当前网络类型
想要判断Android设备当前的网络类型,我们可以使用ConnectivityManager类
通过ConnectivityManager#getActiveNetworkInfo我们可以获取NetworkInfo类,它包含了当前网络相关的信息
我们可以通过NetworkInfo#isAvailable来判断是否连上了网络
通过NetworkInfo#getType来判断当前网络是否wifi类型
至于移动网络的类型,我们可以通过NetworkInfo#getSubtype获取网络的类型,然后通过TelephonyManager#getNetworkClass来判断当前的网络究竟是那种具体类型(不过这个方法是hide,估计是官方觉得不准确就不公开了,我们可以打开源码把该方法拷贝出来使用)
具体判断网络类型的代码如下:
1 | public static final String NETWORK_WIFI = "Wifi"; |
关于Android resources资源的问题
可以参考官方文档解决问题:https://developer.android.com/guide/topics/resources/overview.html
adb shell dumpsys 指令使用
该命令用于打印出当前系统信息,默认打印出设备中所有service的信息,可以在命令后面加指定的service name.
有两种方法可以查看service list:
- adb shell dumpsys:输出信息的开始部分就是所有运行的service
- adb shell service list
只要我们在指令后添加对应service name,就能查看指定service的信息:
adb shell dumpsys activity (查看activity堆栈相关信息)
adb shell dumpsys display (查看显示相关信息,可以查看分辨率)
其中,有些service还可以带上额外的参数,我们可以使用 -h 来查看帮助信息:
adb shell dumpsys activity -h (可以查到top等参数的用法)
ListView中getView反复调用问题
最近在项目中需要在ListView中实现计时器
用TextView配合CountDownTimer实现完成后发现界面卡顿严重,通过debug发现ListView的Adapter在疯狂的调用getView方法
猜测是由于CountDownTimer定时使用TextView#setText刷新文案,而TextView大小为wrap_content导致界面需要重新测量布局,导致ListView会反复调用Adapter#getView
最终通过把TextView大小设为定值解决问题
此次顺便发现了刚进入页面时,Adapter#getView对于相同的position会调用多次的问题
猜测也是ListView需要测量反复调用Adapter#getView所致,通过把ListView在xml中的高度由wrap_content改为match_parent或定值解决问题
最终刚进入页面时,相同的position只会调用一次Adapter#getView
ViewTreeObserver造成内存泄漏问题
ViewTreeObserver的一般用法如下:
1 | // 添加监听器 |
如果我们添加了监听器,而没有在View detach之前执行相应的移除方法,则会造成内存泄漏
下面我们一起来看下具体的源码了解原因:
View#getViewTreeObserver
1 | public ViewTreeObserver getViewTreeObserver() { |
当view还没有attach到window的时候,其mAttachInfo为空,此时我们添加的监听器会被保存在mFloatingTreeObserver中
View#dispatchAttachedToWindow
1 | void dispatchAttachedToWindow(AttachInfo info, int visibility) { |
第5行可以看到attach到window的时候,会执行merge方法,把监听器合并到mAttachInfo中
ViewTreeObserver#merge
1 | void merge(ViewTreeObserver observer) { |
分析过Activity的显示流程我们可以知道,这个attachInfo实际上是由ViewRootImpl来管理的,因此最终所有的监听器会被merge合并到ViewRootImpl中
由于ViewRootImpl一般而言生命周期都是长于普通的View,而View从window执行detach的时候,我们可以看到其实是没有移除这些监听器的
1 | void dispatchDetachedFromWindow() { |
因此,如果我们注册了相应的监听器,在detach之前没有进行清理的话,我们的监听器会被ViewRootImpl一直持有从而导致内存泄漏
修复方案一般是在View#onDetachedFromWindow中执行相应的清理方法即可