Android面试(一)

说明

最近由于自己换了一份工作,这时就有一些小伙伴问我Android的面试会问什么,这次就先给大家展示一下一些简单的Android面试题吧,以后会持续更新,希望对有需要的朋友有帮助。

面试题##

sp操作apply和commit的区别:

1. apply没有返回值而commit返回boolean表明修改是否提交成功 
2. apply是将修改数据原子提交到内存, 而后异步真正提交到硬件磁盘, 
   而commit是同步的提交到硬件磁盘,因此,在多个并发的提交commit的时候,他们会等待正在处理的commit保存到磁盘后在操作,从而降低了效率。
   而apply只是原子的提交到内存,后面有调用apply的函数的将会直接覆盖前面的内存数据,这样从一定程度上提高了很多效率。 
3. apply方法不会提示任何失败的提示。 
   由于在一个进程中,sharedPreference是单实例,一般不会出现并发冲突,如果对提交的结果不关心的话,建议使用apply,当然需要确保提交成功且有后续操作的话,还是需要用commit的。

Activity和Fragment生命周期:
Activity: onCreate(), onStart(), onResume(), onPause(), onStop(), onDestory(),
Fragment: onAttach(), onCreate(), onCreateView(), onActivityCreated(), onStart(), onResume(), onPause(), onStop(), onDestoryView(), onDestory(), onDetach()

两个Activity的跳转执行的方法:
一般情况比如说有两个activity,分别叫A,B ,当在A里面激活B组件的时候, A 会调用 onPause()方法,然后B 调用onCreate() ,onStart(), OnResume() ,
这个时候B覆盖了窗体, A会调用onStop()方法. 如果B呢 是个透明的,或者是对话框的样式, 就不会调用onStop()方法.

横竖屏切换:
设置 Activity 的 android:configChanges="orientation|keyboardHidden|screenSize"时,切屏不会重新调用各个生命周期,只会执行 onConfigurationChanged 方法

Activity启动模式(singleTask, singleInstance, singleTop, standard)
Standard: 不管有没有已存在的实例,都生成新的实例。
SingleTop:如果发现有对应的 Activity 实例正位于栈顶,则重复利用,不再生成新的实例。不是位于栈顶,于是重新生成一个实例。
SingleTask:如果发现有对应的 Activity 实例,则使此 Activity 实例之上的其他 Activity 实例统统出栈,使此 Activity 实例成为栈顶对象,显示到幕前。
SingleInstance:它会启用一个新的栈结构,将 Activity 放置于这个新的栈结构中,并保证不再有其他 Activity 实例进入。

一个启动模式为 singleTop 的 activity,如果再试图启动会怎样
onNewIntent()
该方法被启动模式设置为“singleTop”的 Activity 回调,或者当通过设置 Intent.FLAG_ACTIVITY_SINGLE_TOP
的 Intent 启动 Activity 时被回调。在任何情况下,只要当栈顶的 Activity 被重新启动时没有重新创建一个新的 Activity
实例而是依然使用该 Activity 对象,那么 onNewIntent(Intent)方法就会被回调。
当一个 Activity 接收到新 Intent 的时候会处于暂停状态,因此你可以统计到 onResume()方法会被再次执行,当然这个执行是在 onNewIntent 之后的。
注意:如果我们在 Activity 中调用了 getIntent()方法,那么返回的 Intent 对象还是老的 Intent(也就是第一
次启动该 Activity 时的传入的 Intent 对象),但是如果想让 getIntent()返回最新的 Intent,那么我们可以通过setIntent(Intent)方法设置。

FragmentPagerAdapter和FragmentStatePagerAdapter:
FragmentPagerAdapter:对于不再需要的fragment,选择调用detach方法,仅销毁视图,并不会销毁fragment实例。
FragmentStatePagerAdapter:会销毁不再需要的fragment,当当前事务提交以后,会彻底的将fragmeng从当前Activity的FragmentManager中移除,state标明,
销毁时,会将其onSaveInstanceState(Bundle outState)中的bundle信息保存下来,当用户切换回来,可以通过该bundle恢复生成新的fragment,也就是说,你可以在onSaveInstanceState(Bundle outState)方法中保存一些数据,在onCreate中进行恢复创建。
如上所说,使用FragmentStatePagerAdapter当然更省内存,但是销毁新建也是需要时间的。
一般情况下,如果你是制作主页面,就3、4个Tab,那么可以选择使用FragmentPagerAdapter,
如果你是用于ViewPager展示数量特别多的条目时,那么建议使用FragmentStatePagerAdapter。

广播的注册?方式(静态和动态)有什么区别:
(1)动态注册广播不是常驻型广播,也就是说广播跟随Activity的生命周期。注意在Activity结束前,移除广播接收器。
静态注册是常驻型,也就是说当应用程序关闭后,如果有信息广播来,程序也会被系统调用自动运行。
(2)当广播为有序广播时:优先级高的先接收(不分静态和动态)。同优先级的广播接收器,动态优先于静态
(3)同优先级的同类广播接收器,静态:先扫描的优先于后扫描的,动态:先注册的优先于后注册的。
(4)当广播为默认广播时:无视优先级,动态广播接收器优先于静态广播接收器。同优先级的同类广播接收器,静态:先扫描的优先于后扫描的,动态:先注册的优先于后册的。

多进程的缺点及改进方法:
缺点:1. Application多次创建 ; 2. 静态成员,全局变量,单例会失效 ;3. 共享文件的问题; 4.断点调试问题
改进方法: 在onCreate()的时候判断进程ID来创建;利用Intent、AIDL等来通信; 主程访问; 回归统一进程调试在改回来。

自定义控件的步骤 (onMeasure, onLayout,onDraw)
measure(widthMeasureSpec,heightMeasureSpec)--------->onMeasure(widthMeasureSpec,heightMeasureSpec)

  • MeasureSpec:父控件对子View的测量宽高的期望--------->一个32位的数,前两位表示测量模式——SpecModel;后30位表示测量大小SpecSize。
    • SpecModel测量模式有三种:1、Exactly,精确的,有300px,match_parent。2、AtMost,最大是多少,有wrap_content。3、Unspecfide,无限大。
    • 测量结束后能获取测量的宽和测量的高,也就是widthSpecSize和heightSpecSize。通过getMeasureWidth和getMeasureHeight方法。
    • 代表测量结束的方法setMeasureDimetion(widthSpecSize,heightSpecSize)。
  • 自定义ViewGroup一定要重写onMeasure方法,如果不重写则子View获取不到宽和高。重写是在onMeasure方法中调用measureChildern()方法,遍历出所有子View并对其进行测量。
  • 自定义View如果要使用wrap_content属性的话,则需重写onMeasure方法。
    layout(l,t,r,b)------->onLayout(l,t,r,b)。
  • 该方法其实是指定控件摆放的位置。
  • 自定义View一般不重写onLayout方法。由父控件给其确定位置。
  • 自定义ViewGroup需要重写onLayout方法,要不然子控件不知道摆放在哪。
  • layout方法中调用了setFram(l,t,r,b)方法。该方法内部的实现{mLeft = l;mRight = r;mTop = t;mBottom = b};该方法代表布局完成。
  • 布局完成之后,能够获取getWidth()和getHeight()的值。这两个方法的实现分别是return mRight - mLeft; return mBottom - mTop;
    draw(Canvas canvas)。
  • 一共有六步
      1. 绘制背景
      1. If necessary, save the canvas’ layers to prepare for fading
      1. 绘制View的内容, 在onDraw(canvas)方法中完成
      1. 绘制子View的内容 ,dispatchDraw。
      1. If necessary, draw the fading edges and restore layers
      1. 绘制装饰品(如滚动条)

Handle机制
这里面除了 Handler、Message 外还有隐藏的 Looper 和 MessageQueue 对象。
在主线程中 Android 默认已经调用了 Looper.preper()方法,调用该方法的目的是在 Looper 中创建
MessageQueue 成员变量并把 Looper 对象绑定到当前线程中。当调用 Handler 的 sendMessage(对象)方法的时候就将 Message 对象添加到了 Looper 创建的 MessageQueue 队列中,
同时给 Message 指定了 target 对象,其实这个 target 对象就是 Handler 对象。主线程默认执行了 Looper.looper()方法,该方法从 Looper 的成员变量
MessageQueue 中取出 Message,然后调用 Message 的 target 对象的 handleMessage()方法。这样就完成了整个消息机制。

为什么我们看不见messageQueue,Looper。
* 主线程给我们准备了Looper对象。
* 在Looper的构造函数里面生成了messageQueue对象。
* 主线程中的Looper对象存放在哪里?----->ThreadLocal的values里面。
怎么样来实现主线程向子线程发消息和子线程之间发消息?
* 在子线程new Handler然后在主线程发消息,在子线程处理消息。
* 会报错,因为子线程没有Looper对象,所以不能new Handler,调用Looper.prepare方法,生成Looper。
怎么样来确保发送消息和接收消息的Handler是同一个handler。
* message的target属性就是handler。

view层级优化(merge,ViewStub,include)
1:标签在布局优化中是使用最多的一个标签了,它就是为了解决重复定义布局的问题。如果想使用标签覆盖嵌入布局root布局属性,必须同时覆盖layout_height和layout_width属性,否则会直接报编译时语法错误
2:标签都是与标签组合使用的,它的作用就是可以有效减少View树的层次来优化布局。
对比截图就可以发现上面的四层结构,现在已经是三层结构了。当我们使用标签的时候,系统会自动忽略merge层级,而把TextView直接放置与平级。
标签在使用的时候需要特别注意布局的类型,例如我的标签中包含的是一个LinearLayout布局视图,布局中的元素是线性排列的,如果嵌套进主布局时,include标签父布局时FrameLayout,这种方式嵌套肯定会出问题的,merge中元素会按照FrameLayout布局方式显示。所以在使用的时候,标签虽然可以减少布局层级,但是它的限制也不可小觑。
只能作为XML布局的根标签使用。当Inflate以开头的布局文件时,必须指定一个父ViewGroup,并且必须设定attachToRoot为true。
View android.view.LayoutInflater.inflate(int resource, ViewGroup root, boolean attachToRoot)
root不可少,attachToRoot必须为true。
3:在开发过程中,经常会遇到这样一种情况,有些布局很复杂但是却很少使用。例如条目详情、进度条标识或者未读消息等,这些情况如果在一开始初始化,虽然设置可见性View.GONE,但是在Inflate的时候View仍然会被Inflate,仍然会创建对象,由于这些布局又想到复杂,所以会很消耗系统资源。
ViewStub就是为了解决上面问题的,ViewStub是一个轻量级的View,它一个看不见的,不占布局位置,占用资源非常小的控件。

SurfaceView
SurfaceView是View类的子类,可以直接从内存或者DMA等硬件接口取得图像数据,是个非常重要的绘图视图。它的特性是:可以在主线程之外的线程中向屏幕绘图上。这样可以避免画图任务繁重的时候造成主线程阻塞,从而提高了程序的反应速度。在游戏开发中多用到SurfaceView,游戏中的背景、人物、动画等等尽量在画布canvas中画出。
继承SurfaceView并实现SurfaceHolder.Callback接口 ----> SurfaceView.getHolder()获得SurfaceHolder对象 ---->SurfaceHolder.addCallback(callback)添加回调函数---->SurfaceHolder.lockCanvas()获得Canvas对象并锁定画布----> Canvas绘画 ---->SurfaceHolder.unlockCanvasAndPost(Canvas canvas)结束锁定画图,并提交改变,将图形显示。

anroid提供的网络接?了解(HttpClient, HttpURLConnection)
在Android 2.2版本之前,HttpClient拥有较少的bug,因此使用它是最好的选择。
而在Android 2.3版本及以后,HttpURLConnection则是最佳的选择。
它的API简单,体积较小,因而非常适用于Android项目。压缩和缓存机制可以有效地减少网络访问的流量,在提升速度和省电方面也起到了较大的作用。
对于新的应用程序应该更加偏向于使用HttpURLConnection,因为在以后的工作当中我们也会将更多的时间放在优化HttpURLConnection上面。
1.HttpClient是apache的开源实现,而HttpUrlConnection是安卓标准实现,安卓SDK虽然集成了HttpClient,但官方支持的却是HttpUrlConnection;
2.HttpUrlConnection直接支持GZIP压缩;HttpClient也支持,但要自己写代码处理;我们之前测试HttpUrlConnection的GZIP压缩在传大文件分包trunk时有问题,只适合小文件,不过这个BUG后来官方说已经修复了;
3.HttpUrlConnection直接支持系统级连接池,即打开的连接不会直接关闭,在一段时间内所有程序可共用;HttpClient当然也能做到,但毕竟不如官方直接系统底层支持好;
4.HttpUrlConnection直接在系统层面做了缓存策略处理,加快重复请求的速度。

对https支持:

AIDL:

tcp/udp了了解
连接: tcp三次握手连接,四次握手释放连接
资源: tcp多
结构: tcp复杂 20字节 udp8字节
模式: tcp流结构传输, udp报文传输
可靠性: tcp可靠(停止等待、连续ARQ、滑动窗口、累积确认)慢开始、拥塞避免、快重传、快恢复(3个连续重传请求)

设计模式:
单例(懒汉式、饿汉式)
工厂模式:普通工厂(根据字符类型判断) 多工厂(分别创建)、静态工厂(静态方法)、抽象工厂(分别实现接口)
建造者模式:
装饰着模式:顾名思义,装饰模式就是给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例。
观察者模式:
适配器模式:

第三方框架框架: Rajava,Volley, Okhttp、EventBus、Retrofit、Glide、Picasso等等

RecycleView与ListView;GridView之对比

内存泄漏和内存溢出、OOM
1、一次性加载过多内容。

  • 2、内存泄漏。
    • 1、资源未关闭。---->游标、流、要调用recycle方法的。
    • 2、生命周期长的对象持有了生命周期短的对象的引用。
      • 静态成员变量持有类引用。
      • 非静态内部类持有外部类的引用。
    • 3、线程生命周期不可控。 无限循环动画。

MVC和MVP模式。

  • 1、MVC模式。
    • 目的------->将数据和视图分离。
    • 结构。
      • M------>Model层,包含网络请求数据、数据库读取、文件读写等等。
      • V------>View层,在android工程当中所代表的就是layout文件。
      • C------>Controler层,在android工程当中是Activity和Fragment。
    • 缺点------>Activity不仅仅做了控制层的工作,很多时候还要做View层的工作,因为在android的MVC当中View层是写死的,不灵活Activity和Fragment。
  • 2、MVP模式。
    • 目的:解决MVC中的缺陷。
    • 结构。
      • M------>Model层,包含网络请求数据、数据库读取、文件读写等等。
      • V------>View层,Activity和Fragment。
      • P------->Presenter,自己写的Presenter类。在这个类的构造函数中传入Model和View的对象。将数据显示到视图上。 presenter对象在哪儿new------->activity中。
    • 缺点。这是一个面向接口编程的模式,使用View层和P层的时候都会写接口和实现类,会增加很多的文件和类

线程池

  • 为什么使用线程池?
    • 1、开启线程和销毁线程都有资源消耗。
    • 2、开启线程需要时间。虽然时间短,但蚊子腿上也有肉。
    • 3、便于线程的管理。
  • 怎么去创建线程池。------>参考谷歌市场。
  • 线程池的运行机制?
    • 首先几个重要的参数:corpalsize(核心线程数)、maxSize(最大线程数)、keepAliveTime(存活时间)。
    • 当任务数小于核心线程数的时候?-------->创建线程执行任务。
    • 当任务数大于核心线程数但是小于最大线程数的时候?------->放入队列中等待。
    • 如果队列满了?--------->创建线程执行任务。
    • 当任务数大于最大线程数?------->抛出异常。

AsyncTask(三个参数,onPreExecute(第一个参数),doInBackground, onProgressUpdate(第二个参数),onPostExecute(第三个参数))
核心线程数 cpu + 1, 最大线程数2n+1, 队列最大128 执行过程是mTask队列offer出一个任务加到请求队列去执行

String、StringBuilder和StringBuffer

  1. String 类
      String的值是不可变的,这就导致每次对String的操作都会生成新的String对象,不仅效率低下,而且大量浪费有限的内存空间。
    String a = “a”; //假设a指向地址0x0001
    a = “b”;//重新赋值后a指向地址0x0002,但0x0001地址中保存的"a"依旧存在,但已经不再是a所指向的,a 已经指向了其它地址。
    因此String的操作都是改变赋值地址而不是改变值操作。

  2. StringBuffer是可变类,和线程安全的字符串操作类,任何对它指向的字符串的操作都不会产生新的对象。 每个StringBuffer对象都有一定的缓冲区容量,当字符串大小没有超过容量时,不会分配新的容量,当字符串大小超过容量时,会自动增加容量。

    StringBuffer buf=new StringBuffer(); //分配长16字节的字符缓冲区
    StringBuffer buf=new StringBuffer(512); //分配长512字节的字符缓冲区
    StringBuffer buf=new StringBuffer(“this is a test”)//在缓冲区中存放了字符串,并在后面预留了16字节的空缓冲区。

3.StringBuffer
  StringBuffer和StringBuilder类功能基本相似,主要区别在于StringBuffer类的方法是多线程、安全的,而StringBuilder不是线程安全的,相比而言,StringBuilder类会略微快一点。对于经常要改变值的字符串应该使用StringBuffer和StringBuilder类。

4.线程安全
StringBuffer 线程安全
StringBuilder 线程不安全

5.速度
一般情况下,速度从快到慢:StringBuilder>StringBuffer>String,这种比较是相对的,不是绝对的。

6.总结
(1).如果要操作少量的数据用 = String
(2).单线程操作字符串缓冲区 下操作大量数据 = StringBuilder
(3).多线程操作字符串缓冲区 下操作大量数据 = StringBuffer

总结

这次就先准备了这么多,以后会持续更新的,这里面有还几个问题还没有写出来,我会在面试题二中给出答案,敬请期待。

如有错误,敬请批评指正,或者有更好的知识点,也可反馈给我收纳,谢谢!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值