面试复习(一)Android篇

  • 四大组件
    • ContentProvider,进程间进行数据交互及共享,底层采用Binder机制


      • android 7.0以上应用间文件共享1 2

        禁止对外部应用公开file://格式uri(否则报FileUriExposedException),必须采用content://格式uri,并需要提供临时访问权限
        采用ContentProvider子类FileProvider将uri格式进行转换,并提供临时访问权限

      • 指定打开某个拓展名文件的应用程序

  • activity启动模式launchMode(manifest中声明)

    activity栈后进先出,默认情况下多次启动同一个activity会创建多个实例(任务栈相同,hasCode不同)

    • standard

    • singleTop栈顶复用:已经处于栈顶时,再次创建会调用onNewIntent(任务栈相同,hasCode相同),不创建新实例;不处于栈顶则重新创建新实例

      推送内容对应的页面

    • singleTask栈内复用:当前栈内已存在activity时即复用,并移除同一任务栈内其上的所有其他activity

      应用启动页面(或跳转时setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK))

      淘宝付款完成后回到购物界面

    • singleInstance全局唯一:该activity单独占用一个任务栈,永远位于栈顶,保证在整个系统中都只有一个实例,从不同进程中跳转均只调用该实例的onNewIntent。实质相当于多个进程共享一个应用进程

      闹钟、联系人等节目

  • LruCache与DiskLruCache

    • Least recently used
    • LruCache:保存于内存,将近期使用对象通过强引用保存在LinkedHashMap中,在缓存值达到预设值前将最少使用的对象移除
      • 由于android2.3以来垃圾回收机制更倾向于回收弱引用与软应用,使用软引用弱引用缓存很容易被回收,不再可靠。因此采用了LruCache代替这种方法
    • DiskLruCache:保存于外部存储(硬盘),文件url经过md5加密为文件名key,生成journal文件记录操作日志:
      • dirty:edit()准备写入
      • clean:commit()完成写入
      • remove:abort()写入失败
      • read:get()读取数据
  • 通信

    • activity间

      parcelable

    • activity与fragment

    • activity与service

    • 不同线程间

    • 不同进程间

    • EventBus:消息总线,观察者模式实现

      • 简化组件间的通信,发送者与接收者间耦合度低,避免了可能导致的依赖性问题与生命周期错误。可用于activity、fragment、不同线程间。文件小、高效,已被广泛运用。

      • 事件从发布者post到eventbus,然后eventbus匹配 (handler method for the event type)参数为对应类的已注册方法,将事件发送给匹配的订阅者。

      • 可以通过EventBus.getDefault()获取默认单例,也可以通过EventBus.Builder进行自定义

      • 使用:

        1.创建event实体类,对事件进行定义

        2.注册订阅者subscriber

        3.声明订阅方法,方法名可以自行定义,EventBus通过事件类(handler method for the event type)来进行匹配。要求必须用@Subscribe注解,返回值为void,并且只有一个参数event

        4.发送事件
        * 四种ThreadMode

        1.POSTING:默认模式,订阅者与发送者在同一线程中,避免了线程切换,性能开销最少

        2.MAIN:订阅者位于主线程中,因此要求订阅方法不能耗时过长

        3.BACKGROUND:订阅者位于后台线程,若发送者不在主线程,则需要再开辟一下新线程调用订阅方法;若在则不开辟,直接在当前线程调用。采用这种模式的订阅者需要尽快完成操作,以免阻塞后台线程

        4.ASYNC:订阅者与发送者一定不在一个线程,也不在主线程,必须开辟新线程调用订阅方法。如果订阅者的操作比较耗时则应当使用这种模式(如网络连接)。内部采用了线程池(默认newCachedThreadPool)以重用空闲线程,因此要避免同时允许大量耗时的订阅方法以限制并发线程数量
        * 粘性事件(Sticky Events):事件post出去后仍在内存保留一段时间,使得后注册的订阅者在注册完成后能接收到此前的事件(如service定位完成后发送sticky event给网页端

  • 生命周期 onSaveInstanceState

    • activity:(7个)


      image
      • 当前activity被覆盖一部分:onPause()
      • 转到新的activity或home键、锁屏:先onPause()再onStop()<->再回到activity:先onRestart()再onStart()最后onResume()
      • onPause():被另一个透明或者 Dialog 样式的 Activity 覆盖时的状态.此时它依然与窗口管理器保持连接,系统继续维护其内部状态,所以它仍然可见,但它已经失去了焦点故不可与用户交互.
      • onStop()状态下更高优先级的app需要内存且当前内存不足时被杀死
      • onRestart():onStop()不可见到onStart()可见时调用
      • onSaveInstance():意外退出时才调用,键值对形式临时保存activity状态。一定在onStop()之前,可能在onPause前或之后
        • 按home键
        • 按电源键关闭屏幕
        • 切换横竖屏
    • fragment:(11个)


      image
      • 创建阶段:

        onAttach():与activity建立关联,获取activity传递的值

        onCreate()

        onCreateView():fragment创建视图,加载UI布局

        onActivityCreated():activity的onCreate()完成后调用
        * 显示阶段

        onStart()

        onResume()
        * 被遮挡

        onPause()
        * 不可见阶段

        onStop()
        * 销毁阶段

        onDestoryView():移除fragment的布局

        onDestory()

        onDetach():与activity解除关联
        * 一个activity内加载多个fragment时,首先在activity.onCreate()阶段内顺序连续执行fragment.onAttach()->onActivityCreated()方法,fragment切换时再交替执行fragment.onStart->fragment.onStop()方法。销毁时fragment顺序连续执行fragment.onDestoryView()->onDetach()
        * 两activity A、B跳转:
        * A->B:

        A.onPause

        B.onCreate -> B.onResume

        A.onStop
        * B->A:

        B.onPause

        A.onRestart,A.onStart,A.onResume

        B.onStop,B.onDestory
        * add发生添加fragment:由于add采用叠加的发送,之前的fragment仍是可见的,前一个fragment仍保持再onResume不动,被add进来的fragment在其上完成自己的生命周期
        * replace替换fragment:被替换f移除视图:onPause-> onStop-> onDestoryView—> 替换的f自己的生命周期。替换的f被返回时移出栈顶,使被替换的f再次初始化页面:onCreateView-> onActivityCreated-> onStart-> onResume
        * 由于fragment依赖于所属activity,故创建时各阶段activity生命周期方法均早于fragment生命周期方法,而销毁时各阶段fragment生命周期方法均早于activity生命周期方法
        * Service:
        * onBind方式关联activity:
        * onCreate
        * onBind
        * onUnbind
        * onDestory
        * startService方式启动
        * onCreate
        * onStart
        * onDestory
        * IntentService:
        * onCreate
        * onHandleIntent
        * onDestory

  • ListView原理与优化:

    • ListView的MCV:数据(M)、adapter(C)、ListView(V)
    • 原理:首先调用getCount(),得到listView长度,并由此针对每个item调用getView()来绘制每一项item。
    • 更新listView数据:adapter.notifyDataSetChanged()让listView重新绘制
    • 需要设置两个监听器:onScrollListener、onItemClickListener
    • 优化:
      • 使用convertView,convertView不为空时通过setTag绑定ViewHolder对象,重复利用已经创建的view,避免findviewbid的开销
      • weakReference来引用item中的图片
  • 自定义view
    三种实现方式

    • 组合控件:
      • 在viewgroup子类控件中添加其他组件
      • 写一个对应的java类加载布局及其中子组件,并添加组件监听事件
      • 将此自定义的viewgroup组件添加到页面xml中
    • 自绘控件
      • 创建一个继承View类或子类的控件java类,根据要求重写onMeasure(量测尺寸)、onDraw(绘制view)方法、onLayout(绘制子控件位置)方法参考
      • 在页面xml文件中引入该自定义布局
    • 继承控件
      • 创建自定义View的java类,继承父类View并实现其他接口
      • 在页面xml中引入该自定义布局
  • View的绘制

    • ViewRoot的performTraversals()开始,通过onMeasure()、onLayout()、onDraw()最终绘制出来
    • view视图通过Window呈现,由WindowManager来管理。
    • 父类视图负责测量、定位与绘制,
    • findviewbyid查找时需要遍历整个控件树,代价高,需要尽量避免重复操作
    • view的刷新由父view进行,子view需要刷新时通知父view
  • JNI

  • 第三方平台

  • 看过的源码

  • JSON、XML解析算法

    • jackson
    • SAX:流式处理,不能回退。解析速度快,占用内存少
    • DOM:XML的对象模型,直接访问XML文档,生成树状结构。耗内存,方便插入查找节点
    • PULL:内置于系统中,是官方解析布局文件所用的方式。提供了开始元素、结束元素等事件。使用方便,效率高
      • XMLPullParser工具,读入xml的字节流ByteArrayInputStream(xml.getBytes(),“utf-8”)
      • 对xmlPullParser.getEventType遍历读取event数据类型,判断是xml的何种标签,若为开始标签则开始获取需要的字段数据
  • Manifest原理、APP启动流程、APK打包原理

    • manifest:android应用的入口文件,描述package中暴露的组件、各自实现类、启动位置、能被处理的数据等。作用包括:

      包名、应用命名、图标、设计主题等

      描述各组件、组件命名与intentfilter等属性

      声明权限

      • 安装apk前会对其进行解析,其中就需要解析manifest.xml文件。经过PackageManagerService的解析,其结点信息保存在package对象中,持久存放到setting对象中。
      • android系统启动后,都解析固定目录下apk文件,然后重新安装
    • app启动流程

      • 每个app都运行在独立的进程空间中,由多个组件构成,可以通过组件启动其他app组件,因此没有类似程序入口的main方法。应用在需要的时候才启动,并创建进程
      • 冷启动:应用启动时后台没有其进程。需要系统(zygote进程)创建一个新的进程分配使用
        • 点击图标,调用startActivity,通过Binder机制调用ActivityManagerService来创建新进程实例化目标activity
        • 创建进程:通过zygote进程得到新的进程
        • 创建与初始化application:将新的进程与application绑定起来
        • 创建与初始化launch activity:从已存在的进程启动activity
      • 热启动:启动时后台存在对应的进程,只需要从已有进程中启动
    • apk打包:apk内包括资源文件与classes.dex,打包发送包括集成开发工具与命令行方式

      • 打包资源文件,将res文件夹文件、布局xml文件、manifest文件生成R.java(assets文件不处理)
      • 处理aidl文件生成相应.java文件,若没有则跳过
      • 编译工程源代码、R.java、aidl.java文件生成相应class文件
      • 转换所用class文件,包括源代码文件、R生成的class文件、aidl生成的class文件,得到classes.dex文件(android虚拟机可执行文件)
      • 使用apkBuilder脚本工具,将dex文件、编译过的资源文件、libs文件、未编译的资源等打包生成apk
      • 对apk签名
      • 对apk进行对齐处理:所有资源文件距离文件起始位置偏移4字节整数倍的位置,使得内存映射访问速度更快,减少运行时对内存的使用
  • 文件下载、断点续传:

    • RandomAccessFile支持在文件任意位置读取、写入及修改数据
  • 事件分发

    • 点击事件封装为MotionEvent对象,包括MotionEvent.Action_DOWN、UP、MOVE、CANCLE(非人为因素结束)四个事件类型,组合成实际动作的一系列事件(如DOWN--MOVE--UP)。对点击产生的事件需要由系统传递给一个具体被点击的view处理,即为事件分发。
    • UI界面<-Activity<-ViewGroup<-View,事件发生后,需要通过activity->ViewGroup->View进行传递
    • activity事件分发:点击发生后首先被传递至activity的dispatchTouchEvent()进行分发,并判断是否需要调用onTouchEvent()处理点击事件或分给vG.dispatchTouchEvent()、结束分发,以及当前view是否还能接收该事件列的其他事件
    • ViewGroup事件分发:先调用VG.dispatchTouchEvent(),再调用onInterceptTouchEvent()判断是非需要拦截事件,否则便利VG中所有子View,并调用view.dispatchTouchEvent()进行传递;是则自己对事件进行处理,调用vg.onTouch()
    • View事件分发:先调用v.dispatchTouchEvent(),再调用view.onTouchEvent()进行处理(调用clickListener的onClick()等)



    • 子view内事件优先级:setOnTouchListener.onTouch >onTouchEvent >onClick
  • android Binder机制

  • 消息机制

  • 优化

    • 内存
    • 布局
    • ListView
  • 操作系统管理内存

你可能感兴趣的:(面试复习(一)Android篇)