69、多线程同时访问集合(ConcurrentModificationException)
问题现象:
多线程同时修改集合时常常容易出现 ConcurrentModificationException ,即便是改成用 Collections.synchronizedCollection() 方法同步也无效。
原因分析:
当集合正在迭代时,如果进行修改就会出现异常,@问题13 已经说过该问题。而 synchronizedCollection() 方法虽然对部分操作加上了 synchronized 关键字以保证线程安全,但其 iterator() 操作不是线程安全的,在迭代时操作依然会出现异常,并且效率也比较低。
解决方法:
在 Java 中早已有比较好的替代对象,相比起来有更加细化的锁机制,效率更高,不会出现 ConcurrentModificationException 异常。
- ConcurrentHashMap 为 Map 的同步,设计与实现非常精巧,很适合学习
- CopyOnWriteArrayList 为 List 的同步,采用写入时复制的方式避开并发问题,当修改操作较多时性能上会有比较大的代价
68、Facebook 或 Twitter 自定义登陆按钮样式
问题现象:
使用官方的 SDK 做登陆时,只提供了登陆按钮作为跳转入口,但是样式和设计图有很大出入,直接设置按钮的样式很难达到想要的效果。
原因分析:
官方应该是推荐登陆时使用默认的样式,这样来避免或达到某些目的。虽然尽力想用默认的样式,不过由于与 UI 的其它部分太不搭了,还是决定自定义。
解决方法:
先将官方按钮设置为不可见,然后自定义效果样式按钮,并将点击事件实现为触发官方按钮的点击事件,如下:
67、Android 5.0 的通知栏 SmallIcon 的 BUG
问题现象:
原因分析:
算作是 Android5.0 的 Bug,在 android4.4 和 6.0 中都正常。
解决方法:
66、ScrollView与RecyclerView嵌套后的冲突问题
问题现象:
我们以前在使用GridView,ListView与ScrollView嵌套时一定有遇到显示不全或者滑动冲突等问题,
在我们的RecyclerView使用中仍然难逃此劫,甚至有的时候会有卡顿的问题。下面我们介绍一下如何解决这个问题
原因分析:
同理与ListView与GridView相同
解决方法:
1、卡顿,滑动不流畅问题。
首先解决卡顿的问题使用mRecyclerView.setNestedScrollingEnabled(false);
这个目前是最优解
2、显示不全(6.0以上容易出现此问题)
方法一:在Recycler的父布局中添加RelativeLayout隔离,不用像一些博客说的那样进行高度计算和 OnMeasured()重写。(未解决尝试方式二)
方法二:将你的ScrollView替换成android.support.v4.widget.NestedScrollView,此方法对此问题有优化(未解决尝试方式三)
方法三:如上还未解决或者只显示一行,把design库和V7库升级到23.2以上,然后加上如下代码
mLinearLayoutManager.setSmoothScrollbarEnabled(true);
mLinearLayoutManager.setAutoMeasureEnabled(true);
mRecuclerView.setLayoutManager(mLinearLayoutManager);
mRecuclerView.setHasFixedSize(true);
mRecuclerView.setNestedScrollingEnabled(false);如果还未解决,或者只显示一行,你可以核对一下你的适配器子布局中高度是否使用了
android_layout_height=”match_parent”
65、Android Studio 3.0 编译项目无法找到 Gradle
问题现象:
build.gradle 文件:
Android Studio 3.0 Canary 3 编译项目提醒:
原因分析:找不到,可能是 络或者服务器问题,最终定位到是 Google 更新了针对 Android Gradle 编译的仓库地址有更新。
解决方法:我们需要在 build.gradle 文件中追加:
官方文档说明:
https://android-developers.googleblog.com/2017/05/android-studio-3-0-canary1.html
64、编译时出现jar包内包含相同的文件
问题现象:
我在项目中添加了一些jar的引用,但在编译的时候发现存在相同的文件,导致编译失败。
原因分析:
Error:Execution failed for task ‘:app:transformResourcesWithMergeJavaResForDebug’.
com.android.build.api.transform.TransformException: com.android.builder.packaging.DuplicateFileException: Duplicate files copied in APK META-INF/*.properties
解决方法:
如果依赖的jar使用多次引用版本不同的jar包,那么最好的办法是选择留下最合适的版本jar包。小概率是因为使用不是相同的jar包时出现了文件冲突问题,这时候在build.gradle中添加packagingOptions就能解决。
63、关于so库使用小总结
问题现象:
应用中新添加了so库,在32位处理器上没有问题,跑64处理器的时候出现了UnsatisfiedLinkError的问题,查看了异常是lib64的so库没有找到,之后添加了ndk的配置后,成功在64处理器上跑通,但发现library里的so库受到影响,加载失败。
原因分析:
java.lang.UnsatisfiedLinkError:dalvik.system.PathClassLoader
java.lang.UnsatisfiedLinkError:No implementation found for void io.vov…. MedioPlayer.native_init()
第一个异常里面有lib64 so 没有发现,直接找64和32位的问题,第二个
问题指向的是一个native方法init失败,打包apk发现只有armeabi中的so库,而library中为了优化性能和文件大小,只在armeabi留下一个库,其他都放在了其他文件夹中。
解决方法:
处理64位兼容上,使用了兼容armeabi的方法,在build.gradle中添加了
由于library中armeabi只有一个so库,所以加载失败了。原因是指定要ndk需要兼容的架构(这样其他依赖包里mips,x86,armeabi,arm-v8之类的so会被过滤掉),所以在上面添加了
这样就能兼容我使用的库中的所有so,但还是担心后面有加载库失败的问题,查看了 上abi的资料,没问题。
在项目只包含了 armeabi,那么在所有Android设备都可以运行; 如果项目只包含了 armeabi-v7a,除armeabi架构的设备外都可以运行; 如果项目只包含了 x86,那么armeabi架构和armeabi-v7a的Android设备是无法运行的; 如果同时包含了 armeabi, armeabi-v7a和x86,所有设备都可以运行,程序在运行的时候去加载不同平台对应的so,这是较为完美的一种解决方案,同时也会导致包变大。
62、Google Play Store 过滤问题总结
说明:当用户在 Google Play 上搜索或浏览应用以下载时,会根据哪些应用与其设备兼容来过滤搜索结果。例如,如果应用需要摄像头,Google Play 不会在没有摄像头的设备上显示该应用。这种过滤帮助开发者管理其应用的分发,并且有助于确保为用户提供最佳的体验
过滤规则 : https://developer.android.com/google/play/filters.htmll=zh-cn
注:需要硬件支持的权限也会默认进行设备兼容过滤;
如何去掉指定功能设备兼容过滤 :
Nexus 7 没有电话功能 ,然而在应用的 Manifest 文件里声明了电话相关的权限,希望不支持电话功能的设备也能搜索该 APP
,就在 Manifest 文件添加如下代码 :
注:通过显式声明某项功能并加入 android_required=”false” 属性,可以在 Google Play 上有效停用所有针对指定功能的过滤。
更多功能声明 : https://developer.android.com/guide/topics/manifest/uses-feature-element.htmll=zh-cn#permissions-features
61、媒按键监听,Android5.0+ 不同的监听方式
使用场景描述:
应用中需要播放音乐的时候,通常有个令人捉急的问题就是媒体焦点;假如同时用 QQ音乐 与自己的应用同时播放音乐的时候,媒体焦点到底花落谁家,谁能够响应这次媒体按键;这就要看谁最后申请了这个焦点
以往的方式都是通过广播的形式,来看看 Android 5.0+ 的注册方式
Android 5.0+ 如果用以往广播的方式注册,焦点抢不过来;
详细代码请参考:Android 注册媒体按键监听
60、Android 6.0+设备无法获取权限
描述: Android6.0+以后,系统对权限管理模块进行的升级,并且对权限进行的分类,其中为危险权限。
问题: Android6.0+的手机上使用录音功能后播放无声音,此时用户点击了允许录音的权限,但是仍然无法录音。
解决: 通过现象排查,由于出现此问题的手机Android系统均为6.0+,而6.0+的手机在权限这块改定较大,通过调试发现此问题的原因是无法获取WRITE_EXTERNAL_STORAGE权限,导致录音文件未被写入SD卡,以至于播放录音无声音;将文件存储路径改为/data/data/包名/cache/; 解决此问题,因为此路径为内置路径,在6.0系统( API > 23 )时,不需要申请权限就可以向这个目录写入文件。
59、解决传说中的 Android 65k 问题
在 Android 开发中,有一个之前很少听说,最近偶尔江湖传闻听到过的问题,就是 65k 问题。什么是65k问题呢实很简单,就是 Android 有个限制,你的每个 App 中函数最多只能有 65536 个。
这个限制其实是这样的,因为在编译成 Dalvik 字节码,也就是把你的 Class 们生成打包到一个 classes.dex 中去的时候呢,编译器会给你的 App 中所有的函数方法指定一个 ID, 然后每一个 classes.dex 中 ID 的范围是 [0, 0xffff] 。 所以,你懂的,就有了那么一个 65k 的问题。
编译 错现象
解决方法(针对Android Studio): 在build.gradle中对应的地方添加
multiDexEnabled true 这句代码就行了,如下
58、 android 安装包过大
关于发现 android 安装包过大、想优化安装包大小时,应该先确认是哪些文件使安装包过大,可以先把安装包解压,然后查看各个目录的大小,再在目录底下确认是否引用了比较大的文件(如字体、图片等)。如某些图片过大,可以使用工具进行压缩下(如 pngyu )。如果是 res 文件夹太大,可以减少不必要的图片资源,国际化相关的图片不需要在每个文件夹都放入,只需要使用没有文字的图片,文字可以通过代码添加。而屏幕适配相关的图片能用 .9 图片去实现就用 .9 图片去实现。 res 文件夹太大也可能是存在太多无用的资源,可以使用 Android lint 工具去检查,然后将无用资源文件统统删除。字体文件也可能是导致安装包过大的原因,可以用工具将需要使用的字体保留(如英文、数字等),其他的去除形成新的字体文件。如果使用字体的地方比较少也可使用图片的形式去代替字体文件。项目中没用到的架包也需及时删除。关于一些比较大,但又必须的文件可以先不将其放入工程内,后面采用 络的方式加载。如果发现不了原因可以比较项目不同的版本,来确定是那一次版本的更新使安装包过大,再通过更新的文件来检查原因。
57、BLE中心设备的 onCharacteristicChanged() 方法没有回调
描述: 当设备为 Indication 模式时,设备的值有变化时会主动返回给App,App在 onCharacteristicChanged() 方法中能收到返回的值。
问题: 在App中通过如下代码注册监听,注册成功后就能接收到设备主动反馈的值了。然而以下代码执行后依旧收不到反馈。
解决: 当上面的方法执行返回true后,还要执行如下的代码才能注册成功。(完整代码)
56、Android 6.0 动态请求权限
描述: Android 应用在访问额外的资源或信息时,需要请求相应权限。根据权限的敏感性,系统可能会自动授予权限,或者由用户对请求进行许可。Android6.0及以上应用除了在清单文件中声明权限,敏感权限还需要在用户使用时动态授予。官方定义了普通和危险权限,经测试发现部分手机厂商的敏感权限会有所差异 。
问题: 应用中用到 READ_PHONE_STATE 权限来获取设备ID,在华为、小米的6.0系统的手机上运行都可以正常获取,然而在Nexus5X上获取失败而导致应用闪退。
原因: 华为、小米等系统会默认允许该权限,而官方定义该权限为危险权限默认不被允许。
解决:
1,将 targetSdkVersion 版本 调整为 23 以下,这样应用不需要在运行时请求权限,系统会默认允许清单文件中所有权限。
2,在应用中向用户动态请求权限,请求方式可参考 52、Android6.0扫描不到蓝牙设备的处理办法。更好的做法是使用Github上的第三方权限请求库。
55、事件分发处理总结
在我们Android程序中,除单一控件(继承ViewGroup)的,例如TextView,Button等是无法拦截事件外,其它View均可对事件进行分发,拦截以及向上传递。其中事件分发的事件dispatchTouchEvent。当我们重写此方法时,可以接收父类传递下来的事件,传递true,消费此事件,否则会继续向下传递。
在传递过程中我们也可以通过子类对它进行拦截,方法时重写onInterceptTouchEvent方法,返回true拦截,false不拦截。
如果整个触摸事件过程中,所有事件均未对它拦截,那么他的事件最终也会通过onTouchEvent从最下层的控件逐步向上传递,最终返回到Activity或Fragment的onTouchEvent中。
在整个事件分发中,我们也可以通过getParent或getChildren拿到父类或子类事件进行处理。
常见事件分发冲突案例:ViewPager嵌套Fragment过程中,在其中一个Fragment中有轮播图时,会照成事件冲突,或滑动偏移等情况。
解决方案:自定义Viewpager,重写onTouchEvent,当用户执行down与move事件时,事件分发给轮播图的控件处理,当执行到up与cancle事件时,在将事件重新传给viewPager处理。不过通过测试Android Studio中最近的v4包中好像已经修复了此问题。
54、生命周期引起的监听问题
部分开发者在注册监听时,经常会在注册监听后在Activity或Fragment销毁时未移除此监听。
如果是单一的监听,可能仅仅只是造成内存泄漏的情况,至少用户感知不到,但是如果我们在一个父类中写一个监听,同时有2个子类注册了此监听,
并在父类中有弹出Dialog或者刷新UI的操作时,当其中一个子类被finish掉后,其实例被回收,但其监听仍然会存在。这会引起当我们在另外一个未被finish的实例中去做跟监听相关的操作会,由于之前的实例被finish,监听又存在,回走多次回调,并且很有可能会导致空指针等一系列导致程序崩溃的异常。如果子类的监听无穷多,甚至会导致OOM。
建议,在实例销毁时一定要及时的移除没必要的监听,竟可以节约内存,也可以避免在后期添加需求时代码的耦合性。
53、Android细节点总结
1.timePicker点击确定后需要clearFoucs才能获取手动输入的时间。
2.Handler在子线程使用Looper.prepare,或者new的时候给构造函数传入MainLooper来确保在主线程run。
3.ExpandableListView的子列表不能点击(禁用)要把Adapter的isChildSelectable方法返回true。
4.注意按钮的感应范围不小于9mm否则不易点击;输入框注意光标的位置更易用互输入。
5.服务器和客户端尽量统一唯一标识(有可能是ID),否则多少会有歧义和问题。
6.activity的finish方法中使用了synchronized (this),所以activity的方法尽量不要使用 synchronized来修饰,或者有 synchronized (this)修饰的方法块,因为这些方法或者方法块一旦存在耗时操作,会导致finish方法无法执行,从而造成ANR。
7.PopupWindow中的EditText点击和长按的时候是没有复制,黏贴,全选这些选项弹出来的,这是android的一个系统bug,可以使用Dialog替代PopupWindow来达到同样的效果。
8.如果TextView的text含有特殊字符,使得text不靠TextView的左边显示,可以通过强制设置gravity为left来解决。
9.
Android中animation自从开始起作用后,就缓存到了某个地方,只管不停的绘制,哪怕自己都不存在了,都还在那绘制,clearAnimation的作用就是通知一下他,你都没了,别再画了(比如一个button,startAnimation后没有clearAnimation,你点击的话很难相应,那就是因为这个button一直在不停的绘制,你点击的时候一直获取不到焦点)。
10.
Android中animation对于目标view的位置实际上是没有改变的,当android:fillAfter=”true”时,动画结束后view停在动画最后一祯的位置。
52、Android6.0扫描不到蓝牙设备的处理办法
描述:在Android6.0手机上扫描不到蓝牙设备(如Nexus6),并会抛出一个异常:
解决办法:
1,在清单文件加入权限:
2,在Activity中调用 requestPermissions() 方法来请求权限,系统会弹出需要请求权限的对话框
3,重写Activity的onRequestPermissionsResult()方法,接收权限是否请求的请求状态
示例代码如下:
51、Android输入法弹出,设置控件在软键盘之上显示
描述:当输入法弹出时,布局会被压缩,某些控件被遮挡住,但是需求可能并不想让该控件遮挡住。如何让某些控件也浮在软件盘上,比如“登录”按钮。
解决:给最外层的布局设置一个OnGlobalLayoutListener的监听事件,当布局发生改变时改变控件位置的方式来实现,代码如下:
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!