导入

或许由于经历不同,很多的开发者并不再怎么关心性能优化和代码质量这一块,而在一个真正用心做产品的公司,在产品交付前进行的集中测试会暴露出来非常多平时难以解决的问题;主要原因是这些原因在一开始会被认为太难搞定而且在当时来说并不是那么重要,最后要到发布了,由于时间紧凑而且自身积累不够,导致很多疑难杂症得不到解决;

一般这个时候公司会着急招聘一个看起来很资深的程序员来解决这个问题,不过在我看来这种选择代价并不低,排除这种靠谱的人不太好找外,这个人的开发经历和项目的契合度也有很大关系,更可怕的是,在这位资深开发接管了这个项目后,他可能会觉得很难办,因为把一个快要上线的项目源码读懂就不是一件易事,少说也得花个半年时间吧;

最稳妥的办法当然是防范于未然,当然这并不容易,假如你所在的公司对软件质量的重视程度远没有功能迭代的重视速度,那么你可能整天写代码写到完全没有空去学习并应用,或者说你需要挤出额外的时间来学习时间,这样的效率其实并不高;当然,聊胜于无,这比起那些看完了这篇文章不去做任何实践的人来说要靠谱多了;

以下的内容全部来自我对自己公司产品优化过程中遇到的一些疑难杂症,既然是疑难杂症,自然也是花了不少精力去研究整理的,当然由于可能受限于自身的经验,有些地方描述不准确或者有些地方没有提到的,一方面我自己会在后续时间慢慢更新,另一方面也欢迎大家指正互相学习,我也会把大家建议的内容更新到这个博客上;

因此建议转载这篇文章的表明转载出处,本着开源的伟大精神,让更多的开发者受益;

目录

我猜到这篇文章后面会越来越长,因此先把架构搭好,然后再慢慢完善,其实本文结构和任何一本技术类的书籍结构一致,但是并不说明这篇文章会写的那么详细,因为这篇文章是给中级以上的安卓开发人员准备的;

1. 并发与多线程
2. 集合
3. UI交互
4. 编码习惯
5. 认知误区
6. 编程心态

类型1:并发与多线程

这一类问题是我们遇到最多的问题,这些问题对于新手来说也是最头疼的问题,如果处理不当会导致更多的问题;我们现在从原理把这些东西好好梳理一下;

定义: 并发是指在一段时间内同时做多个事情,多线程并发的本质问题源于多个线程使用同一个资源,这些资源包括不限于内存,cpu,对象等;

故事
以搬砖上楼为例,如果是一个人搬50块砖,正常来说他每次可以搬10块,那么他需要5次才能搬完。
如果有5个人搬,那么只需要一次就能搬完,大大节省了搬运时间,程序运行也是一样;
但是如果有50个人同时搬运,可能因为人太多拥挤楼道导致效率反而很低下,甚至堵死楼道;导致任务协调人员没办法解决问题;

问题

  1. 性能问题(资源过度消耗);
  2. 数据安全问题(读脏数据);
  3. 死锁问题(线程无法退出,UI卡死);
  4. 线程失控问题(sleep导致线程无法及时退出,阻塞读写无法关闭);
  5. 乱加锁问题(不该加锁加锁,该用不同锁的用同一把锁);

1. 资源过度消耗

本因

  1. Cpu占用过高,从而导致手机发热严重,同时导致系统卡顿;
  2. 内存消耗太大:在线程中既存在堆内存消耗(new 对象,局部变量),又存在栈内存消耗(方法运行都需要在栈中开辟内存空间存放需要运行的代码);

案例1

在请求Ftp或者http上传下载的时候,为了测试极限速度,采用了很多条线程同时进行,但是由于采用的线程数过多,导致手机发热严重,同时资源消耗也大,经常被一些手机强制关闭应用;

代码

public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);findViewById(R.id.testBtn).setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {startUploadTest();}});}private void startDownloadTest() {Timer timer = new Timer();timer.scheduleAtFixedRate(new TimerTask() {@Overridepublic void run() {// 1. get st ateint arg1 = 100;double arg2 = 12.00;String arg3 = getArg3();SomeState state = new SomeState(arg1, arg2, arg3);// 2. do something elsefor (int i = 0; i < state.someCount; i++) {doSomeReport(state);}//3. save ResultsaveResult2File();//4. show resultshowTestResult();}}, 0, 200);}
}

解决
1. 禁止使用new Thread的方式产生线程,使用RXJava的小伙伴在每次创建子线程后要保存该线程的引用以便管理生命周期;
2. 通过线程池统一管理线程生命周期,在一次任务完成后记得销毁对应的线程;
3. 命名每一条线程以便于跟踪线程生命周期;
4. 线程中如果有循环特别是死循环如While,do,for,注意出一个可以由外部线程控制运行的接口,否则会导致线程无法停止
5. 控制每一次并发数量,并发数量超过一定值不仅不能提升效率还会导致程序拥堵以至于手机卡顿;

2. 读脏数据

本因

多个线程同时操作同一个数据(对象,内存,文件),并且这些线程不全是读线程;

案例1 ConcurrentModificationException

多发于操作ListView,当适配器还没刷新完上个数据的显示下一个数据就过来了,会抛出异常同时修改异常;

代码

// TODO 邀请大家在这里补齐示例代码

解决
在这里填写解决方案;

案例2 变量被非法修改

循环代码的状态控制标志异常;一个变量控制着线程运行,本来被关闭了,但是被另一个线程打开了;

代码

private boolean isRun = false;private void startTest(){new Thread(){@Overridepublic void run() {super.run();isRun = true;doStartTest();}}.start();}private void testClean() throws InterruptedException {while (!isRun){isRun = doSomeClean();Thread.sleep(200);}}

解决
使用原子锁,在这里填写解决方案;

死锁

本因

代码在执行的时候发现需要请求的锁不能被释放,导致线程阻塞死锁;

案例1 锁嵌套

相同锁的锁嵌套,只要其中一条线程运行在主线程就会导致UI卡死;

代码

// TODO 邀请大家在这里补齐示例代码

解决
synchronized尽量缩小锁的范围,锁的时间尽可能短,能锁对象就不要锁类,能锁代码块就不要锁方法;
线程池管理线程,掌握每一个线程的生命周期,线程命名便于跟踪;

案例2 锁被耗时线程持有

请求的锁所在的线程进入了死循环,进入了休眠,在执行耗时操作,导致锁不能及时释放;

代码

// TODO 邀请大家在这里补齐示例代码

解决

线程失控

本因
线程运行在加锁的代码块中,外部线程无法操作该加锁线程,导致线程必须在同步代码块执行完毕后才能被外部线程访问,如果锁住的代码块是耗时操作,且控制运行的变量也处于同步代码块中,就会出现线程失控;

案例1 阻塞线程无法关闭

阻塞读写线程无法关闭,只能等待阻塞线程自己释放,如socket阻塞,文件读写阻塞等;

代码

// TODO 邀请大家在这里补齐示例代码

解决

案例2 JNI中阻塞导致anr

在主线程中调用的JNI方法阻塞,java无法中断JNI调用,必须等到jni解除阻塞状态这个过程中就会导致anr异常以致界面卡死;

代码2

// TODO 邀请大家在这里补齐示例代码

解决2

案例3
控制循环的开关在同步代码块中;

代码3

// TODO 邀请大家在这里补齐示例代码

解决3

案例4
同步代码块中有睡眠,等待操作;

代码4

// TODO 邀请大家在这里补齐示例代码

解决4

案例5
线程中有睡眠操作又不能直接把线程停掉(有其他业务参与);
比如在执行Autotest任务过程中,有一个变量IsTestRun控制了任务执行是否继续,如果在任务中执行睡眠的睡眠间隔太长,会导致无法及时读到IsTestRun的改变从而停止任务;
线程在sleep或者处于join状态下,由于处于暂停状态,所以线程没办法继续往下执行完线程代码;

代码5

 private boolean waitForAnswer(int callTimeout) {boolean isTimeout = false;long startTime = SystemClock.elapsedRealtime();while (!mStopped) {isTimeout = SystemClock.elapsedRealtime() - startTime < callTimeout;try {sleep(1000);} catch (InterruptedException e) {Log.e(e);Thread.currentThread().interrupt();}}return isTimeout;}

解决5
将休眠时间设置短一点;
可以通过调用interrupt终端线程,使线程归位;

问题

通过一些问题可以巩固我们对知识的理解:

  1. 多个线程同时读写,读线程的数量远远大于写线程,你认为应该如何解决并发的问题?你会选择加什么样的锁?
  2. 除了synchronized关键字之外,你是怎么来保障线程安全的?
  3. 线程池内部工作原理可以说一下么?
  4. 死锁是什么意思,形成条件是什么?出现死锁是可以通过什么方式去排查。
  5. 讲一下怎么使用分布式锁。
  6. 线程池创建有几种,为什么创建定长的线程池个数最好是5,10,15这样的数字。
  7. Java的设计模式,单例有什么模式,懒汉为什么加volotile,volotile的内存屏障,如何避免死锁。

类型2:集合

由于集合是数据集,因此虽然集合本身指向的内存区域不变(比如使用final修饰了集合),但是其内部的数据还是可以发生改变,可以被任何线程随意读写;这就导致了数据的不安全和不可控;
线程安全和线程不安全集合
迭代器遍历导致异常
list浅拷贝深拷贝问题
值传递引用传递问题:基本数据类型(值)+对象类型(引用),集合属于复杂对象
基本数据类型需要加锁吗?
hashMap线程不安全,多线程操作容易发生cpu超高占用;使用ConcurrentHashMap替代;

问题

  1. HashMap和Hashtable的区别。
  2. 实现一个保证迭代顺序的HashMap。
  3. java常用的数据结构有哪些?哪些是线程安全的?是怎么保证线程安全的?
  4. 说说HashMap的原理, 以及HashMap如何扩充bucket的大小。
  5. HashMap在jdk1.7和1.8的区别,为什么引入这个概念?hash碰撞怎么解决,为什么1.8要比1.7更好,好在哪?
  6. hashmap、hashcode一样,不equals怎么处理 ;hashcode实现原理,currentHashMap原理,实现细节,怎么实现同步的;类为什么要有hascode方法,是不是主要在集合类中都要实现hashcode方法;equals方法怎么实现;两个不同的对象可能有相同的hashcode值吗;常用集合有哪些。
  7. 为什么 Map 接口不继承 Collection 接口。
  8. ThreadLocal 用途是什么,原理是什么,用的时候要注意什么?
  9. ThreadPool用法与优势可以说一下么?
  10. synchronized 的原理是什么?synchronized 和 ReentrantLock 有什么不同?
  11. 有T1,T2,T3三个线程,怎么确保它们按顺序执行?怎样保证T2在T1执行完后执行,T3在T2执行完后执行同步块内的线程抛出异常会发生什么?
  12. 什么是乐观锁(Optimistic Locking)?如何实现乐观锁?如何避免ABA问题。
  13. Java中活锁和死锁有什么区别?
  14. Executors类是什么? Executor和Executors的区别?

类型3:UI

  1. 过度绘制问题(大量UI绘制时的频繁刷新使用定时器控制)
  2. 布局复杂嵌套问题
  3. 标题重复问题(每个页面都有各自的标题)
  4. 适配器刷新没有统一的入口;
  5. 适配器没做增量更新导致过度消耗;
  6. 使用dip而不是dp;
  7. 过多使用权重实现固定布局(有些时候需要使用固定dp);
  8. 定时器泛滥问题;

编码风格

权限检查问题:
1.缺乏统一的权限检查入口;

代码风格问题

  1. 不需要增加空格对齐变量;
    2.大括号结束后必须换行;
    3.意思相近的代码之间不需要空行,相反逻辑不相似的代码使用空行隔开;
  2. 在方法体编写中,局部变量尽量定义靠近逻辑代码,不要一股脑定义在最上面,这回增加查找代码的负担;
  3. 独立的功能放在独立的方法,不应该把逻辑内聚度很低的代码写在同一个方法中,更有甚者既在一个方法中处理功能代码又处理逻辑代码;
    7.代码嵌套不能超过3层,超过了看着眼花;
  4. 尽量精简注释,使注释更简洁的描述代码逻辑,同时禁止尾行注释,应该写在代码之上;

过多的静态变量
过多的全局变量
过多的单例使用
过多的EventBus使用

数据没有统一管理接口不知道在哪里修改删除了
经验值缺少统一管理

注释过少问题

功能耦合问题(A功能的代码写在B功能)

类名定义问题

  1. Activity定义成Dialog;
  2. 工具类/功能类做成Sercvice
  3. 适配器写在Activity中;

配置混乱问题
4. 配置key不统一问题:读写Sp有时候不是统一的字符串作为key,导致写入读取不匹配;
5. activity中读写配置问题;
6. 常量配置中写实现问题;
7. 常量和类关系不大问题;

强行耦合问题:
8. 适配器中编写大量的功能代码;
9. 一个方法中实现了过多功能,导致方法臃肿;

资源管理问题
10. 线程无名称问题;
11. 对象不释放问题;

配置混乱问题
12. 配置key不统一问题:读写Sp有时候不是统一的字符串作为key,导致写入读取不匹配;
13. activity中读写配置问题;
14. 常量配置中写实现问题;
15. 常量和类关系不大问题;

强行耦合问题:
16. 适配器中编写大量的功能代码;
17. 一个方法中实现了过多功能,导致方法臃肿;

资源管理问题
18. 线程无名称问题;
19. 对象不释放问题;
20.

逻辑混乱问题:
9. if else嵌套过多;
10. 方法名和参数名随意书写;
11. for循环中嵌套复杂逻辑;
4.单句不加大括号;
5.工匠精神;
6.敢于表达,要有自大精神,并以开放心态迎接批评;

需求规范问题
1.把Answer命令做进了Call命令;

发布版本的测试应该写入周报,并预留时间自己测试;

安全问题

时间问题:用户修改系统时间则获取到错误时间;
场景:System.currentTimemillion 如果使用了doubleClick快速点击判断,或者任何需要和上一个时间进行比对的功能,时间调整后,之前的时间在用来判断就会出错,这会导致很大的问题;
由于System.currentTimemillion获取的时间精度会依赖于操作系统的实现机制因此有时会不太准确;
而且System.currentTimemillion是jni调用效率比非JNI调用比如SystemClock.now()慢很多;
因此在计算这种时间差的场景下,建议使用SystemClock.elapsedRealtime()替代System.currentTimemillion,因为前一个记录的是系统开机到现在的时间(包含了休眠时间);

内存泄漏问题,方法中Activity作为参数容易导致内存泄漏;
类型转换问题(特别是数字转换问题)
字符串截取中正则表达式的规范问题:
广播注册未销毁导致异常;

问题跟踪

日志频繁打印问题,频繁打印的日志建议取消打印;禁止在发布版本中频繁打印堆栈信息;
频繁调用出错的异常应立即处理,如果无法处理,使用能明确出错位置的提示信息替代堆栈打印(太消耗资源);

在功能代码中不要使用trycatch,而应该将异常抛出让业务代码来处理;在业务代码中,不应该把所有的异常一股脑用Exception捕获,而应该区别对待;

JVM

问题

  1. 说一说GC。
  2. JVM如何加载一个类的过程,双亲委派模型中有哪些方法?
  3. 你知道哪些或者你们线上使用什么GC策略? 它有什么优势,适用于什么场景?
  4. JAVA类加载器包括几种?它们之间的父子关系是怎么样的?双亲委派机制是什么意思?有什么好处?
  5. 如何自定义一个类加载器?你使用过哪些或者你在什么场景下需要一个自定义的类加载器吗?
  6. 堆内存设置的参数是什么?
  7. 方法区里什么样的对象有可能被回收。
  8. 线上cpu飙升100%你怎么处理。
  9. 频繁FullGC怎么处理。

认知错误

Jni不是耗时操作,最典型的System.currentTimeMillion();
Jni具备和Native相同的能力和权限;
JNI层规范日志打印。因为jvm崩溃最多只能打印是哪个jni方法调用出错;
java对象四种引用。
JRE、JDK、JVM 及 JIT 之间有什么不同。

心态

如果一个开发技术很牛逼,但是没有闭环思维,那么那就不是一个合格的高级开发,至少在我看来不是;
闭环思维是互联网用语,实质上就是,上级交代你一件事情,不管有没有完成,你都需要给上级一个反馈;
举个例子,你朋友要求你帮忙找个东西,你两天没找到,然后就不回复他了,这个时候他就会一直等你的消息,当等待超过他预期,他就认为你失信了,这就是不靠谱的表现;

我们再开发的时候经常有以下心态,导致产出质量不高,返工率高,维护困难,据我观察,初级程序员可能会花掉1/4时间写代码,剩下的时间基本和编写无关;这种情况下可能是由于上头要的急而自己又不懂得拒绝或者急于求成,反正最终结果就是自己交出去的产品自己都不太满意,同时让自己特别的忙,没有效率;
这种错误的心态有但不限于以下:

1.急于提交心态
2.绝对正确心态
3.直面错误心态;
4.逻辑错误心态;
5.着急完成心态;

【疑难杂症】解决了这些问题,你就迈进了安卓高级工程师门槛相关推荐

  1. APM(pixhawk)飞控疑难杂症解决方法汇总

    摘自:http://www.nufeichuiyun.com/?p=28 APM(pixhawk)飞控疑难杂症解决方法汇总(持续更新) by 怒飞垂云2019年11月4日 APM无法解锁/飞控连不上/ ...

  2. 解决krpano全景视频在QQ浏览器、安卓不能正常播放的问题

    解决krpano全景视频在QQ浏览器.安卓不能正常播放的问题 参考文章: (1)解决krpano全景视频在QQ浏览器.安卓不能正常播放的问题 (2)https://www.cnblogs.com/li ...

  3. 解决Win10系统由于INF文件失效导致安卓MTP驱动安装失败的问题

    解决Win10系统由于INF文件失效导致安卓MTP驱动安装失败的问题 前话 今天电脑C盘莫名就满了.作为Windows insider自然习以为常.肯定又是系统在后台悄悄下载新的更新包造成的.属性里的 ...

  4. 联想笔记本Manjaro dde 18 安装疑难杂症解决

    博主是linux小白,之前用的是windows+ubuntu的双系统,昨天吃了朋友的安利,上手了manjaro deepin version.使用一天后感觉系统非常流畅,速度比ubuntu快很多,软件 ...

  5. 软件疑难杂症解决办法汇总

    2019独角兽企业重金招聘Python工程师标准>>> 现在的搜索引擎质量真的堪忧,找了无数的博客终于找到解决办法了,特此记录一下 MySQL8.0本地连接不上解决办法 https: ...

  6. 疑难杂症——解决 Cinder 僵尸卷问题

    目录 目录 问题描述 问题解决 最后 问题描述 Cinder 的僵尸卷一般是因为操作不当导致分配的卷无法正常使用且无法正常分离或删除. 问题解决 解决僵尸卷问题的思路类似解决 Linux 系统中的僵尸 ...

  7. 计算机中丢失api-ms-win-crt-runtime-l1-1-0.dll的疑难杂症解决方法

    故障: xxx.exe - 系统错误 无法启动此程序,因为计算机中丢失 api-ms-win-crt-runtime-l1-1-0.dll .尝试重新安装该程序以解决此问题. 今天,某网友电脑上的这问 ...

  8. 疑难杂症 解决Ubuntu16.04 蓝牙搜索不到附近设备 适用于18.04

    文章目录 1. 问题描述 2. 问题症结 3. 解决办法 References 1. 问题描述 打开蓝牙搜索设备无果 2. 问题症结 终端下输入命令 dmesg | grep -i blue BCM: ...

  9. 【fluent】UDF环境配置疑难杂症解决记录

    软件版本:fluent 15.0 + vs 2013 UDF 链接到fluent有两种方式,解释方式(interpreted)或者编译(compiled)方式. 解释方式不需要编译器,因此比较方便,但 ...

最新文章

  1. mysql 多配置文件实例安装_mysql安装之多实例多配置文件安装
  2. 皮一皮:这大概就是年轻的味道...
  3. 一文了解自然语言处理神经史(上)
  4. 关于打印数组一直输出地址符号的一个解决方法
  5. python FIFO命名管道
  6. 浅谈MySQL索引背后的数据结构及算法【转】
  7. centos8解压war包
  8. 如何完成企业舆情监测——话题检测与跟踪
  9. android 播放器 samba,(发烧屋)教你如何解决蓝光机 KODI无法打开局域网SMB共享的问题/安卓播放器/硬盘播放器...
  10. java阴阳师抽卡算法_阴阳师抽卡小技巧,抽出SSR很轻松
  11. C++程序避免触发 Win7下的程序兼容助手
  12. ogg_for_bigdata (oracle 数据通过ogg到hbase)
  13. PHP中级工程师面试题
  14. RecyclerView --- 分割线
  15. DM36x IPNC远程升级
  16. OpenCV+YOLO+IP摄像头实现目标检测
  17. C++常用功能汇总-文件读写 计时 随机数
  18. 架构师之数字判断-----------------怎么判断一个字符串是个数字
  19. 图像特征提取4:Surf特征
  20. SEO技术大师-论坛网站SEO设置方法

热门文章

  1. ppp协议 java_PPP协议的应用-网络协议
  2. 版权登记和商标注册的区别
  3. wdr7500 虚拟服务器,教程:普联TL-WDR7500路由器端口转发如何设置
  4. 逃计算机课检讨书600字,逃自习课检讨书
  5. 一款MVC5+EF+Bootstrap搭建的后台通用管理系统模板
  6. stm32驱动LED点阵屏(LY-LED16x16)
  7. 第三方网站实现钉钉(DingTalk)扫码登陆(Vue+SpringBoot)
  8. 手机相机SD卡电脑硬盘数据强力恢复软件
  9. 【转】配置Symbian模拟器支持模拟MMC存储卡
  10. Adams 与MATLAB联合仿真 失败,adams与matlab联合仿真例子(正确没商量).doc