转自 http://www.jianshu.com/p/5f6d79323923

一、Android系统底层研究

关于底层的知识点不是在一篇文章中能讲解清楚,参见本人的Android底层研究系列,不断更新中。

二、Android重要知识点

下面精选了较为常见的知识点,坚决杜绝简单罗列答案的方式,因为那样理解不了也记不住。所以尽量以层层递进而简单粗暴的方式来表达。耐心点看,一定能帮你应对大多数面试问题。

Tips:可以先阅读自己熟悉的知识点,然后再去看那些不太熟悉的。

1、Activity启动模式

什么是启动Activity?

一般而言是先创建一个Activity对象,然后把Activity放入一个Task堆栈里进行管理(包括切换、关闭等)。那么问题来了,如果该堆栈里已经有这个Activity对象呢,我还要不要创建一个新的对象呢?Android提供了四种选择方案:

  • 不管堆栈里有没有,都创建一个新的activity放在堆栈顶部:standard模式
  • 如果我要打开的activity正好在堆栈顶部,那就直接用它;否则也创建新的: singleTop模式
  • 这个比较霸道。即如果activity已经存在在堆栈里,那么当要打开这个activity时,就算栈的上层还有其他activity,也统统移除,成为栈顶。而如果没有,就也要创建。:singleTask模式
  • 这个更加霸道。即打开这个activity时,直接创建一个堆栈,并创建这个activity放入新堆栈中,不去和别人挤一个堆栈。以后再要打开这个activity时,直接跳转到这个新堆栈里去用。:singleInstance模式(即只创建一个实例,单独放在一个task堆栈里给别的task栈共享)

启动模式

2、Activity(或组件)通信机制

Activity(组件)之间何时需要通信?

一般当需要从一个Activity(组件)向其他的Activity(组件)传递数据时,就会涉及到通信机制。例如打开一个新的Activity并向其传递数据。

如何来通信/传递数据?

较常用的是android系统的信使:Intent
Intent的作用是,带上要传递的数据,然后出发前往指定的目的地。
那么问题来了,如果带上各种各样的数据呢?又如何导航去找到目的地呢?

如何带上各种各样的数据?

就像发信一样,你不可能把一堆凌乱的信纸直接塞给信使让人家送,而会用信封把信都装起来。
android里也一样,会把数据先打包,再给信使,这里就涉及到 bundle。对,我们把一堆凌乱的数据放在一个bundle里,然后把bundle给Intent就可以了。

 如何导航找到目的地呢?

如果你想邮寄东西有两种方式:1、在信封上写好详细地址;2、如果你想邮寄没用的棉被书籍给灾区,但你又无所谓寄给哪个灾区,你就可以写帮我随便寄到一个灾区去,然后邮局管理员会在全国范围内查询,如果查到了灾区如汶川,那就邮寄到那。
android里也一样,有显式Intent和隐式Intent,前者会指定特定组件名,如指定目标activity;后者不指定名称,只给一个描述(Intent-Filter)。然后android会依据这个描述去搜索。

但是,关于数据打包,有个问题是,如果传递普通数据可以直接放入bundle中,但 如果要传递对象呢。

如何在bundle中打包并传递对象呢!?

3、对象序列化机制

前面已经知道了,我们要在组件间通信时会先用bundle对数据打包,然后交给信使intent。那么问题来了?

bundle如何打包数据呢?

这里的数据我们分成两种,一种是原型数据,如int、string等,可以直接打包并传递。打包方式如下:

bundle.putInt("key1", 20);
bundle.putString("key2", "hello");

另一种是java对象,这是不能直接给bundle的,而要通过序列化后再给bundle,然后对象会以二进制流形式传输,直到目标组件接受到bundle后,要对该对象二进制数据反序列化才能获取真实的的对象。

那么如何对对象进行序列化呢?

两种方法。

  • 基于java语言的Serializable序列化方法:对象类要实现Serializable接口;
  • 基于android系统提出的Parcel序列化方法:实现Parcelable接口。
    只要类实现了这两个接口,就可以把对象存入bundle中:
bundle.putSerializable(Key, Object);
bundle.putParcelable(Key, Object);

那么

 这两种机制有何区别?
  • JAVA中的Serialize机制是将对象转化为字节流存储在外部设备,在需要重新生成对象(采用java反射机制)。主要用于外部设备保存对象状态,网络传输对象等场景。缺点是产生很多中间对象及造成一定的GC(垃圾回收),简而言之Serialize更慢
  • Android提供的Parcel机制是针对移动设备轻量级高效对象序列化机制。整个过程均在内存进行,不涉及外部设备,反序列化时读取的就是原对象,而不会创建新对象。简单来说Parcel更快;不过它使用复杂

这一步我们知道,要对对象进行序列化后才可以放入bundle由intent传递给其他组件,那么,

为什么需要对对象序列化后才能通过intent在组件间传递呢!?

以startActivity(intent)为例,该函数作用时从A Activity中启动B Activity,同时把数据打包给intent并传递给B。也就是说,如果intent里的数据有java对象,那就要序列化这个对象才能传递给B。
下面直接给出原因:(详细可参见从源码角度轻松学习Intent机制)
因为在启动B Activity过程中,需要离开应用程序所在进程,转而调用native方法,进入linux kernel进程中去执行activity切换的实际操作。完成后再重新把传输数据带回到应用程序进程中,对原始打包数据进行解析。
也就是说,在启动B Activity过程中,所打包的数据要经过不同进程:应用程序->linux kernel->应用程序,而java对象是无法直接在进程间传输的(见下文),所以,我们需要提前序列化java对象,才能让它经得住后面跨进程传输的考验。

4、进程间通信机制IPC

什么是进程?

简单来说,一个进程往往代表一个应用程序。操作系统会为每一个进程分配一定的内存空间等资源,然后进程就在这块内存里跑。

什么是进程间通信?

一般而言,为了保证安全,进程获得的内存空间时一块抽象的内存,然后会映射到实际的某一块物理内存,从而,每个进程都无法访问其他进程所在内存里的数据。比如手机上开了qq和某银行客户端,操作系统会为他俩创建两个独立的内存区供运行,而qq进程是无法访问到银行客户端数据的,这就保证了数据的安全性。
但是,带来的缺陷是,进程间无法直接进行数据传输,所以要引入特有的进程间通信机制。

进程间可以直接传递对象吗?

前面知道,不同进程间通信,也就意味着在两块不同的物理内存间传递数据。首先,原生的数据如int string数据是可以传递的,只要你说明这个数据时什么类型,那么就可以在目标进程里复原。但是对象数据,如果直接把对象传递过去,目标进程是无法复原的,因而也无法识别。

内存区数据通信
Android系统中不同应用程序之间通信示意图。一个进程代表一个应用程序。

进程间通信
附加:IPC机制中很重要的是Binder机制,利用Binder实现不同进程间传递。这部分内容面试一般不会问的太难。
目前本人还在整理该部分内容,继续更新中。

5、多线程管理

前面提到了进程间通信,下面讲解下Android里的多线程管理,后面会讲解线程间通信。
首先,线程基本概念此处不过多讲解,只要知道线程是轻量级进程,在有限的进程内存空间资源中去执行实际操作。

什么是Main Thread 和 Worker Thread?

当应用程序开启时会自动创建一个主线程 (Main Thread),也叫UI线程,主要功能是完成UI界面绘制和与用户交互。除主线程之外的其他线程称为子线程或Worker Thread。这些线程往往用执行耗时操作如网络通信或数据库访问等。
如果在主线程中执行耗时操作,那么在执行过程中,就无法绘制界面或响应用户操作,在用户看来就是卡顿,可能造成ANR现象,破坏用户体验。

主线程是线程不安全的

网上很多地方解释线程不安全并非解释原因,而是在‘解释’结果,即:(因为主线程是线程不安全的,所以)UI控件只能在主线程中操作,而不能在其他线程中操作,否则多个线程同时操作一个控件会出错。
这种解释不仅没解释为何主线程是线程不安全的,反而让人感觉android系统里限定了只能在主线程操作ui元素,容易误解为那不是很安全吗?
我的解释是:
首先我们是的的确确可以通过代码,在子线程里操作UI元素,然后引发程序崩溃。假设主线程是安全的,那么安全的情况是:即使我们不小心在子线程操作UI也不用担心,因为系统会屏蔽这种操作。而实际上系统并未屏蔽,只要开发者在子线程操作UI元素,很有可能会导致崩溃。所以我们才说android系统里主线程并不安全。

附加知识:什么是线程安全/不安全?
在多线程环境里,往往一段代码会被多个线程分别执行。而常见的情况是一个线程执行了该段代码的一部分后,
会被另一个线程抢走时间片又去执行这段代码,并修改其中变量。
当原线程再次回来继续运行时,其实里面的变量已经被别人改动了但它却不知,最后会导致错误。
这种线程就是不安全的。而安全的线程在执行一段代码时,只要没执行完,
其他线程就不能来执行这段代码或修改变量知道它执行完。

那么

多个线程间时如何通信的呢?

这里就涉及到另一套机制handler、looper、MessageQueue机制,在本人另一篇文章中有生动详细的描述,见Handler实现机制
简单来说,每个线程拥有上面这三个东西,如线程A想发送消息给线程B,那么在线程A中调用线程B的handler来发送消息,消息会自动发送线程B的消息队列中,同时线程B的looper在不断遍历这个队列,如果发现有新消息就会自动去处理。

常见问题:

如何避免发生ANR(Application Not Response)现象呢?

尽量在子线程中执行耗时操作(如网络请求或数据库读取等)或者资源开销较大的操作,当子线程执行完毕后,利用handler通知主线程做出更新。

6、内存管理机制

前面提到了,每个应用程序运行在独立的进程中(实际是一个独立的Dalvik进程),而且系统会为它分配固定的内存。在内存使用中常常会发生内存泄漏(memory leak)和内存溢出(OOM),其中,内存溢出非常严重,往往会导致程序崩溃。所以,合理有效的管理内存非常的重要。
详细的管理(包括面试重点:图片缓存机制)均可以参考本人另一篇文章:Android之内存管理及优化

此处不再赘述。

只说一下三种图片缓存思路:

  • LRU算法:设置缓存图片最大数量,当图片数量超过最大值久删除使用较少的图片,从而优化内存。
  • FTU算法:设置图片的缓存时限,从最后一次使用算起,当达到时限即删除。
  • FMU算法:设置固定大小的缓存空间,当达到空间限制后删除最大尺寸的图片。

快速了解Android重要机制的更多相关文章

  1. Android随笔之——Android广播机制Broadcast详解

    在Android中,有一些操作完成以后,会发送广播,比如说发出一条短信,或打出一个电话,如果某个程序接收了这个广播,就会做相应的处理.这个广播跟我们传统意义中的电台广播有些相似之处.之所以叫做广播,就 ...

  2. 理解Android安全机制

    本文从Android系统架构着手,分析Android的安全机制以SE Android,最后给出一些Android安全现状和常见的安全解决方案. 1.Android系统架构 Android采用分层的系统 ...

  3. Android渲染机制和丢帧分析

    http://blog.csdn.net/bd_zengxinxin/article/details/52525781 自己编写App的时候,有时会感觉界面卡顿,尤其是自定义View的时候,大多数是因 ...

  4. Android内存机制分析1——了解Android堆和栈

    //----------------------------------------------------------------------------------- Android内存机制分析1 ...

  5. Android反射机制实现与原理

    本文介绍Android反射机制实现与原理,在介绍之前,要和Java进行比较,所以先看下Java中的反射相关知识: 一.反射的概念及在Java中的类反射 反射主要是指程序可以访问.检测和修改它本身状态或 ...

  6. 巧用第三方快速开发Android App 热门第三方SDK及框架

    巧用第三方快速开发Android App 热门第三方SDK及框架 历经大半年的时间,终于是把这门课程给录制出来了,也就在今天,正式在慕课网上上线了 项目地址:巧用第三方快速开发Android App ...

  7. Android群英传笔记——第六章:Android绘图机制与处理技巧

    Android群英传笔记--第六章:Android绘图机制与处理技巧 一直在情调,时间都是可以自己调节的,不然世界上哪有这么多牛X的人 今天就开始读第六章了,算日子也刚好一个月了,一个月就读一半,这效 ...

  8. 【腾讯Bugly干货分享】经典随机Crash之二:Android消息机制

    本文作者:鲁可--腾讯SNG专项测试组 测试工程师 背景 承上经典随机Crash之一:线程安全 问题的模型 好几次灰度top1.top2 Crash发生场景:在很平常.频繁的使用页面,打开一个界面,马 ...

  9. Android内存机制分析2——分析APP内存使用情况

    上面一篇文章说了Android应用运行在dalvik里面分配的堆和栈内存区别,以及程序中什么代码会在哪里运行.今天主要是讲解一下Android里面如何分析我们程序内存使用情况.以便后续可以分析我们程序 ...

随机推荐

  1. Python常用函数记录

    Python常用函数/方法记录 一. Python的random模块: 导入模块: import random 1. random()方法: 如上如可知该函数返回一个[0,1)(左闭右开)的一个随机的 ...

  2. HTTP 响应时发生错误。这可能是由于服务终结点绑定未使用 HTTP 协议造成的。这还可能是由于服务器中止了 HTTP 请求上下文(可能由于服务关闭)所致。

    第一种:无法序列化 DataTable.未设置 DataTable 名称. 第二种: 排除过程如下: 1.用WCF调试状态下的客户端调用ESB的Publish方法调用成功,证明ESB的推送是没有问题的 ...

  3. PAT (Basic Level) Practice 1006 换个格式输出整数

    个人练习 让我们用字母B来表示“百”.字母S表示“十”,用“12...n”来表示个位数字n(&lt10),换个格式来输出任一个不超过3位的正整数.例如234应该被输出为BBSSS1234,因为 ...

  4. 遗传算法 | Java版GA_TSP(我的第一个Java程序)

    嗯哼,第一次写博客,准确说是第一次通过文字的方式记录自己的工作,闲话少叙,技术汪的博客就该直奔技术主题(关于排版问题,会在不断写博客的过程中慢慢学习,先将就着用吧,重在技术嘛~~~). 遗传算法(Ge ...

  5. 笔记-git-.gitignore

    笔记-git-.gitignore 1.      git忽略文件 有的文件不需要提交到公共仓库中,为此git提供了三种实现方式. gitignore文件 在项目的设置中指定排除文件 定义全局.git ...

  6. CodeForces 785C Anton and Fairy Tale 二分

    题意: 有一个谷仓容量为\(n\),谷仓第一天是满的,然后每天都发生这两件事: 往谷仓中放\(m\)个谷子,多出来的忽略掉 第\(i\)天来\(i\)只麻雀,吃掉\(i\)个谷子 求多少天后谷仓会空 ...

  7. 学习网络请求返回json对应的model

    原来泛型可以这样用: 网络返回基类,返回一个code,msg,body,其中body不确定,所以,我们把它写成泛型 import org.json.JSONObject; /** * 网络请求的基类 ...

  8. Java中的初始化详细解析

    今天所要详细讲解的是Java中的初始化,也就是new对象的过程中,其程序的行走流程. 先说没有静态成员变量和静态代码块的情况. public class NormalInit { public sta ...

  9. centos 7 安装codeblocks

    CentOS7安装Code::Blocks 在CentOS7上安装Codelocks的过程. 1.安装gcc,需要c和c++两部分,默认安装下,CentOS不安装编译器的,在终端输入以下命令即可yum ...

  10. hnust 原石法阵

    问题 F: 原石法阵 时间限制: 1 Sec  内存限制: 128 MB提交: 1098  解决: 161[提交][状态][讨论版] 题目描述 WZH有一个由原石构成的n阶三角形魔法阵,三角形魔法阵如 ...