GVKun编程网logo

kotlin入门,Android内存泄漏总结,系列教学(kotlin内存泄露)

23

在这里,我们将给大家分享关于kotlin入门,Android内存泄漏总结,系列教学的知识,让您更了解kotlin内存泄露的本质,同时也会涉及到如何更有效地2021最新中高阶Android面试题总结,系

在这里,我们将给大家分享关于kotlin入门,Android内存泄漏总结,系列教学的知识,让您更了解kotlin内存泄露的本质,同时也会涉及到如何更有效地2021最新中高阶Android面试题总结,系列教学、android – Java中的内存泄漏,但不是Kotlin(相同的代码库)…为什么?、Android 内存泄漏总结文档、Android之内存泄漏调试学习与总结,全套教学资料的内容。

本文目录一览:

kotlin入门,Android内存泄漏总结,系列教学(kotlin内存泄露)

kotlin入门,Android内存泄漏总结,系列教学(kotlin内存泄露)

前言

先简单交代一下背景吧,本人毕业于某不知名985的本硕,17年毕业加入阿里,今年下半年跳槽到了头条,一直从事后端研发相关的工作。

之前没有实习经历,算是两年半的工作经验吧。这两年半之间完成了一次晋升,换了一家公司,有过开心满足的时光,也有过迷茫挣扎的日子,不过还算顺利地从一只职场小菜鸟转变为了一名资深划水员。

在这个过程中,总结出了一些还算实用的划水经验,有些是自己领悟到的,有些是跟别人交流学到的,在这里跟大家分享一下。

我普通本科毕业,在长沙待了四年,其中只换过两家公司。去年这个时候,公司业绩不好要裁人。主管平时跟我关系比较好,说我本来也在被裁的考率名额中的,他帮我跟领导说了一堆好话才没事。我请主管吃了一顿火锅。

但是我无法像以前那么心安理得混日子了。因为我去年首付买了房。原本想着每个月稳定税后拿个8k的工资。挺满足的(8k在长沙算中高水平了)

想不到我要求这么低还差点被淘汰了??

原来我根本没有资格安于现状。认真的反省之后,我开始想着如何学习提高自我的价值和竞争力。

首先是寻找自己身上的问题,我上知乎看了那些大佬的建议,对于我们这种初级的Android开发真的是一针见血戳到痛点;

那些大佬们的建议大致都一样;先搭建知识框架。然后深入学习,包括底层源码都要去理解和掌握。

为什么要搭建知识框架呢?

我们在学技术的时候,如果没有注重搭建知识框架的话,学到后期的话你就会遇到一个问题,就是会感觉到自己之前学习的知识很乱,不成系统,再加上行业技术更新很快,越往后自己学习的知识会越来越多,而之前的学习又是零散的自己没有整理的,你就会感觉自己所掌握的这些知识是散乱的。

就好像很多点,线可能已经连接成了,但是就是组不成一个面。当我们做一个大一点的项目,需要熟练用到很多相关技术面,甚至会用到组件化技术。如果你长期只使用一些常用的技术点,那么你可能还有很长一段时间要待在小公司搬运代码了。

很多技术大佬总结出来的搭建知识框架的方式;
1.看视频教程
2.搜索相关知识或路径,自我组织知识框架

我选的是第一种相对简单容易实行的方式。于是在多番对比和寻找后,买了一套腾讯课堂的腾讯T3级别的Android高级进阶视频教程学习。工作仍然继续,用来学习的时间都是下班挤出来的。

这一年很长,经常加班加点赶项目,下班了在地铁上就打开手机看直播课学习。大多时候是很晚回去后对着录播视频自己去敲一遍,不过有源码和课件提供而且一直有老师在线答疑,倒是没怎么落下。正常八个月学完的整个系统进阶课程,我用来差不多一年的时间。

今年下半年感觉自我水平有了质的提升,开始准备跳槽的事情,但是长沙没有什么大厂,我的眼光已经不在这些小地方了。

然后联系了技术交流群里平时经常交流的一个上海的朋友,去他那边暂时落脚,然后上海找工作。

九月出裸辞掉后,坐飞机到上海。一个月面试经历,本月中旬拿到了爱奇艺的高级工程师offer。25k,13薪,加年终奖等福利,年薪大概在30W。

刚刚拿到offer的时候真的那种开心带着自豪。我以前根本没想过我能走到这种高度

但是没几天就平静下来了。上海跟长沙不一样,魔都的竞争和生活水平让我很快就不敢再飘了,25k在这也只是普通水平而已,并且我没啥背景,之后要走的路不会轻松的,所以一刻也不能懈怠啊。

下一个目标是,十年内在上海买一套房。

有人可能会想给我泼冷水,上海房价那么贵,买不起怎么办?

买不起就回长沙养老呗哈哈哈

结尾

最后,针对上面谈的内容,给大家推荐一个Android资料,应该对大家有用。

首先是一个知识清单:(对于现在的Android及移动互联网来说,我们需要掌握的技术)

泛型原理丶反射原理丶Java虚拟机原理丶线程池原理丶
注解原理丶注解原理丶序列化
Activity知识体系(Activity的生命周期丶Activity的任务栈丶Activity的启动模式丶View源码丶Fragment内核相关丶service原理等)
代码框架结构优化(数据结构丶排序算法丶设计模式)
APP性能优化(用户体验优化丶适配丶代码调优)
热修复丶热升级丶Hook技术丶IOC架构设计
NDK(c编程丶C++丶JNI丶LINUX)
如何提高开发效率?
MVC丶MVP丶MVVM
微信小程序
Hybrid
Flutter

接下来是资料清单:(敲黑板!!!

领取通道在这里给你们摆上了~

点击我的腾讯文档免费获取

1.数据结构和算法

2.设计模式

3.全套体系化高级架构视频;七大主流技术模块,视频+源码+笔记

4.面试专题资料包(怎么能少了一份全面的面试题总结呢~)

不论遇到什么困难,都不应该成为我们放弃的理由!共勉~

如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足。谢谢。


你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足。谢谢。

[外链图片转存中…(img-HaUqqPIq-1620885306340)]

2021最新中高阶Android面试题总结,系列教学

2021最新中高阶Android面试题总结,系列教学

越来越多的人在提“移动端的下半场”、“Android 开发的焦虑”之类的,也有人在喊“技术天天在变,学也学不完”,“昨天 Kotlin 今天 Flutter”。其实我却认为,如果你技术达到了一定程度,你无需太过在意这些。

移动端真正进入下半场了吗?于我看来并没有,最多说“Android 技术的探索”进入了下半场,而整个市场还是乐观的。以前是 BAT 的天下,而近两年出来越来越多的独角兽:头条、抖音、拼多多、快手、小猿搜题等,这些公司的业务都在移动端上,他们需要招聘更多的移动端人才。如果真要说下半场,只能说很多小型创业公司在退出市场,这确实会导致很多入门工程师失业,但这也说明了这个行业在更加规范。

而且,对于 Android 工程师而言,这更是个好的时代。互联网下沉,那么下沉市场里的用户是使用 Android 多还是 iOS 多,大家都清楚。

那么,对于工程师而言需要做什么才能存活呢?很简单,要么转行,要么提高。我相信,一个技术不错的工程师,不但无需焦虑,而且在这个时代,能够拥有稳定的职业生涯和丰厚的收入。

1. Android架构设计模式

  • MVC架构设计模式:MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写。
  • MVP架构设计模式:MVC全名是Model View Persenter,MVP由MVC演变而来,是现在主流的开发模式。
  • MVVM架构设计模式:MVVM全名是Model-View-viewmodel,它本质上就是MVC的改进版。

各种模型的**主要目的**都是是分离视图(View)和模型(Model),即将UI界面显示和业务逻辑进行分离。

1.1 架构设计模式-MVC

(1) 定义:

在android开发过程中,比较流行的开发框架曾经采用的是MVC框架模式。

  • M(Model)层:实体模型,处理业务逻辑。如:数据库操作,网络操作,I/O操作,复杂操作和耗时任务等。
  • V(View)层:处理数据显示。在Android开发中,它一般对应着xml布局文件。
  • C(Controller)层:处理用户交互。在Android开发中,它一般对应着Activity/Feagment。android中主要通过activity处理用户交互和业务逻辑,接受用户的输入并调用Model和View去完成用户的需求。

(2) 特点

  • 低耦合
  • 可重用易拓展
  • 模块职责划分明确

(3) 实例

android本身的设计结构符合 MVC 模式。

(4) MVC优缺点

  • MVC的优点:MVC模式通过Controller来掌控全局,同时将View展示和Model的变化分离开
  • MVC也有局限性:

View层对应xml布局文件能做的事情非常有限,所以需要把大部分View相关的操作移到Controller层的activity中。导致activity相当于充当了2个角色(View层和Controller层),不仅要处理业务逻辑,还要操作UI。一旦一个页面的业务繁多复杂的话,activity的代码就会越来越臃肿和复杂。

1.2 架构设计模式-MVP

MVP是从经典的MVC模式演变而来,它们的基本思想有相通的地方:Controller/Presenter负责逻辑的处理,Model提供数据,View负责显示。在Android开发中,MVP的具体实现流程是当Presenter接收到View的请求,便从Model层获取数据,将数据进行处理。处理好的数据再通过View层的接口回调给Activity或Fragment。这样MVP能够让Activity或Fragment成为真正的View,只做与UI相关的事而不处理其他业务流程。

(1) 定义

  • M(Model)层:实体模型,处理业务逻辑。如:数据库操作,网络操作,I/O操作,复杂操作和耗时任务等。
  • V(View)层:负责View的绘制以及与用户交互。在Android开发中,它一般对应着xml布局文件和Activity/Fragment
  • P(Presenter)层:负责完成Model层和View层间的数据交互业务逻辑

(2) 实例

(3) MVC和MVP的区别

MVP中的View并不直接使用Model,它们之间的通信是通过Presenter来进行的,所有的交互都发生在Presenter内部,而在MVC中View会直接从Model中读取数据而不通过Controller

  • MVC和MVP的最大区别:MVC的Model层和View层能够直接交互;MVP的Model层和View层不能直接交互,需通过Presenter层来进行交互。
  • Activity职责不同:Activity在MVC中属于Controller层,在MVP中属于View层,这是MVC和MVP很主要的一个区别。可以说Android从MVC转向MVP开发也主要是优化Activity的代码,避免Activity的代码臃肿庞大
  • View层不同:MVC的View层指的是XML布局文件(或用Java自定义的View);MVP的View层是Activity(或Fragment)
  • 控制层不同:MVC的控制层是Activity(或Fragment);MVP的控制层是Presenter,里面没有很多的实际东西,主要负责Model层和View层的交互。

(4) MVP优缺点

  • MVP的优点如下:

模型与视图完全分离,我们可以修改视图而不影响模型;项目代码结构清晰,一看就知道什么类干什么事情;我们可以将一个Presenter用于多个视图,而不需要改变Presenter的逻辑,这个特性非常的有用,因为视图的变化总是比模型的变化更频繁 ;协同工作(例如在设计师没出图之前可以先写一些业务逻辑代码)

  • MVP也有不足之处:

接口过多,一定程度影响了编码效率。一定程度上导致Presenter的代码量过大。
为了降低Presenter中业务繁多的问题,Google又推出了MVVM,试图通过数据驱动来减少Presenter的代码量。

1.3 架构设计模式-MVVM

(1) 定义

  • M(Model)层:仍然是实体模型(但是不同于之前定义的Model层),主要负责数据获取、存储和变化,提供数据接口供 viewmodel 层调用。
  • V(View)层:对应Activity/Feagmentxml布局文件 ,负责View的绘制以及与用户交互
    说明:View层仅能操作UI(数据绑定来实现 UI 更新);不能做任何和业务逻辑有关的数据操作
  • VM(viewmodel)层:负责完成Model层和View层间的数据交互业务逻辑
    说明:viewmodel层仅能做和业务逻辑有关的数据操作;不能做UI相关的操作

2. android插件化

插件化来由:随着业务的增多,业务逻辑代码越来越多,apk包也逐渐增大,不利于维护和升级。通过插件化开发可将功能模块解耦,不同的维护团队仅维护某模块的业务,同时当app升级时可仅对某功能模块进行升级而不需整体升级。

2.1 插件化要解决的问题—如何动态加载apk

(1) android类加载器及区别

类加载器作用:java字节码通过类加载器加载到java虚拟器。

  • PathClassLoader:仅能加载文件目录下的apk。
  • DexClassLoader:可以加载apk文件中的字节码(从dex实体jar文件中加载java字节码)。主要用于动态加载和代码热更新等。

(2)反射: java中的反射使我们在运行时获得这个类的属性、方法和class内部的信息机制,最重要的是我们可以在运行时实例化这个对象调用方法,这也是java反射的最大优点。
(3) 实现动态加载apk

什么是动态加载apk:android中有一个速度程序会主动到指定的sd卡中去加载apk,并通过代理activity去执行。

实现:需要一个代理activity去执行apk中的activity,主要通过反射去获得它的属性和方法,从而进行apk的调用。
实现原理:类加载器(加载类)+反射(获取属性和方法)+动态代理(执行)

最后

其实Android开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。

下图是我进阶学习所积累的历年腾讯、头条、阿里、美团、字节跳动等公司2019-2021年的高频面试题,博主还把这些技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节

以上【历年大厂高级工程师面试题集】、【Android高级进阶教学视频】、【Android高级知识点学习PDF】皆无偿分享给大家。如有需要,点击**【Android架构视频+BATJ面试专题PDF+学习笔记】**即可免费获取。

B8%8D%E4%BC%9A%E8%BF%99%E4%BA%9B%EF%BC%9F%E5%A6%82%E4%BD%95%E9%9D%A2%E8%AF%95%E6%8B%BF%E9%AB%98%E8%96%AA%EF%BC%81.md)**即可免费获取。

整理不易,望各位看官老爷点个关注转发,谢谢!祝大家都能得到自己心仪工作。

android – Java中的内存泄漏,但不是Kotlin(相同的代码库)…为什么?

android – Java中的内存泄漏,但不是Kotlin(相同的代码库)…为什么?

参见英文答案 > Kotlin : safe lambdas (no memory leak)?2个
我在活动中有一段简单的代码……
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(0.0f,1.0f);
        valueAnimator.setRepeatCount(ValueAnimator.INFINITE);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {

            }
        });
        valueAnimator.start();
    }
}

如果活动终止,将会有内存泄漏(Leak Canary证明).

但是,当我将此代码转换为相同的Kotlin代码(使用shift-alt-command-k)时,如下所示

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val valueAnimator = ValueAnimator.ofFloat(0.0f,1.0f)
        valueAnimator.repeatCount = ValueAnimator.INFINITE
        valueAnimator.addUpdateListener { }
        valueAnimator.start()
    }
}

内存泄漏不再发生.为什么?是因为匿名类对象被转换为Lambda?

解决方法

这两个版本之间的区别非常简单.

AnimatorUpdateListener的Java版本包含对外部类的隐式引用(在您的情况下为MainActivity).因此,如果动画在不再需要活动时继续运行,则侦听器会持续保持对活动的引用,从而防止对其进行垃圾回收.

科特林试图在这里变得更聪明.它看到传递给ValueAnimator的lambda不引用外部作用域中的任何对象(即MainActivity),因此它创建了一个单独的AnimatorUpdateListener实例,只要你[重新]启动动画,它就会被重用.并且此实例没有对外部作用域的任何隐式引用.

旁注:如果将对外部作用域中某个对象的引用添加到lambda,Kotlin将生成每次动画重新启动时创建更新侦听器的新实例的代码,这些实例将保持隐式对MainActivity的引用(为了访问您决定在lambda中使用的对象所必需的).

另一方面注意:我强烈建议阅读名为“Kotlin in Action”的书,因为它包含了很多有关Kotlin的有用信息,以及我对Kotlin编译器如何选择是否将隐式引用放入外部范围的解释SAM转换后创建的对象是否来自本书.

Android 内存泄漏总结文档

Android 内存泄漏总结文档

Android 内存泄漏总结

内存管理的目的就是让我们在开发中怎么有效的避免我们的应用出现内存泄漏的问题。内存泄漏大家都不陌生了,简单粗俗的讲,就是该被释放的对象没有释放,一直被某个或某些实例所持有却不再被使用导致 GC 不能回收。最近自己阅读了大量相关的文档资料,打算做个 总结 沉淀下来跟大家一起分享和学习,也给自己一个警示,以后 coding 时怎么避免这些情况,提高应用的体验和质量。

我会从 java 内存泄漏的基础知识开始,并通过具体例子来说明 Android 引起内存泄漏的各种原因,以及如何利用工具来分析应用内存泄漏,最后再做总结。

Java 内存分配策略

Java 程序运行时的内存分配策略有三种,分别是静态分配,栈式分配,和堆式分配,对应的,三种存储策略使用的内存空间主要分别是静态存储区(也称方法区)、栈区和堆区。

  • 静态存储区(方法区):主要存放静态数据、全局 static 数据和常量。这块内存在程序编译时就已经分配好,并且在程序整个运行期间都存在。

  • 栈区 :当方法被执行时,方法体内的局部变量(其中包括基础数据类型、对象的引用)都在栈上创建,并在方法执行结束时这些局部变量所持有的内存将会自动被释放。因为栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。

  • 堆区 : 又称动态内存分配,通常就是指在程序运行时直接 new 出来的内存,也就是对象的实例。这部分内存在不使用时将会由 Java 垃圾回收器来负责回收。

栈与堆的区别:

在方法体内定义的(局部变量)一些基本类型的变量和对象的引用变量都是在方法的栈内存中分配的。当在一段方法块中定义一个变量时,Java 就会在栈中为该变量分配内存空间,当超过该变量的作用域后,该变量也就无效了,分配给它的内存空间也将被释放掉,该内存空间可以被重新使用。

堆内存用来存放所有由 new 创建的对象(包括该对象其中的所有成员变量)和数组。在堆中分配的内存,将由 Java 垃圾回收器来自动管理。在堆中产生了一个数组或者对象后,还可以在栈中定义一个特殊的变量,这个变量的取值等于数组或者对象在堆内存中的首地址,这个特殊的变量就是我们上面说的引用变量。我们可以通过这个引用变量来访问堆中的对象或者数组。

举个例子:

public class Sample {
    int s1 = 0;
    Sample mSample1 = new Sample();

    public void method() {
        int s2 = 1;
        Sample mSample2 = new Sample();
    }
}

Sample mSample3 = new Sample();

Sample 类的局部变量 s2 和引用变量 mSample2 都是存在于栈中,但 mSample2 指向的对象是存在于堆上的。 mSample3 指向的对象实体存放在堆上,包括这个对象的所有成员变量 s1 和 mSample1,而它自己存在于栈中。

结论:

局部变量的基本数据类型和引用存储于栈中,引用的对象实体存储于堆中。—— 因为它们属于方法中的变量,生命周期随方法而结束。

成员变量全部存储与堆中(包括基本数据类型,引用和引用的对象实体)—— 因为它们属于类,类对象终究是要被 new 出来使用的。

了解了 Java 的内存分配之后,我们再来看看 Java 是怎么管理内存的。

Java 是如何管理内存

Java 的内存管理就是对象的分配和释放问题。在 Java 中,程序员需要通过关键字 new 为每个对象申请内存空间 (基本类型除外),所有的对象都在堆 (Heap) 中分配空间。另外,对象的释放是由 GC 决定和执行的。在 Java 中,内存的分配是由程序完成的,而内存的释放是由 GC 完成的,这种收支两条线的方法确实简化了程序员的工作。但同时,它也加重了 JVM 的工作。这也是 Java 程序运行速度较慢的原因之一。因为,GC 为了能够正确释放对象,GC 必须监控每一个对象的运行状态,包括对象的申请、引用、被引用、赋值等,GC 都需要进行监控。

监视对象状态是为了更加准确地、及时地释放对象,而释放对象的根本原则就是该对象不再被引用。

为了更好理解 GC 的工作原理,我们可以将对象考虑为有向图的顶点,将引用关系考虑为图的有向边,有向边从引用者指向被引对象。另外,每个线程对象可以作为一个图的起始顶点,例如大多程序从 main 进程开始执行,那么该图就是以 main 进程顶点开始的一棵根树。在这个有向图中,根顶点可达的对象都是有效对象,GC 将不回收这些对象。如果某个对象 (连通子图) 与这个根顶点不可达 (注意,该图为有向图),那么我们认为这个 (这些) 对象不再被引用,可以被 GC 回收。 以下,我们举一个例子说明如何用有向图表示内存管理。对于程序的每一个时刻,我们都有一个有向图表示 JVM 的内存分配情况。以下右图,就是左边程序运行到第 6 行的示意图。

Java 使用有向图的方式进行内存管理,可以消除引用循环的问题,例如有三个对象,相互引用,只要它们和根进程不可达的,那么 GC 也是可以回收它们的。这种方式的优点是管理内存的精度很高,但是效率较低。另外一种常用的内存管理技术是使用计数器,例如 COM 模型采用计数器方式管理构件,它与有向图相比,精度行低 (很难处理循环引用的问题),但执行效率很高。

什么是 Java 中的内存泄露

在 Java 中,内存泄漏就是存在一些被分配的对象,这些对象有下面两个特点,首先,这些对象是可达的,即在有向图中,存在通路可以与其相连;其次,这些对象是无用的,即程序以后不会再使用这些对象。如果对象满足这两个条件,这些对象就可以判定为 Java 中的内存泄漏,这些对象不会被 GC 所回收,然而它却占用内存。

在 C++ 中,内存泄漏的范围更大一些。有些对象被分配了内存空间,然后却不可达,由于 C++ 中没有 GC,这些内存将永远收不回来。在 Java 中,这些不可达的对象都由 GC 负责回收,因此程序员不需要考虑这部分的内存泄露。

通过分析,我们得知,对于 C++,程序员需要自己管理边和顶点,而对于 Java 程序员只需要管理边就可以了 (不需要管理顶点的释放)。通过这种方式,Java 提高了编程的效率。

因此,通过以上分析,我们知道在 Java 中也有内存泄漏,但范围比 C++ 要小一些。因为 Java 从语言上保证,任何对象都是可达的,所有的不可达对象都由 GC 管理。

对于程序员来说,GC 基本是透明的,不可见的。虽然,我们只有几个函数可以访问 GC,例如运行 GC 的函数 System.gc (),但是根据 Java 语言规范定义, 该函数不保证 JVM 的垃圾收集器一定会执行。因为,不同的 JVM 实现者可能使用不同的算法管理 GC。通常,GC 的线程的优先级别较低。JVM 调用 GC 的策略也有很多种,有的是内存使用到达一定程度时,GC 才开始工作,也有定时执行的,有的是平缓执行 GC,有的是中断式执行 GC。但通常来说,我们不需要关心这些。除非在一些特定的场合,GC 的执行影响应用程序的性能,例如对于基于 Web 的实时系统,如网络游戏等,用户不希望 GC 突然中断应用程序执行而进行垃圾回收,那么我们需要调整 GC 的参数,让 GC 能够通过平缓的方式释放内存,例如将垃圾回收分解为一系列的小步骤执行,Sun 提供的 HotSpot JVM 就支持这一特性。

同样给出一个 Java 内存泄漏的典型例子,

Vector v = new Vector(10);
for (int i = 1; i < 100; i++) {
    Object o = new Object();
    v.add(o);
    o = null;   
}

在这个例子中,我们循环申请 Object 对象,并将所申请的对象放入一个 Vector 中,如果我们仅仅释放引用本身,那么 Vector 仍然引用该对象,所以这个对象对 GC 来说是不可回收的。因此,如果对象加入到 Vector 后,还必须从 Vector 中删除,最简单的方法就是将 Vector 对象设置为 null。

详细 Java 中的内存泄漏

1.Java 内存回收机制

不论哪种语言的内存分配方式,都需要返回所分配内存的真实地址,也就是返回一个指针到内存块的首地址。Java 中对象是采用 new 或者反射的方法创建的,这些对象的创建都是在堆(Heap)中分配的,所有对象的回收都是由 Java 虚拟机通过垃圾回收机制完成的。GC 为了能够正确释放对象,会监控每个对象的运行状况,对他们的申请、引用、被引用、赋值等状况进行监控,Java 会使用有向图的方法进行管理内存,实时监控对象是否可以达到,如果不可到达,则就将其回收,这样也可以消除引用循环的问题。在 Java 语言中,判断一个内存空间是否符合垃圾收集标准有两个:一个是给对象赋予了空值 null,以下再没有调用过,另一个是给对象赋予了新值,这样重新分配了内存空间。

2.Java 内存泄漏引起的原因

内存泄漏是指无用对象(不再使用的对象)持续占有内存或无用对象的内存得不到及时释放,从而造成内存空间的浪费称为内存泄漏。内存泄露有时不严重且不易察觉,这样开发者就不知道存在内存泄露,但有时也会很严重,会提示你 Out of memory。j

Java 内存泄漏的根本原因是什么呢?长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄漏,尽管短生命周期对象已经不再需要,但是因为长生命周期持有它的引用而导致不能被回收,这就是 Java 中内存泄漏的发生场景。具体主要有如下几大类:

1、静态集合类引起内存泄漏:

像 HashMap、Vector 等的使用最容易出现内存泄露,这些静态变量的生命周期和应用程序一致,他们所引用的所有的对象 Object 也不能被释放,因为他们也将一直被 Vector 等引用着。

例如

Static Vector v = new Vector(10);
for (int i = 1; i<100; i++)
{
Object o = new Object();
v.add(o);
o = null;
}

在这个例子中,循环申请 Object 对象,并将所申请的对象放入一个 Vector 中,如果仅仅释放引用本身(o=null),那么 Vector 仍然引用该对象,所以这个对象对 GC 来说是不可回收的。因此,如果对象加入到 Vector 后,还必须从 Vector 中删除,最简单的方法就是将 Vector 对象设置为 null。

2、当集合里面的对象属性被修改后,再调用 remove () 方法时不起作用。

例如:

public static void main(String[] args)
{
Set<Person> set = new HashSet<Person>();
Person p1 = new Person("唐僧","pwd1",25);
Person p2 = new Person("孙悟空","pwd2",26);
Person p3 = new Person("猪八戒","pwd3",27);
set.add(p1);
set.add(p2);
set.add(p3);
System.out.println("总共有:"+set.size()+" 个元素!"); //结果:总共有:3 个元素!
p3.setAge(2); //修改p3的年龄,此时p3元素对应的hashcode值发生改变

set.remove(p3); //此时remove不掉,造成内存泄漏

set.add(p3); //重新添加,居然添加成功
System.out.println("总共有:"+set.size()+" 个元素!"); //结果:总共有:4 个元素!
for (Person person : set)
{
System.out.println(person);
}
}

3、监听器

在 java 编程中,我们都需要和监听器打交道,通常一个应用当中会用到很多监听器,我们会调用一个控件的诸如 addXXXListener () 等方法来增加监听器,但往往在释放对象的时候却没有记住去删除这些监听器,从而增加了内存泄漏的机会。

4、各种连接

比如数据库连接(dataSourse.getConnection ()),网络连接 (socket) 和 io 连接,除非其显式的调用了其 close()方法将其连接关闭,否则是不会自动被 GC 回收的。对于 Resultset 和 Statement 对象可以不进行显式回收,但 Connection 一定要显式回收,因为 Connection 在任何时候都无法自动回收,而 Connection 一旦回收,Resultset 和 Statement 对象就会立即为 NULL。但是如果使用连接池,情况就不一样了,除了要显式地关闭连接,还必须显式地关闭 Resultset Statement 对象(关闭其中一个,另外一个也会关闭),否则就会造成大量的 Statement 对象无法释放,从而引起内存泄漏。这种情况下一般都会在 try 里面去的连接,在 finally 里面释放连接。

5、内部类和外部模块的引用

内部类的引用是比较容易遗忘的一种,而且一旦没释放可能导致一系列的后继类对象没有释放。此外程序员还要小心外部模块不经意的引用,例如程序员 A 负责 A 模块,调用了 B 模块的一个方法如: public void registerMsg (Object b); 这种调用就要非常小心了,传入了一个对象,很可能模块 B 就保持了对该对象的引用,这时候就需要注意模块 B 是否提供相应的操作去除引用。

6、单例模式

不正确使用单例模式是引起内存泄漏的一个常见问题,单例对象在初始化后将在 JVM 的整个生命周期中存在(以静态变量的方式),如果单例对象持有外部的引用,那么这个对象将不能被 JVM 正常回收,导致内存泄漏,考虑下面的例子:

class A{
public A(){
B.getInstance().setA(this);
}
....
}
//B类采用单例模式
class B{
private A a;
private static B instance=new B();
public B(){}
public static B getInstance(){
return instance;
}
public void setA(A a){
this.a=a;
}
//getter...
} 

显然 B 采用 singleton 模式,它持有一个 A 对象的引用,而这个 A 类的对象将不能被回收。想象下如果 A 是个比较复杂的对象或者集合类型会发生什么情况

Android 中常见的内存泄漏汇总


集合类泄漏

集合类如果仅仅有添加元素的方法,而没有相应的删除机制,导致内存被占用。如果这个集合类是全局性的变量 (比如类中的静态属性,全局性的 map 等即有静态引用或 final 一直指向它),那么没有相应的删除机制,很可能导致集合所占用的内存只增不减。比如上面的典型例子就是其中一种情况,当然实际上我们在项目中肯定不会写这么 2B 的代码,但稍不注意还是很容易出现这种情况,比如我们都喜欢通过 HashMap 做一些缓存之类的事,这种情况就要多留一些心眼。

单例造成的内存泄漏

由于单例的静态特性使得其生命周期跟应用的生命周期一样长,所以如果使用不恰当的话,很容易造成内存泄漏。比如下面一个典型的例子,

public class AppManager {
private static AppManager instance;
private Context context;
private AppManager(Context context) {
this.context = context;
}
public static AppManager getInstance(Context context) {
if (instance == null) {
instance = new AppManager(context);
}
return instance;
}
}

这是一个普通的单例模式,当创建这个单例的时候,由于需要传入一个 Context,所以这个 Context 的生命周期的长短至关重要:

1、如果此时传入的是 Application 的 Context,因为 Application 的生命周期就是整个应用的生命周期,所以这将没有任何问题。

2、如果此时传入的是 Activity 的 Context,当这个 Context 所对应的 Activity 退出时,由于该 Context 的引用被单例对象所持有,其生命周期等于整个应用程序的生命周期,所以当前 Activity 退出时它的内存并不会被回收,这就造成泄漏了。

正确的方式应该改为下面这种方式:

public class AppManager {
private static AppManager instance;
private Context context;
private AppManager(Context context) {
this.context = context.getApplicationContext();// 使用Application 的context
}
public static AppManager getInstance(Context context) {
if (instance == null) {
instance = new AppManager(context);
}
return instance;
}
}

或者这样写,连 Context 都不用传进来了:

在你的 Application 中添加一个静态方法,getContext() 返回 Application 的 context,

...

context = getApplicationContext();

...
   /**
     * 获取全局的context
     * @return 返回全局context对象
     */
    public static Context getContext(){
        return context;
    }

public class AppManager {
private static AppManager instance;
private Context context;
private AppManager() {
this.context = MyApplication.getContext();// 使用Application 的context
}
public static AppManager getInstance() {
if (instance == null) {
instance = new AppManager();
}
return instance;
}
}

匿名内部类 / 非静态内部类和异步线程

非静态内部类创建静态实例造成的内存泄漏

有的时候我们可能会在启动频繁的 Activity 中,为了避免重复创建相同的数据资源,可能会出现这种写法:

        public class MainActivity extends AppCompatActivity {
        private static TestResource mResource = null;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if(mManager == null){
        mManager = new TestResource();
        }
        //...
        }
        class TestResource {
        //...
        }
        }

这样就在 Activity 内部创建了一个非静态内部类的单例,每次启动 Activity 时都会使用该单例的数据,这样虽然避免了资源的重复创建,不过这种写法却会造成内存泄漏,因为非静态内部类默认会持有外部类的引用,而该非静态内部类又创建了一个静态的实例,该实例的生命周期和应用的一样长,这就导致了该静态实例一直会持有该 Activity 的引用,导致 Activity 的内存资源不能正常回收。正确的做法为:

将该内部类设为静态内部类或将该内部类抽取出来封装成一个单例,如果需要使用 Context,请按照上面推荐的使用 Application 的 Context。当然,Application 的 context 不是万能的,所以也不能随便乱用,对于有些地方则必须使用 Activity 的 Context,对于 Application,Service,Activity 三者的 Context 的应用场景如下:

其中: NO1 表示 Application 和 Service 可以启动一个 Activity,不过需要创建一个新的 task 任务队列。而对于 Dialog 而言,只有在 Activity 中才能创建

### 匿名内部类

android 开发经常会继承实现 Activity/Fragment/View,此时如果你使用了匿名类,并被异步线程持有了,那要小心了,如果没有任何措施这样一定会导致泄露

    public class MainActivity extends Activity {
    ...
    Runnable ref1 = new MyRunable();
    Runnable ref2 = new Runnable() {
        @Override
        public void run() {

        }
    };
       ...
    }

ref1 和 ref2 的区别是,ref2 使用了匿名内部类。我们来看看运行时这两个引用的内存:

可以看到,ref1 没什么特别的。

但 ref2 这个匿名类的实现对象里面多了一个引用:

this$0 这个引用指向 MainActivity.this,也就是说当前的 MainActivity 实例会被 ref2 持有,如果将这个引用再传入一个异步线程,此线程和此 Acitivity 生命周期不一致的时候,就造成了 Activity 的泄露。

Handler 造成的内存泄漏

Handler 的使用造成的内存泄漏问题应该说是最为常见了,很多时候我们为了避免 ANR 而不在主线程进行耗时操作,在处理网络任务或者封装一些请求回调等 api 都借助 Handler 来处理,但 Handler 不是万能的,对于 Handler 的使用代码编写一不规范即有可能造成内存泄漏。另外,我们知道 Handler、Message 和 MessageQueue 都是相互关联在一起的,万一 Handler 发送的 Message 尚未被处理,则该 Message 及发送它的 Handler 对象将被线程 MessageQueue 一直持有。

由于 Handler 属于 TLS (Thread Local Storage) 变量,生命周期和 Activity 是不一致的。因此这种实现方式一般很难保证跟 View 或者 Activity 的生命周期保持一致,故很容易导致无法正确释放。

举个例子:

    public class SampleActivity extends Activity {

    private final Handler mLeakyHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
      // ...
    }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // Post a message and delay its execution for 10 minutes.
    mLeakyHandler.postDelayed(new Runnable() {
      @Override
      public void run() { /* ... */ }
    }, 1000 * 60 * 10);

    // Go back to the previous Activity.
    finish();
    }
    }

在该 SampleActivity 中声明了一个延迟 10 分钟执行的消息 Message,mLeakyHandler 将其 push 进了消息队列 MessageQueue 里。当该 Activity 被 finish () 掉时,延迟执行任务的 Message 还会继续存在于主线程中,它持有该 Activity 的 Handler 引用,所以此时 finish () 掉的 Activity 就不会被回收了从而造成内存泄漏(因 Handler 为非静态内部类,它会持有外部类的引用,在这里就是指 SampleActivity)。

修复方法:在 Activity 中避免使用非静态内部类,比如上面我们将 Handler 声明为静态的,则其存活期跟 Activity 的生命周期就无关了。同时通过弱引用的方式引入 Activity,避免直接将 Activity 作为 context 传进去,见下面代码:

public class SampleActivity extends Activity {

  /**
   * Instances of static inner classes do not hold an implicit
   * reference to their outer class.
   */
  private static class MyHandler extends Handler {
    private final WeakReference<SampleActivity> mActivity;

    public MyHandler(SampleActivity activity) {
      mActivity = new WeakReference<SampleActivity>(activity);
    }

    @Override
    public void handleMessage(Message msg) {
      SampleActivity activity = mActivity.get();
      if (activity != null) {
        // ...
      }
    }
  }

  private final MyHandler mHandler = new MyHandler(this);

  /**
   * Instances of anonymous classes do not hold an implicit
   * reference to their outer class when they are "static".
   */
  private static final Runnable sRunnable = new Runnable() {
      @Override
      public void run() { /* ... */ }
  };

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // Post a message and delay its execution for 10 minutes.
    mHandler.postDelayed(sRunnable, 1000 * 60 * 10);

    // Go back to the previous Activity.
    finish();
  }
}

综述,即推荐使用静态内部类 + WeakReference 这种方式。每次使用前注意判空。

前面提到了 WeakReference,所以这里就简单的说一下 Java 对象的几种引用类型。

Java 对引用的分类有 Strong reference, SoftReference, WeakReference, PhatomReference 四种。

在 Android 应用的开发中,为了防止内存溢出,在处理一些占用内存大而且声明周期较长的对象时候,可以尽量应用软引用和弱引用技术。

软 / 弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收器回收,Java 虚拟机就会把这个软引用加入到与之关联的引用队列中。利用这个队列可以得知被回收的软 / 弱引用的对象列表,从而为缓冲器清除已失效的软 / 弱引用。

假设我们的应用会用到大量的默认图片,比如应用中有默认的头像,默认游戏图标等等,这些图片很多地方会用到。如果每次都去读取图片,由于读取文件需要硬件操作,速度较慢,会导致性能较低。所以我们考虑将图片缓存起来,需要的时候直接从内存中读取。但是,由于图片占用内存空间比较大,缓存很多图片需要很多的内存,就可能比较容易发生 OutOfMemory 异常。这时,我们可以考虑使用软 / 弱引用技术来避免这个问题发生。以下就是高速缓冲器的雏形:

首先定义一个 HashMap,保存软引用对象。

private Map <String, SoftReference<Bitmap>> imageCache = new HashMap <String, SoftReference<Bitmap>> ();

再来定义一个方法,保存 Bitmap 的软引用到 HashMap。

使用软引用以后,在 OutOfMemory 异常发生之前,这些缓存的图片资源的内存空间可以被释放掉的,从而避免内存达到上限,避免 Crash 发生。

如果只是想避免 OutOfMemory 异常的发生,则可以使用软引用。如果对于应用的性能更在意,想尽快回收一些占用内存比较大的对象,则可以使用弱引用。

另外可以根据对象是否经常使用来判断选择软引用还是弱引用。如果该对象可能会经常使用的,就尽量用软引用。如果该对象不被使用的可能性更大些,就可以用弱引用。

ok,继续回到主题。前面所说的,创建一个静态 Handler 内部类,然后对 Handler 持有的对象使用弱引用,这样在回收时也可以回收 Handler 持有的对象,但是这样做虽然避免了 Activity 泄漏,不过 Looper 线程的消息队列中还是可能会有待处理的消息,所以我们在 Activity 的 Destroy 时或者 Stop 时应该移除消息队列 MessageQueue 中的消息。

下面几个方法都可以移除 Message:

public final void removeCallbacks(Runnable r);

public final void removeCallbacks(Runnable r, Object token);

public final void removeCallbacksAndMessages(Object token);

public final void removeMessages(int what);

public final void removeMessages(int what, Object object);

尽量避免使用 static 成员变量

如果成员变量被声明为 static,那我们都知道其生命周期将与整个 app 进程生命周期一样。

这会导致一系列问题,如果你的 app 进程设计上是长驻内存的,那即使 app 切到后台,这部分内存也不会被释放。按照现在手机 app 内存管理机制,占内存较大的后台进程将优先回收,yi''wei 如果此 app 做过进程互保保活,那会造成 app 在后台频繁重启。当手机安装了你参与开发的 app 以后一夜时间手机被消耗空了电量、流量,你的 app 不得不被用户卸载或者静默。

这里修复的方法是:

不要在类初始时初始化静态成员。可以考虑 lazy 初始化。 架构设计上要思考是否真的有必要这样做,尽量避免。如果架构需要这么设计,那么此对象的生命周期你有责任管理起来。

避免 override finalize ()

1、finalize 方法被执行的时间不确定,不能依赖与它来释放紧缺的资源。时间不确定的原因是: 虚拟机调用 GC 的时间不确定 Finalize daemon 线程被调度到的时间不确定

2、finalize 方法只会被执行一次,即使对象被复活,如果已经执行过了 finalize 方法,再次被 GC 时也不会再执行了,原因是:

含有 finalize 方法的 object 是在 new 的时候由虚拟机生成了一个 finalize reference 在来引用到该 Object 的,而在 finalize 方法执行的时候,该 object 所对应的 finalize Reference 会被释放掉,即使在这个时候把该 object 复活 (即用强引用引用住该 object),再第二次被 GC 的时候由于没有了 finalize reference 与之对应,所以 finalize 方法不会再执行。

3、含有 Finalize 方法的 object 需要至少经过两轮 GC 才有可能被释放。

资源未关闭造成的内存泄漏

对于使用了 BraodcastReceiver,ContentObserver,File,游标 Cursor,Stream,Bitmap 等资源的使用,应该在 Activity 销毁时及时关闭或者注销,否则这些资源将不会被回收,造成内存泄漏。

一些不良代码造成的内存压力

有些代码并不造成内存泄露,但是它们,或是对没使用的内存没进行有效及时的释放,或是没有有效的利用已有的对象而是频繁的申请新内存。

比如: Bitmap 没调用 recycle () 方法,对于 Bitmap 对象在不使用时,我们应该先调用 recycle () 释放内存,然后才它设置为 null. 因为加载 Bitmap 对象的内存空间,一部分是 java 的,一部分 C 的(因为 Bitmap 分配的底层是通过 JNI 调用的 )。 而这个 recyle () 就是针对 C 部分的内存释放。 构造 Adapter 时,没有使用缓存的 convertView , 每次都在创建新的 converView。这里推荐使用 ViewHolder。

总结

对 Activity 等组件的引用应该控制在 Activity 的生命周期之内; 如果不能就考虑使用 getApplicationContext 或者 getApplication,以避免 Activity 被外部长生命周期的对象引用而泄露。

尽量不要在静态变量或者静态内部类中使用非静态外部成员变量(包括 context ),即使要使用,也要考虑适时把外部成员变量置空;也可以在内部类中使用弱引用来引用外部类的变量。

对于生命周期比 Activity 长的内部类对象,并且内部类中使用了外部类的成员变量,可以这样做避免内存泄漏:

    将内部类改为静态内部类
    静态内部类中使用弱引用来引用外部类的成员变量

Handler 的持有的引用对象最好使用弱引用,资源释放时也可以清空 Handler 里面的消息。比如在 Activity onStop 或者 onDestroy 的时候,取消掉该 Handler 对象的 Message 和 Runnable.

在 Java 的实现过程中,也要考虑其对象释放,最好的方法是在不使用某对象时,显式地将此对象赋值为 null,比如使用完 Bitmap 后先调用 recycle (),再赋为 null, 清空对图片等资源有直接引用或者间接引用的数组(使用 array.clear () ; array = null)等,最好遵循谁创建谁释放的原则。

正确关闭资源,对于使用了 BraodcastReceiver,ContentObserver,File,游标 Cursor,Stream,Bitmap 等资源的使用,应该在 Activity 销毁时及时关闭或者注销。

保持对对象生命周期的敏感,特别注意单例、静态对象、全局性集合等的生命周期

Android之内存泄漏调试学习与总结,全套教学资料

Android之内存泄漏调试学习与总结,全套教学资料

����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������### 一、Android面试题

Android面试题包括Android基础,还有一些源码级别的、原理这些等。所以想去美团面试,一定要多看看源码和实现方式,常用框架可以试试自己能不能手写实现一下,锻炼一下自己。 (一)Android基础知识点

  • 四大组件是什么

  • 四大组件的生命周期和简单用法

  • Activity之间的通信方式

  • Activity各种情况下的生命周期

  • 横竖屏切换的时候,Activity 各种情况下的生命周期

  • Activity与Fragment之间生命周期比较

  • Activity上有Dialog的时候按Home键时的生命周期

  • 两个Activity 之间跳转时必然会执行的是哪几个方法?

  • 前台切换到后台,然后再回到前台,Activity生命周期回调方法。弹出Dialog,生命值周期回调方法。

  • Activity的四种启动模式对比

  • Activity状态保存于恢复

  • fragment各种情况下的生命周期

  • Fragment状态保存startActivityForResult是哪个类的方法,在什么情况下使用?

  • 如何实现Fragment的滑动?

  • fragment之间传递数据的方式?

  • Activity 怎么和Service 绑定?

  • 怎么在Activity 中启动自己对应的Service?

  • service和activity怎么进行数据交互?

  • Service的开启方式

  • 请描述一下Service 的生命周期

  • 谈谈你对ContentProvider的理解

  • 说说ContentProvider、ContentResolver、ContentObserver 之间的关系

  • 请描述一下广播broadcastReceiver的理解

  • 广播的分类

  • 广播使用的方式和场景

  • 在manifest 和代码中如何注册和使用broadcastReceiver?

  • 本地广播和全局广播有什么差别?

  • broadcastReceiver,LocalbroadcastReceiver 区别

  • AlertDialog,popupWindow,Activity区别

  • Application 和 Activity 的 Context 对象的区别

  • Android属性动画特性

  • 如何导入外部数据库?

  • LinearLayout、RelativeLayout、FrameLayout的特性及对比,并介绍使用场景。

  • 谈谈对接口与回调的理解

  • 回调的原理

  • 写一个回调demo

  • 介绍下SurfView

  • RecycleView的使用

  • 序列化的作用,以及Android两种序列化的区别

  • 差值器

  • 估值器

  • Android中数据存储方式

    image

(二)Android源码相关分析

  • Android动画框架实现原理

  • Android各个版本API的区别

  • Requestlayout,onlayout,onDraw,DrawChild区别与联系

  • invalidate和postInvalidate的区别及使用

  • Activity-Window-View三者的差别

  • 谈谈对Volley的理解

  • 如何优化自定义view

  • 低版本SDK如何实现高版本api?

  • 描述一次网络请求的流程

  • HttpUrlConnection 和 okhttp关系

  • Bitmap对象的理解

  • looper架构

  • ActivityThread,AMS,WMS的工作原理

  • 自定义view如何考虑机型适配

  • 自定义view的事件

  • AstncTask+HttpClient 与 AsyncHttpClient有什么区别?

  • LaunchMode应用场景

  • AsyncTask 如何使用?

  • SpareArray原理

  • 请介绍下ContentProvider 是如何实现数据共享的?

  • AndroidService与Activity之间通信的几种方式

  • IntentService原理及作用是什么?

  • 说说Activity、Intent、Service 是什么关系

  • ApplicationContext和ActivityContext的区别

  • SP是进程同步的吗?有什么方法做到同步?

  • 谈谈多线程在Android中的使用

  • 进程和 Application 的生命周期

  • 封装View的时候怎么知道view的大小

  • RecycleView原理

  • AndroidManifest的作用与理解

    image

    (三)常见的一些原理性问题

  • Handler机制和底层实现

  • Handler、Thread和HandlerThread的差别

  • handler发消息给子线程,looper怎么启动?

  • 关于Handler,在任何地方new Handler 都是什么线程下?

  • ThreadLocal原理,实现及如何保证Local属性?

  • 请解释下在单线程模型中Message、Handler、Message Queue、Looper之间的关系

  • 请描述一下View事件传递分发机制

  • Touch事件传递流程

  • 事件分发中的onTouch 和onTouchEvent 有什么区别,又该如何使用?

  • View和ViewGroup分别有哪些事件分发相关的回调方法

  • View刷新机制

  • View绘制流程

  • 自定义控件原理

  • 自定义view如何提供获取View属性的接口?

  • Android代码中实现WAP方式联网

  • AsyncTask机制

  • AsyncTask原理及不足

  • 如何取消AsyncTask?

  • 为什么不能在子线程更新UI?

  • ANR产生的原因是什么?

  • ANR定位和修正

  • oom是什么?

  • 什么情况导致oom?

  • 有什么解决方法可以避免OOM?

  • Oom 是否可以try catch?为什么?

  • 内存泄漏是什么?

  • 什么情况导致内存泄漏?

  • 如何防止线程的内存泄漏?

  • 内存泄露场的解决方法

  • 内存泄漏和内存溢出区别?

  • LruCache默认缓存大小

  • ContentProvider的权限管理(解答:读写分离,权限控制-精确到表级,URL控制)

  • 如何通过广播拦截和abort一条短信?

  • 广播是否可以请求网络?

  • 广播引起anr的时间限制是多少?

  • 计算一个view的嵌套层级

  • Activity栈

  • Android线程有没有上限?

  • 线程池有没有上限?

  • Android为什么引入Parcelable?

  • 有没有尝试简化Parcelable的使用?

    image

二、Java面试题

熟练掌握java是很关键的,大公司不仅仅要求你会使用几个api,更多的是要你熟悉源码实现原理,甚至要你知道有哪些不足,怎么改进,还有一些java有关的一些算法,设计模式等等。 (一、)Java

  • HashMap 和 HashTable 以及 CurrentHashMap 的区别。

  • synchronized 和 volatile 、reentrantlock 、CAS 的区别。

  • JVM 类加载机制、垃圾回收算法对比、Java 虚拟机结构等。

  • Java 的四大引用

  • Java 的泛型,<? super T> 和 <? extends T> 的区别。

  • Java 线程有哪些状态,有哪些锁,各种锁的区别。

  • final 、finally、finalize 区别。

  • 接口和抽象类的区别。

  • sleep 、wait、yield 的区别,wait 的线程如何唤醒它?

    image

三、高级开发技术面试题

一、图片

1、图片库对比 2、LRUCache原理 3、图片加载原理 4、自己去实现图片库,怎么做? 5、Glide源码解析 6、Glide使用什么缓存? 7、Glide内存缓存如何控制大小?

image

二、网络和安全机制

1.网络框架对比和源码分析
2.自己去设计网络请求框架,怎么做?
3.网络请求缓存处理,okhttp如何处理网络缓存的
4.从网络加载一个10M的图片,说下注意事项
5.TCP的3次握手和四次挥手
6.TCP与UDP的区别
7.TCP与UDP的应用
8.HTTP协议
9.HTTP1.0与2.0的区别
10.HTTP报文结构
11.HTTP与HTTPS的区别以及如何实现安全性
12.如何验证证书的合法性?
13.https中哪里用了对称加密,哪里用了非对称加密,对加密算法(如RSA)等是否有了解?
14.client如何确定自己发送的消息被server收到?
15.谈谈你对WebSocket的理解
16.WebSocket与socket的区别
17.谈谈你对安卓签名的理解。
18.请解释安卓为啥要加签名机制?
19.视频加密传输
20.App 是如何沙箱化,为什么要这么做?
21.权限管理系统(底层的权限是如何进行 grant 的)?

image

三、数据库

1.sqlite升级,增加字段的语句
2.数据库框架对比和源码分析
3.数据库的优化
4.数据库数据迁移问题

image

四、插件化、模块化、组件化、热修复、增量更新、Gradle

1.对热修复和插件化的理解 2.插件化原理分析 3.模块化实现(好处,原因) 4.热修复、插件化 5.项目组件化的理解 6.描述清点击 Android Studio 的 build 按钮后发生了什么

image

五、架构设计和设计模式

1.谈谈你对Android设计模式的理解 2.MVC MVP MVVM原理和区别 3.你所知道的设计模式有哪些? 4.项目中常用的设计模式 5.手写生产者/消费者模式 6.写出观察者模式的代码 7.适配器模式,装饰者模式,外观模式的异同? 8.用到的一些开源框架,介绍一个看过源码的,内部实现过程。 9.谈谈对RxJava的理解 10.Rxjava发送事件步骤 11.RxJava的作用,与平时使用的异步操作来比的优缺点 12.说说EventBus作用,实现方式,代替EventBus的方式 13.从0设计一款App整体架构,如何去做? 14.说一款你认为当前比较火的应用并设计(比如:直播APP,P2P金融,小视频等) 15.谈谈对java状态机理解 16.Fragment如果在Adapter中使用应该如何解耦? 17.Binder机制及底层实现 18.对于应用更新这块是如何做的?(解答:灰度,强制更新,分区域更新)? 19.实现一个Json解析器(可以通过正则提高速度) 20.统计启动时长,标准

image

六、性能优化

1.启动 app 黑白屏优化
2.稳定——内存优化
3.流畅——卡顿优化
4.节省——耗电优化
5.安装包——APK 瘦身
6.冷启动与热启动
7.内存泄漏的场景和解决办法
8. Bitmap 优化
9.LRU 的原理
10.webview 优化
11.如何避免 OOM?
...

image

七、Android Framework

1.Android 系统架构
2.View 的事件分发机制?滑动冲突怎么解决?
3.View 的绘制流程?
4.跨进程通信
5.Android 系统启动流程是什么?
6.启动一个程序,可以主界面点击图标进入,也可 以从一个程序中 跳转过去,二者有什么区别?
7.AMS 家族重要术语解释
8.用到的一些开源框架,介绍一个看过源码的,内部实现过程。
...

image

八、Android优秀三方库源码

1.网络底层框架:OkHttp 实现原理 2.网络封装框架:Retrofifit 实现原理 3.响应式编程框架:RxJava 实现原理 4.图片加载框架:Glide 实现原理 5.事件总线框架:EventBus 实现原理 6.内存泄漏检测框架:LeakCanary 实现原理 7.依赖注入框架:ButterKnife 实现原理 8.依赖全局管理框架:Dagger2 实现原理 9.数据库框架:GreenDao 实现原理

image

新的开始

改变人生,没有什么捷径可言,这条路需要自己亲自去走一走,只有深入思考,不断反思总结,保持学习的热情,一步一步构建自己完整的知识体系,才是最终的制胜之道,也是程序员应该承担的使命。

如果有需要进阶Android高级工程师系统学习资料的,我可以免费分享给大家,需要完整版的朋友,【点这里可以看到全部内容】。

《系列学习视频》

《系列学习文档》

《我的大厂面试之旅》

工程师系统学习资料的,我可以免费分享给大家,需要完整版的朋友,【点这里可以看到全部内容】。

《系列学习视频》
[外链图片转存中…(img-GYIYuq89-1617348206535)]

《系列学习文档》

[外链图片转存中…(img-fMXaHK69-1617348206536)]

《我的大厂面试之旅》

[外链图片转存中…(img-bJZSUVC3-1617348206537)]

今天关于kotlin入门,Android内存泄漏总结,系列教学kotlin内存泄露的介绍到此结束,谢谢您的阅读,有关2021最新中高阶Android面试题总结,系列教学、android – Java中的内存泄漏,但不是Kotlin(相同的代码库)…为什么?、Android 内存泄漏总结文档、Android之内存泄漏调试学习与总结,全套教学资料等更多相关知识的信息可以在本站进行查询。

本文标签: