今天,在Q群中有网友(@广州-包晴天)发出了网上的一个相对经典的问题,问题具体见下图。

本来是无意写此文的,但群里多个网友热情不好推却,于是,撰此文予以分析。

从这个问题的陈述中,我们发现,提问者明显对Android中的几个基本概念在理解上是存在误区的(或直接称之为理解错误)。且这种误区,我发现是较为广泛的存在于不少Android开发心中的。

理解误区主要体现在对以下几个概念没有区分清:

1,Activity的onDestory回方法;

2,Activity的销毁;

3,Activity的内存回收;

4,内存泄露;

5,Activity中动态注册的BroadcastReceiver与Activity的引用持有关系。

下面我们来一个个具体分析下。

1,Activity的onDestory回调方法。

onDestory作为Activity中生命周期中的一个常见的方法,我们先来看一下官方文档中的描述。

从这个定义中,我们得出如下几点细节:

a,onDestory回调方法是Activity被销毁前的最后一个Activity中回调方法;

b,onDestory回调方法的触发时机是Activity被外部主动调用了finish()方法,或因系统内存空间不足而导致的临行性的销毁该Activity实例,以便获得内存空间。

在实际操作中,onDestory回调方法的触发时机(或称之为Activity销毁的触发时机)主要表现在如下四种情况:

a,人为的主动的调用了finish()方法,以期望去销毁当前的Activity;

b,人为的主动操作导致的系统去销毁当前Activity,如常见的按下手机上的返回键;

c,系统因内存不足导致的临行性的销毁该Activity实例,如从A Activity跳转到B Activity后,系统内存不足的情况下可能会销毁掉A Activity;

d,打开手机上的“开发者选项”中的“不保留活动”选项,其中,这个更多的是为了模拟出C场景,效果同C。

另外,上述的b中的按下手机上的返回键,系统源码中也是调用了finish()方法。

区分上述的ab与cd两种方式可以通过isFinishing()方法的返回值来判断。

为了行文方便,且从ab与cd的人为主观性角度出发,本文将ab情形称之为“主动销毁”,cd情形称之为“被动销毁”。

2,Activity的销毁。

相较于onDestory作为的Activity生命周期中的回调方法,“销毁”一词在Activity中更多的表示的是Activity所处声明周期中的一种“状态”。

处于此种状态的Activity实例,对于User Interface层来说是不再可见的(无论是当前界面还是按返回键等各种情况)。

实践中,处于“销毁状态”的Activity与上述的Activity销毁的触发时机具有一致的逻辑关系,这种逻辑关系具体体现为:

a,对于主动销毁,除却Activity实例不再可见外,当前Activity实例也直接被Activity栈中移除,直接表位为对用户操作导航路径的影响;

b,对于被动销毁,当前Activity实例依然不再可见,但与主动销毁不同的是,Activity实例的对应关系在Activity栈中依然存在,此时,对用户操作导航路径并无影响。

如B Activity中,A Activity虽然被动销毁,但未改变栈结构,按下返回键依然看到A,不够此时的A与之前的A并非一个Activity实例。

需要注意的是,处于“销毁状态”的Activity,严格意义上与当前Activity的真实内存占用是否释放没有直接的对应关系。也就是说,Activity的销毁,并不意味着Activity的内存就已经被回收。

3,Activity的内存回收。

Android是基于Java基础之上,虽然在内存回收机制等方面做了一定的处理与优化(主要是基于Dalvik/ART),但针基本的GC原理上并无差别。主要表现在:

a,对于一个堆内存中的对象空间,一旦还有其他的强引用可达,该内存空间就处于不可回收状态;

b,GC的触发时机依然具有不可确定性,取决于系统依据当前的运行状态与其系统本身的GC机制判定进行。

也就是说,即使堆内存中对象已经处于可回收状态,但只要GC未被触发,内存依然被占用。

在此,需要区分下GC的不可回收状态与可回收状态的区别,严格意义上来说,其并非对立面,因为针对可回收状态,还有可能对应的软引用与弱引用需要加以考虑。

4,内存泄露。

Android中,内存泄露作为一个基本的概念,常常被提及且实践中也需尽量掌握。网上关于内存泄露的文章林林总总。

终究内存泄露的本质,是指当前对象在实际运行中超出了其本身意义上生命周期范围的,从而导致本该处于内存可回收状态的但实际上却一直处于不可回收状态的内存占用非正常现象。

内存泄露在出现,常常见于如下两种情况(为行文方便,下述将发生了内存泄露的对象称之为M):

a,因异步回调中持有M,异步回调本身的生命时长长于M本身而导致的M发生内存泄露(如最常见的是Activity中的Handler以及异步线程导致Activity本身发生内存泄露);

b,因静态属性所指向的对象中持有了M而导致的M一直被强引用可达,使得M发生内存泄露(如最常见的单例对象中强引用了外部的非静态对象)。

内存泄露过多会导致应用内存的不断上升,达到一定程度会直接导致内存溢出(OOM)。具体解决内存泄露时,主要都是针对上述AB两种情况分析排查即可。

5,Activity中动态注册的BroadcastReceiver与Activity的引用持有关系。

对于Android中的广播机制,可以先参考文章:《Android总结篇系列:Android广播机制》

Activity中动态注册的广播接收器,一般性写法都是此Activity种持有创建的广播接收器的对象引用。并指明广播接收器对应的接收广播类型(IntentFilter)。

Activity中调用registerReceiver(mBroadcastReceiver, intentFilter)方法进行广播接收器的注册。此时,通过Binder机制向AMS(Activity Manager Service)进行注册。

AMS会对应的记录Activity上下文、广播接收器以及对应的IntentFilter等内容,并形成类似于消息的发布-订阅存储模式与结构。

当对应的广播发出时,在定义的广播接收器的onReceive(context, intent)方法回调中,对于Activity中动态注册的广播接收器,onReceive方法回调中的context指的是Activity Context!

也就是说,Activity与mBroadcastReceiver此时实际上是通过AMS相互持有强引用的。因此,对于Activity中动态注册的广播接收器,一定要在对应的声明周期回调方法中去unregisterReceiver,以斩断此关联。

否则,就会出现当前Activity的内存泄露。

Android中一个经典理解误区的剖析的更多相关文章

  1. Android中一个类实现的接口数不能超过七个

    近期一段时间,在开发Android应用程序的过程中,发现Android中一个类实现的接口数超过七个的时候,常常会出现超过第7个之后的接口不能正常使用.

  2. android中一个评分的控件

    RatingBar android中一个评分的控件 如何使用 Android Studio下: dependencies { compile 'com.hedgehog.ratingbar:app:1 ...

  3. php中一个经典的!==的用法

    php中一个经典的!==的用法 <?php $str = 'Every time you bleed for reaching greatness.'; $cha = 'E'; if(strpo ...

  4. Android中AIDL的理解与使用(二)——跨应用绑定Service并通信

    跨应用绑定Service并通信: 1.(StartServiceFromAnotherApp)AIDL文件中新增接口: void setData(String data); AppService文件中 ...

  5. (四)Android中Context的理解与使用

    一.Context的作用 Context可用于访问全局资源. public class MainActivity extends Activity { private TextView tv; @Ov ...

  6. 【转】android中layout_weight的理解

    android Layout_weight的理解 SDK中的解释: Indicates how much of the extra space in the LinearLayout will be ...

  7. Android中一个关于ListView的奇怪问题

    今天在做项目的时候发现了一个比较奇怪的问题,是关于ListView的,即ListView的android:height属性会影响程序中ListView的getView()方法的调用次数,如果设置Lis ...

  8. Android中AIDL的理解与使用(一)——跨应用启动/绑定Service

    AIDL(Android Interface Definition Language)--安卓接口定义语言 一.startService/stopService 1.同一个应用程序启动Service: ...

  9. Android中Context的理解及使用(二)——Application的用途和生命周期

    实现数据共享功能: 多个Activity里面,可以使用Application来实现数据的共享,因为对于同一个应用程序来说,Application是唯一的. 1.实现全局共享的数据App.java继承自 ...

随机推荐

  1. C程序员眼里的Python

    注释 Phython的注释和C语言非常不同,第一种 #开头的注释,类似于C的//开头,而"""对 包围注释,类似于C的/* */,以及xml类的<!--    -- ...

  2. 利用webmagic获取天猫评论

    引言 爬取商品信息 爬取商品评论 数据清洗 1. 引言 现代网页往往其HTML只有基本结构,而数据是通过AJAX或其他方法获取后填充,这样的模式对爬虫有一定阻碍,但是熟练以后获取并不困难,本文以爬取天 ...

  3. HTTP相关:TCP/IP、DNS

    最近在看HTTP的书,看得有点慢,而且断断续续的,很多东西看完就忘了.知识点多且零散,感觉要多看几遍才能消化. TCP/IP协议族按层次分为4层: 应用层: 应用层决定了向用户提供应用服务时通信的活动 ...

  4. linux C中调用shell命令和运行shell脚本

    1.system(执行shell 命令) 相关函数 fork,execve,waitpid,popen表头文件 #include<stdlib.h>定义函数 int system(cons ...

  5. 斐波那契数列第n项的值及前n项之和

    <script>// 算法题 // 题1:斐波那契数列:1.1.2.3.5.8.13.21...// // 一.斐波那契数列第n项的值 // // 方法一//递归的写法function a ...

  6. Ubuntu物理机中解决VirtualBox虚拟机无法连接USB设备的问题

    本文由荒原之梦原创,原文链接:http://zhaokaifeng.com/?p=611 问题描述: 在安装完VirtualBox的USB控制器扩展(关于在VirtualBox中安装USB控制器扩展的 ...

  7. centos6.9 升级内核版本

    想在centos6.9上安装docket,不过因为内核版本是2.6的故而想升级到最新的内核版本 晚上有编译升级的比较麻烦,不过有助于理解内核升级,我使用的直接升级到最新版方法 1. 导入public ...

  8. Mongodb---操作备忘

     mysql/mongodb对比 CREATE TABLE USERS (a Number, b Number) Implicit or use MongoDB::createCollection() ...

  9. CSS3 :nth-child() 选择器---挖坑

    E:nth-child(n) 语法: E:nth-child(n) { sRules } 说明: 匹配父元素的第n个子元素E,假设该子元素不是E,则选择符无效.(也就是说,会检查从body开始的每个元 ...

  10. latex中长公式换行,很好的办法

    今天在编辑公式时,有一个公式很长,写到一行就出去了.当时之前换行都是方程组或者在括号完之后换,都没有问题.但是今天我也换行的是在括号中间断开.这样出现问题,编辑的时候会出错误提醒.上网查了一些论坛,也 ...