Mxn 一日三省吾身,续之或者改之

Java类型信息rtti

RTTI Run-Time Type Infomation(运行时类型信息),在Java运行时,RTTI维护类的相关信息,识别类和对象的信息。 多态(polymorphism)是基于RTTI实现的。RTTI的功能主要是由Class类实现的。

严格的说,反射也是一种形式的RTTI,不过,一般的文档资料中把RTTI和反射分开,因为一般的,大家认为RTTI指的是传统的RTTI ,通过继承和多态来实现,在运行时通过调用超类的方法来实现具体的功能(超类会自动实例化为子类,或使用instance of)。

传统的RTTI有3种实现方式:

1.向上转型或向下转型(upcasting and downcasting),在java中,向下转型(父类转成子类)需要强制类型转换。

2.Class对象(用了Class对象,不代表就是反射,如果只是用Class对象cast成指定的类,那就还是传统的RTTI)。

3.instanceof或isInstance()。

下载阅读android源码笔记

这几天下载编译了android源码,下面是一些笔记。

准备工作

1.安装JDK和Xcode, 一般android开发人员的mac电脑上这两样应该是必备的吧。我的JDK是1.7版本,没试过1.8是否可行。 安装Xcode是因为需要里面Command Line Tools的。从Yosemite开始,Command Line Tools可以单独安装,无需像之前一样 必须先安装Xcode才能安装Command Line Tools。但是直接装Xcode是最方便的,避免后续的其他问题。

2.准备50G的硬盘空间,由于我的mac空间不足50G,我也不想浪费大量的空间,所以我直接下载到移动硬盘上,用mac的磁盘工具对 移动硬盘分区,选择格式的时候选择mac OS扩展(区分大小写)。

Java中的volatile关键字

Java 语言中的 volatile 变量可以被看作是一种 “程度较轻的 synchronized”;与 synchronized 块相比,volatile 变量所需的编码较少, 并且运行时开销也较少,但是它所能实现的功能也仅是 synchronized 的一部分。

当volatile用于一个作用域时,Java保证如下:

1.(适用于Java所有版本)读和写一个volatile变量有全局的排序。也就是说每个线程访问一个volatile作用域时会在继续执行之前读取它的当前值, 而不是(可能)使用一个缓存的值。(但是并不保证经常读写volatile作用域时读和写的相对顺序,也就是说通常这并不是有用的线程构建)。

2.(适用于Java5及其之后的版本)volatile的读和写建立了一个happens-before关系,类似于申请和释放一个互斥锁。

使用volatile会比使用锁更快,但是在一些情况下它不能工作。volatile使用范围在Java5中得到了扩展,特别是双重检查锁定现在能够正确工作。 volatile可以用在任何变量前面,但不能用于final变量前面,因为final型的变量是禁止修改的。也不存在线程安全的问题。

了解volatile关键字关键字之前,首先了解下原子操作和happens-before关系的相关概念。

原子操作

原子(atom)本意是“不能被进一步分割的最小粒子”,而原子操作(atomic operation)意为”不可被中断的一个或一系列操作” 。 原子操作在操作完毕之前不会线程调度器中断。在Java中,对除了long和double之外的基本类型的简单操作都具有原子性。 简单操作就是赋值或者return。比如”a = 1;“和 “return a;”这样的操作都具有原子性。但是在Java中,类似“a += b”这样的操作不具有原子性, 所以如果方法不是同步的就会出现难以预料的结果。在某些JVM中”a += b”可能要经过这样三个步骤: 1.取出a和b。2.计算a+b。3.将计算结果写入内存。

如果有两个线程t1,t2在进行这样的操作。t1在第二步做完之后还没来得及把数据写回内存就被线程调度器中断了,于是t2开始执行, t2执行完毕后t1又把没有完成的第三步做完。这个时候就出现了错误,相当于t2的计算结果被无视掉了。 类似的,像“a++”这样的操作也都不具有原子性。所以在多线程的环境下一定要记得进行同步操作。

要搞清楚这个问题,首先应该明白计算机内部都做什么了。比如做了一个i++操作,计算机内部做了三次处理:读取-修改-写入。 同样,对于一个long型数据,做了个赋值操作,在32系统下需要经过两步才能完成,先修改低32位,然后修改高32位。

假想一下,当将以上的操作放到一个多线程环境下操作时候,有可能出现的问题,是这些步骤执行了一部分,而另外一个线程就已经引用了变量值, 这样就导致了读取脏数据的问题。

理解快速排序算法

快速排序在平均状况下,排序n个项目要Ο(n log n)次比较。在最坏状况下则需要Ο(n^2)次比较,但这种状况并不常见。事实上,快速排序通常明显比 其他Ο(n log n)算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地被实现出来。

快速排序使用分治法(Divide and conquer)策略来把一个序列(list)分为两个子序列(sub-lists)。

步骤为:

1.从数列中挑出一个元素,称为”基准”(pivot), 2.重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区结束之后,该基准就处于数列的中间位置。这个称为分区(partition )操作。 3.递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。

效果图如下:

Surfaceview浅析

在一般情况下,应用程序的View都是在相同的GUI线程中绘制的。这个主应用程序线程同时也用来处理所有的用户交互(例如按钮单击或者文本输入)。 对于一个View的onDraw()方法,不能够满足将其移动到后台线程中去。因为从后台线程修改一个GUI元素会被显式地禁止的。 当需要快速地更新View的UI,或者当前渲染代码阻塞GUI线程的时间过长的时候,SurfaceView就是解决上述问题的最佳选择。 SurfaceView封装了一个Surface对象,而不是Canvas。因为Surface可以使用后台线程绘制。对于那些资源敏感的操作, 或者那些要求快速更新或者高速帧率的地方,例如使用3D图形,创建游戏,或者实时预览摄像头,这一点特别有用。

下面分别介绍Surface相关的几个类:

Surface

Surface对应了一块屏幕缓冲区,每个window对应一个Surface,任何View都要画在Surface的Canvas上,传统的view共享一块屏幕缓冲区, 所有的绘制必须在UI线程中进行。 关于Surface有如下特点:

1、通过Surface(因为Surface是句柄)就可以获得原生缓冲器以及其中的内容. 2、原始缓冲区(a raw buffer)是用于保存当前窗口的像素数据的. 3、Surface中有一个Canvas成员,专门用于画图的.

可以认为Android中的Surface就是一个用来画图形(graphics)或图像(image)的地方。通常画图是在一个Canvas对象上面进行的, 由此,可以推知一个Surface对象中应该包含有一个Canvas(画布)对象。

SurfaceView

SurfaceView继承自View,但是SurfaceView却有自己的Surface,SurfaceView的源码:

 
if (mWindow == null) {    
         mWindow = new MyWindow(this);    
         mLayout.type = mWindowType;    
         mLayout.gravity = Gravity.LEFT|Gravity.TOP;    
         mSession.addWithoutInputChannel(mWindow, mWindow.mSeq, mLayout,    
         mVisible ? VISIBLE : GONE, mContentInsets);     
   }