美文网首页
安卓面试总结——基础部分

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

作者: Alex_ecb1 | 来源:发表于2019-12-10 21:35 被阅读0次

    基本组件

    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来操作数据

    相关文章

      网友评论

          本文标题:安卓面试总结——基础部分

          本文链接:https://www.haomeiwen.com/subject/xukngctx.html