安卓面试总结——基础部分

基本组件

1.activity

running(栈顶 ---paused(可见,失去交互能力,没有焦点-----stopped(不可见,完全覆盖----killed(已回收

oncreate预加载---onstart可见,不可交互-----onresume可交互

android进程优先级

前台--可见--服务--后台--空


注意:

1、不设置Activity的android:configChanges时,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次

2、设置Activity的android:configChanges="orientation"时,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次

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

横竖屏

onSaveInstanceState-->

onPause-->

onStop-->

onDestroy-->

onCreate-->

onStart-->

onRestoreInstanceState-->

onResume-->

比方说我们点击跳转一个新Activity,这个时候Activity会入栈,同时它的生命周期也会从onCreate()到onResume()开始变换,这个过程是在ActivityStack里完成的,ActivityStack是运行在Server进程里的,这个时候Server进程就通过ApplicationThread的代理对象ApplicationThreadProxy向运行在app进程ApplicationThread发起操作请求。

ApplicationThread接收到操作请求后,因为它是运行在app进程里的其他线程里,所以ApplicationThread需要通过Handler向主线程ActivityThread发送操作消息。

主线程接收到ApplicationThread发出的消息后,调用主线程ActivityThread执行响应的操作,并回调Activity相应的周期方法。

Activity:通过LoadedApk的makeApplication()方法创建。

Service:通过LoadedApk的makeApplication()方法创建。

2.service

不可做耗时操作,运行在主线程中

因为activity被销毁后无法对thread进行控制,不能获取之前子线程实例,所以需要用到service

启动方式:

1.startservice(intent) stopservice

2.bindservice unbindservice

使用startService()方法启用服务,调用者与服务之间没有关连,即使调用者退出了,服 务仍然运行。

使用bindService()方法启用服务,调用者与服务绑定在了一起,调用者一旦退出,服务也就终止。

如果打算采用startService()方法启动服务,在服务未被创建时,系统会先调用服务的onCreate()方法,接着调用onStartCommand()方法。如果调用startService()方法前服务已经被创建,多次调用startService()方法并不会导致多次创建服务,但会导致多次调用onStartCommand()方法。采用startService()方法启动的服务,只能调用stopService()方法结束服务,服务结束时会调用onDestroy()方法。(由于会多次调用onStartCommand方法,所以可以通过intent来传值)

如果打算采用bindService()方法启动服务,在服务未被创建时,系统会先调用服务的onCreate()方法,接着调用onBind()方法。这个时候调用者和服务绑定在一起,调用者退出了,系统就会先调用服务的onUnbind()方法,接着调用onDestroy()方法。如果调用bindService()方法前服务已经被绑定,多次调用bindService()方法并不会导致多次创建服务及绑定(也就是说onCreate()和onBind()方法并不会被多次调用,但是多次执行bindService)。如果调用者希望与正在绑定的服务解除绑定,可以调用unbindService()方法,调用该方法也会导致系统调用服务的onUnbind()-->onDestroy()方法。(由于onBind只会调用一次,而onBind又会传入intent参数,所以intent的值不会改变,个人认为本实例不适合bindService)

让service和activity交互,oncreate - > onbind ->onserviceconnected

a.服务端创建Ibinder接口实现实例,给客户端调用

 b.用onBind()回调方法返回Binder实例

 c.客户端用new serviceconnction,重写onserviceConnectd回调方法接收IBinder,并用bindservice绑定服务

onServiceDisconnected 在服务意外终止时会调用

onUnbind会destroy服务

在Activity中创建一个thread跟在service中创建一个thread之间的区别?

在Activity中被创建:该Thread的就是为这个Activity服务的,完成这个特定的Activity交代的任务,主动通知该Activity一些消息和事件,Activity销毁后,该Thread也没有存活的意义了。

在Service中被创建:这是保证最长生命周期的Thread的唯一方式,只要整个Service不退出,Thread就可以一直在后台执行,一般在Service的onCreate()中创建,在onDestroy()中销毁。所以,在Service中创建的Thread,适合长期执行一些独立于APP的后台任务,比较常见的就是:在Service中保持与服务器端的长连接。

3.broadcast 

类似java观察者模式

定义:发送携带数据的intent来广播

 1.normal broadcast  sendBroadcast

 2.有序广播 包括系统广播 sendOrderedBroadcast

 3.local broadcast app内

静态注册 manifest 一直运行,app杀死也能收到

动态注册,更灵活,跟随activity生命周期,需要unregist

实现机制:

1.自定义broadcastReceiver,复写onreceive

2.通过binder机制向AcitivityManagerService注册

3.发送者通过binder机制向AcitivityManagerService发送广播

4.AcitivityManagerService查找符合条件(intentfilter/permission)的receiver,将其发到相应receiver的消息循环队列中

5.消息循环拿到此广播,回调broadcastreceiver的onreceive方法

local broadcast

1.app内传播,不担心数据泄漏

2.不可接受其他应用广播

3.比系统广播高效

高效的原因是因为内部通过关联主线程getlooper handler实现,sendbroadcast其实通过handler的sendMessage实现

2.内部协作主要靠两个hashmap, mReceivers和mActions,还有mPendBroadCasts 接受的广播对象,存储广播接收器

PackageManager是包管理服务,在安装的时候,就会将静态注册的广播保存下来。发送对应广播的时候,AMS就会根据intent去查找PackagManager保存的静态注册receiver,若进程还没起来,会先启动进程,然后调用receiver的onReceive函数。

4.view

绘制原理https://www.jianshu.com/p/6bdb3b50d788

由于onCreate会先于handleResumeActivity执行,我们在onCreate中调用了setContentView,也只是生成DecorView并给这个DecorView的内容设置了布局而已,此时还并没有把这个DecorView添加到Window中,同样,WMS中也还没有这个Window,所以此时并不能做任何事情(绘制、接收点击事件等),虽然调用了requestLayout和invalidate,并不会真正触发布局和重绘(因为还没有与ViewRootImpl进行绑定)

Activity与Window产生联系,是在调用activity#attach方法中,生成了一个PhoneWindow,并把这个activity对象自身,设置给了Window的Callback回调(Activity实现了Window的Callback接口)

Window与WindowManagerService产生联系,是在handleResumeActivity中,先执行了onResume方法,通过调用WindowManagerImpl#addView方法将这个Activity对应的DecorView添加到了这个Window中,addView方法是一个IPC操作,将这个Window也添加到了WindowManagerService中;

ViewRootImpl与Window产生联系,是在WindowManagerImpl#addView方法中,这个过程中会new一个ViewRootImpl与DecorView相对应,保存在WindowManagerGloable中;

总的来说,就是setContentView生成了DecorView及其视图,在onResume之后才把这个视图添加进了Window和WMS中,具备了交互能力;

布局查看工具view inspector 取代了hierachy viewer,但是没有性能查看功能,hv仍然可以在sdk tools里找到

measure--layout--draw

measure

自上而下遍历,每个ViewGroup会向它内部的每个子View发送measure命令,然后由具体子View的onMeasure()来测量自己的尺寸。最后测量的结果调用setMeasureDimension()保存在View的mMeasuredWidth和mMeasuredHeight中

1.viewGroup.LayoutParams 视图高宽

2.MeasureSpec 测量规格,传递父布局对子View尺寸测量的约束信息 32位int   3种模式1.不确定 2.exactly 3.atMost mode+大小 

 子view的layoutPrams结合父容器的mode生成子view的measureSpec

就是ScrollView内部嵌套ListView,而该ListView数据条数是不确定的,所以需要设置为包裹内容,然后就会发现ListView就会显示第一行出来。需要继承ListView,覆写onMeasure方法。

scrollview的super.onInterceptTouchEvent就不会拦截listview的滑动

measure的过程

framelayout 和relativelayout会measure两次

layout

子视图的具体位置是相对父视图而言,view.onlayout方法是抽象实现,必须重新实现,自上而下拜访

draw 

invalidate()

请求重绘 View 树,即 draw 过程,假如视图发生大小没有变化就不会调用layout()过程,并且只绘制那些调用了invalidate()方法的 View。

requestLayout()

当布局变化的时候,比如方向变化,尺寸的变化,会调用该方法,在自定义的视图中,如果某些情况下希望重新测量尺寸大小,应该手动去调用该方法,它会触发measure()和layout()过程,但不会进行 draw。

5.fragment

Fragment实现滑动可以借助ViewPager。ViewPager为了让滑动的时候防止出现卡顿现象,它的内部有一个缓存机制,默认情况下,ViewPager会提前创建好当前Fragment旁的两个Fragment。但是如果加载的数据比较耗时或者占用内存较大,就需要考虑是否实现懒加载来加载fragment。

加载方式:

1.xml 

2.动态加载 fragmentManager管理,fragmentTrancaction.add  ----commit() //add和replace的区别,replace替换最上方的F

页面较少使用 FragmentpagerAdapter,只会detach fragment,  较多使用FragmentStatePagerAdapter,会释放不用的fragment,节约内存

生命周期

onattach--onCreate--onCreateView---onViewCreated--(Activity onCreate)---onActivityCreated--(Acitivity onstart)--onStart--(Activity onResume)---onResume

onpause--(Activity onpause)---onstop---(Activity onstop)--onDestroyView--onDestroy--onDettach--(Activity onedestroy)

Fragment和acitivity互相调用

1.fragment调用getActivity

2.Activity调用F,接口回调,F中定义接口,activity实现

     使用三方库,Eventbus实现,具体怎么实现百度Eventbus用法

     广播

     使用观察者模式

3.F调用F,找到另一个activity,然后调用另一个F的实例

8.事件分发机制

dispatchevent 决定是触摸事件是由自己的ontouchevent处理还是分发给子view,并递归调用自身dispatchevent使之向下传递

oninterceptTouchEvent是ViewGroup的方法, 父控件下发事件给子控件处理,若true子控件需要拦截,那dispatchevent 来调用onTouchEvent,返回false继续下发,返回true表示已拦截

在Action_Down的情况下,事件会先传递到最顶层的ViewGroup,调用ViewGroup的dispatchTouchEvent(),

①如果ViewGroup的onInterceptTouchEvent()返回false不拦截该事件,则会分发给子View,调用子View的dispatchTouchEvent(),如果子View的dispatchTouchEvent()返回true,则调用View的onTouchEvent()消费事件。

②如果ViewGroup的onInterceptTouchEvent()返回true拦截该事件,则调用ViewGroup的onTouchEvent()消费事件,接下来的Move和Up事件将由该ViewGroup直接进行处理

当某个子View的dispatchTouchEvent()返回true时,会中止Down事件的分发,同时在ViewGroup中记录该子View。接下来的Move和Up事件将由该子View直接进行处理。

Activity---phoneWindow---decorview--viewGroup---子view

分发到最后也没有消费哦,事件依次反转,最后回到activity,若activity也不处理,则放弃

10.webview 

1.APi16前有安全漏洞,没有限制addJavascriptInterface ,远程攻击者可以执行任何对象的方法

2.布局文件中使用,webview写在其他容器中时,当destroy时,需要从容器中remove webview,然后webview.destroy,不然内存泄漏

3.jsbridge 本地native和远程js互相调用

4.页面加载完成时调用webView.onPageFinished用webChrome.onProgressChanged

5.后台耗电,webview没正常销毁,webview有时会开线程

6.webView硬件加速导致页面渲染问题:webView设置硬件加速有可能会出现闪烁,加载白块等现象,解决问题的方法就是关闭webView硬件加速。

解决方法

1、独立进程,简单暴力,不过可能涉及到进程间通信(和handler持有外部引用一样

2、动态添加WebView,对传入WebView中使用的Context使用弱引用,动态添加WebView意思是在布局创建一个ViewGroup用来放置WebView,activity创建时add进来,在Activity停止时remove掉。

开发中,webview使用完毕直接干掉进程

11.Binder

https://www.jianshu.com/p/afa794939379

什么是binder

1.binder是一种跨进程的通信机制

2.对server来说,binder指binder本地对象,对client来说,binder是proxy对象

3.对传输过程而言,binder是可以跨进程的对象,用内核转换

为什么用binder,比socket性能好,更安全

在android系统当中,它是运行在内核当中,它负责各个用户进程,通过binder通讯的内核来进行交互的一个模块。

binder通信模型:

1.server端在serviceManager中注册方法attachinterface

2.client端去sm中查询此方法

3.sm给client一个proxy对象的方法(空方法

4.内核在client调用此方法时会调用server端的方法

如果是自己写的service是同一进程是Binder,也就是直接调用,这个在编译后生成的IXXXX.java的asInterface中能看到

如果调用系统的service,显然是跨进程的,是BinderProxy,这里也是在asInterface中转换的。

AIDL 是对Binder的一种封装

 全自动生成stub的静态内部抽象类,建立binder本地对象,需要自己实现方法

1.创建一个aidl文件,在android studio中直接右键->new->aidl->aidl file

2.创建一个Service,使用一个内部类继承Stub,实现其中的方法;重写OnBind()方法,返回这个内部类的实例。

3.将该aidl文件复制到要调用接口的项目中,其中包名也要和原项目相同。接着编译该项目。

4.在新项目中连接远程服务,重写onServiceConnected方法,通过Stub的asInterface方法将IBinder对象转换成相应的aidl类,最后就能通过这个aidl类做爱做的事了。

原理:

进程A与进程B进行通信,二者都含有一个相同的aidl文件。假设A要将消息发送给B,则A中的Proxy将消息发送到系统IBinder中,IBinder再将该消息发送到B中的Stub。即Proxy是发送方,Stub是接受方。

在Stub的构造方法中有一个attachInterface方法,这是Binder类中的方法,它将当前的AIDL对象进行注册到系统中,用于之后判断是本地调用还是远程调用

 asInterface方法,

     asInterface方法用于在客户端获取aidl对象。该方法获取IBinder对象后,将IBinder中的DESCRIPTOR与本地的DESCRIPTOR比较,若相同,则直接返回本地的AIDL对象;若不同,再通过Proxy创建一个AIDL对象。

onTransact方法,根据AIDL函数返回编号进行相应方法调用

12Listview

recyclebin机制,内部类,只显示 暴露的几条item

优化

getview用convertview参数,缓存view

viewholder封装view,避免多次findviewbyid,控件过多应i选哪个性能

getview少做耗时操作,不然滑动卡顿,可以加监听,滑动停止时再加载,减少半透明缓存

添加头布局必须在setAdapter之前

13.launcher流程

Launcher也是个应用程序,不过是个特殊的应用。俗称“桌面”。通过PackageManagerService查询所有已安装的应用程序,并保存相应的图标、应用名称、包名和第一个要启动的类名等。

总体流程

点击桌面应用图标,Launcher进程将启动Activity(MainActivity)的请求以Binder的方式发送给了AMS。

AMS接收到启动请求后,交付ActivityStarter处理Intent和Flag等信息,然后再交给ActivityStackSupervisior/ActivityStack

处理Activity进栈相关流程。同时以Socket方式请求Zygote进程fork新进程。

Zygote接收到新进程创建请求后fork出新进程。

在新进程里创建ActivityThread对象,新创建的进程就是应用的主线程,在主线程里开启Looper消息循环,开始处理创建Activity。

ActivityThread利用ClassLoader去加载Activity、创建Activity实例,并回调Activity的onCreate()方法。这样便完成了Activity的启动。

14 activity启动模式

singleTop的使用

使用singleTop模式的Activity在栈顶时只会在Task中存在一个实例

防止用户多次触发startActivity,可以将目标Activity设置为singleTop

singleTask的使用

当Task中存在Activity实例,不会创建Activity,而是销毁Activity上面所有其他的Activity,以此来使将要跳转的Activity至于栈顶显示,如果不存在,则在栈顶创建一个Activity实例(所以假如有人问如何快速关闭100个Activity,只要给跳转的Activity设置singleTask即可)

一般都是主页和登陆页。

singleTask的Activity如果设置了独立的taskAffinity属性值,启动时就会在新的Task中,否则会在已有Task中

15contentporvider

public boolean onCreate()

在创建ContentProvider时使用

public Cursor query()

用于查询指定uri的数据返回一个Cursor

public Uri insert()

用于向指定uri的ContentProvider中添加数据

public int delete()

用于删除指定uri的数据

public int update()

用户更新指定uri的数据

public String getType()

用于返回指定的Uri中的数据MIME类型

注册ContentObserver来监听对应uri的数据变化,这步不是必须的,如果不需要监听数据变化也可以不注册

在构造函数中我们需要传递一个Handler,到此我们可以明白,ContentObserver在收到数据变化的通知后通过Handler机制来通知主线程更新UI

通过ContentProvider来操作数据

你可能感兴趣的:(安卓面试总结——基础部分)