android程序员面试宝典


    以下问题对做android的都一个自我检测和反馈的功能,其中有很多问题

可以自我扩展和补充。



1.AsyncTask应用场景,优点,缺点?

AsyncTask 运用的场景就是我们需要进行一些耗时的操作,耗时操作完成后更新主线程,或者在操作过程中对主线程的UI进行更新。

缺陷:AsyncTask中维护着一个长度为128的线程池,同时可以执行5个工作线程,还有一个缓冲队列,当线程池中已有128个线程,缓冲队列已满时,如果此时向线程提交任务,将会抛出Reject

解决:由一个控制线程来处理AsyncTask的调用判断线程池是否满了,如果满了则线程睡眠否则请求AsyncTask继续处理。


2.activity四种启模式

1,standard标准模式:
每次启动一个Activity都会重新创建一个实例,即调用Activity创建时的生命周期方法onCreate,onStart,onResume;被启动的Activity会自动添加到启动它的Activity的任务栈中,因此用ApplicationContext启动standard模式的Activity时会报错(Context没有所谓的任务栈)
2,singleTop栈顶复用模式:
新启动的Activity已经位于任务栈的栈顶,那么此Activity将不会被重建,而是会回调其onNewIntent方法,如果新启动的Activity不是位于栈顶,此时将重新创建新的Activity实例并添加到栈顶.
3,singleTask栈内复用模式:
这是一种简单的单例模式,这种模式下只要被启动的Activity位于栈内,那么无论它是否位于栈顶都不会重新创建新的Activity实例,而是直接将其调回到栈顶并回调其onNewIntent方法,如果在其上有其他Activity的时候会将这些Activity进行出栈处理
4,singleInstance单实例模式:
这是一种加强的singleTask模式,除了具有singleTask的特点外还加了一点,具体此模式的Activity会单独位于一个独立的任务栈,如ActivityA为singleInstance启动模式,当A启动后,系统会单独为其建一个任务栈,A将独自位于这个任务栈中,以后的请求均不会创建新的Activity直至这个任务栈被销毁.

点击打开链接


3.如何避免 OOM 异常

首先OOM是什么?

扫描二维码关注公众号,回复: 929403 查看本文章

当程序需要申请一段“大”内存,但是虚拟机没有办法及时的给到,即使做了GC操作以后

这就会抛出 OutOfMemoryException 也就是OOM

Android的OOM怎么样?

为了减少单个APP对整个系统的影响,android为每个app设置了一个内存上限。

public void getMemoryLimited(Activity context)

    {

        ActivityManager activityManager =(ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);

 System.out.println(activityManager.getMemoryClass());

System.out.println(activityManager.getLargeMemoryClass());  System.out.println(Runtime.getRuntime().maxMemory()/(1024*1024));

    }

通过这段代码程序运行我们可以获取到当前APP的内存上限

如何避免OOM

减少内存对象的占用

I.ArrayMap/SparseArray代替hashmap

II.避免在android里面使用Enum

III.减少bitmap的内存占用

inSampleSize:缩放比例,在把图片载入内存之前,我们需要先计算出一个合适的缩放比例,避免不必要的大图载入。

decode format:解码格式,选择ARGB_8888/RBG_565/ARGB_4444/ALPHA_8,存在很大差异。

IV.减少资源图片的大小,过大的图片可以考虑分段加载

内存对象的重复利用

大多数对象的复用,都是利用对象池的技术。

I.listview/gridview/recycleview contentview的复用

II.inBitmap 属性对于内存对象的复用ARGB_8888/RBG_565/ARGB_4444/ALPHA_8

这个方法在某些条件下非常有用,比如要加载上千张图片的时候。

III.避免在ondraw方法里面 new对象

IV.StringBuilder 代替+



4.Framework 工作方式及原理,Activity 是如何生成一个 view 的,机制是什么


Framework是android 系统对 linux kernel,lib库等封装,提供WMS,AMS,bind机制,handler-message机制等方式,供app使用。

简单来说framework就是提供app生存的环境。

1)Activity在attch方法的时候,会创建一个phonewindow(window的子类)

2)onCreate中的setContentView方法,会创建DecorView

3)DecorView 的addview方法,会把layout中的布局加载进来。


5.内存溢出和内存泄漏有什么区别?何时会产生内存泄漏?内存优化有哪些方法?

内存溢出通俗理解就是软件(应用)运行需要的内存,超出了它可用的最大内存。

内存泄漏就是我们对某一内存空间的使用,使用完成后没有释放。

内存优化:Android中容易内存溢出的部分,就是图片的加载,我们可以使用图片的压缩加上使用LruCache缓存的目的来控制图片所能够使用的内存。

还有对于比较耗资源的对象及时的关闭,例如Database Conn , 各种传感器 , Service 等等。


6.ListView的优化方案

1、如果自定义适配器,那么在getView方法中要考虑方法传进来的参数contentView是否为null,如果为null就创建contentView并返回,如果不为null则直接使用。在这个方法中尽可能少创建view。

2、给contentView设置tag(setTag()),传入一个viewHolder对象,用于缓存要显示的数据,可以达到图像数据异步加载的效果。

3、如果listview需要显示的item很多,就要考虑分页加载。比如一共要显示100条或者更多的时候,我们可以考虑先加载20条,等用户拉到列表底部的时候再去加载接下来的20条。


7.说说mvc模式的原理,它在android中的运用,android的官方建议应用程序的开发采用mvc模式。何谓mvc?

mvc是model,view,controller的缩写,mvc包含三个部分:

模型(model)对象:是应用程序的主体部分,所有的业务逻辑都应该写在该层。

视图(view)对象:是应用程序中负责生成用户界面的部分。也是在整个mvc架构中用户唯一可以看到的一层,接收用户的输入,显示处理结果。

控制器(control)对象:是根据用户的输入,控制用户界面数据显示及更新model对象状态的部分,控制器更重要的一种导航功能,响应用户出发的相关事件,交给m层处理。

android鼓励弱耦合和组件的重用,在android中mvc的具体体现如下:

1)视图层(view):一般采用xml文件进行界面的描述,使用的时候可以非常方便的引入,当然,如果你对android了解的比较的多了话,就一定可以想到在android中也可以使用JavaScript+html等的方式作为view层,当然这里需要进行java和javascript之间的通信,幸运的是,android提供了它们之间非常方便的通信实现。


2)控制层(controller):android的控制层的重任通常落在了众多的acitvity的肩上,这句话也就暗含了不要在acitivity中写代码,要通过activity交割model业务逻辑层处理,这样做的另外一个原因是android中的acitivity的响应时间是5s,如果耗时的操作放在这里,程序就很容易被回收掉。


3)模型层(model):对数据库的操作、对网络等的操作都应该在model里面处理,当然对业务计算等操作也是必须放在的该层的。



8.描述一下android的系统架构


android系统架构分从下往上为linux 内核层、运行库、应用程序框架层、和应用程序层。

linuxkernel:负责硬件的驱动程序、网络、电源、系统安全以及内存管理等功能。

libraries和 android runtime:libraries:即c/c++函数库部分,大多数都是开放源代码的函数库,例如webkit(引擎),该函数库负责 android网页浏览器的运行,例如标准的c函数库libc、openssl、sqlite等,当然也包括支持游戏开发2dsgl和 3dopengles,在多媒体方面有mediaframework框架来支持各种影音和图形文件的播放与显示,例如mpeg4、h.264、mp3、 aac、amr、jpg和png等众多的多媒体文件格式。android的runtime负责解释和执行生成的dalvik格式的字节码。

applicationframework(应用软件架构),java应用程序开发人员主要是使用该层封装好的api进行快速开发。

applications:该层是java的应用程序层,android内置的googlemaps、e-mail、即时通信工具、浏览器、mp3播放器等处于该层,java开发人员开发的程序也处于该层,而且和内置的应用程序具有平等的位置,可以调用内置的应用程序,也可以替换内置的应用程序。

上面的四个层次,下层为上层服务,上层需要下层的支持,调用下层的服务,这种严格分层的方式带来的极大的稳定性、灵活性和可扩展性,使得不同层的开发人员可以按照规范专心特定层的开发。

android应用程序使用框架的api并在框架下运行,这就带来了程序开发的高度一致性,另一方面也告诉我们,要想写出优质高效的程序就必须对整个 applicationframework进行非常深入的理解。精通applicationframework,你就可以真正的理解android的设计和运行机制,也就更能够驾驭整个应用层的开发。



9.Service和Thread的区别?

servie是系统的组件,它由系统进程托管(servicemanager);它们之间的通信类似于client和server,是一种轻量级的ipc通信,这种通信的载体是binder,它是在linux层交换信息的一种ipc。而thread是由本应用程序托管。1). Thread:Thread 是程序执行的最小单元,它是分配CPU的基本单位。可以用 Thread 来执行一些异步的操作。

2). Service:Service 是android的一种机制,当它运行的时候如果是Local Service,那么对应的 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 做不到的。



10.有没有用过自定义View?

 有用过,一般指定View都需要进行这几个步骤,首先可以自定义一些自己的属性,在resalues/attrs.xml里面定义,然后在layout中使用,在View中通过context.obtainStyledAttributes(attrs,R.styleable.自定义属性的名字)进行获取。
然后在测量onMeasure,一般通过他的三个模式(EXACTLY,AT_MODE,,UNSPECIFIED)进行测量,调用setMeasuredDimension进行传入设置的值。
接着如果是ViewGroupt 的话我们还需要设置下子View的位置,一般是通过requestLayout去触发onLayout的方法的。
最后在onDraw里面通过Canvas的一些方法进行绘制。
如果需要进行触摸事件的话,一般需要有实现onTouchEvent事件,注意,如果需要多点触摸,需要实现ACTION_POINTER_DOWN和ACTION_POINTER_UP进行处理。


11.使用Handler的时候一般会遇到什么问题?

比如说子线程更新UI,是因为触发了checkThread方法检查是否在主线程更新UI,还有就是子线程中没有Looper,这个原因是因为Handler的机制引起的,因为Handler发送Message的时候,需要将Message放到MessageQueue里面,而这个时候如果没有Looper的话,就无法循环输出MessageQueue了,这个时候就会报Looper为空的错误。


12.怎么在主线程中通知子线程?这样做有什么好处?

可以利用HandlerThread进行生成一个子线程的Handler,并且实现handlerMessage方法,然后在主线程里面也生成一个Handler,然后通过调用sendMessage方法进行通知子线程。同样,子线程里面也可以调用sendMessage方法进行通知主线程。这样做的好处比如有些图片的加载啊,网络的访问啊可能会比较耗时,所以放到子线程里面做是比较合适的。


13.异步处理有几种方式?

可以采用Handler的形式,利用官方提供的HandlerThread类进行声明一个子线程的Handler,然后在Handler里面就可以做耗时的操作了,注意,需要在子线程中提前准备好Looper对象,可以使用Looper.prepare方法,最后需要使用Looper.loop方法进行循环。还可以直接用AsyncTask进行操作,一般会构造函数有三个参数,一个是传入参数,一个是进度,还有一个是结果,然后一般会实现一些方法,比如:execute用来用来执行一个异步任务,就是实现的AsyncTask的类调用的,还有onPreExecute,就是调用后立即执行,doInBackground,在onPreExecute完成后立即执行,用于执行较为费时的操作,此方法将接收输入参数和返回计算结果。onProgressUpdate,可以直接将进度信息更新到UI界面上。onPostExecute,后台结束时候调用的方法,会返回结果。注意,不能执行多次,不然会报错,且必须在UI线程中调用,至于上面提到的方法都不要手动调用。doInBackground方法中不可以更新UI。

(因为Android3.0以后必须要求网络访问在子线程中,不然会抛NetworkOnMainThreadException异常,尝试ANR现象(5秒不响应现象))


14.Fragment的生命周期是怎么样的,跟Activity有什么关系?

Fragment是Activity的一个组件片段,也就是说他的生命周期是依赖于Activity的,但是它比Activity多了几个生命步骤,首先onAttach当fragment加入Activity的时候调用,然后是onCreate进行启动Activity,接着是onCreateView进行绘制View,一般的View就是这里绘制的,然后是onActivityCreated,接着跟Activity的生命周期差不多,调用onStart和onResume,然后是onPause,onStop,如果这个时候需要回收Fragment的时候,就会调用,接着是onDestoryView销毁布局,然后是onDestory和onDetach完成。


15.为什么在Service中创建子线程而不是Activity中?

因为假如在Activity中创建子线程的话,当Activity销毁的时候,这个时候重新再调用该Activity就会重新走新的生命周期,这个时候就无法再重新获取到刚才的子线程,而且如果在一个Activity中创建子线程,另一个Activity也无法操作该子线程,但是Service就不一样,所有的Activity都可以和Service关联,即使是Activity被销毁了,只要再重新建立联系就好了,所以,一般后台任务都是通过Service去控制的。


16.如何解决应用被强制杀死?

如果在每一个Activity的onCreate里判断是否被强杀,冗余了,封装到Activity的父类中,如果被强杀,跳转回主界面,如果没有被强杀,执行Activity的初始化操作,给主界面传递intent参数,主界面会调用onNewIntent方法,在onNewIntent跳转到欢迎页面,重新来一遍流程。


17.Android怎么优化启动速度?

因为Android启动应用程序一般分为两种,一种是冷启动,就是要启动的应用程序没有后台进程的启动,这个时候需要重新分配一个进程给他,所以这个时候会先初始化Application类,再创建和初始化MainAcitvity 类,最后显示到界面上,还有一种是热启动,就是后台还有该应用的进程,比如说按下的home键或者返回键,虽然表面上退出了,但是在任务栈里面仍然还存在的,这个时候就不需要再初始化Application类了,只要重新初始化MainActivity了。因为大多数应用的启动都是冷启动(用户习惯将应用程序在任务栈中删除),所以这个时候可以采取这几个步骤,比如尽量不在Application的构造器,attachBaseContext方法和onCreaete方法中做过多的耗时操作,将一些数据预取放在异步线程中,可以采Callback的方式。优化MainActivity,尽量不要在MainActivity的onCreate,onStart和onResume等方法里面做过多的耗时操作。


18.Android怎么加快Activity的显示速度?

首先因为Activiy的显示是在这几个生命周期之间的,onCreate,onStart,和onResume,这个时候我们需要将我们需要初始化的数据分类,比如说我们将一些只需要初始化的一次的数据放到onCreate中,尽量不要在onCreate中做耗时的操作,然后将需要加载比较长时间的数据放到onResume中,可以利用handler的机制进行更新UI,或者放到AsyncTask逐个显示,然后可以设置一些动画进行显示,如果这个时候有许多数据都是一次显示的, 那么可以在onCreate里面进行标记,并且在onResume里面判断是否需要初始化,初始化完成以后就立刻false掉,这样就可以避免多次初始化了,也可以提升Activity的显示速度。


19.如何理解Activity,View,Window三者之间的关系?

这个问题真的很不好回答。所以这里先来个算是比较恰当的比喻来形容下它们的关系吧。Activity像一个工匠(控制单元),Window像窗户(承载模型),View像窗花(显示视图)LayoutInflater像剪刀,Xml配置像窗花图纸。


1:Activity构造的时候会初始化一个Window,准确的说是PhoneWindow。

2:这个PhoneWindow有一个“ViewRoot”,这个“ViewRoot”是一个View或者说ViewGroup,是最初始的根视图。

3:“ViewRoot”通过addView方法来一个个的添加View。比如TextView,Button等

4:这些View的事件监听,是由WindowManagerService来接受消息,并且回调Activity函数。比如onClickListener,onKeyDown等。


20.Touch事件的传递机制 

publicbooleandispatchTouchEvent(MotionEventev);  //用来分派event

publicbooleanonInterceptTouchEvent(MotionEventev);//用来拦截event

publicbooleanonTouchEvent(MotionEventev);//用来处理event


其中Activity和View控件(TextView)拥有分派和处理事件方法,View容器(LinearLayout)具有分派,拦截,处理事件方法。这里也有个比喻:领导都会把任务向下分派,一旦下面的人把事情做不好,就不会再把后续的任务交给下面的人来做了,只能自己亲自做,如果自己也做不了,就只能告诉上级不能完成任务,上级又会重复他的过程。另外,领导都有权利拦截任务,对下级隐瞒该任务,而直接自己去做,如果做不成,也只能向上级报告不能完成任务。



21.view如何刷新?简述什么是双缓冲?


android中实现view的刷新有两个方法,一个是invalidate(),另一个是postInvalidate(),其中前者是在UI线程自身中使用,而后者在非UI线程中使用。

出现屏幕闪烁是图形编程的一个常见问题。当进行复杂的绘制操作时会导致呈现的图像闪烁或具有其他不可接受的外观。双缓冲的使用解决这些问题。双缓冲使用内存缓冲区来解决由多重绘制操作造成的闪烁问题。当使用双缓冲时,首先在内存缓冲区里完成所有绘制操作,而不是在屏幕上直接进行绘图。当所有绘制操作完成后,把内存缓冲区完成的图像直接复制到屏幕。因为在屏幕上只执行一个图形操作,所以消除了由复杂绘制操作造成的图像闪烁问题。

在android中实现双缓冲,可以使用一个后台画布backcanvas,先把所有绘制操作都在这上面进行。等图画好了,然后在把backcanvas拷贝到

与屏幕关联的canvas上去,如下:

Canvas backcanvas = new Canvas(bitmapBase)

backcanvas.draw()...//画图

Canvas c = lockCanvas(null);

c.drawbitmap(bitmapBase);//把已经画好的图像输出到屏幕上

unlock(c)....



22.谈谈Android的IPC(进程间通信)机制


IPC 是内部进程通信的简称,是共享”命名管道”的资源。Android 中的 IPC 机制是为了

让 Activity 和 Service 之间可以随时的进行交互,故在 Android 中该机制,只适用于 Activity和 Service 之间的通信,类似于远程方法调用,类似于 C/S 模式的访问。通过定义 AIDL 接口文件来定义 IPC 接口。Servier 端实现 IPC 接口,Client 端调用 IPC 接口本地代理。



23.try catch finally的执行顺序

try { //执行的代码,其中可能有异常。一旦发现异常,则立即跳到catch执行。否则不会执行catch里面的内容 }

catch { //除非try里面执行代码发生了异常,否则这里的代码不会执行 }

finally { //不管什么情况都会执行,包括try catch 里面用了return ,可以理解为只要执行了try或者catch,就一定会执行 finally


24.Anr发生原则,发生原因,和避免方法?

1.发生原则

1.只有主线程才会产生ANR,主线程就是UI线程;

2.必须发生某些输入事件或特定操作,比如按键或触屏等输入事件,在BroadcastReceiverService的各个生命周期调用函数;

3.上述事件响应超时,不同的context规定的上限时间不同

    a.主线程对输入事件5秒内没有处理完毕

    b.主线程在执行BroadcastReceiveronReceive()函数时10秒内没有处理完毕

    c.主线程在Service的各个生命周期函数时20秒内没有处理完毕。

那么导致ANR的根本原因是什么呢?

1.主线程执行了耗时操作,比如数据库操作或网络编程

2.其他进程(就是其他程序)占用CPU导致本进程得不到CPU时间片,比如其他进程的频繁读写操作可能会导致这个问题。

2.导致ANR的原因有如下几点:

1.耗时的网络访问

2.大量的数据读写

3.数据库操作

4.调用threadjoin()方法、sleep()方法、wait()方法或者等待线程锁的时候

5.service binder的数量达到上限

6.system server中发生WatchDog ANR

7.service忙导致超时无响应

8.其他线程持有锁,导致主线程等待超时

9.其它线程终止或崩溃导致主线程一直等待

3.那么如何避免ANR的发生呢或者说ANR的解决办法是什么呢?

1.避免在主线程执行耗时操作,所有耗时操作应新开一个子线程完成,然后再在主线程更新UI



猜你喜欢

转载自blog.csdn.net/zhuxingchong/article/details/78769550