做了一年多的android应用开发,准备换个工作环境,结果在面试中却成了一个典型的面霸,两周的十个工作日里,笔试+面试了很多家公司,有得也有失。在这些面试中学到了不少东西!下面把我的android面试经历中被问到的一些常见的问题给大家分享一下,以后有些常见的问题会及时更新。有些不足和不称意的地方请大家多多指教。网上有的一般比较分散不是很全不容易记,看起来也比较浪费时间。这里简单整理一下和大家来分享免得以后走弯路。
首先,面试的过程中,问到的一些问题如果不会的话,面试完了记得一定要查清楚(至少面试再被问到的时候能回答个大概),因为同一个问题被下家问的的可能性非常大,面试多了问题很多都是共性的。
如果你简历上写了做过javaWeb相关的项目,面试官肯定是要先问一些javaWeb相关的知识,这里大体先说一下java基础知识面试常被问到的相关题目。之后再具体介绍一下android的相关题目:
面向对象的特征:封装 继承 多态。
多态的两种表现形式:重载和重写。
重载:是发生在同一类中,具有相同的方法名,主要是看参数的个数,类型,顺序不同实现方法的重载的,返回值的类型可以不同。
重写:是发生在两个类中(父类和子类),具有相同的方法名,主要看方法中参数,个数,类型必须相同,返回值的类型必须相同。
常见的排序算法(面试的过程中,面试官可能会当面让你写一种排序算法,从大到小或从小到大。当时我就用的冒泡排序排序写的)列举几种常见的排序算法(从小到大排序)具体代码大家可以百度在此不一一列举
2.1 冒泡法排序 原理:第一轮冒泡比较把最大值放在了最后,第二轮比较次大又放在了最后然后比较n - 1 轮(可以形象的比喻近视眼只认识相邻的)
2.2 选择法排序 原理:类似于排队时体育老师挨个敲把最小的放在最前面
2.3 插入法排序 原理:类似于摸牌
2.4 二分查找 前提是已排序的数组中查找
int 和 integer的区别
Java 提供两种不同的类型:引用类型和原始类型(或内置类型)。Int是java 的原始数据类型,Integer是java为int提供的封装类。Java为每个原始类型提供了封装类。
原始类型封装类
booleanBoolean
charCharacter
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
引用类型和原始类型的行为完全不同,并且它们具有不同的语义。引用类型和原始类型具有不同的特征和用法,它们包括:大小和速度问题,这种类型以哪种类型的数据结构存储,当引用类型和原始类型用作某个类的实例数据时所指定的缺省值。对象引用实例变量的缺省值为 null,而原始类型实例变量的缺省值与它们的类型有关。
String StringBuffer StringBuilder的区别(笔试中常见)
String(大姐,出生于JDK1.0时代) 不可变字符序列
StringBuffer(二姐,出生于JDK1.0时代) 线程安全的可变字符序列,里面的 方法是同步的
StringBuilder(小妹,出生于JDK1.5时代) 非线程安全的可变字符序列 ,里 面的方法是不同步的
list列表和set列表存储特点等(面试时候可以说是必问的,面试官可能会问你比较熟悉的一种存储方式简单介绍一下存储过程和原理)
List存储特点:有序可重复;Set存储特点:无序不可重复
List中常见的ArrayList和LinkList
ArrayList:采用的是数组的形式来保存对象的,这种方式将对象放在连续的位置上,所以最大的缺点就是插入删除时非常麻烦。
LinkList:采用的是将对象放在独立的空间中,而且在每个空间中还保存下一个链接的索引,但缺点是查找非常麻烦,要从第一个索引开始。
Set存储 :hashSet 无序不可重复无下标;TreeSet 有序不可重复无下标
抽象类和接口的区别
6.1 首先根据java继承的特征只支持单继承,不允许多继承,一个子类只能有一个父类。所以一个类只能继承一个抽象类,而一个类可以实现多个接口
6.2 抽象类中可以有各种类型的变量和方法而接口中的成员变量只能是public static final类型的
6.3 抽象类中可以有静态代码块和静态方法;接口中不能有静态代码块和静态方法
6.4 抽象类中可以含有抽象方法和非抽象的方法并且可以提供成员方法的实现细节,含有抽象方法的类肯定是抽象类,而接口中都是抽象的方法
java四种引用类型(问到的可能性也很大,面试官也可能会问你怎样理解gc垃圾回收机制,当时我就是被这么问到的,还好没有被问懵为未自己的机智回答点个赞哈哈)
7.1 强引用(必不可少) 垃圾回收器绝对不会回收它。如Object obj = new Object(); obj便是内存不足时,java虚拟机宁愿抛出OutofMemorryError错误导致程序崩溃异常终止,也 不会回收强引用的对象。
7.2 软引用(可有可无)如果内存空间足够,垃圾回收就不会回收它,如果内存空间不足了,就会回收这些对象的内存
7.3 弱引用(可有可无)垃圾回收器一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存,当发生GC的时候,弱引用的对象总是被回收
7.4 虚引用 当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前把这个虚引用加入到与之前关联的引用队列中。与弱引用不同点就是虚引用必须和引用队列联合使用。
hashMap和hashTable的区别
8.1 hashMap 是map接口的一个实现类; hashTable是Dictionary的子类
8.2 hashMap中的方法是非同步的(多线程中需要同步处理);hashTable的方法是同步的(多线程应用程序中不用专门的操作就可以安全的使用)
8.3 hashMap允许null键和null值;
比较:HashMap比HashTable功能更多,而且它不是基于一些陈旧的类所以一般情况下HashMap优于HashTable
什么是不可变对象(immutable object),不可变对象有什么好处,在什么情况下应该用,或者更具体一些,Java的String类为什么要设成immutable类型?
不可变对象,顾名思义就是创建后不可以改变的对象,典型的例子就是Java中的String类,
String s = “ABC”;
s.toLowerCase();
.toLowerCase()并没有改变“ABC”的值,而是创建了一个新的String类”abc”,然后将新的实例的指向变量s。
相对于可变对象,不可变对象有很多优势。
1)不可变对象可以提高String Pool的效率和安全性。如果你知道一个对象是不可变的,那么需要拷贝这个对象的内容时,就不用复制它的本身而只是复制它的地址,复制地址( 通常一个指针的大小)需要很小的内存效率也很高。对于同时引用这个“ABC”的其他变量也不会造成影响。
2)不可变对象对于多线程是安全的,因为在多线程同时进行的情况下,一个可变对象的值很可能被其他进程改变,这样会造成不可预期的结果,而使用不 可变对象就可以避免这种情况。当然也有其他方面原因,但是最Java把String设成immutable最大的原因应该就是效率和安全的。
单例种类和表现形式以及各自优缺点比较
单例模式有以下特点:
1、单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。
10.1 饿汉式单例类
//饿汉式单例类.在类初始化时,已经自行实例化
public class Singleton1{
//私有的默认构造子
private Singleton1(){}
//已经自行实例化
private static final Singleton1 single=new Singleton1();
//静态工厂方法
public static Singleton1 getInstance(){
return single;
}
}
特点:此类加载时就初始化单例对象,较大时会影响系统加载速度
10.2 懒汉式单例类
//懒汉式单例类.在第一次调用的时候实例化
public class Singleton2{
//私有的默认构造子
private Singleton2(){}
//注意,这里没有final
private static Singleton2 single=null;
//静态工厂方法
public synchronized static Singleton2 getInstance(){
if(single==null){
single=new Singleton2();
}
return single;
}
}
特点:只有访问到单例对象的时候才去检查和实例化单例对象,多线程访问
需要加同步锁影响访问效率
10.3 使用静态内部类作为Singleton容器(登记式单例类)
public class Singleton{
private Singleton(){ }
private static class SingletonHolder{
// 私有内部类在Singleton加载时不初始化
private static Singleton instance = new Singleton();
}
public static Singleton getInstance(){
return SingletonHolder.instance;
}
}
特点:能延迟加载,又能保证线程安全 原理是直接用classLoader(jvm类加载机制) 进行异步加锁的问题,并减少了内存消耗保证了初始化instance时只有一个线程,所以是线程安全的
(二)接下来总结一下Android原生相关的面试题:
handler机制的原理
andriod提供了 Handler 和 Looper 来满足线程间的通信。Handler 先进先出原则。Looper类用来管理特定线程内对象之间的消息交换(Message Exchange)。
1)Looper: 一个线程可以产生一个Looper对象,由它来管理此线程里的Message Queue(消息队列)。
2)Handler: 你可以构造Handler对象来与Looper沟通,以便push新消息到Message Queue里;或者接收Looper从Message Queue取出)所送来的消息。
3) Message Queue(消息队列):用来存放线程放入的消息。
4)线程:UI thread 通常就是main thread,而Android启动程序时会替它建立一个Message Queue
Android四大组件(重点看下广播和服务)
①activity 提供用户界面 用于与用户交互的组件,(活动窗体)它需要为保持各界面的状态,做很多持久化的事情,妥善管理生命周期以及一些跳转逻辑
②content Provider
为应用程序之间访问提供的接口的组件,实现数据共享,结构化数据集合,以表的形式对外提供数据,可以像数据库一样记性选择排序
③BroadCastReceiver (广播)
采用异步机制完成组件之间消息的传递,异步是指广播的发送方将消息标记后发出,不需要得到对方的回应,可以继续做自己的操作
默认情况下,所有的组件都有接收广播的能力,组件想要接收广播就注册与发送方一致的标记
包括普通广播和有序广播:
发送有序广播:sendOrderedBroadCast(…);
有序广播可以进行应用程序之间传递消息,可以根据manifest文件中注册的优先级的高低判断接收的顺序。
无序广播:sendBroadCast();
实现过程:
创建一个类继承BroadCastReceiver,重写其中的onReceiver()方法,进行接收广播之后的操作。
广播 的生命周期注册方式: 1. 程序代码中动态注册 退出时要取消注册。特点:关掉后,广播就失效了
2. 清单文件中静态注册。 特点:无需关心广播接收器是否关闭,广播触发时,即使退出应用也会对它起作用
④server(服务)
不需要提供用户界面,在后台运行服务于activity,执行长时间耗时操作的组件
3. 网络存储方式
3.1 使用SharedPreferences存储数据
适用范围:保存少量的数据,且这些数据的格式非常简单:字符串型、基本类型的值。比如应用程序的各种配置信息(如是否打开音效、是否使用震动效果、小游戏的玩家积分等),解锁口 令密码等
3.2 文件存储数据
核心原理: Context提供了两个方法来打开数据文件里的文件IO流 ,FileInputStream openFileInput(String name); FileOutputStream(String name , int mode),这两个方法第一个参数 用于指定文件名,第二个参数指定打开文件的模式。
3.3 SQLite存储数据
SQLite是轻量级嵌入式数据库引擎,它支持 SQL 语言,并且只利用很少的内存就有很好的性能。
3.4 网络存储
可以调用webService返回的数据或是解析http协议实现网络数据交互等等
3.5 ContentProvider
为应用程序之间访问提供的接口组件,实现数据共享结构化数据集合,以表的形式对外提供数据,可以像数据库一样选择排序
4. 服务如何启用Service,如何停用Service。Android中的service类似于windows中的service,service一般没有用户操作界面,它运行于系统中不容易被用户发觉,
可以使用它开发如监控之类的程序。
首先服务的特点:1 没有界面用户不可见 2 程序在后台运行做耗时操作
服务分三类:startService() bindService() intentService
一。步骤
第一步:继承Service类
public class SMSService extends Service { }
第二步:在AndroidManifest.xml文件中的节点里对服务进行配置:
二。Context.startService()和Context.bindService
服务不能自己运行,需要通过调用Context.startService()或Context.bindService()方法启动服务。这两个方法都可
以启动Service,但是它们的使用场合有所不同。
4.1 使用startService()方法启用服务,调用者与服务之间没有关连,即使调用者退出了,服务仍然运行。
使用bindService()方法启用服务,调用者与服务绑定在了一起,调用者一旦退出,服务也就终止。
4.2 采用Context.startService()方法启动服务,在服务未被创建时,系统会先调用服务的onCreate()方法,
接着调用onStart()方法。如果调用startService()方法前服务已经被创建,多次调用startService()方法并
不会导致多次创建服务,但会导致多次调用onStart()方法。
采用startService()方法启动的服务,只能调用Context.stopService()方法结束服务,服务结束时会调用
onDestroy()方法。
4.3 采用Context.bindService()方法启动服务,在服务未被创建时,系统会先调用服务的onCreate()方法,
接着调用onBind()方法。这个时候调用者和服务绑定在一起,调用者退出了,系统就会先调用服务的onUnbind()方法,
。接着调用onDestroy()方法。如果调用bindService()方法前服务已经被绑定,多次调用bindService()方法并不会
导致多次创建服务及绑定(也就是说onCreate()和onBind()方法并不会被多次调用)。如果调用者希望与正在绑定的服务
解除绑定,可以调用unbindService()方法,调用该方法也会导致系统调用服务的onUnbind()–>onDestroy()方法。
三。Service的生命周期
4.4 Service常用生命周期回调方法如下:
onCreate() 该方法在服务被创建时调用,该方法只会被调用一次,无论调用多少次startService()或bindService()方法,
服务也只被创建一次。 onDestroy()该方法在服务被终止时调用。
4.5 Context.startService()启动Service有关的生命周期方法
onStart() 只有采用Context.startService()方法启动服务时才会回调该方法。该方法在服务开始运行时被调用。
多次调用startService()方法尽管不会多次创建服务,但onStart()方法会被多次调用。
4.6 Context.bindService()启动Service有关的生命周期方法
onBind()只有采用Context.bindService()方法启动服务时才会回调该方法。该方法在调用者与服务绑定时被调用,
当调用者与服务已经绑定,多次调用Context.bindService()方法并不会导致该方法被多次调用。
onUnbind()只有采用Context.bindService()方法启动服务时才会回调该方法。该方法在调用者与服务解除绑定时被调用。
备注:
4.7 采用startService()启动服务
Intent intent =new Intent(DemoActivity.this, DemoService.class);
startService(intent);
4.8 Context.bindService()启动
Intent intent =new Intent(DemoActivity.this, DemoService.class);
bindService(intent, conn, Context.BIND_AUTO_CREATE);
//unbindService(conn);//解除绑定
4.9 intentService()
大部分的service不需要同时处理多个请求(处理多个请求是一个比较危险的多线程场景)这样的情况下最好使用intentService类去实现
原因:intentService会直接创建默认的工作线程,在服务中无须再自己写线程,自带一个重写的onhandleIntent()方法,该方法对意图的处理是按照
是不会阻塞别的事情,当处理完所有intent后该方法会自动调用stopself()所以不需要手动调用。
5. 网络处理Volley和okHttp为什么结合使用
volley重点在于request的队列的管理,其他的地方都很不强,你可以通
都是volley+okhttp接合使用,不过如果没有特别多的网络性能要求(一般而言,比如你们用户量没有超百万)还是默认使用volley那套网络连接方式就够了。
相关链接:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0720/3209.html
6. 自定义View是如何绘制到界面上的
自定义View包含文字,几何图形,Bitmap
继承view
6.1 绘制文字 => onDraw()
Paint paint = new Paint(); Canvas.drawText("text",0,50,paint); 0和
50分别指横坐标X和纵坐标Y
可以通过paint设置文字颜色,字体大小~~~
6.2 绘制几何图形
6.2.1 直线 canvas.drawLine(x,x,y,y,paint) 参数分别为start end
start end.
6.2.2 矩形 canvas.drawRect(left,top,right,bottom,paint)
6.2.3 圆形 canvas.drawCircle(cx,cy,radius,paint)
6.3 绘制图像
canvas.drawBitmap(bitmap, left,top,paint)
bitmap 可在构造方法中动态产生 如bitmap = BitmapFactory.decodeResource(getResource(), R.drawable.ic_launcher)
6.4 在XML中定义样式和属性对显示效果的影响(位置,颜色,滚动~~~)
6.4.1 res-->value-->attires.xml
6.4.2 在布局中引入命名空间 nt="http:...res/conn + 项目包名"
6.4.3 nt: num="3";
6.4.4 代码中解析:TypedArray对象 Ta = context.obtainStyleAttribute(...)
获取样式属性 解析后要释放Ta.recycle()
详细了解可以点击此链接 自定义View,自定义属性(带进度的圆形进度条):http://www.cnblogs.com/linlf03/p/3577828.html
6.5 绘制流程以及自定义View的主要步骤:
我们知道,不管是自定义View还是系统提供的TextView这些,它们都必须放置在LinearLayout等一些ViewGroup中,因此理论上我们可以很好的 理onMeasure(),onLayout(),onDraw()这三个函数:1.View组件及子组建本身大小多少,这由onMeasure()决定;2.View在ViewGroup中的位置如何,这由onLayout()决定;3.绘制View的内容,onDraw()定义了如何绘制这个View;
invalidate():调用该方法,可以强制重新绘制界面
requestLayout():当前布局大小改变时,可以调用该方法,刷新布局。
相关详细链接:http://blog.csdn.net/chziroy/article/details/43069975
7. Android中跨进程通信
7.1 隐式意图
7.2 广播
7.3 contentProvider
7.4 service(服务)
7.5 aidl跨进程通信 之前整理过的一博客做了详细的介绍: http://blog.csdn.net/yshr1991/article/details/51568752
8. intent和intentfilter区别
是一个Service或者是启动一个BroadcastReceiver,都可以使用统一的
Intent这种”启动意图”,这样就使用了一致的编程模型,也是一种高层
次的”解耦”。
8.1. 从名字来看IntentFilter 比Intent 多了个Filter 即后者比前者多了
个筛选作用筛选条件: action、data和category
力,所以一般不会在java代码中设置,而是在应用的manifest文件中作为元素的方式声明。 一个例外是,为broadcast receiver
注册动态的filter,可以调用Context.registerReceiver()方法,通过直接
实例化IntentFilter对象创建
9. 事件分发传递
方法主要涉及三个方法:
9.1 分发事件 boolean dispatchTouchEvent(MotionEvent e)因为
控件 所有的事件都是通过 Activity 的 dispatchTouchEvent 进行分
发的;控件直接从 Activity 获取到事件,这个事件会首先被传递到控
件的dispatchTouchEvent 方法
dispatchTouchEvent()无论返回true还是false,事件都不会进行分发,
愿望,能否分发成功还需要经过事件拦截的审核,true表示自身本层消费。
法进行消费,同时事件会停止向下传递;
如果 return false,事件分发分为两种情况:
如果当前 View 获取的事件直接来自 Activity,则会将事件返回给
Activity 的 onTouchEvent 进行消费;View 的 onTouchEvent 进
行消费
9.2 拦截事件 boolean onInterceptTouchEvent(MotionEvent e)
true 则拦截交由本层view的onTouchEvent()处理
false 不拦截 发给子view 由子view的dispatchTouchEvent()
处理进行
事件分发
如果 onInterceptTouchEvent 返回 super.onInterceptTouchEvent(ev),事件默认会被拦截,并将拦截到的事件交由当前 View 的 onTouchEvent
进行处理。
9.3 处理事件 boolean onTouchEvent()
并且 onInterceptTouchEvent 返回 true 或返回、super.onInterceptTouchEvent(ev) 的情况下 onTouchEvent 会被调用
返回true或返回 super.onInterceptTouchEvent(ev) )。才可以交由本
层的onTouchEvent 的事件响应逻辑如下:
如果事件传递到当前 View 的 onTouchEvent 方法,而该方法返回了
false,那么这个事件会从当前 View 向上传递,并且都是由上层 View
的 onTouchEvent 来接收,如果传递到上面的 onTouchEvent 也返回
false,这个事件就会“消失”,而且接收不到下一次事件。
如果返回了 true 则会接收并消费该事件。
如果返回 super.onTouchEvent(ev) 默认处理事件的逻辑和返回 false
时相同。
相关详细链接: http://www.jianshu.com/p/e99b5e8bd67b
相关详细链接2(通过代码比较容易理解): http://www.cnblogs.com/sunzn/archive/2013/05/10/3064129.html
onTouch和onTouchEvent的区别链接:http://www.cnblogs.com/Claire6649/p/5947139.html
10. ListView的优化方案
10.1 在getView()方法中复用convertView尽可能的少创建view
10.2 定义一个viewHolder对象,用于缓存显示数据,减少
findViewById的次数
10.3 如果item显示很多,就要考虑分页加载,规定每页加载多少
条,每次请求加载多少条,用户拉到列表底部的时候,再去加载接
下来的条目。
11. Android中数据存储方式
11.1 文件 通过读取文件里的文件io流,SD卡等存储
11.2 SQLite数据库 一些增删查改的操作
11.3 网络 如解析http协议 实现网络数据交互 上传文件等等~~~
原理:基于XML存储的key-value键值对数据,通常一些简单的配置信息。
11.5 ContentProvider 为应用程序之间访问提供的接口组件,实
现数据共享,结构化数据集合,以表的形式对外提供数据可以像数
据库一样
12. Android中动画的使用(类别 特点和区别)
Android 3.0之前分两种:
12.1 tween动画(补间动画)
这种实现方式可以使视图组件 移动 缩放 旋转 和 淡入淡出四种动
画操作
12.2 Frame(帧)动画
传统的动画方法,通过顺序的播放排列好的图片来实现,类似于放
电影
Android 3.0之后系统提供了一种全新的动画模式:属性动画,可以
完全取代补间动画
补间动画缺陷:
1 对一个view对象进行操作,如果自定义view 使用canvas根据paint
坐标进行绘制只能用属性动画
2 补间动画只能实现移动 缩放 旋转 和 淡入淡出四种动画操作,如view
背景进行改变就用属性动画了
3 只能改变view的显示效果,不能改变属性,比如说,现在屏幕的左上
角有一个按钮,然后我们通过补间动画将它移动到了屏幕的右下角,现
在你可以去尝试点击一下这个按钮,点击事件是绝对不会触发的,因为
实际上这个按钮还是停留在屏幕的左上角,只不过补间动画将这个按钮
绘制到了屏幕的右下角而已。
属性动画的机制:通过对目标对象进行赋值,并修改其属性来实现
13. Android 四种启动模式以及应用场景
13.1 standard
默认模式,可以不用写配置。在这个模式下,每次激活Activity都会默认
创建一个新的Activity实例并放入栈中,可以有多个相同的实例,也允许
多个 相同Activity叠加。
应用场景:Activity可以多次入栈
13.2 singleTop
可以有多个实例,但是不允许多个相同Activity叠加。如果Activity在栈
顶的时候,启动相同的Activity,不会创建新的实例,而会调用其onNewIntent方法,否则会创建新的实例并放入栈顶,即使栈中存在该
Activity的实例,只要是不在栈顶都会创建新的实例
应用场景:栈顶存在则复用,不存在则创建;浏览器的书签界面,
接受通知启动的内容显示界面
13.3 singleTask
若栈中存在activity实例,就会重用该实例(调用其onNewIntent
方法),并将其上的实例移出栈,如果栈中不存在该实例,将会创
建新的实例放入栈中。
应用场景:如果一个activity创建需要占用系统大量的资源(如CPU
, 内存。。。) 就用singleTask;浏览器的Activity不管从多个浏览
器只会启动主界面一次,其余情况都会走onNewIntent方法并会清
空主界面上的其它界面
13.4 singleInstance
单一实例启动模式 Activity会运行在自己的任务栈中,并且这个任
务栈中只有一个实例存在,不允许其它activity在本任务栈中。
应用场景:如果activity在整个手机系统中只允许一个实例存在,
如电话拨打界面。
14. 内存泄露和内存溢出的区别
14.1 内存泄漏:向系统申请了一块内存空间使用new关键字 使
用完了没有及时释放掉程序时间越长占用内存越多最终崩溃
原因可能存在如下:
14.1.1 查询数据库时没有关闭游标cursor
14.1.2 构造Adapter时,没有复用convertView
14.1.3 bitmap使用完后,没有recycle()给释放掉
14.1.4 广播推出程序后,未取消注册
14.1.5 使用文件或访问网络资源时未关闭InputStrean/OutStream
14.2 内存溢出:你要求分配的内存超出了系统能给的,系统不能
提供满足需求的内存,还没有new就直接崩溃了。
Android笔记拓展知识链接
http://blog.csdn.net/aqi00/article/details/50012511
15.
Android中Java和JavaScript交互
https://droidyue.com/blog/2014/09/20/interaction-between-java-and-javascript-in-android/
// 交互三种方式的讲解和比较
http://blog.csdn.net/carson_ho/article/details/64904691
http://blog.csdn.net/Lennie_Z/article/details/75424032
//app中常涉及
http://mobile.51cto.com/aprogram-452011.htm
未完,待续~~~