整理了以前的笔记,把以前遇到的问题记录下来

1.App的首次安装。

问题描述:在我们安装完成一个app时,在安装界面直接点击打开。我们进入了app的首页,这时我们按home键返回桌面,再点击应用图标,会发现没有直接进入首页,而是先进入了app的闪屏页,在进入首页。重复这一步一直如此。这时我们按back键返回,发现没有直接退回桌面,而是返回到之前打开的多个首页。但是如果一开始安装完我们不是直接打开,而是在桌面点击应用进入就不会这样了。

解决方法:

if (!isTaskRoot()) {

Intent intent = getIntent();

String action = intent.getAction();

if (intent.hasCategory(Intent.CATEGORY_LAUNCHER) && action != null && action.equals(Intent.ACTION_MAIN)) {

finish();

return;

}

}

2. RecyclerView Bug

问题描述:这个是在友盟的错误分析中报的,错误信息如下:

java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter positionViewHolder{42fb7f40 position=11 id=-1, oldPos=-1, pLpos:-1 no parent}

at android.support.v7.widget.RecyclerView$Recycler.validateViewHolderForOffsetPosition(RecyclerView.java:4801)

at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4932)

at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4913)

at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2029)

at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1414)

at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1377)

at android.support.v7.widget.LinearLayoutManager.scrollBy(LinearLayoutManager.java:1193)

at android.support.v7.widget.LinearLayoutManager.scrollVerticallyBy(LinearLayoutManager.java:1043)

at android.support.v7.widget.RecyclerView.scrollByInternal(RecyclerView.java:1552)

at android.support.v7.widget.RecyclerView.onTouchEvent(RecyclerView.java:2649)

at android.view.View.dispatchTouchEvent(View.java:7706)

at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2224)

at android.view.ViewRootImpl$ConsumeBatchedInputRunnable.run(ViewRootImpl.java:5651)

at android.view.Choreographer$CallbackRecord.run(Choreographer.java:761)

at android.view.Choreographer.doCallbacks(Choreographer.java:574)

at android.view.Choreographer.doFrame(Choreographer.java:542)

at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:747)

at android.os.Handler.handleCallback(Handler.java:733)

at android.os.Handler.dispatchMessage(Handler.java:95)

at android.os.Looper.loop(Looper.java:136)

at android.app.ActivityThread.main(ActivityThread.java:5162)

at java.lang.reflect.Method.invokeNative(Native Method)

at java.lang.reflect.Method.invoke(Method.java:515)

at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:789)

at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:602)

重现的方法是:使用 RecyclerView 加官方下拉刷新的时候,如果绑定的 List 对象在更新数据之前进行了 clear,而这时用户紧接着迅速上滑 RV,就会造成崩溃,而且异常不会报到你的代码上,属于RV内部错误。初次猜测是,当你 clear 了 list 之后,这时迅速上滑,而新数据还没到来,导致 RV 要更新加载下面的 Item 时候,找不到数据源了,造成 crash.

解决方法:就是在刷新,也就是 clear 的同时,让 RecyclerView 暂时不能够滑动,之后再允许滑动即可。代码就是在 RecyclerView 初始化的时候加上是否在刷新进而拦截手势:

mRecyclerView.setOnTouchListener(

new View.OnTouchListener() {

@Override

public boolean onTouch(View v, MotionEvent event) {

if (mIsRefreshing) {

return true;

} else {

return false;

}

}

}

);

3. 华为部分设备不打印Log

部分的华为设备工程模式下log是关闭的

1.如果是华为手机,进入拨号界面输入:##2846579##进入页面设置。

2.如果是华为pad,进入计算器输入:()()2846579()()= 进入页面设置。

4. 一些注意点

平时为了给apk瘦身,我们会对图片进行有损或无损的压缩。那我平时有用到的一个有损压缩网站tinypng,但是切记我们的9图不要压缩,不然会有问题。(这里顺便分享一个做9图的在线工具http://inloop.github.io/shadow4android/)

Android的透明主题需谨慎使用。

5. RecyclerView自动滚动

RecyclerView 嵌套 RecyclerView,结果内部的RecyclerView会自动的滑动至顶部。解决方法:

两种解决办法:

一. 内部Recyclerview去除焦点,父布局里获得焦点。

mChildrecyclerview.setFocusableInTouchMode(false);

mParentrecyclerview.requestFocus();

比如父布局的一个textview。

textview.setFocusableInTouchMode(true);

textview.requestFocus();

二. 内部的Recyclerview加

android:overScrollMode="never"

父布局覆盖子VIew获取焦点:

android:descendantFocusability="blocksDescendants"

6. NumberFormatException

起因是Bugly上报了一个错误,说是我格式化“0,00”这样的一个字符串。代码大致如下:

String.format("%.2f", number);

发现是格式化的问题。也就是在不指定Locale时,是跟随系统语言。在法语、德语、意大利语的语言中,格式化小数是逗号的。所以解决方法:

String.format(Locale.CHINA, "%.2f", number);

7. SecurityException

在之前项目中做了6.0的动态权限后,Bugly报错如下:

image.png

更奇怪的是报错的全部都是6.0的手机。

查了一下,找到了问题。发现是6.0的一个bug,在部分6.0上CHANGE_NETWORK_STATE权限获取不到,那么只能去获取WRITE_SETTINGS这个权限了。这个问题已经在6.0.1修复了。附上链接地址,

既然是6.0的问题,我们可以对6.0进行单独处理。抛出异常处或者在使用CHANGE_NETWORK_STATE权限前跳转到系统设置页去设置。

if(Build.VERSION.SDK_INT == Build.VERSION_CODES.M) {

if (!Settings.System.canWrite(context)) {

Intent intent = new Intent(android.provider.Settings.ACTION_MANAGE_WRITE_SETTINGS);

intent.setData(Uri.parse("package:" + context.getPackageName()));

intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

context.startActivity(intent);

}

}

8. WebView中Http和Https加载问题

比如https页面加载http图片或者http页面加载https图片时图片显示不出来,原因是因为在Android 5.0开始WebView默认不允许加载http与https混合页面,

解决办法:

//https与http混合资源处理

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {

webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);

}

9. SIGBUS和SIGSEGV

首先是这两个名词的说明:

SIGBUS(Bus error)意味着指针所对应的地址是有效地址,但总线不能正常使用该指针。通常是未对齐的数据访问所致。

SIGSEGV(Segment fault)意味着指针所对应的地址是无效地址,没有物理内存对应该地址。

有人一看,什么指针不指针的,对于大多数开发人员来说,不涉及NDK这方面的开发。所以可以想到的就是我们使用的so库。

我这里碰到的SIGBUS相关问题主要集中在集成的极光推送,在极光社区的这篇帖子和我的问题一样。我收集到的信息集中在CPU架构为arm64-v8a,android 5.x 的 OPPO R9M、OPPO R7SM、OPPO A59M、OPPO A59S等OPPO手机。如下图:

image.png

问题起因是这样,为了瘦身我们的apk文件,我只添加了armeabi-v7a 架构的相关so文件。因为现在绝大部分的设备都已经是 armeabi-v7a 和 arm64-v8a,虽然我也可以使用armeabi,但是性能关系我最终只保留了armeabi-v7a 。

按道理arm64-v8a设备可以兼容arm64-v8a、armeabi-v7a、armeabi。但结果在oppo的这些手机上没有兼容,或者说更加的严格,导致了未对齐的数据访问。为什么这么说,因为后来有观察再升级极光的sdk后,发现这类问题有所下降。当然如果你直接添加上arm64-v8a,则不会有这个问题。

导致这个问题有多方面的因素,有我们使用的三方sdk的问题,也有手机问题。但在手机不可变的基础上,只能我们去解决,所以尽量不要通过这种方法瘦身APK。(实在不行可以用折中方案,保留armeabi-v7a 和 arm64-v8a)。

而SIGSEGV问题排除掉架构兼容问题,相对于集中在5.0以下及机子。这块问题相对比较复杂,我碰到了这样一个问题:

image.png

10. TimeoutException

从buyly的统计看主要集中在oppo 5.0~6.0及个别华为5.0机型

image.png

反馈上来的远比截图看的多,我只取了截取了一小部分。新版本已经“解决了”这个问题,所以现在报上来的主要都是老版本。

bugly异常信息如下:

image.png

image.png

错误堆栈信息:

FinalizerWatchdogDaemon

java.util.concurrent.TimeoutException

android.os.BinderProxy.finalize() timed out after 120 seconds

android.os.BinderProxy.destroy(Native Method)

android.os.BinderProxy.finalize(Binder.java:547)

java.lang.Daemons$FinalizerDaemon.doFinalize(Daemons.java:214)

java.lang.Daemons$FinalizerDaemon.run(Daemons.java:193)

java.lang.Thread.run(Thread.java:818)

首先来说明一下发生问题的原因,在GC时,为了减少应用程序的停顿,会启动四个GC相关的守护线程。FinalizerWatchdogDaemon就是其中之一,它是用来监控FinalizerDaemon线程的执行。

FinalizerDaemon:析构守护线程。对于重写了成员函数finalize的对象,它们被GC决定回收时,并没有马上被回收,而是被放入到一个队列中,等待FinalizerDaemon守护线程去调用它们的成员函数finalize,然后再被回收。

一旦检测到执行成员函数finalize时超出一定的时间,那么就会退出VM。我们可以理解为GC超时了。这个时间默认为10s,我通过翻看oppo、华为的Framework源码发现这个时间在部分机型被改为了120s和30s。

image.png

虽然时间加长了,但还是一样的超时了,具体在oppo手机上为何这么慢,暂时无法得知,但是可以肯定的是Finalizer对象过多导致的。知道了原因,所以要模拟这个问题也很简单了。也就是引用一个重写finalize方法的实例,同时这个finalize方法有耗时操作,这时我们手动GC就行了。刚好前几天,在我订阅的张绍文老师的《Android开发高手课中》,老师提到了这个问题,同时分享了一个模拟问题并解决问题的Demo。有兴趣的可以试试。

那么解决问题的方法也就来了,我们可以在Application的attachBaseContext中调用(可以针对问题机型及系统版本去处理,不要矫枉过正):

try {

final Class clazz = Class.forName("java.lang.Daemons$FinalizerWatchdogDaemon");

final Field field = clazz.getDeclaredField("INSTANCE");

field.setAccessible(true);

final Object watchdog = field.get(null);

try {

final Field thread = clazz.getSuperclass().getDeclaredField("thread");

thread.setAccessible(true);

thread.set(watchdog, null);

} catch (final Throwable t) {

Log.e(TAG, "stopWatchDog, set null occur error:" + t);

t.printStackTrace();

try {

// 直接调用stop方法,在Android 6.0之前会有线程安全问题

final Method method = clazz.getSuperclass().getDeclaredMethod("stop");

method.setAccessible(true);

method.invoke(watchdog);

} catch (final Throwable e) {

Log.e(TAG, "stopWatchDog, stop occur error:" + t);

t.printStackTrace();

}

}

} catch (final Throwable t) {

Log.e(TAG, "stopWatchDog, get object occur error:" + t);

t.printStackTrace();

}

public static void fix() {

try {

Class clazz = Class.forName("java.lang.Daemons$FinalizerWatchdogDaemon");

Method method = clazz.getSuperclass().getDeclaredMethod("stop");

method.setAccessible(true);

Field field = clazz.getDeclaredField("INSTANCE");

field.setAccessible(true);

method.invoke(field.get(null));

}

catch (Throwable e) {

e.printStackTrace();

}

}

两种方法都是通过反射最终将FinalizerWatchdogDaemon中的thread置空,这样也就不会执行此线程,所以不会再有超时异常发生。推荐老师的方法,更加全面完善。因为在Android 6.0之前会有线程安全问题,如果直接调用stop方法,还是会有几率触发此异常。5.0源代码如下:

private static abstract class Daemon implements Runnable {

private Thread thread;// 一种是直接置空thread

public synchronized void start() {

if (thread != null) {

throw new IllegalStateException("already running");

}

thread = new Thread(ThreadGroup.systemThreadGroup, this, getClass().getSimpleName());

thread.setDaemon(true);

thread.start();

}

public abstract void run();

protected synchronized boolean isRunning() {

return thread != null;

}

public synchronized void interrupt() {

if (thread == null) {

throw new IllegalStateException("not running");

}

thread.interrupt();

}

public void stop() {

Thread threadToStop;

synchronized (this) {

threadToStop = thread;

thread = null; // 一种是通过调用stop置空thread

}

if (threadToStop == null) {

throw new IllegalStateException("not running");

}

threadToStop.interrupt();

while (true) {

try {

threadToStop.join();

return;

} catch (InterruptedException ignored) {

}

}

}

public synchronized StackTraceElement[] getStackTrace() {

return thread != null ? thread.getStackTrace() : EmptyArray.STACK_TRACE_ELEMENT;

}

}

这个所谓的线程安全问题就在stop方法中的threadToStop.interrupt()。在6.0开始,这里变为了interrupt(threadToStop),而interrupt方法加了同步锁。

public synchronized void interrupt(Thread thread) {

if (thread == null) {

throw new IllegalStateException("not running");

}

thread.interrupt();

}

虽然崩溃不会出现了,但是问题依然存在,可谓治标不治本。通过这个问题也提醒我们,尽量避免重写finalize方法,同时不要在其中有耗时操作。其实我们Android中的View都有实现finalize方法,那么减少View的创建就是一种解决方法。

11. SchedulerPoolFactory

前一阵在用Android Studio的内存分析工具检测App时,发现每隔一秒,都会新分配出20多个实例,跟踪了一下发现是RxJava2中的SchedulerPoolFactory创建的。

image.png

一般来说如果一个页面创建加载好后是不会再有新的内存分配,除非页面有动画、轮播图、EditText的光标闪动等页面变化。当然了在应用退到后台时,或者页面不可见时,我们会停止这些任务。保证不做这些无用的操作。然而我在后台时,这个线程池还在不断运行着,也就是说CPU在周期性负载,自然也会耗电。那么就要想办法优化一下了。

SchedulerPoolFactory 的作用是管理 ScheduledExecutorServices的创建并清除。

SchedulerPoolFactory 部分源码如下:

static void tryStart(boolean purgeEnabled) {

if (purgeEnabled) {

for (;;) { // 一个死循环

ScheduledExecutorService curr = PURGE_THREAD.get();

if (curr != null) {

return;

}

ScheduledExecutorService next = Executors.newScheduledThreadPool(1, new RxThreadFactory("RxSchedulerPurge"));

if (PURGE_THREAD.compareAndSet(curr, next)) {

// RxSchedulerPurge线程池,每隔1s清除一次

next.scheduleAtFixedRate(new ScheduledTask(), PURGE_PERIOD_SECONDS, PURGE_PERIOD_SECONDS, TimeUnit.SECONDS);

return;

} else {

next.shutdownNow();

}

}

}

}

static final class ScheduledTask implements Runnable {

@Override

public void run() {

for (ScheduledThreadPoolExecutor e : new ArrayList(POOLS.keySet())) {

if (e.isShutdown()) {

POOLS.remove(e);

} else {

e.purge();//图中154行,purge方法可用于移除那些已被取消的Future。

}

}

}

}

我查了相关问题,在stackoverflow找到了此问题,同时也给RxJava提了Issue,得到了回复是可以使用:

// 修改周期时间为一小时

System.setProperty("rx2.purge-period-seconds", "3600");

当然你也可以关闭周期清除:

System.setProperty("rx2.purge-enabled", false);

作用范围如下:

static final class PurgeProperties {

boolean purgeEnable;

int purgePeriod;

void load(Properties properties) {

if (properties.containsKey(PURGE_ENABLED_KEY)) {

purgeEnable = Boolean.parseBoolean(properties.getProperty(PURGE_ENABLED_KEY));

} else {

purgeEnable = true; // 默认是true

}

if (purgeEnable && properties.containsKey(PURGE_PERIOD_SECONDS_KEY)) {

try {

// 可以修改周期时间

purgePeriod = Integer.parseInt(properties.getProperty(PURGE_PERIOD_SECONDS_KEY));

} catch (NumberFormatException ex) {

purgePeriod = 1; // 默认是1s

}

} else {

purgePeriod = 1; // 默认是1s

}

}

}

1s的清除周期我觉得有点太频繁了,最终我决定将周期时长改为60s。最好在首次使用RxJava前修改,放到Application中最好。

简书 android bug记录,记录工作中的Bug相关推荐

  1. 给简书找BUG赢好礼17.06.02——简书Android 2.4.0 公测【私密文章支持预览/手机支持直接提现】...

    你给简书找bug,简书给你送好礼.即日起,参与简书公测就有机会获得简书提供的精美周边!公测版下载>>公测版下载备用地址>> 本期公测版本-简书Android 2.4.0: 更新 ...

  2. 简书Android客户端更新日志1504010-离线下载

    时间悄悄溜进了四月,这是一个草长莺飞.万物复苏的季节,小动物们都忙着繁衍下一代,简书君却还苦逼的坐在办公室里感叹岁月这把杀猪的刀.说到四月不得不提的就是欢乐的愚人节,本次愚人节简书君也和大家开了一个小 ...

  3. 简书Android APP上线了

    在历经了几个月的奋战之后简书Android客户端终于和大家见面了,下载链接轻戳这里,当然您也能在国内各大应用商店下载的到.(记得好评哦-) 先上张图给大家感受一下吧 Android APP截图(左为启 ...

  4. vue添加滚动事件,解决简书Carol_笑一笑方案中vue移除滚动事件失效的问题

    vue添加滚动事件,解决简书Carol_笑一笑方案中vue移除滚动事件失效的问题 参考文章: (1)vue添加滚动事件,解决简书Carol_笑一笑方案中vue移除滚动事件失效的问题 (2)https: ...

  5. 简书android,简书 - 一个基于内容分享的社区 - Android 应用 - 【最美应用】

    一款让人愿意逗留的 App 最开始打开简书的时候,让我印象最深的不是其简洁的界面和橘黄的配色,而是其流畅的操作和几乎可以忽略不计的反应时间,对稳定性和性能的优化让我在使用这款应用的时候非常踏实,不会因 ...

  6. RIP 此篇用来记录日常工作中使用到的正则表达式

    在工作中偶尔会使用到正则表达式,  每次用到之后都是上网找,  太浪费时间了, 所以此贴专门用于收录日常工作中使用到的正则表达式 ( 不要问我为什么不自己学学正则表达式,   因为: lan ) 用心 ...

  7. 【Web】解决简书图片不显示问题“系统维护中,图片暂时无法加载”

    个人博客: http://www.milovetingting.cn 简书不显示图片的解决方法 首次编辑于2019-6-6 最近几天在浏览简书上的文章时,发现图片显示不出来,提示"系统维护中 ...

  8. 高仿简书Android,高仿简书个人中心页面

    高仿简书个人中心页面 Demo下载地址: 先贴上效果图 1. 步骤 1.1 把APP的主题改为NoAction 1.2 引入Material Design 包 implementation 'com. ...

  9. yy 服务器维护中 图片无法显示,解决简书图片不显示问题“系统维护中,图片暂时无法加载”...

    天突然发现之前的文章图片全部都这样了,我还以为图片丢了! 9949918-5bfb96c2b65b9c7e.png 但是其实图片还是可以访问的 配合 Chrome,Safari 扩展程序 Tamper ...

最新文章

  1. “买傅园慧送胡歌”,信息安全何以如此廉价
  2. AJAX是一门艺术: XHR篇
  3. Linux下cacti的安装RedHat9,RedHat AS4
  4. 学习python之序言
  5. 《深入理解计算机系统》(2) 信息的表示和处理
  6. visio图形包解压
  7. 【CSAPP】程序的机器级表示:基础知识
  8. jdk 7 下载地址(全新)
  9. A,B,C,D,E五个人在某天夜里去捕鱼
  10. repost 从APP工厂到游戏工厂?字节跳动进攻腾讯腹地
  11. JAVA中(PO,VO,TO,BO,DAO,POJO)分别是指什么
  12. FPGA Vivado AXI _DMA IP介绍
  13. win32画窗体背景
  14. 正运动学及逆运动学求解方法
  15. 解决eclipse debug运行项目时下一步按钮一直为灰色不可用的问题
  16. 计算机网络——路由器和交换机
  17. linux打包tar包命令,Linux tar打包命令详解
  18. Computer Organization and Architecture 10th - William Stallings
  19. 递推和递归(C语言)
  20. liunx上安装MySQL没有默认my.cnf文件解决方案

热门文章

  1. 广州php快板老师牛,老师快板“打出”最牛评语
  2. vue element || vant 日历加农历、节日、节气,周六日改变颜色
  3. 在Anaconda下安装Pytorch的超详细步骤
  4. 护眼灯色温多少合适?教你读懂护眼灯参数及如何选择
  5. tp-link无线路由器如何限制别人网速
  6. Pangolin-0.4
  7. 区块链 | “抢人”大战,外部高薪挖人,员工“坐地起价”
  8. VR全景的制作流程你知道吗
  9. “运动”主题创作——手绘与码绘的比较
  10. 【Python】解决Python调用接口返回带菱形问号和乱码