美文网首页
Android面试题总结一

Android面试题总结一

作者: 谢尔顿 | 来源:发表于2017-05-28 15:04 被阅读146次

    1.Dalvik和Sun JVM

    共同点

    • 都是解释执行 byte code(字节码)
    • 都是每个 OS 进程运行一个 VM,并执行一个单独的程序
    • 在较新版本中(Froyo / Sun JDK 1.5)都实现了相当程度的 JIT compiler(即时编译) 用于提速
      不同点
    • dvm执行的是.dex格式文件,jvm执行的是.class文件,android程序编译完之后生产.class文件,然后,dex工具会把.class文件处理成.dex文件,然后把资源文件和.dex文件等打包成.apk文件。apk就是android package的意思。jvm执行的是.class文件。
    • dvm是基于寄存器的虚拟机 而jvm执行是基于虚拟栈的虚拟机。寄存器存取速度比栈快的多,dvm可以根据硬件实现最大的优化,比较适合移动设备。
    • .class文件存在很多的冗余信息,dex工具会去除冗余信息,并把所有的.class文件整合到.dex文件中。减少了I/O操作,提高了类的查找速度。

    2.IPC进程间通信

    在一个应用中存在多个进程的情况(不讨论两个应用之间的多进程情况),开启多进程:在Android的manifests文件中指定android:process属性,除此之外没有其他办法。
    进程名:以“:“开头的进程属于当前应用的私有进程,其他的应用不可以和它跑在同一个进程中,而进程名不以”:“开头的属于全局进程,其他应用通过shareUID方式可以和它跑在同一个进程中。Android给每一个进程都分配一个独立的虚拟机,所以运行在不同进程中的四大组件,只要他们之间需要通过内存来共享数据,都会共享失败,这也是多进程所带来的主要影响。
    使用多进程会造成问题:

    • 静态成员和单例模式完全失效;
    • 线程同步机制完全失效;
    • SharedPreferences的可靠性下降;
    • Application会多次创建;
      SharedPreferences的可靠性降低,因为 SharedPrefrences不支持两个进程同时执行写操作,可能会导致一定几率的数据丢失,这是因为sharedPrefrences底层是通过读写xml实现的。
      实现跨进程通信的方式:
    • 通过Intent来传递数据(通过Bundle来绑定数据)
    • 共享文件和SharedPrefereneces
    • 基于Binder的Messenger和AIDL以及Socket等
      序列化:静态成员变量属于类不属于对象,所以不会参与序列化过程,其次transient关键字标记的成员变量不参与序列化过程。 实现了Parcelable接口的类都是可以直接序列化的,Android中已经实现了parcelable类的有:Intent,Bundle, Bitmap等,同时 list 和 map 也可以序列化,前提是他们的每个元素都能序列化。实现了parcelable接口的类都可以通过Intent和Binder来传递。
      Binder类:实现了IBinder接口,从IPC角度来说,Binder是Android中一种垮进程通信方式,还可以理解为一种虚拟的物理设备,该通信在Linux中是没有的。Binder是连接各种manager(ActivityManager, windowManager)和相应的ManagerService的桥梁。
      binder驱动是整个流程的核心
    • Server将自己的binder通过binder驱动在SM中进行注册。
    • binder驱动会建立一个binder实体的数据节点和实体的引用。
    • Binder驱动再把名字和引用打包发给SM。
    • Client通过binder驱动拿着他所需要的binder名字向SM请求binder。
    • SM在自己的查找表里面找到对应的引用之后再通过binder驱动返回给client。


      Binder流程图

    3.Intent传递数据

    根据API文档,Intent/Bundle支持传递基本类型的数据和基本类型的数组数据,以及String/CharSequence。而对于其它类型的数据貌似无能为力,其实不然,我们可以在Intent/Bundle的API中看到Intent/Bundle还可以传递 Parcelable(包裹化,邮包)和Serializable(序列化)类型的数据,以及它们的数组/列表数据。

    4.数据存储

    Android数据存储方式:

    • SharedPreferences,键值对的形式存储,底层是xml文件。并且可以设置权限,是否和其他应用共享该项文件。
      使用步骤:
      (1)通过getSharedPreference方法获取一个SharedPreference对象;
      (2)通过SharedPreference对象的edit方法获取一个Editor对象;
      (3)通过调用Editor的putXxxx方法进行添加内容;
      (4)最后通过调用Editor的apply方法提交数据;
      (6)读取数据时,调用SharedPreference的getXxx方法;
    • SQLite,
      使用步骤:
      (1)创建一个SQLiteOpenHelper的子类DbHelper;
      (2)在构造器中调用父类的构造器,super(context,"myapp",null,4),参数2代表数据库文件名,参数四代表版本信息。
      (3)重写onCreate方法,用于创建数据库,利用SQLiteDatabase的execSQL方法创建表;
      (4)重写onUpgrade方法,用于版本升级的实现;
      (5)在Activity中实例化DbHelper,调用getWritableDatabase方法,会得到一个SQLiteDatabase对象,即可创建数据库;
      (6)利用SQLiteDatabase对象的insert、delete、update、和query方法对数据进行操作;
      (7)在onDestroy方法中调用SQLiteDatabase的close方法
    • File, Android分为内部存储和外部存储,内部存储中属于APP私有,其他任何应用都不能访问,APP卸载时自动删除。外部存储又分为两类:公共文件和私有文件,私有文件:本属于您的应用且应在用户卸载您的应用时删除的文件。尽管这些文件在技术上可被用户和其他应用访问(因为它们在外部存储上),它们是实际上不向您的应用之外的用户提供值的文件。当用户卸载您的应用时,系统会删除应用外部专用目录中的所有文件。

    内部存储

    写数据:
    通过context.openFileOutput(文件名,读写文件的方式)的到一个文件输出流;
    调用文件输出流的write方法将文本信息写到手机内存中;
    读数据:
    通过context.openFileInput(文件名)获取一个输入流;
    获取一个字节数组输出流,和实例化一个字节数组;
    从输入流中读取数据写入到内存流中,最后调用内存流的toString方法将数据输出;

    外部存储

    写数据:
    读写外部数据时需要设置权限;
    写数据之前首先要判断SDCard的状态,Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
    获取sdcard的根路径rootPath,Environment.getExternalStorageDirectory();
    创建要写入的sdcard的文件file,new File(sdcard的根路径,文件名);
    创建文件输出流fos,参数为上步的file;
    调用fos的write方法,将内容写入输出流中;
    读数据:
    第1、2、3步同写数据的,
    获取要读取的文件file,new File(rootPath.getAbsolutePath()+"/"+文件名);
    调用file的exists方法判断文件是否存在;
    创建一个文件输入流fis,参数为上步的file;
    创建一个字节数组输出流(内存流),和一个自己数组;
    将读取输入流中内容写入到字节数组输出流中,最后通过内存流的to;

    • 网络存储

    5.Android动画

    • View动画:
      View动画注意事项:View动画执行之后并未改变View的真实布局属性值。切记这一点,譬如我们在Activity中有一 个Button在屏幕上方,我们设置了平移动画移动到屏幕下方然后保持动画最后执行状态呆在屏幕下方,这时如果点 击屏幕下方动画执行之后的Button是没有任何反应的,而点击原来屏幕上方没有Button的地方却响应的是点击Button的事件。
      View动画有四种变换效果,对应Animation的四个子类:TranslateAnimation(平移),ScaleAnimation(缩放),AlphaAnimation(透明度动画),RotateAnimation(旋转),一般采用XML的方式来定义动画,可读性更好。
    • 帧动画:
      对应AnimationDrawale来使用帧动画,通过XML来定义个AnimationDrawable,然后把xml作为Drawable作为view的背景来播放动画就可以了。
      注意:
      帧动画使用比较简单,比较容易引起OOM,所以尽量避免使用过多尺寸较大的图片。
    • 属性动画
      通过动态地改变对象的属性从而达到动画效果,属性动画为API 11新特性。主要由ValueAnimator,ObjectAnimator和AnimatorSet类实现。

    6.Bitmap加载

    Bitmap代表一张图片,BitmapFactory提供了四类方法:decodeFile,decodeResource,decodeStream和 decodeByteArray加载bitmap。

    7.Retrofit优点

    • 采用动态代理机制和反射,使代码写起来特别简单,看起来也很清晰。
    • 可以配合OkHttp拦截器,很方便的在header加token或者处理cookie等等。
    • 可配合RxJava,可以让多个接口调用组合变的更简单。

    8.RequestLayout,invalidate和postInvalidate

    • requestLayout:当view确定自身已经不再适合现有的区域时,该view本身调用这个方法要求parent view重新调用他的onMeasure,onLayout来对重新设置自己位置。
      特别的当view的layoutparameter发生改变,并且它的值还没能应用到view上,这时候适合调用这个方法。
    • invalidate:invalidate内部最终会调用到performTraversals(视图绘制的入口),但由于没有设置强制视图重新测量的标志位,所以只会执行onDraw方法。
    • postInvalidate:是在非UI线程使用

    9.Rxjava

    一个词:异步。
    RxJava 在 GitHub 主页上的自我介绍是 "a library for composing asynchronous and event-based programs using observable sequences for the Java VM"(一个在 Java VM上使用可观测的序列来组成异步的、基于事件的程序的库)。这就是 RxJava ,概括得非常精准。
    在 RxJava 的默认规则中,事件的发出和消费都是在同一个线程的。在不指定线程的情况下, RxJava 遵循的是线程不变的原则,即:在哪个线程调用 subscribe(),就在哪个线程生产事件;在哪个线程生产事件,就在哪个线程消费事件。如果需要切换线程,就需要用到 Scheduler (调度器)。
    RxJava 的异步实现,是通过一种扩展的观察者模式来实现的
    变换(map(),flatmap()):
    Map行为:

    • 创建了一个新的 Observable
    • 创建了一个新的 OnSubscribe: 其中的 call 方法是整个调用链的关键. 它调用了上一级 Observable.onSubscribe.call, 同时, 还将结果通过transform 对 ‘第一步’ 处理后的结果进行变形, 然后将变形后的结果再转发给‘第三步’ 中 subscribe(Subscriber…) 中的 Subscriber 处理. 那我们马上看一下 ‘第三步’。

    10.Glide优点

    • 默认Bitmap格式是RGB_565,内存占用更少,原因在于Picasso是加载了全尺寸的图片到内存,然后让GPU来实时重绘大小。而Glide加载的大小和ImageView的大小是一致的,因此更小。
    • 磁盘缓存
    • Picasso和Glide在磁盘缓存策略上有很大的不同。Picasso缓存的是全尺寸的,而Glide缓存的是跟ImageView尺寸相同。
    • Glide可以加载GIF动态图,而Picasso不能。
    • 除了gif动画之外,Glide还可以将任何的本地视频解码成一张静态图片。

    11.Material Design

    • TextInputLayout必须把EditText包含起来,不能单独使用。
    <android.support.design.widget.TextInputLayout
            android:id="@+id/til_pwd"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
            <EditText
                android:layout_width="match_parent"
                android:layout_height="wrap_content"/>
    </android.support.design.widget.TextInputLayout>
    
    • FloatingActionButton继承自ImageView。
    • NavigationView实现DrawerLayout导航菜单界面,通过
    app:headerLayout="@layout/navigation_header"
            app:menu="@menu/drawer_view"
    

    12.SQLite

    public long insert (String table, String nullColumnHack, ContentValues values) 插入数据参数介绍 :

    • 参数① table : 数据库中的表名, 要插入数据的表;
    • 参数② nullColumnHack : 该参数是可选的, 数据库表中不允许插入一行空的数据, 插入数据至少有一列不为null才能插入, 如果后面的values是null, 并且不知道列的名称, 那么插入操作会失败, 为了避免这种情况, 就出现了本参数, 为了防止 values为null的情况;
    • 参数③ ContentValues : 相当于一个Map集合,键是列名,值是对应列名要插入的数据;
      插入原则 : 不管第三个 ContentValues参数是否为null, 执行insert()方法都会添加一条记录, 如果values参数为null, 会添加一个除主键之外其它字段都为null的记录;
      nullColumnHack参数作用分析SQL语句 : 在SQL语句中在表名后面必须跟着一个列名, 例如 " insert into appale_info(name) values("乔帮主")", 这是values参数不为null的情况下,如果values参数为null, 那么导致表名 "apple_info" 后面的列名也为null, 这样SQL语句就不合法了, 因此这里必须加上一个默认的列名, 以防values参数为null;

    13.HttpURLConnection

    设置请求头或响应头
    HTTP请求允许一个key带多个用逗号分开的values,但是HttpURLConnection只提供了单个操作的方法:

    • setRequestProperty(key,value)
    • addRequestProperty(key,value)
      setRequestProperty和addRequestProperty的区别就是,setRequestProperty会覆盖已经存在的key的所有values,有清零重新赋值的作用。而addRequestProperty则是在原来key的基础上继续添加其他value。
      发送URL请求
      建立实际连接之后,就是发送请求,把请求参数传到服务器,这就需要使用outputStream把请求参数传给服务器:
    • getOutputStream
      获取响应
      请求发送成功之后,即可获取响应的状态码,如果成功既可以读取响应中的数据,获取这些数据的方法包括:
    • getContent
    • getHeaderField
    • getInputStream
      对于大部分请求来说,getInputStream和getContent是用的最多的。
      相应的信息头用以下方法获取:
    • getContentEncoding
    • getContentLength
    • getContentType
    • getDate
    • getExpiration
    • getLastModifed
      任何网络连接都需要经过socket才能连接,HttpURLConnection不需要设置socket,所以,HttpURLConnection并不是底层的连接,而是在底层连接上的一个请求。这就是为什么HttpURLConneciton只是一个抽象类,自身不能被实例化的原因。HttpURLConnection只能通过URL.openConnection()方法创建具体的实例。虽然底层的网络连接可以被多个HttpURLConnection实例共享,但每一个HttpURLConnection实例只能发送一个请求。请求结束之后,应该调用HttpURLConnection实例的InputStream或OutputStream的close()方法以释放请求的网络资源,不过这种方式对于持久化连接没用。对于持久化连接,得用disconnect()方法关闭底层连接的socket。

    14.Json

    一种轻量级的数据交换格式,具有良好的可读和便于快速编写的特性。可在不同平台之间进行数据交换。JSON采用兼容性很高的文本格式,同时也具备类似于C语言体系的行为。
    优点:

    • 数据格式比较简单,易于读写,格式都是压缩的,占用带宽小;
    • 易于解析
    • 支持多种语言,包括ActionScript, C, C#, ColdFusion, Java, JavaScript, Perl, PHP, Python, Ruby等服务器端语言,便于服务器端的解析;
    • 因为JSON格式能直接为服务器端代码使用,大大简化了服务器端和客户端的代码开发量,且完成任务不变,并且易于维护。
      缺点 :
    • 没有XML格式这么推广的深入人心和喜用广泛,没有XML那么通用性;
    • JSON格式目前在Web Service中推广还属于初级阶段

    15.Parcelable和Serializable

    • 作用:
      Serializable的作用是为了保存对象的属性到本地文件、数据库、网络流、rmi以方便数据传输,当然这种传输可以是程序内的也可以是两个程序间的。简单易用,这种方法的缺点是使用了反射,序列化的过程较慢。这种机制会在序列化的时候创建许多的临时对象,容易触发垃圾回收。
      而Android的Parcelable的设计初衷是因为Serializable效率过慢,为了在程序内不同组件间以及不同Android程序间(AIDL)高效的传输数据而设计,这些数据仅在内存中存在,Parcelable是通过IBinder通信的消息的载体。
    • 效率:
      Parcelable的性能比Serializable好,在内存开销方面较小,所以在内存间数据传输时推荐使用Parcelable,如activity间传输数据,而Serializable可将数据持久化方便保存,所以在需要保存或网络传输数据时选择Serializable,因为android不同版本Parcelable可能不同,所以不推荐使用Parcelable进行数据持久化
      从上面的设计上我们就可以看出优劣了。
    • 整个读写全是在内存中进行,所以效率比JAVA序列化中使用外部存储器会高很多;
    • 读写时是4字节对齐的
    • 如果预分配的空间不够时,会一次多分配50%;
    • 对于普通数据,使用的是mData内存地址,对于IBinder类型的数据以及FileDescriptor使用的是mObjects内存地址。后者 是通过flatten_binder()和unflatten_binder()实现的,目的是反序列化时读出的对象就是原对象而不用重新new一个新对象。

    16.Android 四大组件

    Lemon_95的Android 四大组件

    17.Application context和Activity context的区别有哪些?

    (1)Application的context是一个全局静态变量,官方文档表明,只有当你引用这个context的生命周期超过了当前activity的生命周期,而和整个应用的生命周期挂钩时,才会使用Application的context;
    注:一种推荐的用法就是应用程序注册广播broadcast时,使用这个context。
    (2)引用Application context不会导致内存泄漏,引用activity的context才会导致内存泄漏,当你的引用对象的生命周期超过了当前activity的生命周期时,系统回收你的activity时发现还有一个context的强引用,而不去销毁这个activity,导致内存泄漏;
    注:在开发过程中,需要非常重视这一点,一个全距静态变量在引用context时一定要使用application的context而不使用activity的;
    (3)避免context相关的内存泄漏:
    不要让比Activity声明周期长的对象引用Activity context,即保证引用activity的对象要与activity本身生命周期是一样的;
    对于生命周期长的对象可以使用Application context,
    避免非静态的内部类,尽量使用静态类,避免声明周期问题,注意内部类对外部对象的引用导致的生命周期变化。

    18.请分析一张400*500尺寸的PNG图片加载到程序中占用内存中的大小?

    一张图片占用的内存=图片长度 x 图片宽度 x 单位像素占用的字节数;
    注:图片长度和宽度的单位是像素;
    创建一个Bitmap时,其单位像素占用的字节数由其参数BitmapFactory.Options的inPrefenredConfig变量决定,inPreferredConfig为Bitmap.Config类型,(Bitmap.Config类是个枚举类型),其取值如下图所示:


    在Android4.0之前,Bitmap的内存是分配在Native堆中,调用recycle可以立即释放Native内存;
    在Android4.0开始,Bitmap的内存是分配在dalvik堆中,即java堆中的。

    19.xml三种解析方式的区别sax、pull、dom,官方推荐哪种?]

    (1)解析XML:

    • pull(开发首选);
      原理:pull的解析方式与SAX解析类似,都是基于事件的模式,pull提供了开始元素和结束元素,当某个元素开始时,我们可以调用parser.nextText从XML文档中提取所有字符数据,与sax不同的是,在pull解析过程中触发相应的事件调用方法返回的是数字,且我们需要自己获取产生的事件然后做出相应操作,而不像SAX那样由处理器触发一种事件的方法而执行代码,当解释到一个文档结束时,自动生成EndDocument事件;
      优点:SAX的优点pull都有,而且解析方法比sax更加简单;
      缺点:可扩展性差,无法对xml数内容进行修改;
      使用情境:适用于需要处理大型XML文档、性能要求较高、不需要对解析文档进行修改且不需要对解析文档多次访问的场合。
    • dom;
      原理:基于文档驱动,是先把dom全部文件读入到内存中,构建一个主流内存的树结构,然后使用dom的API遍历所有数据,调用API检索想要的数据和操作数据;
      优点:整个文档树存在内存中,课对XML文档进行操作,删除、修改等待,可多次访问已解析的文档,由于在内存中以树形结构存放,因此检索和更新效率会更高。
      缺点:解析xml文件时会将整个XML文件的内容解析成树形结构存放在内存中并创建新对象,比较消耗时间和内存。
      使用情境:对于像手机这样的启动设备来讲,内存是非常有效的,在XML文档比较小、需要对解析文档进行一定的操作且一旦解析了文档需要多次访问这些数据的情况下可以考虑使用dom方式,因为其检索和解析效率较高。
    • sax;
      原理:基于事件驱动,在读取XML文档内容时,事件源顺序地对文档进行扫描,当扫描到文档的开始与结束(Document)标签、节点元素的开始与结束(Element)标签时,直接调用对应的方法,并将状态信息以参数的形式传递到方法中,然后我们可以依据状态信息来执行相关的自定义操作。
      优点:解析效率高、占存少、灵活性高;
      缺点:解析方式复杂,代码量大,可扩展性差,无法对XML树内容结构进行任何修改;
      使用情境:适用于需要处理大型XML文档、性能要求高、不需要对文档进行修改且不需要对解析文档多次访问的场合。
      (2) 解析JSON
    • JSON-java(原生);
    • fastjson;
    • Gson;
      json解析框架的原理:
      利用反射和注解,属性名和注解的对应,如果属性名和字段名一样直接赋值,不一样的话,利用注解,如果属性对应的类型不同,Gson的解决方案是构造一个适配器,fastjson的方案是利用注解进行方法的回调。

    相关文章

      网友评论

          本文标题:Android面试题总结一

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