美文网首页我爱编程
整理一下Android的面试题

整理一下Android的面试题

作者: 若无初见 | 来源:发表于2017-05-26 23:56 被阅读341次

    近来由于公司运作上出现的问题,导致离职的人员大大增加,同时也大大增加了自己工作量。渐渐萌生出离职的念头。(虽然公司就剩一个Android老板肯定不会放人)

    好了!废话不多说。不管怎么样,不忘初心,多学习多整理。万变不离其宗,基础永远是最重要的。今天开始参考其他资料的同时结合自身整理一份Android面试题。(欢迎小伙伴们一起带着你们的面试经验来完善它。)

    Android基础: 

    Q: Android的四大组件是哪些,它们的作用?

     Android四大组件:Activity、service、content Provider、BroadCastReceiver

    1、activity

    (1)一个Activity通常就是一个单独的屏幕(窗口)。

    (2)Activity之间通过Intent进行通信。

    (3)android应用中每一个Activity都必须要在AndroidManifest.xml配置文件中声明,否则系统将不识别也不执行该Activity。

    2、service

    (1)service用于在后台完成用户指定的操作。service分为两种:

         (a)started(启动):当应用程序组件(如activity)调用startService()方法启动服务时,服务处于started状态。

         (b)bound(绑定):当应用程序组件调用bindService()方法绑定到服务时,服务处于bound状态。

     ( 2 )startService()与bindService()区别:

          ( a )started service(启动服务)是由其他组件调用startService()方法启动的,这导致服务的onStartCommand()方法被调用。当服务是started状态时,其生命周期与启动它的组件无关,并且可以在后台无限期运行,即使启动服务的组件已经被销毁。因此,服务需要在完成任务后调用stopSelf()方法停止,或者由其他组件调用stopService()方法停止。

          ( b )使用bindService()方法启用服务,调用者与服务绑定在了一起,调用者一旦退出,服务也就终止,大有“不求同时生,必须同时死”的特点。

      ( 3 )开发人员需要在应用程序配置文件中声明全部的service,使用标签。

      ( 4 )Service通常位于后台运行,它一般不需要与用户交互,因此Service组件没有图形用户界面。Service组件需要继承Service基类。Service组件通常用于为其他组件提供后台服务或监控其他组件的运行状态。

    3、content provider

    (1)android平台提供了Content Provider使一个应用程序的指定数据集提供给其他应用程序。其他应用可以通过ContentResolver类从该内容提供者中获取或存入数据。

    (2)只有需要在多个应用程序间共享数据是才需要内容提供者。例如,通讯录数据被多个应用程序使用,且必须存储在一个内容提供者中。它的好处是统一数据访问方式。

    (3)ContentProvider实现数据共享。ContentProvider用于保存和获取数据,并使其对所有应用程序可见。这是不同应用程序间共享数据的唯一方式,因为android没有提供所有应用共同访问的公共存储区。

    (4)开发人员不会直接使用ContentProvider类的对象,大多数是通过ContentResolver对象实现对ContentProvider的操作。

    (5)ContentProvider使用URI来唯一标识其数据集,这里的URI以content://作为前缀,表示该数据由ContentProvider来管理。

    4、broadcast receiver

    (1)你的应用可以使用它对外部事件进行过滤,只对感兴趣的外部事件(如当电话呼入时,或者数据网络可用时)进行接收并做出响应。广播接收器没有用户界面。然而,它们可以启动一个activity或serice来响应它们收到的信息,或者用NotificationManager来通知用户。通知可以用很多种方式来吸引用户的注意力,例如闪动背灯、震动、播放声音等。一般来说是在状态栏上放一个持久的图标,用户可以打开它并获取消息。

    (2)广播接收者的注册有两种方法,分别是程序动态注册和AndroidManifest文件中进行静态注册。

    (3)动态注册广播接收器特点是当用来注册的Activity关掉后,广播也就失效了。静态注册无需担忧广播接收器是否被关闭,只要设备是开启状态,广播接收器也是打开着的。也就是说哪怕app本身未启动,该app订阅的广播在触发时也会对它起作用。

    Q:Android 6大布局

    常用五种布局方式,分别是:FrameLayout(框架布局),LinearLayout (线性布局),AbsoluteLayout(绝对布局),RelativeLayout(相对布局),TableLayout(表格布局)。还有一种 GridLayout

    一、FrameLayout:所有东西依次都放在左上角,会重叠,这个布局比较简单,也只能放一点比较简单的东西。

    二、LinearLayout:线性布局,每一个LinearLayout里面又可分为垂直布局(android:orientation="vertical")和水平布局(android:orientation="horizontal" )。当垂直布局时,每一行就只有一个元素,多个元素依次垂直往下;水平布局时,只有一行,每一个元素依次向右排列。

    三、AbsoluteLayout:绝对布局用X,Y坐标来指定元素的位置,这种布局方式也比较简单,但是在屏幕旋转时,往往会出问题,而且多个元素的时候,计算比较麻烦。

    四、RelativeLayout:相对布局可以理解为某一个元素为参照物,来定位的布局方式。主要属性有:相对于某一个元素android:layout_below、      android:layout_toLeftOf相对于父元素的地方android:layout_alignParentLeft、android:layout_alignParentRigh;

    五、TableLayout:表格布局,每一个TableLayout里面有表格行TableRow,TableRow里面可以具体定义每一个元素。每一个布局都有自己适合的方式,这五个布局元素可以相互嵌套应用,做出美观的界面。

    六、GridLayout :布局使用虚细线将布局划分为行、列和单元格,也支持一个控件在行、列上都有交错排列。而GridLayout使用的其实是跟LinearLayout类似的API,只不过是修改了一下相关的标签而已,所以对于开发者来说,掌握GridLayout还是很容易的事情。

    Q:Activity的生命周期函数有哪些?点击HOME键、BACK键等操作时,生命周期函数如何迁移?

    onCreate 、onStart、onRestart、onResume、onPause、onDestory

    onCreate // 创建Act实例时调用。通常进行一些数据的初始化,比如获取控件、申请数组或集合的内存、变量赋值

    onRestart // Act停留在onStop但是没有onDestory

    onStart // 该方法在onCreate或者onRestart之后调用,调用之后,Act进入可视生命周期

    onResume // onStart之后调用,调用该方法后Act进入活动(运行、前台)状态,可以和用户进行交互,比如响应用户的输入、点击、触摸等操作

    onPause // 在onResume之后,调用该方法,此Act就不能继续和用户交互,用户自定义的一些数据可以在此方法中进行保存

    onStop // onPause之后进行调用,一旦调用onStop,Act就退出了可视状态,但是Act实例并没有销毁

    onDestroy // 此方法是在销毁Act的时候调用,一旦调用表明该Act实例的生命周期就结束了,通常会在此方法做一些释放资源的操作,比如将引用变量值置为null

    HOME键的执行顺序:onPause->onStop->onRestart->onStart->onResume

    BACK键的顺序: onPause->onStop->onDestroy->onCreate->onStart->onResume

    Q:activity在屏幕旋转时的生命周期

    不设置Activity的android:configChanges时,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次;

    设置Activity的android:configChanges="orientation"时,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次;

    设置Activity的android:configChanges="orientation|keyboardHidden"时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法可以在这里保存数据。

    Q : Activity缓存方法。

    有a、b两个Activity,当从a进入b之后一段时间,可能系统会把a回收,这时候按back,执行的不是a的onRestart而是onCreate方法,a被重新创建一次,这是a中的临时数据和状态可能就丢失了。

    可以用Activity中的onSaveInstanceState()回调方法保存临时数据和状态,这个方法一定会在活动被回收之前调用。方法中有一个Bundle参数,putString()、putInt()等方法需要传入两个参数,一个键一个值。数据保存之后会在onCreate中恢复,onCreate也有一个Bundle类型的参数。

    一、onSaveInstanceState (Bundle outState)

    当某个activity变得“容易”被系统销毁时,该activity的onSaveInstanceState就会被执行,除非该activity是被用户主动销毁的,例如当用户按BACK键的时候。

    注意上面的双引号,何为“容易”?言下之意就是该activity还没有被销毁,而仅仅是一种可能性。这种可能性有哪些?通过重写一个activity的所有生命周期的onXXX方法,包括onSaveInstanceState和onRestoreInstanceState方法,我们可以清楚地知道当某个activity(假定为activity A)显示在当前task的最上层时,其onSaveInstanceState方法会在什么时候被执行,有这么几种情况:

    1、当用户按下HOME键时。

    这是显而易见的,系统不知道你按下HOME后要运行多少其他的程序,自然也不知道activity A是否会被销毁,故系统会调用onSaveInstanceState,让用户有机会保存某些非永久性的数据。以下几种情况的分析都遵循该原则

    2、长按HOME键,选择运行其他的程序时。

    3、按下电源按键(关闭屏幕显示)时。

    4、从activity A中启动一个新的activity时。

    5、屏幕方向切换时,例如从竖屏切换到横屏时。(如果不指定configchange属性) 在屏幕切换之前,系统会销毁activity A,在屏幕切换之后系统又会自动地创建activity A,所以onSaveInstanceState一定会被执行

    总而言之,onSaveInstanceState的调用遵循一个重要原则,即当系统“未经你许可”时销毁了你的activity,则onSaveInstanceState会被系统调用,这是系统的责任,因为它必须要提供一个机会让你保存你的数据(当然你不保存那就随便你了)。另外,需要注意的几点:

    1.布局中的每一个View默认实现了onSaveInstanceState()方法,这样的话,这个UI的任何改变都会自动地存储和在activity重新创建的时候自动地恢复。但是这种情况只有在你为这个UI提供了唯一的ID之后才起作用,如果没有提供ID,app将不会存储它的状态。

    2.由于默认的onSaveInstanceState()方法的实现帮助UI存储它的状态,所以如果你需要覆盖这个方法去存储额外的状态信息,你应该在执行任何代码之前都调用父类的onSaveInstanceState()方法(super.onSaveInstanceState())。 既然有现成的可用,那么我们到底还要不要自己实现onSaveInstanceState()?这得看情况了,如果你自己的派生类中有变量影响到UI,或你程序的行为,当然就要把这个变量也保存了,那么就需要自己实现,否则就不需要。

    3.由于onSaveInstanceState()方法调用的不确定性,你应该只使用这个方法去记录activity的瞬间状态(UI的状态)。不应该用这个方法去存储持久化数据。当用户离开这个activity的时候应该在onPause()方法中存储持久化数据(例如应该被存储到数据库中的数据)。

    4.onSaveInstanceState()如果被调用,这个方法会在onStop()前被触发,但系统并不保证是否在onPause()之前或者之后触发。

    二、onRestoreInstanceState (Bundle outState)

    至于onRestoreInstanceState方法,需要注意的是,onSaveInstanceState方法和onRestoreInstanceState方法“不一定”是成对的被调用的,(本人注:我昨晚调试时就发现原来不一定成对被调用的!)

    onRestoreInstanceState被调用的前提是,activity A“确实”被系统销毁了,而如果仅仅是停留在有这种可能性的情况下,则该方法不会被调用,例如,当正在显示activity A的时候,用户按下HOME键回到主界面,然后用户紧接着又返回到activity A,这种情况下activity A一般不会因为内存的原因被系统销毁,故activity A的onRestoreInstanceState方法不会被执行

    另外,onRestoreInstanceState的bundle参数也会传递到onCreate方法中,你也可以选择在onCreate方法中做数据还原。 还有onRestoreInstanceState在onstart之后执行。 至于这两个函数的使用,给出示范代码(留意自定义代码在调用super的前或后):


    Q:Activity的四种加载(启动)模式分别是?各自有什么特点?

    standard:特点是每一次启动该Act时,会重建一个新的该Act实例

    singleTop:特点是每一次该Act时,检查栈顶是否存在该Act的实例

    存在:则直接重用该Act实例

    不存在:则需要新建一个该Act的实例

    singleTask:特点是每一次启动该Act时,需要在栈中去检查栈中是否存在该Act的实例

    存在:

    在栈顶:直接复用该Act的实例

    不在栈顶:首先要把其上的Act实例移除掉,使该Act的实例回到栈顶去,然后再复用该Act的实例

    不存在:新建一个该Act的实例

    singleInstance:看进程中是否有该Act实例

    存在:直接从该独享栈中取出该Act的实例复用

    不存在:新建一个栈,然后新建一个该Act的实例,放入该栈中

    注意:如果把程序入口MainAct的Act设置为singleInstance时,通过该Act启动了别的Act(SecondAct),再由其他 (SecondAct)启动ThirdAct,在ThirdAct中启动MainAct,由MainAct再去启动SecondAct时,不会产生第四个实例只是把MainAct所在的栈切换到SecondAct所在的栈,把栈顶的Act实例展示出来

    Q : Activity意外退出时,如何进行数据保存和恢复?

    开发者提前可以复写onSaveInstanceState方法,创建一个Bundle类型的参数,把数据存储在这个Bundle对象中,这样即使Activity意外退出,Activity被系统摧毁,当重新启动这个Activity而调用onCreate方法时,上述Bundle对象会作为参数传递给onCreate方法,开发者可以从Bundle对象中取出保存的数据,利用这些数据将Activity恢复到被摧毁之前的状态。

    Q:Intent是什么?有什么用处?intent传值?

    Intent(意图):作用是调用某个组件去做一个事情,它既能充当桥梁的角色,也能传递数据

    Intent的传值: 通过Intent类提供的setData和putExtra方法传递。

    Bundle(捆):

    两个Activity之间的通讯可以通过bundle类来实现,把要传递的数据通过key-value的形式加入数据,另外一个Activity里面取出数据时,用key找出对应的value

    如何实现使用Intent来传递自定义对象:

    序列化要传递的自定义的对象,再通过Bundle来传值。

    (序列化:将对象的状态转换为可保持或传输的格式的过程)与序列化相对的是反序列化,它将流转换为对象。通过序列化和反序列化就可以实现存储和传输数据。

    Q:为什么在Service中创建子线程而不是Activity中

    这是因为Activity很难对Thread进行控制,当Activity被销毁之后,就没有任何其它的办法可以再重新获取到之前创建的子线程的实例。而且在一个Activity中创建的子线程,另一个Activity无法对其进行操作。但是Service就不同了,所有的Activity都可以与Service进行关联,然后可以很方便地操作其中的方法,即使Activity被销毁了,之后只要重新与Service建立关联,就又能够获取到原有的Service中Binder的实例。因此,使用Service来处理后台任务,Activity就可以放心地finish,完全不需要担心无法对后台任务进行控制的情况。

    Q : Service和Thread的区别?

    答:servie是系统的组件,它由系统进程托管(servicemanager);它们之间的通信类似于client和server,是一种轻量级的ipc通信,这种通信的载体是binder,它是在linux层交换信息的一种ipc。而thread是由本应用程序托管。

    1). Thread:Thread是程序执行的最小单元,它是分配CPU的基本单位。可以用Thread来执行一些异步的操作。

    2). Service:Service是android的一种机制,当它运行的时候如果是LocalService,那么对应的Service是运行在主进程的main线程上的。如:onCreate,onStart这些函数在被系统调用的时候都是在主进程的main线程上运行的。如果是Remote Service,那么对应的Service则是运行在独立进程的main线程上。

    既然这样,那么我们为什么要用Service呢?其实这跟android的系统机制有关,我们先拿Thread来说。Thread的运行是独立于Activity的,也就是说当一个Activity被finish之后,如果你没有主动停止Thread或者Thread里的run方法没有执行完毕的话,Thread也会一直执行。因此这里会出现一个问题:当Activity被finish之后,你不再持有该Thread的引用。另一方面,你没有办法在不同的Activity中对同一Thread进行控制。

    举个例子:如果你的Thread需要不停地隔一段时间就要连接服务器做某种同步的话,该Thread需要在Activity没有start的时候也在运行。这个时候当你start一个Activity就没有办法在该Activity里面控制之前创建的Thread。因此你便需要创建并启动一个Service,在Service里面创建、运行并控制该Thread,这样便解决了该问题(因为任何Activity都可以控制同一Service,而系统也只会创建一个对应Service的实例)。

    因此你可以把Service想象成一种消息服务,而你可以在任何有Context的地方调用Context.startService、Context.stopService、Context.bindService,Context.unbindService,来控制它,你也可以在Service里注册BroadcastReceiver,在其他地方通过发送broadcast来控制它,当然这些都是Thread做不到的。

    Q : android中的动画有哪几类,它们的特点和区别是什么

    A:两种,一种是Tween动画、还有一种是Frame动画。

    Tween动画,这种实现方式可以使视图组件移动、放大、缩小以及产生透明度的变化;

    Frame动画,传统的动画方法,通过顺序的播放排列好的图片来实现,类似电影。

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

    答:XML解析主要有三种方式,SAX、DOM、PULL。

    常规在PC上开发我们使用Dom相对轻松些,但一些性能敏感的数据库或手机上还是主要采用SAX方式;

    SAX读取是单向的,优点:不占内存空间、解析属性方便,但缺点就是对于套嵌多个分支来说处理不是很方便。

    DOM方式会把整个XML文件加载到内存中去,这里Android开发网提醒大家该方法在查找方面可以和XPath很好的结合如果数据量不是很大推荐使用。

    PULL常常用在J2ME对于节点处理比较好,类似SAX方式,同样很节省内存,在J2ME中我们经常使用的KXML库来解析。

    Q : 谈谈android数据存储方式。

    答:Android提供了5种方式存储数据:

    (1)使用SharedPreferences存储数据;它是Android提供的用来存储一些简单配置信息的一种机制,采用了XML格式将数据存储到设备中。只能在同一个包内使用,不能在不同的包之间使用。

    (2)文件存储数据;文件存储方式是一种较常用的方法,在Android中读取/写入文件的方法,与Java中实现I/O的程序是完全一样的,提供了openFileInput()和openFileOutput()方法来读取设备上的文件。

    (3)SQLite数据库存储数据;SQLite是Android所带的一个标准的数据库,它支持SQL语句,它是一个轻量级的嵌入式数据库。

    (4)使用ContentProvider存储数据;主要用于应用程序之间进行数据交换,从而能够让其他的应用保存或读取此Content Provider的各种数据类型。

    (5)网络存储数据;通过网络上提供给我们的存储空间来上传(存储)和下载(获取)我们存储在网络空间中的数据信息。

    Q:Android多线程与界面交互的方法

    Activity.runOnUiThread(Runnable)

    View.post(Runnable),View.postDelay(Runnable,long)

    Handler

    AsyncTask

    www.jianshu.com/p/8e756803211f

    Q : Android 多线程

    AsyncTask

    HandlerThread

    IntentService

    android中的线程池

    www.jianshu.com/p/c7b16c3c4625

    Q : 什么是OOM?如何避免OOM?

    OOM概念:内存溢出(OutOfMemor),内存占有量超过了JVM分配的最大内存。

    避免OOM:

    <1.避免对activity的超过生命周期的引用(尽量使用application代替activity)。

    因为程序一般是由很多个Activity构成的,从一个Activity跳转了以后,

    系统就有可能回收这个Activity的各种内存占用。可是此时如果你的一些不可回收变量(比如静态变量)保持了对此Activity对象的引用,

    那么GC就不会对此Activity进行回收,无故占用了大量的内存。这种情况最好的办法就是用application代替activity。

    用Context.getApplicationContext() 或者 Activity.getApplication()可以很方便的得到application对象。

    <2.在展示高分辨率图片时,先将图片进行压缩到与空间大小相近。

    <3.及时释放不使用的Bitmap,动态回收内存,方法:bitmap.recycle()。

    <4.对适配器视图进行优化处理,避免过多加载数据和对象的生成。

    Q:什么是ANR?产生ANR的原因是什么?如何避免ANR的发生?

    ANR概念:

    应用程序无响应(application not response)。

    原因:

    主线程中做了非常耗时的操作。

    解决办法:

    <1.运行在主线程里的任何方法都尽可能少做事情,尽量用Handler来处理UIthread和别的thread之间的交互;

    <2.应用程序应该避免在BroadcastReceiver里做耗时的操作或计算;

    <3.避免在Intent Receiver里启动一个Activity,因为它会创建一个新的画面,并从当前用户正在运行的程序上抢夺焦点;

    <4.在主线程中更新UI。

    Q : View, surfaceView, GLSurfaceView有什么区别。

    答:view是最基础的,必须在UI主线程内更新画面,速度较慢。

    SurfaceView是view的子类,类似使用双缓机制,在新的线程中更新画面所以刷新界面速度比view快

    GLSurfaceView是SurfaceView的子类,opengl专用的

    Q:根据自己的理解描述下Android数字签名。

    答:(1)所有的应用程序都必须有数字证书,Android系统不会安装一个没有数字证书的应用程序

    (2)Android程序包使用的数字证书可以是自签名的,不需要一个权威的数字证书机构签名认证

    (3)如果要正式发布一个Android,必须使用一个合适的私钥生成的数字证书来给程序签名,而不能使用adt插件或者ant工具生成的调试证书来发布。

    (4)数字证书都是有有效期的,Android只是在应用程序安装的时候才会检查证书的有效期。如果程序已经安装在系统中,即使证书过期也不会影响程序的正常功能

    Q:目前能否保证service不被杀死的方法有哪些?

    Service设置成START_STICKY

    kill 后会被重启(等待5秒左右),重传Intent,保持与重启前一样

    提升service优先级

    在AndroidManifest.xml文件中对于intent-filter可以通过android:priority = "1000"这个属性设置最高优先级,1000是最高值,如果数字越小则优先级越低,同时适用于广播

    【结论】目前看来,priority这个属性貌似只适用于broadcast,对于Service来说可能无效

    提升service进程优先级

    Android中的进程是托管的,当系统进程空间紧张的时候,会依照优先级自动进行进程的回收

    当service运行在低内存的环境时,将会kill掉一些存在的进程。因此进程的优先级将会很重要,可以在startForeground()使用startForeground()将service放到前台状态。这样在低内存时被kill的几率会低一些。

    【结论】如果在极度极度低内存的压力下,该service还是会被kill掉,并且不一定会restart()

    onDestroy方法里重启service

    service +broadcast 方式,就是当service走onDestory()的时候,发送一个自定义的广播,当收到广播的时候,重新启动service

    也可以直接在onDestroy()里startService

    【结论】当使用类似口口管家等第三方应用或是在setting里-应用-强制停止时,APP进程可能就直接被干掉了,onDestroy方法都进不来,所以还是无法保证

    监听系统广播判断Service状态

    通过系统的一些广播,比如:手机重启、界面唤醒、应用状态改变等等监听并捕获到,然后判断我们的Service是否还存活,别忘记加权限

    【结论】这也能算是一种措施,不过感觉监听多了会导致Service很混乱,带来诸多不便

    在JNI层,用C代码fork一个进程出来

    这样产生的进程,会被系统认为是两个不同的进程.但是Android5.0之后可能不行

    root之后放到system/app变成系统级应用

    大招: 放一个像素在前台(手机QQ)



    Android进阶:

    Q:谈谈对Android View的理解

    view负责Android ui 的绘制以及event的处理。Android 利用 View 打造出所 Widgets,利用 Widget 可打造出互动式的使用者介面,每个View 负责一定区域的绘制。

    View 的几个重要方法:

    requestLayout

    View重新调用一次layout过程

    invalidate

    View重新调用一次draw过程

    forceLayout

    标识View在下一次重绘,需要重新调用layout过程。

    postInvalidate

    这个方法与invalidate方法的作用是一样的,都是使View树重绘,但两者的使用条件不同,postInvalidate是在非UI线程中调用,invalidate则是在UI线程中调用。

    View 的绘制:

    1.测量——onMeasure():决定View的大小

    2.布局——onLayout():决定View在ViewGroup中的位置

    3.绘制——onDraw():如何绘制这个View。

    自定义View的分类

    继承View

    继承ViewGroup

    继承系统控件(Button,LinearLayout…)

    Q:recyclerview 滑动优化

    RecyclerView 虽然比ListView灵活的多, 但是滚动时的卡顿确比ListView更明显, 有些人有疑问 :这难道不是绑定数据造成的吗?  其实不然。 是在滚动时会额外通过getView或者onCreateViewHolder  里面继续inflate.layout造成的。 很多有又有疑问, 为啥我没有这个感觉呢?? 那是因为很多layout不复杂的情况下效果很轻微, 但是如果你设置了header, 第一屏幕显示的都是header view , 而list是从第2页屏幕开始显示的呢??? 你有试过吗, 如果试过或者知道它们的缓存原理就会发现, 再RecyclerView或者ListView被显示出来的时候, getView或者onCreateViewHolder 并没有被执行, 或者执行很少, 只有在滚动时才获取执行, 然后就造成了卡顿。 那是因为它们的缓存机制导致, 初始化只加载可视范围内的item (这里不细细谈到它们的缓存机制,如果需要验证, 那么请在getView 或者onCreateViewHolder 哪里添加打印) 。 而滚动时inflate.layout  如果不复杂可能10ms就解决了, 你还能流程的体验, 但是如果复杂, 那么就会体验到滚动开始有轻微的卡顿(丢帧) , 知道它们的缓存机制完全足以支撑你的滚动, 不再去重新inflate.layout 。

    卡顿的原因:

    1.产品设计不合理导致卡片布局过度复杂,产品只追求界面的高大上,而忽略了实现上的复杂度。

    2.卡顿的另一个最大原因是图片的加载,如果图片过大,卡顿甚至崩溃都不是问题。即使图片使用了压缩后的,并且用了Fresco等图片加载框架,发现还是会有卡顿。虽然图片是异步加载的,但是图片的加载都伴随着三级缓存,图片IO会导致卡

    经过以上1,2分析,然后就开始考虑该怎么去优化呢?如果产品的需求改变不了,那就要另辟蹊径了。也就是主要的优化集中在复杂布局和图片加载。

    复杂布局的优化:

    1.尽量减少布局嵌套,层级越深,每次测量时间久越久。

    2. 如果布局很复杂,可以考虑自定义布局能不能实现。

    3.尽量减少过度绘制区域。这个可以在开发者选项中看到:调试GPU过度绘制。

    Q:ButterKnife原理

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

    Q : Handler内存泄漏分析及解决

    ###一、介绍

    首先,请浏览下面这段handler代码:

    public class SampleActivity extends Activity {

    private final Handler mLeakyHandler = new Handler() {

            @Override

             public void handleMessage(Message msg) {

    // ...

              }

         }

    }

    在使用handler时,这是一段很常见的代码。但是,它却会造成严重的内存泄漏问题。在实际编写中,我们往往会得到如下警告:

    ⚠ In Android, Handler classes should be static or leaks might occur.

    ###二、分析

    1、 Android角度

    当Android应用程序启动时,framework会为该应用程序的主线程创建一个Looper对象。这个Looper对象包含一个简单的消息队列Message Queue,并且能够循环的处理队列中的消息。这些消息包括大多数应用程序framework事件,例如Activity生命周期方法调用、button点击等,这些消息都会被添加到消息队列中并被逐个处理。

    另外,主线程的Looper对象会伴随该应用程序的整个生命周期。

    然后,当主线程里,实例化一个Handler对象后,它就会自动与主线程Looper的消息队列关联起来。所有发送到消息队列的消息Message都会拥有一个对Handler的引用,所以当Looper来处理消息时,会据此回调[Handler#handleMessage(Message)]方法来处理消息。

    2、 Java角度

    在java里,非静态内部类 和 匿名类 都会潜在的引用它们所属的外部类。但是,静态内部类却不会。

    ###三、泄漏来源

    请浏览下面一段代码:

    public class SampleActivity extends Activity {

           private final Handler mLeakyHandler = new Handler() {

                   @Override

                   public void handleMessage(Message msg) {

                  // ...

                   }

    }

    @Override

    protected void onCreate(Bundle savedInstanceState) {

           super.onCreate(savedInstanceState);

           // Post a message and delay its execution for 10 minutes.

           mLeakyHandler.postDelayed(new Runnable() {

          @Override

              public void run() { /* ... */ }

            }, 1000 * 60 * 10);

            // Go back to the previous Activity.

           finish();

           }

    }

    当activity结束(finish)时,里面的延时消息在得到处理前,会一直保存在主线程的消息队列里持续10分钟。而且,由上文可知,这条消息持有对handler的引用,而handler又持有对其外部类(在这里,即SampleActivity)的潜在引用。这条引用关系会一直保持直到消息得到处理,从而,这阻止了SampleActivity被垃圾回收器回收,同时造成应用程序的泄漏。

    <<<<<<< HEAD 注意,上面代码中的Runnable类--非静态匿名类--同样持有对其外部类的引用。从而也导致泄漏。

    ======= 注意,上面代码中的Runnable类--非静态匿名类--同样持有对其外部类的引用。从而也导致泄漏。

    四、泄漏解决方案

    首先,上面已经明确了内存泄漏来源:

    只要有未处理的消息,那么消息会引用handler,非静态的handler又会引用外部类,即Activity,导致Activity无法被回收,造成泄漏;

    Runnable类属于非静态匿名类,同样会引用外部类。

    为了解决遇到的问题,我们要明确一点:静态内部类不会持有对外部类的引用。所以,我们可以把handler类放在单独的类文件中,或者使用静态内部类便可以避免泄漏。

    另外,如果想要在handler内部去调用所在的外部类Activity,那么可以在handler内部使用弱引用的方式指向所在Activity,这样统一不会导致内存泄漏。

    对于匿名类Runnable,同样可以将其设置为静态类。因为静态的匿名类不会持有对外部类的引用。

    public class SampleActivity extends Activity {

    /**

    * Instances of static inner classes do not hold an implicit

    * reference to their outer class.

    */

         private static class MyHandler extends Handler {

               private final WeakReference mActivity;

               public MyHandler(SampleActivity activity) {

                     mActivity = new WeakReference(activity);

                }

               @Override

                public void handleMessage(Message msg) {

                        SampleActivity activity = mActivity.get();

                             if (activity != null) {

                                // ...

                             }

                }

    }

    private final MyHandler mHandler = new MyHandler(this);

    /**

    * Instances of anonymous classes do not hold an implicit

    * reference to their outer class when they are "static".

    */

    private static final Runnable sRunnable = new Runnable() {

    @Override

    public void run() { /* ... */ }

    };

    @Override

    protected void onCreate(Bundle savedInstanceState) {

               super.onCreate(savedInstanceState);

               // Post a message and delay its execution for 10 minutes.

               mHandler.postDelayed(sRunnable, 1000 * 60 * 10);

             // Go back to the previous Activity.

               finish();

           }

    }

    ###五、小结

    <<<<<<< HEAD 虽然静态类与非静态类之间的区别并不大,但是对于Android开发者而言却是必须理解的。至少我们要清楚,如果一个内部类实例的生命周期比Activity更长,那么我们千万不要使用非静态的内部类。最好的做法是,使用静态内部类,然后在该类里使用弱引用来指向所在的Activity。

    Q : 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%

    更长的应用安装时间

    Q : Android 内存泄漏的总结

    总结

    1、对 Activity 等组件的引用应该控制在 Activity 的生命周期之内; 如果不能就考虑使用 getApplicationContext 或者 getApplication,以避免 Activity 被外部长生命周期的对象引用而泄露。

    2、尽量不要在静态变量或者静态内部类中使用非静态外部成员变量(包括context ),即使要使用,也要考虑适时把外部成员变量置空;也可以在内部类中使用弱引用来引用外部类的变量。

    3、对于生命周期比Activity长的内部类对象,并且内部类中使用了外部类的成员变量,可以这样做避免内存泄漏:

    将内部类改为静态内部类

    静态内部类中使用弱引用来引用外部类的成员变量

    4、Handler 的持有的引用对象最好使用弱引用,资源释放时也可以清空 Handler 里面的消息。比如在 Activity onStop 或者 onDestroy 的时候,取消掉该 Handler 对象的 Message和 Runnable.

    5、在 Java 的实现过程中,也要考虑其对象释放,最好的方法是在不使用某对象时,显式地将此对象赋值为 null,比如使用完Bitmap 后先调用 recycle(),再赋为null,清空对图片等资源有直接引用或者间接引用的数组(使用 array.clear() ; array = null)等,最好遵循谁创建谁释放的原则。

    6、正确关闭资源,对于使用了BraodcastReceiver,ContentObserver,File,游标 Cursor,Stream,Bitmap等资源的使用,应该在Activity销毁时及时关闭或者注销。

    7、保持对对象生命周期的敏感,特别注意单例、静态对象、全局性集合等的生命周期。

    未完待续...

    分享几个不错的资源:

    github.com/GeniusVJR/LearningNotes/blob/master/README.md

    github.com/JackyAndroid/AndroidInterview-Q-A/blob/master/README-CN.md

    www.jianshu.com/p/13786463635d

    www.diycode.cc/wiki/androidinterview

    Android插件化从入门到放弃-最强合集

    github.com/Tim9Liu9/TimLiu-Android

    Android面试相关文章以及github整理,偏2018,持续更新

    相关文章

      网友评论

        本文标题:整理一下Android的面试题

        本文链接:https://www.haomeiwen.com/subject/agqffxtx.html