资料来源:https://www.jianshu.com/p/916b2f8e1456
Activity
Activity的生命周期?
onCreate() 表示Activity正在被创建做初始化工作,如setContentView界面资源、初始化数据。Bundle参数为该Activity上次被异常情况销毁时保存的状态信息。
onStart() 表示Activity正在启动,这时Activity可见但不在前台。无法与用户交互。
onResume() 表示Activity获取焦点,可见且在前台活动。可以与用户交互。
onPause() 表示Activity正在停止,可做数据存储,停止动画等操作。
onStop() 表示Activity即将停止,可做取消网络连接、注销广播接收器等。
onDestroy() 表示Activity即将销毁,可做释放资源等操作。
onRestart() 表示Activity重新启动,由不可见到可见时会调用,
onStart()和onResume()/onPause()和onStop()的区别?
onStart()与onStop()是从Activity是否可见这个角度调用
onResume()和onPause()是从Activity是否显示在前台这个角度来回调
Activity A启动另一个Activity B会回调哪些方法?如果Activity B是完全透明呢?如果启动的是一个Dialog呢?
会调用A 的onPause() -> B 的onCreate() -> onStart() -> onResume() -> A的onStop()
不会调用A 的onStop()方法,Dialog同理。
谈谈onSaveInstanceState()方法?何时会调用?
当非人为终止Activity时会调用onSavaInstanceState()来保存状态。(系统配置发生改变时导致Activity被杀死并重新创建、资源内存不足导致低优先级的Activity被杀死)
该方法在onStop()方法之前调用,与onPause()没有时序关系。
onSaveInstanceState()与onPause()的区别?
onSaveInstanceState()适用于非人为终止Activity时临时性状态的保存。
onPause() 适用于对数据的持久化保存,停止动画等。
说下Activity的四种启动模式?
standard标准模式、默认模式:每次启动一个Activity都会创建一个新的实例,并放入栈顶位置。
singleTop栈顶复用模式:如果启动的Activity已经位于任务栈的栈顶,就不会重新创建实例,而是调用onNewIntent(intent)方法。反之创建新的实例加入栈中。
singleTask栈内复用模式:只要该Activity在一个任务栈中存在,就不会重新创建新的实例。并把栈中在其之上的其他Activity Destroy掉,调用onNewIntent(intent)方法。如果不存在,创建新的实例并入栈。
singleInstance单实例模式:Activity只能单独位于一个任务栈中,并且这个任务栈只存在这一个实例。
谈谈singleTop和singleTask的区别以及应用场景
singleTop允许同个Activity在栈中可以有多个实例,即可以重复创建;为防止快速点击时多次startActivity,可以将目标Activity设置为singleTop。
singleTask同个Activity在栈中只有一个实例,即不存在重复创建;常用于主页和登陆页
了解哪些Activity启动模式的标记位?
Intent.FLAG_ACTIVITY_NEW_TASK:使用一个新的Task来启动Activity
Intent.FLAG_ACTIVITY_SINGLE_TOP:类似singleTop
Intent.FLAG_ACTIVITY_CLEAR_TOP:类似singleTask
Intent.FLAG_ACTIVITY_NO_HISTORY:使用这种模式启动Activity,当该Activity启动其他Activity后,该Activity就消失了,不会保留在Task栈中。
onNewIntent()调用时机?
启动模式为singleTop或singleTask的Activity会出现回调onNewIntent()的情况
如何避免配置改变时Activity重建?
在AndroidManifest.xml中对应的Activity中设置android:configChanges="orientation|keyboardHidden|screenSize"。此时发生屏幕旋转,该Activity不会被系统杀死和重建,会调用onConfigurationChanged方法。
优先级低的Activity在内存不足被回收后怎样做可以恢复到销毁前状态?
低优先级的Activity在内存不足被回收后重新启动会引发Activity重建,会调用onRestoreInstanceState方法,并将onSaveInstanceState方法保存的Bunble对象作为参数传到onRestoreInstanceState 和 onCreate方法中。因此可通过onRestoreInstanceState(Bundle savedInstanceState)和onCreate((Bundle savedInstanceState)来判断Activity是否被重建,并取出数据进行恢复。在onCreate中需要判断savedInstanceState是否为空。
如何启动其他应用的Activity?
在保证有权限访问的情况下,通过隐式Intent进行目标Activity的IntentFilter匹配
Activity的启动过程?
调用startActivity()后经过重重方法会转移到ActivityManagerService的startActivity(),并通过一个IPC回到ActivityThread的内部类ApplicationThread中,并调用其scheduleLaunchActivity()将启动Activity的消息发送并交由Handler H处理。Handler H对消息的处理会调用handleLaunchActivity()->performLaunchActivity()得以完成Activity对象的创建和启动。
Fragment
谈一谈Fragment的生命周期?
Fragment生命周期方法:onAttach() -> onCreate() -> onCreateView() -> onActivityCreated() -> onStart() -> onResume() -> onPause() -> onStop() -> onDestroyView() -> onDestroy() -> onDetach()
onAttach() 当Fragment和Activity建立关联时调用
onCreateView() 当Fragment创建视图时调用
onActivityCreated() 当与Fragment相关联的Activity完成onCreate()之后调用
onDestroyView() 在Fragment中的布局被移除时调用
onDetach() 当Fragment和Activity解除关联时调用
Activity和Fragment的异同?
相同点:它们都可包含布局、可有自己的生命周期,Fragment可看似迷你活动。
不同点:Fragment是依附在Activity上的,多了些和宿主Activity相关的生命周期方法,如onAttach()、onCreateView()、onActivityCreated()、onDestroyView()、onDetach();另外,Fragment的生命周期方法是由宿主Activity而不是操作系统调用的。
Activity和Fragment的关系?
它可作为Activity界面的组成部分,可在Activity运行中实现动态地加入、移除和交换。
一个Activity中可同时出现多个Fragment,一个Fragment也可在多个Activity中使用。
Activity的FragmentManager负责调用队列中Fragment的生命周期方法,只要Fragment的状态与Activity的状态保持了同步,宿主Activity的FragmentManager便会继续调用其他生命周期方法以继续保持Fragment与Activity的状态一致。
何时会考虑使用Fragment?
用两个Fragment封装两个界面模块,这样只使一套代码就能适配两种设备,达到两种界面效果
单一场景切换时使用Fragment更轻量化,如ViewPager和Fragment搭配使用
Service
谈一谈Service的生命周期?
onCreate():服务第一次被创建时调用
onStartCommand():服务启动时调用
onBind():服务被绑定时调用
onUnBind():服务被解绑时调用
onDestroy():服务停止时调用
Service的两种启动方式?区别在哪?
第一种:startService()方法可以启动一个Service,并回调服务中的onStartCommand()方法。如果该服务之前还没创建,那么回调的顺序是onCreate() -> onStartCommand();如果服务是开启状态,在次调用startService()不会回调onCreate()方法。服务启动了之后会一直保持运行状,直到 stopService() 或 stopSelf() 方法被调用,服务停止并回调onDestroy()(无论调用多少次startService()方法,只需调用一次stopService()或stopSelf()方法,服务就会停止了)。
第二种:bindService()方法可以绑定一个Service,并回调服务中的onBind()方法。如果该服务之前还没创建,那么回调的顺序是onCreate() -> onBind();如果服务是开启状态,在次调用startService()不会回调onCreate()方法。调用方可以获取到onBind()方法里返回的IBinder对象的实例,从而实现和服务进行通信。只要调用方和服务之间的连接没有断开,服务就会一直保持运行状态,直到调用了 unbindService()方法服务会停止,回调顺序onUnBind() -> onDestroy()。
一个Activty先start一个Service后,再bind时会回调什么方法?此时如何做才能回调Service的onDestroy()方法?
这两种启动方法并不冲突,startService()启动Service之后,再bindService()绑定,此时只会回调onBind()方法
需要同时调用 stopService()和 unbindService()方法才能让服务销毁掉。
Service如何和Activity进行通信?
通过bindService()可以实现Activity调用Service中的方法。
通过广播实现Service向Activity发送消息。
用过哪些系统Service?
WINDOW_SERVICE 管理打开的窗口程序
LAYOUT_INFLATER_SERVICE 取得XML里定义的View
ACTIVITY_SERVICE 管理应用程序的系统状态
POWER_SERVICE 电源服务
ALARM_SERVICE 闹钟服务
NOTIFICATION_SERVICE 状态栏服务
KEYAUARD_SERVICE 键盘锁服务
是否能在Service进行耗时操作?如果非要可以怎么做?
Service默认并不会运行在子线程中,也不运行在一个独立的进程中,它同样执行在主线程中(UI线程)。
手动打开一个子线程,否则有可能出现主线程被阻塞(ANR)的情况。
前台服务是什么?和普通服务的不同?如何去开启一个前台服务?
前台服务的服务状态可以被用户看到。它和普通服务最大的区别是,前者会一直有一个正在运行的图标在系统的状态栏显示,下拉状态栏后可以看到更加详细的信息,且当系统内存不足服务被杀死时,通知会被移除。
创建一个Notification实例,调用startForeground()方法,不需要NotificationManager将通知显示出来。
是否了解ActivityManagerService,谈谈它发挥什么作用?
ActivityManagerService是Android中最核心的服务 , 主要负责系统中四大组件的启动、切换、调度及应用进程的管理和调度等工作。
如何保证Service不被杀死?
在Service的onStartCommand()中设置flages值为START_STICKY,使得Service被杀死后尝试再次启动Service
提升Service优先级,比如设置为一个前台服务
在Activity的onDestroy()通过发送广播,并在广播接收器的onReceive()中启动Service。
BroadcastReceiver
广播有几种形式?什么特点?
普通广播:一种完全异步执行的广播,在广播发出之后,所有的广播接收器几乎都会在同一时刻接收到这条广播消息,因此它们接收的先后是随机的。
有序广播:一种同步执行的广播,在广播发出之后,同一时刻只会有一个广播接收器能够收到这条广播消息,当这个广播接收器中的逻辑执行完毕后,广播才会继续传递,所以此时的广播接收器是有先后顺序的,且优先级(priority)高的广播接收器会先收到广播消息。有序广播可以被接收器截断使得后面的接收器无法收到它。
本地广播:发出的广播只能够在应用程序的内部进行传递,并且广播接收器也只能接收本应用程序发出的广播。
粘性广播:这种广播会一直滞留,当有匹配该广播的接收器被注册后,该接收器就会收到此条广播。
广播的两种注册形式?区别在哪?
静态注册:广播接收器即便程序未启动也能接收到广播。
动态注册:广播接收器必须要在程序启动之后才能接收到广播。当用来注册广播的Activity关掉后,广播也就失效了
ContentProvider & 数据存储
ContentProvider了解多少?
ContentProvider可以让不同应用程序之间进行数据共享,它还可以选择只对哪一部分数据进行共享,从而保证程序中的隐私数据不会有泄漏风险。
Android中提供哪些数据持久存储的方法?
File 文件存储:Context类中提供了openFileInput()和openFileOutput()方法来打开数据文件里的文件IO流。
SharedPreferences存储:一种轻型的数据存储方式,常用来存储一些简单的配置信息,基于XML文件存储key-value键值对数据。
SQLite数据库存储:一款轻量级的关系型数据库,它的运算速度非常快,占用资源很少,在存储大量复杂的关系型数据的时可以使用。
ContentProvider:四大组件之一,用于数据的存储和共享,不仅可以让不同应用程序之间进行数据共享,还可以选择只对哪一部分数据进行共享,可保证程序中的隐私数据不会有泄漏风险。
Java中的I/O流读写怎么做?
Context类中提供了openFileInput()和openFileOutput()方法来打开数据文件里的文件IO流。
bufferedReader.readLine()读
bufferedWriter.write(text)写
SharePreferences适用情形?使用中需要注意什么?
SharePreferences是一种轻型的数据存储方式,适用于存储一些简单的配置信息,如int、string、boolean、float和long。由于系统对SharedPreferences的读/写有一定的缓存策略,即在内存中有一份该文件的缓存,因此在多进程模式下,其读/写会变得不可靠,甚至丢失数据。
了解SQLite中的事务处理吗?是如何做的?
SQLite在做CRDU操作时都默认开启了事务,然后把SQL语句翻译成对应的SQLiteStatement并调用其相应的CRUD方法,此时整个操作还是在rollback journal这个临时文件上进行,只有操作顺利完成才会更新.db数据库,否则会被回滚。
使用SQLite做批量操作有什么好的方法吗?
使用SQLiteDatabase的beginTransaction()方法开启一个事务,将批量操作SQL语句转化成SQLiteStatement并进行批量操作,结束后endTransaction()
如果现在要删除SQLite中表的一个字段如何做?
SQLite数据库只允许增加表字段而不允许修改和删除表字段,只能采取复制表思想,即创建一个新表保留原表想要的字段、再将原表删除。
使用SQLite时会有哪些优化操作?
使用事务做批量操作。
及时关闭Cursor,避免内存泄漏
耗时操作异步化:数据库的操作属于本地IO,通常比较耗时,建议将这些耗时操作放入异步线程中处理
ContentValues的容量调整:ContentValues内部采用HashMap来存储Key-Value数据,ContentValues初始容量为8,扩容时翻倍。因此建议对ContentValues填入的内容进行估量,设置合理的初始化容量,减少不必要的内部扩容操作
使用索引加快检索速度:对于查询操作量级较大、业务对查询要求较高的推荐使用索引
View
MotionEvent是什么?包含几种事件?什么条件下会产生?
ACTION_DOWN:第一个手指触摸屏幕
ACTION_MOVE:手指在屏幕上移动
ACTION_UP:最后一个手指离开屏幕
ACTION_CANCEL:手势被取消,不再接受后续事件;从当前控件转移到外层控件时会触发
ACTION_OUTSIDE:标志着用户触碰到了正常的UI边界
ACTION_POINTER_DOWN:出现一个新的触摸点
ACTION_POINTER_UP:非最后一个手指抬起
scrollTo()和scrollBy()的区别?
scrollBy内部调用了scrollTo,它是基于当前位置的相对滑动;而scrollTo是绝对滑动
两者都只能对view内容进行滑动,而不能使view本身滑动。
Scroller中最重要的两个方法是什么?主要目的是?
在MotionEvent.ACTION_UP事件触发时调用startScroll()方法,该方法并没有进行实际的滑动操作,而是记录滑动相关量
马上调用invalidate/postInvalidate()方法,请求View重绘,导致View.draw方法被执行
紧接着会调用View.computeScroll()方法,此方法是空实现,需要自己处理逻辑。具体逻辑是:先判断computeScrollOffset(),若为true(表示滚动未结束),则执行scrollTo()方法,它会再次调用postInvalidate(),如此反复执行,直到返回值为false。
谈一谈View的事件分发机制?
事件传递顺序:Activity(Window) -> ViewGroup -> View
dispatchTouchEvent:进行事件的分发(传递)。返回值是 boolean 类型,受当前onTouchEvent和下级view的dispatchTouchEvent影响
onInterceptTouchEvent:对事件进行拦截。该方法只在ViewGroup中有,View(不包含 ViewGroup)是没有的。一旦拦截,则执行ViewGroup的onTouchEvent,在ViewGroup中处理事件,而不接着分发给View。且只调用一次,所以后面的事件都会交给ViewGroup处理。
onTouchEvent:进行事件处理。
如何解决View的滑动冲突?
外部拦截法:指点击事件都先经过父容器的拦截处理,如果父容器需要此事件就拦截,否则就不拦截。onInterceptTouchEvent方法
内部拦截法:指父容器不拦截任何事件,而将所有的事件都传递给子容器,如果子容器需要此事件就直接消耗,否则就交由父容器进行处理。子View的dispatchTouchEvent方法并设置requestDisallowInterceptTouchEvent方法,父View需要重写onInterceptTouchEvent方法
谈一谈View的工作原理?
View工作流程简单来说就是,先measure测量,用于确定View的测量宽高,再 layout布局,用于确定View的最终宽高和四个顶点的位置,最后 draw绘制,用于将View 绘制到屏幕上
MeasureSpec是什么?有什么作用?
UNSPECIFIED:父容器不对View有任何限制,要多大有多大。常用于系统内部。
EXACTLY(精确模式):父视图为子视图指定一个确切的尺寸SpecSize。对应LyaoutParams中的match_parent或具体数值。
AT_MOST(最大模式):父容器为子视图指定一个最大尺寸SpecSize,View的大小不能大于这个值。对应LayoutParams中的wrap_content。
作用:通过宽测量值widthMeasureSpec和高测量值heightMeasureSpec决定View的大小
自定义View/ViewGroup需要注意什么?
设置View支持wrap_content
设置View支持padding
尽量不要在View中使用Handler
View中有线程或动画需要及时停止
处理滑动嵌套
onTouch()、onTouchEvent()和onClick()关系?
优先度onTouch()>onTouchEvent()>onClick()。因此onTouchListener的onTouch()方法会先触发;如果onTouch()返回false才会接着触发onTouchEvent(),同样的,内置诸如onClick()事件的实现等等都基于onTouchEvent();如果onTouch()返回true,这些事件将不会被触发。
SurfaceView和View的区别?
View需要在UI线程对画面进行刷新,而SurfaceView可在子线程进行页面的刷新
View适用于主动更新的情况,而SurfaceView适用于被动更新,如频繁刷新,这是因为如果使用View频繁刷新会阻塞主线程,导致界面卡顿
SurfaceView在底层已实现双缓冲机制,而View没有,因此SurfaceView更适用于需要频繁刷新、刷新时数据处理量很大的页面
invalidate()和postInvalidate()的区别?
invalidate()与postInvalidate()都用于刷新View,主要区别是invalidate()在主线程中调用,若在子线程中使用需要配合handler;而postInvalidate()可在子线程中直接调用。
Animation
Android中有哪几种类型的动画?
ViewAnimation补间动画:容易设置和能满足许多应用程序的需要。AlphaAnimation(透明度动画)、RotateAnimation(旋转动画)、ScaleAnimation(缩放动画)、TranslateAnimation(平移动画)四种类型的补间动画。并且View动画框架还提供了动画集合类(AnimationSet),通过动画集合类可以将多个补间动画以组合的形式显示出来,不能真正的改变view的位置。
PropertyAnimation属性动画:这种动画可以设置给任何Object,包括那些还没有渲染到屏幕上的对象。这种动画是可扩展的,可以让你自定义任何类型和属性的动画。对该类对象进行动画操作,真正改变了对象的属性。
DrawableAnimation:专门用来一个一个的显示Drawable的resources,就像放幻灯片一样。
帧动画在使用时需要注意什么?
使用祯动画要注意不能使用尺寸过大的图片,否则容易造成OOM
View动画和属性动画的区别?
View动画:通过不断图形变换实现动画效果,不能真正的改变view的位置;只能作用在View上。
属性动画:通过动态改变对象的属性实现动画效果,真正改变View的位置;能作用在任何对象上。
View动画为何不能真正改变View的位置?而属性动画为何可以?
View动画改变的只是View的显示,而没有改变View的响应区域;而属性动画会通过反射技术来获取和执行属性的get、set方法,从而改变了对象位置的属性值。
属性动画插值器和估值器的作用?
插值器(Interpolator):根据时间流逝的百分比计算出当前属性值改变的百分比。确定了动画效果变化的模式,如匀速变化、加速变化等等。
类型估值器(TypeEvaluator):根据当前属性改变的百分比计算出改变后的属性值。针对于属性动画,View动画不需要类型估值器。
Drawable
了解哪些Drawable?适用场景?
BitmapDrawable 表示一张图片
NinePatchDrawable 可自动地根据所需的宽/高对图片进行相应的缩放并保证不失真(表示一张.9格式的图片)
ShapeDrawable 表示纯色、有渐变效果的基础几何图形(矩形,圆形,线条等)
LayerDrawable 可通过将不同的Drawable放置在不同的层上面从而达到一种叠加后的效果
StateListDrawable 表示一个Drawable的集合且每个Drawable对应着View的一种状态
TransitionDrawable LayerDrawable的子类,实现两层 Drawable之间的淡入淡出效果。
InsetDrawable 表示把一个Drawable嵌入到另外一个Drawable的内部,并在四周留一些间距。
ScaleDrawable 表示将Drawable缩放到一定比例。
ClipDrawable 表示裁剪一个Drawable。
mipmap系列中xxxhdpi、xxhdpi、xhdpi、hdpi、mdpi和ldpi存在怎样的关系?
表示不同密度的图片资源,像素从高到低依次排序为xxxhdpi>xxhdpi>xhdpi>hdpi>mdpi>ldpi,根据手机的dpi不同加载不同密度的图片
dp、dpi、px的区别?
px:像素,如分辨率1920x1080表示高为1920个像素、宽为1080个像素
dpi:每英寸的像素点
dp:密度无关像素,是个相对值
res目录和assets目录的区别?
res/raw中的文件会被映射到R.java文件中,访问时可直接使用资源ID,不可以有目录结构
assets文件夹下的文件不会被映射到R.java中,访问时需要AssetManager类,可以创建子文件夹
Window
Activity、View、Window三者之间的关系?
在Activity启动过程其中的attach()方法中初始化了PhoneWindow,而PhoneWindow是Window的唯一实现类,然后Activity通过setContentView将View设置到了PhoneWindow上,而View通过WindowManager的addView()、removeView()、updateViewLayout()对View进行管理。
Window的内部机制
ViewManager接口中定义三个对Window操作方法:添加、更新和删除。
WindowManager也是一个接口,它继承了ViewManager接口
WindowManager的具体实现类是WindowManagerImpl,并没有直接实现Window的三大操作,而是交给了WindowManagerGlobal。
WindowManagerGlobal以单例模式向外提供自己的实例,因此通过WindowManagerGlobal的addView()、updateViewLayout()、removeView()实现WindowManager对Window的添加、删除和修改。
Windows的三大操作最终都会通过一个IPC过程移交给WindowManagerService。
Window和View通过ViewRootImpl来联系,ViewRootImpl可控制View的测量、布局和重绘。
Window有哪几种类型?
应用Window:对应一个Activity。
子Window:不能单独存在,需附属特定的父Window。如Dialog。
系统Window: 需申明权限才能创建。如Toast。
Activity创建和Dialog创建过程的异同?
相同点
创建WindowDialog。和Activity类似,同样是通过PolicyManager.makeNewWindow()来实现。
初始化DecorView并将Dialog的视图添加到DecorView中去。和Activity类似,同样是通过Window.setContentView() 来实现。
将DecorView添加到Window中显示。和Activity一样,都是在自身要出现在前台时才会将添加Window。
不同点
Dialog.show()方法:完成DecorView的显示。
WindowManager.remoteViewImmediate()方法:当Dialog被dismiss时移除DecorView。
Hander
谈谈消息机制Hander?作用?有哪些要素?流程是怎样的?
作用:跨线程通信。
使用场景:当子线程中进行耗时操作后需要更新UI时,通过Handler将有关UI的操作切换到主线程中执行。
Message:需要被传递的消息,其中包含了消息ID,消息处理对象以及处理的数据等,由MessageQueue统一列队,最终由Handler处理。
MessageQueue:用来存放Handler发送过来的消息,内部通过单链表的数据结构来维护消息列表,等待Looper的抽取。
Handler:负责发送及处理Message消息。
Looper:通过Looper.loop()不断地从MessageQueue中抽取Message,按分发机制将消息分发给目标处理者。
Handler.sendMessage()发送消息时,会通过MessageQueue.enqueueMessage()向MessageQueue中添加一条消息;通过Looper.loop()开启循环后,不断轮询调用MessageQueue.next();调用目标Handler.dispatchMessage()去传递消息,目标Handler收到消息后调用Handler.handlerMessage()处理消息。
为什么系统不建议在子线程访问UI?
UI控件非线程安全,在多线程中并发访问可能会导致UI控件处于不可预期的状态。而不对UI控件的访问加上锁机制的原因有:上锁会让UI控件变得复杂和低效;上锁后会阻塞某些进程的执行
一个Thread可以有几个Looper?几个Handler?
一个Thread只能有一个Looper,可以有多个Handler;
Looper有一个MessageQueue,可以处理来自多个Handler的Message;
MessageQueue有一组待处理的Message,这些Message可来自不同的Handler;
Message中记录了负责发送和处理消息的Handler;
Handler中有Looper和MessageQueue;
如何将一个Thread线程变成Looper线程?Looper线程有哪些特点?
通过Looper.prepare()可将一个Thread线程转换成Looper线程。Looper线程和普通Thread不同,它通过MessageQueue来存放消息和事件、Looper.loop()进行消息轮询。
可以在子线程直接new一个Handler吗?那该怎么做?
不同于主线程直接new一个Handler,由于子线程的Looper需要手动去创建,在创建Handler时需要多一些方法: Looper.prepare();Looper.loop();
Message可以如何创建?哪种效果更好,为什么?
Message msg = new Message();
Message msg = Message.obtain();
Message msg = handler.obtainMessage();
后两种方法都是从整个Messge池中返回一个新的Message实例,能有效避免重复Message创建对象,因此更鼓励这种方式创建###Message
这里的ThreadLocal有什么作用?
ThreadLocal类可实现线程本地存储的功能,把共享数据的可见范围限制在同一个线程之内,无须同步就能保证线程之间不出现数据争用的问题,这里可理解为ThreadLocal帮助Handler找到本线程的Looper。
主线程中Looper的轮询死循环为何没有阻塞主线程?
Android是依靠事件驱动的,通过Loop.loop()不断进行消息循环,可以说Activity的生命周期都是运行在 Looper.loop()的控制之下,一旦退出消息循环,应用也就退出了。而所谓的导致ANR多是因为某个事件在主线程中处理时间太耗时,因此只能说是对某个消息的处理阻塞了Looper.loop()。
使用Hanlder的postDealy()后消息队列会发生什么变化?
post delay的Message并不是先等待一定时间再放入到MessageQueue中,而是直接进入并阻塞当前线程,然后将其delay的时间和队头的进行比较,按照触发时间进行排序,如果触发时间更近则放入队头,保证队头的时间最小、队尾的时间最大。此时,如果队头的Message正是被delay的,则将当前线程堵塞一段时间,直到等待足够时间再唤醒执行该Message,否则唤醒后直接执行。
线程
Android中还了解哪些方便线程切换的类?
AsyncTask:一种轻量级的异步任务类。底层封装了线程池和Handler,便于执行后台任务以及在子线程中进行UI操作。
HandlerThread:一种具有消息循环的线程,其内部可使用Handler。
IntentService:是一种异步、会自动停止的服务,内部采用HandlerThread。
AsyncTask相比Handler有什么优点?不足呢?
Handler的缺点:代码相对臃肿;多任务同时执行时不易精确控制线程。
AsyncTask的优点:创建异步任务更简单,直接继承AsyncTask便可实现后台异步任务的执行和进度的回调更新UI,而无需编写任务线程和Handler实例就能完成相同的任务。
使用AsyncTask需要注意什么?
不要直接调用onPreExecute()、doInBackground()、onProgressUpdate()、onPostExecute()和onCancelled()方法
开始和结束异步任务的方法必须在主线程调用,一个异步对象只能调用一次execute()方法,
AsyncTask中的五个核心方法
onPreExecute():运行在主线程,在异步任务执行之前被调用。可用于进行一些界面上的初始化操作。
doInBackground():运行在子线程,用于处理所有耗时任务,更新UI需调用 publishProgress()方法。任务一旦完成就通过return语句将任务的执行结果返回。
onProgressUpdate():运行在主线程,在后台任务中调用publishProgress()之后该方法会被调用,可利用方法中携带的参数如Progress来对UI进行相应地更新。
onPostExecute():运行在主线程,在异步任务执行完毕并通过return语句返回时被调用,可利用方法中返回的数据来进行一些UI操作。
onCancelled():运行在主线程,当异步任务被取消时调用,用于界面取消的更新。
AsyncTask中使用的线程池大小?
SerialExecutor:用于任务的排队,默认是串行的线程池,在3.0以前核心线程数为5、线程池大小为128,而3.0以后变为同一时间只能处理一个任务
THREAD_POOL_EXECUTOR:用于真正执行任务。
HandlerThread有什么特点?
HandlerThread是一个线程类,它继承自Thread。与普通Thread不同,HandlerThread具有消息循环的效果,这是因为它内部HandlerThread.run()方法中有Looper,能通过Looper.prepare()来创建消息队列,并通过Looper.loop()来开启消息循环。
快速实现子线程使用Handler
实例化一个HandlerThread对象,参数是该线程名称;通过handlerThread.start()启动线程;实例化一个Handler并传入handlerThread中looper对象,使得与HandlerThread绑定;利用handler即可执行异步任务;当不需要HandlerThread时,通过quit()或quitSafely()方法来终止线程的执行。
IntentService的工作原理?
在IntentService.onCreate()方法中实例化一个HandlerThread,利用其内部的Looper会实例化一个ServiceHandler对象;
任务请求的Intent会被封装到Message并通过ServiceHandler发送给Looper的MessageQueue,最终在HandlerThread中执行;
在ServiceHandler.handleMessage()中会调用IntentService.onHandleIntent()和stopSelf(),可在onHandleIntent方法中处理后台任务的逻辑,stopSelf方法终止服务。
IntentService的特点?
相比于线程:由于是服务,优先级比线程高,更不容易被系统杀死。因此较适合执行一些高优先级的后台任务。
相比于普通Service:可自动创建子线程来执行任务,且任务执行完毕后自动退出。
为何不用bindService方式创建IntentService?
当有Intent任务请求时会把Intent封装到Message,然后ServiceHandler会把消息发送出,而发送消息是在onStartCommand()完成的,只能通过startService()才可走该生命周期方法,因此不能通过bindService创建IntentService。
线程池的好处、原理、类型?
线程池的优点:
重用线程池中的线程,避免线程的创建和销毁带来的性能消耗;
有效控制线程池的最大并发数,避免大量的线程之间因互相抢占系统资源而导致阻塞现象;
进行线程管理,提供定时/循环间隔执行等功能
线程池的原理
通过ThreadPoolExecutor并通过一系列参数来配置各种各样的线程池,具体参数:
corePoolSize核心线程数:一般会在线程中一直存活
maximumPoolSize最大线程数:当活动线程数达到这个数值后,后续的任务将会被阻塞
keepAliveTime非核心线程超时时间:超过这个时长,闲置的非核心线程就会被回收
unit:用于指定keepAliveTime参数的时间单位
workQueue任务队列:通过线程池的execute()方法提交的Runnable对象会存储在这个参数中。
threadFactory:线程工厂,可创建新线程
handler:在线程池无法执行新任务时进行调度
线程池的类型
FixThreadPool:线程数量固定的线程池,所有线程都是核心线程,当线程空闲时不会被回收,能快速响应外界请求。
CachedThreadPool:线程数量不定的线程池,只有非核心线程,空闲线程有超时机制,超时回收,适合于执行大量的耗时较少的任务
ScheduledThreadPool:核心线程数量固定,非核心线程数量不定,适合于执行定时任务和固定周期的任务。
SingleThreadExecutor:只有一个核心线程,可确保所有的任务都在同一个线程中按顺序执行,无需处理线程同步问题。
ThreadPoolExecutor的工作策略?
若程池中的线程数量未达到核心线程数,则会直接启动一个核心线程执行任务。
若线程池中的线程数量已达到或者超过核心线程数量,则任务会被插入到任务列表等待执行。 若任务无法插入到任务列表中,往往由于任务列表已满,此时如果线程数量未达到线程池最大线程数,则会启动一个非核心线程执行任务;线程数量已达到线程池规定的最大值,则拒绝执行此任务。
什么是ANR?什么情况会出现ANR?如何避免?在不看代码的情况下如何快速定位出现ANR问题所在?
ANR(Application Not Responding,应用无响应):当操作在一段时间内系统无法处理时,会在系统层面弹出ANR对话框。
产生ANR可能是因为5s内无响应用户输入事件、10s内未结束BroadcastReceiver、20s内未结束Service
想要避免ANR就不要在主线程做耗时操作,而是通过开子线程操作。
IPC(跨进程通信)
Android中进程和线程的关系?
进程是操作系统分配和管理资源的单位,线程是CPU调度和管理的单位,是CPU调度的最小单元
进程拥有独立的地址空间,一个进程崩溃后,在保护模式下不会对其他进程产生影响,而线程间共享地址空间,线程有自己的堆栈和局部变量,一个线程崩溃会导致整个进程崩溃掉。
一个进程可包含多个线程,即一个应用程序上可以同时执行多个任务。
为何需要进行IPC?多进程通信可能会出现什么问题?
所有运行在不同进程的四大组件,只要它们之间需要通过内存共享数据,都会共享失败。由于Android为每个应用分配了独立的虚拟机,不同的虚拟机在内存分配上有不同的地址空间
静态变量和单例模式失效:由独立的虚拟机造成。
线程同步机制失效:由独立的虚拟机造成
SharedPreference的不可靠下降: SharedPreferences不支持两个进程同时进行读写操作,即不支持并发读写,有一定几率导致数据丢失。
Application多次创建:Android系统会为新的进程分配独立虚拟机,相当于系统又把这个应用重新启动了一次
什么是序列化?Serializable接口和Parcelable接口的区别?为何推荐使用后者?
序列化表示将一个对象转换成可存储或可传输的状态。序列化后的对象可以在网络上进行传输,也可以存储到本地。
Serializable:Java序列化接口,将一个对象转化成可存储或可传输的状态,操作简单、效率低、开销大,ObjectOutputStream和ObjectInputStream过程都需要大量的I/O操作。适合将对象序列化到存储设备或将对象序列化后通过网络设备传输。
Parcelable:Android序列化接口,将一个对象进行分解,且分解后的每一个部分都是传递可支持的类型。操作比较麻烦、效率高。主要用在内存的序列化。
Android中为何新增Binder来作为主要的IPC方式?
传输效率高、可操作性强:传输效率主要影响因素是内存拷贝的次数,拷贝次数越少,传输速率越高。
实现C/S架构方便:Linux的众IPC方式除了Socket以外都不是基于C/S架构,而Socket主要用于网络间的通信且传输效率较低。Binder基于C/S 架构 ,Server端与Client端相对独立,稳定性较好。
安全性高:传统Linux IPC的接收方无法获得对方进程可靠的UID/PID,从而无法鉴别对方身份;而Binder机制为每个进程分配了UID/PID且在Binder通信时会根据UID/PID进行有效性检测。
Binder框架中ServiceManager的作用?
在Binder框架定义了四个角色:Server,Client,ServiceManager和Binder驱动。其中Server、Client、ServiceManager运行于用户空间,Binder驱动运行于内核空间。
ServiceManager:服务的管理者,将Binder名字转换为Client中对该Binder的引用,使得Client可以通过Binder名字获得Server中Binder实体的引用。
Server&Client:服务器&客户端。在Binder驱动和ServiceManager提供的基础设施上,进行Client-Server之间的通信。
Android中有哪些基于Binder的IPC方式?简单对比下?
Bundle:Bundle实现了Parcelable接口,方便在不同的进程中传输数据。支持在activity、service、receiver之间通过intent.putExtra()传递Bundle数据。Bundle内部是通过ArrayMap来存取数据。Bundle不支持的数据类型无法在进程中被传递
Messager:底层实现是AIDL,即对AIDL进行了封装,更便于进行进程间通信。支持一对多串行通信,支持实时通信。
文件共享:两个进程通过读/写同一个文件来交换数据。比如A进程把数据写入文件,B进程通过读取这个文件来获取数据。对数据同步要求不高的进程之间进行通信,并且要妥善处理并发读/写的问题。
ContentProvider:专门用来进行不同应用间数据共享的方式。
AIDL:支持一对多并发通信,支持实时通信。
Socket:不仅可跨进程,还可以跨设备通信。网络数据交换
是否了解AIDL?原理是什么?如何优化多模块都使用AIDL的情况?
如果在一个进程中要调用另一个进程中对象的方法,可使用AIDL生成可序列化的参数,AIDL会生成一个服务端对象的代理类,通过它客户端实现间接调用服务端对象的方法。
当有多个业务模块都需要AIDL来进行IPC,此时需要为每个模块创建特定的aidl文件,那么相应的Service就会很多。必然会出现系统资源耗费严重、应用过度重量级的问题。解决办法是建立Binder连接池,即将每个业务模块的Binder请求统一转发到一个远程Service中去执行,从而避免重复创建Service。
每个业务模块创建自己的AIDL接口并实现此接口,然后向服务端提供自己的唯一标识和其对应的Binder对象。服务端只需要一个Service,服务器提供一个queryBinder接口,它会根据业务模块的特征来返回相应的Binder对像,不同的业务模块拿到所需的Binder对象后就可进行远程方法的调用了
内存
加载图片的时候需要注意什么?
直接加载大容量的高清Bitmap很容易出现显示不完整、内存溢出OOM的问题,所以最好按一定的采样率将图片缩小后再加载进来
为减少流量消耗,可对图片采用内存缓存策略,又为了避免图片占用过多内存导致内存溢出,最好以软引用方式持有图片
如果还需要网上下载图片,注意要开子线程去做下载的耗时操作
LRU算法的原理?
LruCache(内存缓存):LruCache类是一个线程安全的泛型类:内部采用一个LinkedHashMap以强引用的方式存储外界的缓存对象,并提供get和put方法来完成缓存的获取和添加操作,当缓存满时会移除较早使用的缓存对象,再添加新的缓存对象。
DiskLruCache(磁盘缓存): 通过将缓存对象写入文件系统从而实现缓存效果。
项目中如何做性能优化的?
布局优化:布局嵌套过深;使用合适的布局三种常见的ViewGroup的绘制速度:FrameLayout > LinerLayout > RelativeLayout;使用include标签可以指定插入一段布局文件到当前布局。这样的话既提高了布局复用,也减少了我们的代码书写,
线程优化:使用AsyncTask或者线程池对线程进行管理,可以提升APP的性能。推荐使用Rxjava来实现异步操作,既方便又优雅。
内存泄漏优化:内存泄露会导致APP占用内存过高,影响效率,严重的话会导致OOM。因此如果项目存在内存泄露的话要优先解决。查找内存泄露可以用LeakCanary等工具。
绘制优化:View过度绘制;onDraw中不要创建新的局部对象;onDraw方法中不要做耗时的任务,也不能执行成千上万次的循环操作,大量的循环仍然十分抢占CPU的时间片,这会造成View的绘制过程不流畅。
响应速度优化:避免在主线程中做耗时操作
列表优化:列表控件优化convertView的复用,ViewHolder的使用避免重复遍历节点
Bitmap优化: 对加载图片进行压缩,避免加载图片多大导致OOM出现
内存泄漏和内存溢出的区别
内存泄漏(Memory Leak)是指程序在申请内存后,无法释放已申请的内存空间。是造成应用程序OOM的主要原因之一。
内存溢出(out of memory)是指程序在申请内存时,没有足够的内存空间供其使用。
内存泄漏是什么?为什么会发生?常见哪些内存泄漏的例子?都是怎么解决的?
内存泄漏(Memory Leak)是指程序在申请内存后,无法释放已申请的内存空间。
发生内存泄漏是由于长周期对象持有对短周期对象的引用,使得短周期对象不能被及时回收。
动画:无限循环动画,导致动画一直播放。
解决方法:当播放的Activity退出,在onDestroy中停止动画
静态成员View:静态成员生命周期与应用的生命周期一样,当前View持有Activity的引用,当前Activity销毁,而他的引用一直存在,导致当前Activity无法被回收。
Handler导致的内存泄漏:Message持有对Handler的引用,而非静态内部类的Handler又隐式持有对外部类Activity的引用,使得引用关系会保持至消息得到处理,从而阻止了Activity的回收。
解决办法:使用静态内部类+WeakReference弱引用;当外部类结束生命周期时清空消息队列。
单例模式导致的内存泄漏
匿名内部类导致的内存泄漏:匿名内部类隐式持有对所在Activity的引用。
解决办法:静态内部类或独立出来;内部采用弱引用
资源未关闭导致的内存泄漏:未及时注销资源导致内存泄漏,如BraodcastReceiver、File、Cursor、Stream、Bitmap等。
解决办法:在Activity销毁的时候要及时关闭或者注销。
什么情况会导致内存溢出?
内存泄漏是导致内存溢出的主要原因;直接加载大图片也易造成内存溢出