持续更新Android工程师常见面试题型
Posted AlexP5
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了持续更新Android工程师常见面试题型相关的知识,希望对你有一定的参考价值。
Activity与Fragment的生命周期1.Activity:onCreate-->onStart-->onResume(此时activity处于running阶段)-->(当另外一个activity覆盖原来的时)onPause(activity处于不可见状态)-->onStop-->onDestroy
onPause(当activity被重新激活)-->onResume
onStop(当不可见的activity被重新激活)-->onRestart-->onStart-->onResume
2.Fragment:onAttach-->onCreate-->onCreateView-->onActivityCreated-->onStart-->onResume(Fragment处于激活阶段)-->(Fragment被removed或replace掉)onPause-->onStop-->onDestroyView-->onDestroy-->onDetach
onDestroyView(Fragment重新被显示出来)-->onCreateView-->onActivityCreated-->onStart-->onResume
Service
1.Service还是运行在主线程当中的,所以如果需要执行一些复杂的逻辑操作,最好在服务的内部手动创建子线程进行处理,否则会出现UI线程被阻塞的问题
2.Service与Activity怎么实现通信:
1)添加一个继承Binder的内部类,重写Service的onBind方法,返回我们刚刚定义的那个内部类实例,Activity中创建一个ServiceConnection的匿名内部类,并且重写里面的onServiceConnected方法和onServiceDisconnected方法
2)通过BroadCast的形式当我们的进度发生变化的时候我们发送一条广播,然后在Activity的注册广播接收器,接收到广播之后更新视图
3.IntentService是Service的子类,是一个异步的,会自动停止的服务,很好解决了传统的Service中处理完耗时操作忘记停止并销毁Service的问题,IntentService不会阻塞UI线程,而普通Serveice会导致ANR(程序无响应)异常
URL与URI
URL是一种具体的URI,它不仅唯一标识资源,而且还提供了定位该资源的信息。URI是一种语义上的抽象概念,可以是绝对的,也可以是相对的,而URL则必须提供足够的信息来定位,所以,是绝对的,而通常说的relative URL,则是针对另一个absolute URL,本质上还是绝对的。
线程与进程
进程是系统分配资源和调度的一个独立的单位,有独立的地址空间,执行开销比线程大,但比线程健壮
线程是进程的一个实体,可以与属于同个进程设为其他线程共享进程的全部资源(共享内存)
线程池
开辟一个内存空间,里面存放一堆线程,由线程池管理器来进行调度,当有线程任务时,从池中取一个来执行,执行完后再扔进池子里,这样能避免反复创建线程对象造成开销过大
AsyncTask与Handler
1.AsyncTask底层是一个线程池,代码封装比handler简单(重写doInBackground实现主要业务操作),但更耗资源,适合简单的异步处理
2.Handler在子线程启动一个子线程Thread,通过子线程的sendMessage()方法将子线程数据传递给主线程以实现动态更新UI数据。每一 个handler都必须关联一个looper,looper负责从其内部的messageQueue中 拿出一个个的message给handler进行处理。
AsyncTask优点:简单快捷,过程可控。缺点:在使用多个异步操作和并需要进行UI变更时,就变得复杂起来Handler优点:结构清晰,功能明确。对于多个后台任务时,简单,清晰。缺点:实现起来较为复杂。
wait()和sleep()的区别
sleep来自Thread类,和wait来自Object类.
调用sleep方法的过程中,线程不会释放对象锁。而调用 wait方法线程会释放对象锁
sleep睡眠后不出让系统资源,wait让出系统资源其他线程可以占用CPU
sleep(ms)需要指定一个睡眠时间,时间一到会自动唤醒
ImageLoader与图片二级缓存机制
1.使用步骤:
1).在Application(为了那些需要保存全局变量设计的基本类,属于系统组件)中进行配置和初始化:
DisplayImageOptions和ImageLoaderConfiguration基于Builder模式进行配置初始参数,然后ImageLoader通过单例模式完成初始化配置工作:
ImageLoader.getInstance().init(ImageLoaderConfiguration config)
2).在需要的地方调用ImageLoader.getInstance().displayImage(String path, ImageView iv, DisplayImageOptions ops)进行图片加载工作
2.内部机制:
开启图片监听-->从内存中获取bitmap-->如果bitmap存在,显示此图片/如果不存在,从本地取或从网上下(分同步和异步),此时显示预设图片-->从文件缓存中读取uri对应的资源,若缓存存在,生成bitmap并显示/若不存在,进行网络下载,同时通过前面的config来决定是否保存到本地,最后将生成的bitmap显示。
ListView的优化问题
1.传统方法
Activity中:
MyAdapter ad = new MyAdapter (context,dataList,R.layout.item)
ad.notifyDataSetChanged();
listView.setAdapter(ad);
MyAdapter中:
MyAdapter extends BaseAdapter
核心方法:View getView(int pos, View convertView, ViewGroup parent)
通过复用converView来减少不必要的view的创建:如果convertView为空,绑定UI,convertView.setTag(holder),否则直接通过tag取到holder(holder里面存有view)
通过ImageLoader或glide来加载getView里面的图片
2.使用 RecyclerView 代替ListView 每个item内容的变动,ListView都需要去调用notifyDataSetChanged来更新全部的item,太浪费性能了。
RecyclerView可以实现当个item的局部刷新,并且引入了增加和删除的动态效果,在性能上和定制上都有很大的改善
View中 setOnTouchListener的执行顺序
onTouch-->onTouchEvent-->onClick
ANR与OOM
1.ANR一般是主线程做了太多耗时工作导致的,解决方法有:onCreate和onResume回调中尽量避免耗时的代码,通过handler或AsyncTask去更新主线程UI、下载网络数据等,还有避免在BroadcastReceiver中做耗时操作(建议使用IntentService处理,因为它是异步的)
2.申请的内存资源超过了MaxHeapSize会造成内存溢出,造成的原因有:Cursor、File等资源对象未关闭,handler内存泄漏,bitmap过大等。解决方法:try catch后,在finally方法中关闭连接,对bitmap进行压缩,提高bitmap的复用率,在onDestroy中执行:handler.removeCallbacksAndMessages(null)以及bitmap.recycle(),涉及大量字符串拼接工作使用StringBuilder等,还有一个策略是善于应用软引用与弱引用。
强引用、软引用、弱引用、虚应用
目的:第一是可以让程序员通过代码的方式决定某些对象的生命周期;第二是有利于JVM进行垃圾回收。
强引用:普通的new 对象(Object o = new Object()),String 赋值(String s = "hello")等
软引用:内存不足时JVM会回收对象,适合用来实现网页缓存、图片缓存等
弱引用:无论内存是否充足都会被JVM回收
虚引用:如果一个对象与虚引用关联,则跟没有引用与之关联一样,在任何时候都可能被垃圾回收器回收,主要用来跟踪对象被垃圾回收器回收的活动
引用队列:强引用跟弱引用可以和引用队列联合使用,当对象被回收时会被加入到引用队列中,而虚引用则必须跟引用队列结合使用才行。
e.g:
String s1= "StrongReference";
System.out.println(s1);
System.gc(); // 通知JVM的gc进行垃圾回收
System.out.println(s1);
SoftReference<String> sr = new SoftReference<String>(new String("SoftReference"));
System.out.println(sr.get());
System.gc();
System.out.println(sr.get());
WeakReference<String> sr2 = new WeakReference<String>(new String("WeakReference"));
System.out.println(sr2.get());
System.gc();
System.out.println(sr2.get());
ReferenceQueue<String> queue = new ReferenceQueue<String>();
PhantomReference<String> pr = new PhantomReference<String>(new String("PhantomReference"), queue);
System.out.println(pr.get());
System.gc();
System.out.println(pr.get());
输出:
StrongReference
StrongReference //不会被回收
SoftReference
SoftReference //内存不足时才会回收
WeakReference
null //无论内存是否足够都会被回收
null //虚引用
null
1.MVC的所有通信都是单向的,view或用户传送指令到controller,controller完成业务逻辑后更新model状态,model再将更新后的数据发送到view,用户得到反馈。缺点:view强依赖于model,导致view里面会包含model信息以及一些业务逻辑。还有就是view层控制能力太弱,所有控制逻辑只能写到activity/fragment中,导致代码过多。
2.MVP将controller改为presenter,各部分间通信是双向的,但model不与view通信。MVP削弱了view的主动性,将fragment放到了view层,activity仅用于创造view层,并将view与model分开,将重要逻辑都交给presenter处理。优点是结构清晰易于维护,但缺点是角色之间通信过于复杂,回调链过长,代码量增加。
网络相关
1.主线程不能访问网络,可通过AsyncTask或Handler在子线程进行操作
2.通过Httpmime框架实现body传参
方法:String postData(String url, Map<String,String>textParam, Map<String,String>fileParam)
步骤:
1) 初始化:开启一个客户端http请求,创建HTTP POST请求,初始化MultipartEntityBuilder builder;
2) 处理参数:StringBody sb, builder.addPart(keyStr,sb) //文本参数; builder.addBinaryBody(keyStr,new File(valueStr)) //文件参数
3) 发出请求:HttpEntity ety = builder.build(); post.setEntity(ety); //设置请求参数 HtttpResponse response = client.execute(post) //发出请求
4) 获取并解析请求:将response解析成String类型即为返回的json数组
3.网络协议相关
(1)TCP/IP是个协议组,可分为:网络层、传输层、应用层
网络层协议:IP, ICMP, ARP, RARP, BOOTP
传输层:TCP, UDP
应用层:FTP, HTTP, TELNET, SMTP, DNS ...
总结:
TCP/IP代表传输控制协议/网际协议,指的是一系列协议。
TCP和UDP使用IP协议从一个网络传送数据包到另一个网络。把IP想像成一种高速公路,它允许其它协议在上面行驶并找到到其它电脑的出口。TCP和UDP是高速公路上的“卡车”,它们携带的货物就是像HTTP,文件传输协议FTP这样的协议等。
TCP和UDP是FTP,HTTP和SMTP之类使用的传输层协议。虽然TCP和UDP都是用来传输其他协议的,它们却有一个显著的不同:TCP提供有保证的数据传输,而UDP不提供。这意味着TCP有一个特殊的机制来确保数据安全的不出错的从一个端点传到另一个端点,而UDP不提供任何这样的保证。
HTTP(超文本传输协议)是利用TCP在两台电脑(通常是Web服务器和客户端)之间传输信息的协议。客户端使用Web浏览器发起HTTP请求给Web服务器,Web服务器发送被请求的信息给客户端。
记住,需要IP协议来连接网络;TCP是一种允许我们安全传输数据的机制,,使用TCP协议来传输数据的HTTP是Web服务器和客户端使用的特殊协议。
Socket 接口是TCP/IP网络的API,Socket接口定义了许多函数或例程,用以开发TCP/IP网络上的应用程序。
HTTP协议是建立在请求/响应模型上的。首先由客户建立一条与服务器的TCP链接,并发送一个请求到服务器,请求中包含请求方法、URI、协议版本以及相关的MIME样式的消息。服务器响应一个状态行,包含消息的协议版本、一个成功和失败码以及相关的MIME式样的消息。
HTTP/1.0为每一次HTTP的请求/响应建立一条新的TCP链接,因此一个包含html内容和图片的页面将需要建立多次的短期的TCP链接。一次TCP链接的建立将需要3次握手。
另外,为了获得适当的传输速度,则需要TCP花费额外的回路链接时间(RTT)。每一次链接的建立需要这种经常性的开销,而其并不带有实际有用的数据,只是保证链接的可靠性,因此HTTP/1.1提出了可持续链接的实现方法。HTTP/1.1将只建立一次TCP的链接而重复地使用它传输一系列的请求/响应消息,因此减少了链接建立的次数和经常性的链接开销。
TCP的三次握手与四次挥手
建立TCP需要三次握手才能建立,而断开连接则需要四次握手。
首先Client端发送连接请求报文,Server段接受连接后回复ACK报文,并为这次连接分配资源。Client端接收到ACK报文后也向Server段发生ACK报文,并分配资源,这样TCP连接就建立了。
断开连接过程如下:
【注意】中断连接端可以是Client端,也可以是Server端。
假设Client端发起中断连接请求,也就是发送FIN报文。Server端接到FIN报文后,意思是说"我Client端没有数据要发给你了",但是如果你还有数据没有发送完成,则不必急着关闭Socket,可以继续发送数据。所以你先发送ACK,"告诉Client端,你的请求我收到了,但是我还没准备好,请继续你等我的消息"。这个时候Client端就进入FIN_WAIT状态,继续等待Server端的FIN报文。当Server端确定数据已发送完成,则向Client端发送FIN报文,"告诉Client端,好了,我这边数据发完了,准备好关闭连接了"。Client端收到FIN报文后,"就知道可以关闭连接了,但是他还是不相信网络,怕Server端不知道要关闭,所以发送ACK后进入TIME_WAIT状态,如果Server端没有收到ACK则可以重传。“,Server端收到ACK后,"就知道可以断开连接了"。Client端等待了2MSL后依然没有收到回复,则证明Server端已正常关闭,那好,我Client端也可以关闭连接了。Ok,TCP连接就这样关闭了!
其中客户端经历的过程:
服务端经历的过程:
【注意】 在TIME_WAIT状态中,如果TCP client端最后一次发送的ACK丢失了,它将重新发送。TIME_WAIT状态中所需要的时间是依赖于实现方法的。典型的值为30秒、1分钟和2分钟。等待之后连接正式关闭,并且所有的资源(包括端口号)都被释放。
【问题1】为什么连接的时候是三次握手,关闭的时候却是四次握手?
答:因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,"你发的FIN报文我收到了"。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。
【问题2】为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态?
答:虽然按道理,四个报文都发送完毕,我们可以直接进入CLOSE状态了,但是我们必须假象网络是不可靠的,有可以最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。
(2)Socket
Socket 是应用层与 TCP/IP 协议族通信的中间软件抽象层,它是一组接口(API)。在设计模式中, Socket 其实就是一个门面模式,它把复杂的 TCP/IP 协议族隐藏在 Socket 接口后面,对用户来说,一组简单的接口就是全部,让 Socket 去组织数据,以符合指定的协议。以上是关于持续更新Android工程师常见面试题型的主要内容,如果未能解决你的问题,请参考以下文章