Android面试题整理

断点续传的实现

  • 从字面上理解,所谓断点续传就是从停止的地方重新下载。 断点:线程停止的位置。 续传:从停止的位置重新下载。
  • 用代码解析就是:断点: 当前线程已经下载完成的数据长度。续传: 向服务器请求上次线程停止位置之后的数据。原理知道了,功能实现起来也简单。每当线程停止时就把已下载的数据长度写入记录文件,当重新下载时,从记录文件读取已经下载了的长度。而这个长度就是所需要的断点。
  • 总结来说就是下载过程中要使用数据库实时存储到底存储到文件的哪个位置了,这样点击开始继续传递时,才能通过HTTP的GET请求中的 urlConnection.setRequestProperty("Range","bytes=" + start + "-" + info.getLength()); 方法可以告诉服务器,数据从哪里开始,到哪里结束。同时在本地的文件写入时,RandomAccessFile的seek()方法也支持在文件中的任意位置进行写入操作。同时通过广播将子线程的进度告诉Activity的ProcessBar。

app 启动速度的优化

影响启动的因素
  • 高耗时任务
  • 复杂的View层级
  • 类过于复杂
  • 主题及Activity配置
启动耗时检测
  • 查看Logcat
  • adb shell
  • 代码打点(函数插桩)
  • 启动速度分析工具 — TraceView
  • 启动速度分析工具 — Systrace
解决方案
  • 异步初始化 : 充分利用CPU多核,自动梳理任务顺序。
  • 延迟初始化 :第三方库懒加载,按需初始化; 利用IdleHandler特性,在CPU空闲时执行,对延迟任务进行分批初始化
  • Multidex预加载优化 启动时单独开一个进程去异步进行Multidex的第一次加载,即Dex提取和Dexopt操作。
  • 复杂的View层级 减少层级嵌套
  • 主题切换:使用Activity的windowBackground主题属性预先设置一个启动图片
  • 保活
  • WebView启动优化

线程优化

线程调度模型
  • 分时调度模型 轮流获取、均分CPU
  • 抢占式调度模型 优先级高的获取
如何干预线程调度?
  • 设置线程优先级。
Android异步方式
  • Thread 直接创建,缺点很多,比如说不容易被复用,导致频繁创建和销毁线程的开销大,不建议使用
  • HandlerThread 本质上也是一个 Thread,自带了消息循环,串行执行
  • IntentService 是 Service 组件的子类,它的内部有一个 HandlerThread,所以它具备了 HandlerThread 的特性。优点:使用了 Service,会提高应用的优先级;异步,不占用主线程
  • AsyncTask 内部实现使用了线程池
  • 线程池 易复用,减少频繁创建、销毁的时间;功能强大,如定时、任务队列、并发数控制等
  • RxJava 功能强大,提供了不同的线程池:IO Computation
Android线程优化实战
  • 严禁使用new Thread方式。
  • 提供基础线程池供各个业务线使用,避免各个业务线各自维护一套线程池,导致线程数过多。
  • 根据任务类型选择合适的异步方式:优先级低,长时间执行,HandlerThread;定时执行耗时任务,线程池。
  • 创建线程必须命名,以方便定位线程归属,在运行期Thread.currentThread().setName修改名字。
  • 关键异步任务监控,注意异步不等于不耗时,建议使用AOP的方式来做监控。
  • 重视优先级设置(根据任务具体情况),Process.setThreadPriority();可以设置多次。
线程收敛优雅实践初步
  • 基础库内部暴露API:setExecutor。
  • 初始化的时候注入统一的线程库。

布局优化

  • 减少层级
    • 合理使用RelativeLayout和LinearLayout,RelativeLayout会对子View做两次测量。但如果在LinearLayout中有weight属性,也需要进行两次测量,因为没有更多的依赖关系,所以仍然会比RelativeLayout的效率高。
    • 合理使用Merge
  • 提供显示速度
    • ViewStub
  • 布局复用
    • 通过标签来实现
  • 其他
    • 使用标签加载一些不常用的布局。
    • 尽可能少用wrap_content,wrap_content会增加布局measure时的计算成本,已知宽高为固定值时,不用wrap_content。
    • 使用TextView替换RL、LL。
    • 使用低端机进行优化,以发现性能瓶颈。
    • 使用TextView的行间距替换多行文本:lineSpacingExtra/lineSpacingMultiplier。
    • 使用Spannable/Html.fromHtml替换多种不同规格文字。
    • 尽可能使用LinearLayout自带的分割线。
    • 使用Space添加间距。
    • 多利用lint + alibaba规约修复问题点。
    • 嵌套层级过多可以考虑使用约束布局

减少过度绘制

导致过度绘制的主要原因是:
  • XML布局:控件有重叠且都有设置背景。
  • View自绘:View.OnDraw里面同一个区域被绘制多次。
如何避免过度绘制
  • 布局上的优化

    • 移除XML中非必需的背景
    • 有选择性地移除窗口背景:getWindow().setBackgroundDrawable(null)
    • 按需显示占位背景图片
  • 自定义View优化

    • 通过canvas.clipRect()来帮助系统识别那些可见的区域
    • 绘制一个单元之前,首先判断该单元的区域是否在Canvas的剪切域内。若不在,直接返回

webview的优化

  • WebView首次创建比较耗时,需要预先创建WebView提前将其内核初始化。
  • 使用WebView缓存池,用到WebView的时候都从缓存池中拿,注意内存泄漏问题。
  • 本地离线包,即预置静态页面资源
  • 本地接口请求,缓存
  • DNS解析优化
  • 另外开启webView进程
  • 首屏静态html
  • 骨架屏

Handler 如何规避内存泄漏

  • 原因:非静态内部类会隐性地持有外部类的引用
  • 监控内存泄漏
    • LeakCanary
    • 原理,利用了Java的WeakReference和ReferenceQueue,通过将Activity包装到WeakReference中,被WeakReference包装过的Activity对象如果被回收,该WeakReference引用会被放到ReferenceQueue中,通过监测ReferenceQueue里面的内容就能检查到Activity是否能够被回收
  • 解决方法
    • 在onDestory中移除 handler.removeCallbacksAndMessages(null)
    • 声明一个静态的Handler内部类,并持有外部类的弱引用

横竖屏切换的生命周期的变化

  • 没有设置configChanges属性,当前的界面调用onSaveInstanceState走一遍流程,然后重启调用onRestoreInstanceState再走一遍完整流程,最终destory。
  • 设置了configChanges属性为orientation之后,Android6.0 同没有设置configChanges情况相同,会调用了onSaveInstanceState和onRestoreInstanceState方法;Android 7.0则会先回调onConfigurationChanged方法,剩下的流程跟Android 6.0 保持一致;Android 8.0只是回调了onConfigurationChanged方法
  • android:configChanges="orientation|screenSize" 只会调用onConfigurationChanged方法

多进程通讯的方式

  • bundle 四大组件可以通过这种方式传递数据
  • 文件共享 简单的文件读写,序列化文件,sp
  • AIDL 客户端,服务端,接口
  • Messenger 底层是binder,客户端和服务端两端,主要是对消息的处理,串行的方式处理消息
  • ContentProvider 底层是binder,和数据库操作息息相关
  • Socket serverSocket
  • 应用场景:lego日志,验号器中传递数据

内存泄漏

  • 常见的内存泄漏
    • Handler 引起的内存泄漏
    • 单例模式引起的内存泄漏。主要是对context的引用
    • 非静态内部类创建静态实例引起的内存泄漏。
    • 非静态匿名内部类引起的内存泄漏。
    • 注册/反注册未成对使用引起的内存泄漏。
    • 资源对象没有关闭引起的内存泄漏。
    • 集合对象没有及时清理引起的内存泄漏。
  • 如何分析内存泄漏
    • Android 内存监控器

oom

  • 产生原因
    • Java堆溢出,虚拟机栈和本地方法栈溢出,运行时常量池溢出,方法区溢出,本机直接内存溢出
  • 是否可以try catch
    • 只有在一种情况下,这样做是可行的:在try语句中声明了很大的对象,导致OOM,并且可以确认OOM是由try语句中的对象声明导致的,那么在catch语句中,可以释放掉这些对象,解决OOM的问题,继续执行剩余语句。
  • 解决办法
    • 释放强引用,使用软引用、弱引用
    • 图像处理:
      • 在内存中压缩图片,设置inSampleSize;
      • 使用完图片后回收图片所占内存;
      • 降低要显示的图片色彩质量,RGB_565
      • 查询图片信息时不把图片加载到内存中 inJustDecodeBounds = true
    • largeHeap android:largeHeap="true"

Activity的启动模式

你可能感兴趣的:(Android面试题整理)