亲爱的面试官,这个我可没看过!(Android 部分)

20,500 阅读42分钟

导读:Android面试中高频率出现的题都在这了。试题大部分从互联网收集,博主下了一番功夫进行梳理总结,难免有不足之处,还请见谅。这篇博客属于Androi,你够了!!!专题中的一篇,其余文章会陆续发表,第一时间会发布在本人Github上,敬请关注。这篇博客包括五个部分:热点,基础,进阶,性能优化,高级。后续会不断补充完善,希望能为小伙伴们找工作增加点自信😄。

热点

如何保证Service不被杀死

Android 进程不死从3个层面入手:

  • A.提供进程优先级,降低进程被杀死的概率
    方法一:监控手机锁屏解锁事件,在屏幕锁屏时启动1个像素的 Activity,在用户解锁时将 Activity 销毁掉。
    方法二:启动前台service。
    方法三:提升service优先级:
    在AndroidManifest.xml文件中对于intent-filter可以通过android:priority = "1000"这个属性设置最高优先级,1000是最高值,如果数字越小则优先级越低,同时适用于广播。
  • B. 在进程被杀死后,进行拉活
    方法一:注册高频率广播接收器,唤起进程。如网络变化,解锁屏幕,开机等
    方法二:双进程相互唤起。
    方法三:依靠系统唤起。
    方法四:onDestroy方法里重启service:service +broadcast 方式,就是当service走ondestory的时候,发送一个自定义的广播,当收到广播的时候,重新启动service;
  • C. 依靠第三方
    根据终端不同,在小米手机(包括 MIUI)接入小米推送、华为手机接入华为推送;其他手机可以考虑接入腾讯信鸽或极光推送与小米推送做 A/B Test。

参考博客:Android 进程保活招式大全

ButterKnife原理
ButterKnife对性能的影响很小,因为没有使用使用反射,而是使用的Annotation Processing Tool(APT),注解处理器,javac中用于编译时扫描和解析Java注解的工具。在编译阶段执行的,它的原理就是读入Java源代码,解析注解,然后生成新的Java代码。新生成的Java代码最后被编译成Java字节码,注解解析器不能改变读入的Java 类,比如不能加入或删除Java方法。

参考资料:
最新ButterKnife框架原理
Java Annotation 及几个常用开源项目注解原理简析
ButterKnife 简单原理实现

基础

Activity和Fragment生命周期有哪些?


屏幕快照 2016-09-19 下午8.10.27.png

横竖屏切换时候Activity的生命周期

不设置Activity的android:configChanges时,切屏会重新回掉各个生命周期,切横屏时会执行一次,切竖屏时会执行两次
设置Activity的android:configChanges=”orientation”时,切屏还是会调用各个生命周期,切换横竖屏只会执行一次
设置Activity的android:configChanges=”orientation |keyboardHidden”时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法

AsyncTask
源码解析
AsyncTask的缺陷和问题
关于线程池:asynctask对应的线程池ThreadPoolExecutor都是进程范围内共享的,都是static的,所以是asynctask控制着进程范围内所有的子类实例。由于这个限制的存在,当使用默认线程池时,如果线程数超过线程池的最大容量,线程池就会爆掉(3.0后默认串行执行,不会出现这个问题)。针对这种情况,可以尝试自定义线程池,配合asynctask使用。

关于默认线程池:核心线程池中最多有CPU_COUNT+1个,最多有CPU_COUNT*2+1个,线程等待队列的最大等待数为128,但是可以自定义线程池。线程池是由AsyncTask来管理的,线程池允许tasks并行运行,需要注意的是并发情况下数据的一致性问题,新数据可能会被老数据覆盖掉,类似volatile变量。所以希望tasks能够串行运行的话,使用SERIAL_EXECUTOR。

AsyncTask在不同的SDK版本中的区别:
调用AsyncTask的excute方法不能立即执行程序的原因分析及改善方案
通过查阅官方文档发现,AsyncTask首次引入时,异步任务是在一个独立的线程中顺序的执行,也就是说一次只能执行一个任务,不能并行的执行,从1.6开始,AsyncTask引入了线程池,支持同时执行5个异步任务,也就是说同时只能有5个线程运行,超过的线程只能等待,等待前面的线程某个执行完了才被调度和运行。换句话说,如果一个进程中的AsyncTask实例个数超过5个,那么假如前5个都运行很长时间的话,那么第6个只能等待机会了。这是AsyncTask的一个限制,而且对于2.3以前的版本无法解决。如果你的应用需要大量的后台线程去执行任务,那么你只能放弃使用AsyncTask,自己创建线程池来管理Thread。不得不说,虽然AsyncTask较Thread使用起来方便,但是它最多只能同时运行5个线程,这也大大局限了它的实力,你必须要小心设计你的应用,错开使用AsyncTask的时间,尽力做到分时,或者保证数量不会大于5个,否则就会遇到上次提到的问题。可能是Google意识到了AsyncTask的局限性了,从Android3.0开始对AsyncTask的API作出了一些调整:每次只启动一个线程执行一个任务,完成之后再执行第二个任务,也就是相当于只有一个后台线程在执行所提交的任务。

  • 1.生命周期
    很多开发者会认为一个在Activity中创建的AsyncTask会随着Activity的销毁而销毁。然而事实并非如此。AsyncTask会一直执行,直到doInBackground()方法执行完毕。然后,如果cancel(boolean)被调用,那么onCancelled(Result result)方法会被执行;否则,执行onPostExecute(Result result)方法。如果我们的Activity销毁之前,没有取消AsyncTask,这有可能让我们的AsyncTask崩溃(crash)。因为它想要处理的view已经不在了。所以,我们总是必须确保在销毁活动之前取消任务。总之,我们使用AsyncTask需要确保AsyncTask正确的取消。
  • 2.内存泄漏
    如果AsyncTask被声明为Activity的非静态的内部类,那么AsyncTask会保留一个对Activity的引用。如果Activity已经被销毁,AsyncTask的后台线程还在执行,它将继续在内存里保留这个引用,导致Activity无法被回收,引起内存泄漏。
  • 3.结果丢失
    屏幕旋转或Activity在后台被系统杀掉等情况会导致Activity的重新创建,之前运行的AsyncTask会持有一个之前Activity的引用,这个引用已经无效,这时调用onPostExecute()再去更新界面将不再生效。
  • 4.并行还是串行
    在Android1.6之前的版本,AsyncTask是串行的,在1.6至2.3的版本,改成了并行的。在2.3之后的版本又做了 修改,可以支持并行和串行,当想要串行执行时,直接执行execute()方法,如果需要执行executeOnExecutor(Executor)。

Acitivty的任务栈

使用android:launchMode="standard|singleInstance|single Task|singleTop"来控制Acivity任务栈。

任务栈是一种后进先出的结构。位于栈顶的Activity处于焦点状态,当按下back按钮的时候,栈内的Activity会一个一个的出栈,并且调用其onDestory()方法。如果栈内没有Activity,那么系统就会回收这个栈,每个APP默认只有一个栈,以APP的包名来命名.

  • standard : 标准模式,每次启动Activity都会创建一个新的Activity实例,并且将其压入任务栈栈顶,而不管这个Activity是否已经存在。Activity的启动三回调(onCreate()->onStart()->onResume())都会执行。
  • singleTop : 栈顶复用模式.这种模式下,如果新Activity已经位于任务栈的栈顶,那么此Activity不会被重新创建,所以它的启动三回调就不会执行,同时Activity的onNewIntent()方法会被回调.如果Activity已经存在但是不在栈顶,那么作用于standard模式一样.
  • singleTask: 栈内复用模式.创建这样的Activity的时候,系统会先确认它所需任务栈已经创建,否则先创建任务栈.然后放入Activity,如果栈中已经有一个Activity实例,那么这个Activity就会被调到栈顶,onNewIntent(),并且singleTask会清理在当前Activity上面的所有Activity.(clear top)
  • singleInstance : 加强版的singleTask模式,这种模式的Activity只能单独位于一个任务栈内,由于栈内复用的特性,后续请求均不会创建新的Activity,除非这个独特的任务栈被系统销毁了
    Activity的堆栈管理以ActivityRecord为单位,所有的ActivityRecord都放在一个List里面.可以认为一个ActivityRecord就是一个Activity栈

onSaveInstanceState() 与onRestoreIntanceState()
用户或者程序员主动去销毁一个Activity的时候不会掉用,其他情况都会调动,来保存界面信息。如代码中finish()或用户按下back,不会掉用。

android中进程的优先级?

  • 前台进程:即与用户正在交互的Activity或者Activity用到的Service等,如果系统内存不足时前台进程是最后被杀死的
  • 可见进程:可以是处于暂停状态(onPause)的Activity或者绑定在其上的Service,即被用户可见,但由于失去了焦点而不能与用户交互
  • 服务进程:其中运行着使用startService方法启动的Service,虽然不被用户可见,但是却是用户关心的,例如用户正在非音乐界面听的音乐或者正在非下载页面自己下载的文件等;当系统要空间运行前两者进程时才会被终止
  • 后台进程:其中运行着执行onStop方法而停止的程序,但是却不是用户当前关心的,例如后台挂着的QQ,这样的进程系统一旦没了有内存就首先被杀死
  • 空进程:不包含任何应用程序的程序组件的进程,这样的进程系统是一般不会让他存在的

Serializable和Parcelable
序列化,表示将一个对象转换成可存储或可传输的状态。序列化后的对象可以在网络上进行传输,也可以存储到本地。

  • Serializable(Java自带):
    Serializable是序列化的意思,表示将一个对象转换成可存储或可传输的状态。序列化后的对象可以在网络上进行传输,也可以存储到本地。
  • Parcelable(android 专用):
    除了Serializable之外,使用Parcelable也可以实现相同的效果,
    不过不同于将对象进行序列化,Parcelable方式的实现原理是将一个完整的对象进行分解,
    而分解后的每一部分都是Intent所支持的数据类型,这样也就实现传递对象的功能了。
  • tween 补间动画。通过指定View的初末状态和变化时间、方式,对View的内容完成一系列的图形变换来实现动画效果。 Alpha, Scale ,Translate, Rotate。
  • frame 帧动画 AnimationDrawable 控制 animation-list xml布局
  • PropertyAnimation 属性动画 3.0引入,属性动画核心思想是对值的变化。

属性动画
详解

Property Animation 动画有两个步聚:
1.计算属性值
2.为目标对象的属性设置属性值,即应用和刷新动画


valuecaculate.png


计算属性分为3个过程:

  • 过程一:计算已完成动画分数 elapsed fraction 为了执行一个动画,你需要创建一个 ValueAnimator,并且指定目标对象属性的开始、结束值和持续时间。在调用 start 后的整个动画过程中, ValueAnimator 会根据已经完成的动画时间计算得到一个 0 到 1 之间的分数,代表该动画的已完成动画百分比。0 表示 0%,1 表示 100%。
  • 过程二:计算插值(动画变化率)interpolated fraction 当 ValueAnimator 计算完已完成动画分数后,它会调用当前设置的 TimeInterpolator,去计算得到一个 interpolated(插值)分数,在计算过程中,已完成动画百分比会被加入到新的插值计算中。
  • 过程三:计算属性值 当插值分数计算完成后,ValueAnimator 会根据插值分数调用合适的 TypeEvaluator 去计算运动中的属性值。
    以上分析引入了两个概念:已完成动画分数(elapsed fraction)、插值分数( interpolated fraction )。

Android的数据存储形式

  • SQLite:SQLite是一个轻量级的数据库,支持基本的SQL语法,是常被采用的一种数据存储方式。 Android为此数据库提供了一个名为SQLiteDatabase的类,封装了一些操作数据库的api
  • SharedPreference: 以键值对的形势储存。其本质就是一个xml文件,常用于存储较简单的参数设置。
  • File: 即常说的文件(I/O)存储方法,常用语存储大数量的数据,但是缺点是更新数据将是一件困难的事情。
  • ContentProvider: Android系统中能实现所有应用程序共享的一种数据存储方式,由于数据通常在各应用间的是互相私密的,所以此存储方式较少使用,但是其又是必不可少的一种存储方式。例如音频,视频,图片和通讯录,一般都可以采用此种方式进行存储。每个Content Provider都会对外提供一个公共的URI(包装成Uri对象),如果应用程序有数据需要共享时,就需要使用Content Provider为这些数据定义一个URI,然后其他的应用程序就通过Content Provider传入这个URI来对数据进行操作。

Context相关

Activity和Service以及Application的Context是不一样的,Activity继承自ContextThemeWraper.其他的继承自ContextWrapper.
每一个Activity和Service以及Application的Context都是一个新的ContextImpl对象
getApplication()用来获取Application实例的,但是这个方法只有在Activity和Service中才能调用的到。那么也许在绝大多数情况下我们都是在Activity或者Service中使用Application的,但是如果在一些其它的场景,比如BroadcastReceiver中也想获得Application的实例,这时就可以借助getApplicationContext()方法,getApplicationContext()比getApplication()方法的作用域会更广一些,任何一个Context的实例,只要调用getApplicationContext()方法都可以拿到我们的Application对象。
创建Toast和对话框不可以用Application 的context,只能用Activity的context。
Context的数量等于Activity的个数 + Service的个数 + 1,这个1为Application

Android各版本新特性
Android5.0新特性

  • MaterialDesign设计风格
  • 支持多种设备
  • 支持64位ART虚拟机

Android6.0新特性

  • 大量漂亮流畅的动画
  • 支持快速充电的切换
  • 支持文件夹拖拽应用
  • 相机新增专业模式

Android7.0新特性

Json

  • JSON的全程是JavaScript Object Notation,也就是JavaScript 对象表示法
  • JSON是存储和交换文本信息的语法,类似XML,但是比XML更小、更快,更易解析
  • JSON是轻量级的文本数据交换格式,独立于语言,具有自我描述性,更易理解

对象可以包含多个名称/值对,比如:

{"name":"zhangsan" , "age":25}

使用谷歌的GSON包进行解析
在 Android Studio 里引入依赖:

compile 'com.google.code.gson:gson:2.7'

值得注意的是实体类中变量名称必须和json中的值名相同。
json1的解析
我们这里的实体类是Student.class

Gson gson = new Gson();
Student student = gson.fromJson(json1, Student.class);

json2的解析
我们可以解析成int数组,也可以解析成Integer的List。
解析成数组:

Gson gson = new Gson();
int[] ages = gson.fromJson(json2, int[].class);
解析成List:

Gson gson = new Gson();
List ages = gson.fromJson(json2, new TypeToken>(){}.getType);

json3的解析
同样可以解析成List或者数组,我们就直接解析成List.

Gson gson = new Gson();
List students = gson.fromJson(json3, new TypeToke>(){}.getType);

android中有哪几种解析xml的类,官方推荐哪种?以及它们的原理和区别

  • DOM解析

    优点:
    1.XML树在内存中完整存储,因此可以直接修改其数据和结构.
    2.可以通过该解析器随时访问XML树中的任何一个节点.
    3.DOM解析器的API在使用上也相对比较简单.
    缺点:如果XML文档体积比较大时,将文档读入内存是非常消耗系统资源的.
    使用场景:DOM 是用与平台和语言无关的方式表示 XML 文档的官方 W3C 标准.DOM 是以层次结构组织的节点的集合.这个层次结构允许开发人员在树中寻找特定信息.分析该结构通常需要加载整个文档和构造层次结构,然后才能进行任何工作.DOM是基于对象层次结构的.

  • SAX解析

    优点:
    SAX 对内存的要求比较低,因为它让开发人员自己来决定所要处理的标签.特别是当开发人员只需要处理文档中所包含的部分数据时,SAX 这种扩展能力得到了更好的体现.
    缺点:
    用SAX方式进行XML解析时,需要顺序执行,所以很难访问到同一文档中的不同数据.此外,在基于该方式的解析编码过程也相对复杂.
    使用场景:
    对于含有数据量十分巨大,而又不用对文档的所有数据进行遍历或者分析的时候,使用该方法十分有效.该方法不用将整个文档读入内存,而只需读取到程序所需的文档标签处即可.

  • Xmlpull解析

    android SDK提供了xmlpull api,xmlpull和sax类似,是基于流(stream)操作文件,然后根据节点事件回调开发者编写的处理程序.因为是基于流的处理,因此xmlpull和sax都比较节约内存资源,不会象dom那样要把所有节点以对橡树的形式展现在内存中.xmlpull比sax更简明,而且不需要扫描完整个流.

Jar和Aar的区别

Jar包里面只有代码,aar里面不光有代码还包括代码还包括资源文件,比如 drawable 文件,xml 资源文件。对于一些不常变动的 Android Library,我们可以直接引用 aar,加快编译速度

什么是三级缓存

  • 网络加载,不优先加载,速度慢,浪费流量
  • 本地缓存,次优先加载,速度快
  • 内存缓存,优先加载,速度最快

三级缓存原理

首次加载 Android App 时,肯定要通过网络交互来获取图片,之后我们可以将图片保存至本地SD卡和内存中
之后运行 App 时,优先访问内存中的图片缓存,若内存中没有,则加载本地SD卡中的图片
总之,只在初次访问新内容时,才通过网络获取图片资源

Android为每个应用程序分配的内存大小是多少
android程序内存一般限制在16M,也有的是24M

更新UI方式
Activity.runOnUiThread(Runnable)
View.post(Runnable),View.postDelay(Runnable,long)
Handler
AsyncTask

进阶

引起内存泄漏的情况

  • 对于使用了BraodcastReceiver,ContentObserver,File,游标 Cursor,Stream,Bitmap等资源的使用,应该在Activity销毁时及时关闭或者注销。
  • 静态内部类持有外部成员变量(或context):可以使用弱引用或使用ApplicationContext。
  • 内部类持有外部类引用,异步任务中,持有外部成员变量。
  • 集合中没用的对象没有及时remove。
  • 不用的对象及时释放,如使用完Bitmap后掉用recycle(),再赋null。
  • handler引起的内存泄漏,MessageQueue里的消息如果在activity销毁时没有处理完,就会引起内存的泄漏,可以使用弱引用解决。
  • 构造Adapter时,没有使用缓存的convertView。
  • 设置过的监听不用时,及时移除。如在Destroy时及时remove。尤其以addListener开头的,在Destroy中都需要remove。
  • activity泄漏可以使用LeakCanary。

Android 内存溢出解决方案(OOM) 整理总结

  • 在内存引用上做些处理,常用的有软引用、弱引用
  • 在内存中加载图片时直接在内存中作处理,如:边界压缩
  • 动态回收内存
  • 优化Dalvik虚拟机的堆内存分配
  • 自定义堆内存大小

Activity/Window/View三者的差别,fragment的特点

Activity像一个工匠(控制单元),Window像窗户(承载模型),View像窗花(显示视图) LayoutInflater像剪刀,Xml配置像窗花图纸。

在Activity中调用attach,创建了一个Window
创建的window是其子类PhoneWindow,在attach中创建PhoneWindow
在Activity中调用setContentView(R.layout.xxx)
其中实际上是调用的getWindow().setContentView()
调用PhoneWindow中的setContentView方法
创建ParentView: 
作为ViewGroup的子类,实际是创建的DecorView(作为FramLayout的子类)
将指定的R.layout.xxx进行填充
通过布局填充器进行填充【其中的parent指的就是DecorView】
调用到ViewGroup
调用ViewGroup的removeAllView(),先将所有的view移除掉
添加新的view:addView()

Fragment 特点
Fragment可以作为Activity界面的一部分组成出现;
可以在一个Activity中同时出现多个Fragment,并且一个Fragment也可以在多个Activity中使用;
在Activity运行过程中,可以添加、移除或者替换Fragment;
Fragment可以响应自己的输入事件,并且有自己的生命周期,它们的生命周期会受宿主Activity的生命周期影响。

JVM 和Dalvik虚拟机的区别

  • JVM:
    .java -> javac -> .class -> jar -> .jar
    架构: 堆和栈的架构.
  • DVM:
    .java -> javac -> .class -> dx.bat -> .dex
    架构: 寄存器(cpu上的一块高速缓存)

怎么考虑数据传输的安全性
如果应用对传输的数据没有任何安全措施,攻击者设置的钓鱼网络中更改DNS服务器。这台服务器可以获取用户信息,或充当中间人与原服务器交换数据。在SSL/TLS通信中,客户端通过数字证书判断服务器是否可信,并采用证书的公钥与服务器进行加密通信。

自定义View的相关方法
1.自定义属性
2.onLayout(Viewgroup)
3.onMesure
4.onDraw
5.交互:
onIntercepterTouchEvent()
onTouchEvent()

事件传递机制 详解
当手指触摸到屏幕时,系统就会调用相应View的onTouchEvent,并传入一系列的action。

dispatchTouchEvent的执行顺序为:

首先触发ACTIVITY的dispatchTouchEvent,然后触发ACTIVITY的onInterceptTouchEvent.
然后触发LAYOUT的dispatchTouchEvent,然后触发LAYOUT的onInterceptTouchEvent
这就解释了重写ViewGroup时必须调用super.dispatchTouchEvent();

(1)dispatchTouchEvent:

此方法一般用于初步处理事件,因为动作是由此分发,所以通常会调用super.dispatchTouchEvent。这样就会继续调用onInterceptTouchEvent,再由onInterceptTouchEvent决定事件流向。

(2)onInterceptTouchEvent:

若返回值为true事件会传递到自己的onTouchEvent();若返回值为false传递到下一个View的dispatchTouchEvent();

(3)onTouchEvent():

若返回值为true,事件由自己消耗,后续动作让其处理;若返回值为false,自己不消耗事件了,向上返回让其他的父View的onTouchEvent接受处理

三大方法关系的伪代码:如果当前View拦截事件,就交给自己的onTouchEvent去处理,否则就丢给子View继续走相同的流程。

public boolean dispatchTouchEvent(MotionEvent ev)
{
    boolean consume = false;
    if(onInterceptTouchEvent(ev))
    {
        consume = onTouchEvent(ev);
    }
    else
    {
        consume = child.dispatchTouchEvent(ev);
    }
    return consume;
}

onTouchEvent的传递:

当有多个层级的View时,在父层级允许的情况下,这个action会一直传递直到遇到最深层的View。所以touch事件最先调用的是最底层View的onTouchEvent,如果View的onTouchEvent接收到某个touch action并做了相应处理,最后有两种返回方式return true和return false;return true会告诉系统当前的View需要处理这次的touch事件,以后的系统发出的ACTION_MOVE,ACTION_UP还是需要继续监听并接收的,并且这次的action已经被处理掉了,父层的View是不可能触发onTouchEvent的了。所以每一个action最多只能有一个onTouchEvent接口返回true。如果返回false,便会通知系统,当前View不关心这一次的touch事件,此时这个action会传向父级,调用父级View的onTouchEvent。但是这一次的touch事件之后发出任何action,该View都不在接受,onTouchEvent在这一次的touch事件中再也不会触发,也就是说一旦View返回false,那么之后的ACTION_MOVE,ACTION_UP等ACTION就不会在传入这个View,但是下一次touch事件的action还是会传进来的。

父层的onInterceptTouchEvent

前面说了底层的View能够接收到这次的事件有一个前提条件:在父层允许的情况下。假设不改变父层级的dispatch方法,在系统调用底层onTouchEvent之前会调用父View的onInterceptTouchEvent方法判断,父层View是否要截获本次touch事件之后的action。如果onInterceptTouchEvent返回了true,那么本次touch事件之后的所有action都不会向深层的View传递,统统都会传给父层View的onTouchEvent,就是说父层已经截获了这次touch事件,之后的action也不必询问onInterceptTouchEvent,在这次的touch事件之后发出的action时onInterceptTouchEvent不会再被调用,直到下一次touch事件的来临。如果onInterceptTouchEvent返回false,那么本次action将发送给更深层的View,并且之后的每一次action都会询问父层的onInterceptTouchEvent需不需要截获本次touch事件。只有ViewGroup才有onInterceptTouchEvent方法,因为一个普通的View肯定是位于最深层的View,touch能够传到这里已经是最后一站了,肯定会调用View的onTouchEvent()。

底层View的getParent().requestDisallowInterceptTouchEvent(true)

对于底层的View来说,有一种方法可以阻止父层的View获取touch事件,就是调用getParent().requestDisallowInterceptTouchEvent(true)方法。一旦底层View收到touch的action后调用这个方法那么父层View就不会再调用onInterceptTouchEvent了,也无法截获以后的action(如果父层ViewGroup和最底层View需要截获不同焦点,或不同手势的touch,不能使用这个写死)。

ART和Dalvik区别

art上应用启动快,运行快,但是耗费更多存储空间,安装时间长,总的来说ART的功效就是”空间换时间”。

ART: Ahead of Time
Dalvik: Just in Time

什么是Dalvik:Dalvik是Google公司自己设计用于Android平台的Java虚拟机。Dalvik虚拟机是Google等厂商合作开发的Android移动设备平台的核心组成部分之一,它可以支持已转换为.dex(即Dalvik Executable)格式的Java应用程序的运行,.dex格式是专为Dalvik应用设计的一种压缩格式,适合内存和处理器速度有限的系统。Dalvik经过优化,允许在有限的内存中同时运行多个虚拟机的实例,并且每一个Dalvik应用作为独立的Linux进程执行。独立的进程可以防止在虚拟机崩溃的时候所有程序都被关闭。

什么是ART:Android操作系统已经成熟,Google的Android团队开始将注意力转向一些底层组件,其中之一是负责应用程序运行的Dalvik运行时。Google开发者已经花了两年时间开发更快执行效率更高更省电的替代ART运行时。ART代表Android Runtime,其处理应用程序执行的方式完全不同于Dalvik,Dalvik是依靠一个Just-In-Time(JIT)编译器去解释字节码。开发者编译后的应用代码需要通过一个解释器在用户的设备上运行,这一机制并不高效,但让应用能更容易在不同硬件和架构上运行。ART则完全改变了这套做法,在应用安装的时候就预编译字节码到机器语言,这一机制叫Ahead-Of-Time(AOT)编译。在移除解释代码这一过程后,应用程序执行将更有效率,启动更快。

ART优点:

系统性能的显著提升
应用启动更快、运行更快、体验更流畅、触感反馈更及时。
更长的电池续航能力
支持更低的硬件

ART缺点:
更大的存储空间占用,可能会增加10%-20%
更长的应用安装时间

Scroller原理

Scroller执行流程里面的三个核心方法

mScroller.startScroll()
mScroller.computeScrollOffset()
view.computeScroll()
1、在mScroller.startScroll()中为滑动做了一些初始化准备,比如:起始坐标,滑动的距离和方向以及持续时间(有默认值),动画开始时间等。

2、mScroller.computeScrollOffset()方法主要是根据当前已经消逝的时间来计算当前的坐标点。因为在mScroller.startScroll()中设置了动画时间,那么在computeScrollOffset()方法中依据已经消逝的时间就很容易得到当前时刻应该所处的位置并将其保存在变量mCurrX和mCurrY中。除此之外该方法还可判断动画是否已经结束。

Android中Java和JavaScript交互

webView.addJavaScriptInterface(new Object(){xxx}, "xxx");
1
答案:可以使用WebView控件执行JavaScript脚本,并且可以在JavaScript中执行Java代码。要想让WebView控件执行JavaScript,需要调用WebSettings.setJavaScriptEnabled方法,代码如下:

WebView webView = (WebView)findViewById(R.id.webview);
WebSettings webSettings = webView.getSettings();
//设置WebView支持JavaScript
webSettings.setJavaScriptEnabled(true);
webView.setWebChromeClient(new WebChromeClient());

JavaScript调用Java方法需要使用WebView.addJavascriptInterface方法设置JavaScript调用的Java方法,代码如下:

webView.addJavascriptInterface(new Object()
{
    //JavaScript调用的方法
    public String process(String value)
    {
        //处理代码
        return result;
    }
}, "demo");       //demo是Java对象映射到JavaScript中的对象名

可以使用下面的JavaScript代码调用process方法,代码如下: