美文网首页
面试总结(Android基础)

面试总结(Android基础)

作者: 我叫王凯 | 来源:发表于2020-06-02 16:08 被阅读0次

    Activity的启动模式

    Activity的四大启动模式:

    • standard:标准模式,每次都会在活动栈中生成一个新的Activity实例。通常我们使用的活动都是标准模式。
    • singleTop:栈顶复用,如果Activity实例已经存在栈顶,那么就不会在活动栈中创建新的实例。比较常见的场景就是给通知跳转的Activity设置,因为你肯定不想前台Activity已经是该Activity的情况下,点击通知,又给你再创建一个同样的Activity。
    • singleInstance:栈内复用,如果Activity实例在当前栈中已经存在,就会将当前Activity实例上面的其他Activity实例都移除栈。常见于跳转到主界面。
    • singleTask:单实例模式,创建一个新的任务栈,这个活动实例独自处在这个活动栈中。

    Activity中onStart和onResume的区别?onPause和onStop的区别?

    Activity有三类:

    • 可见可交互Activity:活跃的Activity,正在和用户交互的Activity。
      生命周期:
      onCreate->onStart->onResume
    • 可见不可交互的Activity:用户可以看到该Activity,但是无法与该也看进行交互。例子:在一个Activity1中启动一个Activity2,背景透明或者为Dialog样式,处在Activity2下面的Activity1就是可见不可交互的Activity。
      生命周期:
      Activity1 : onCreate->onStart->onResume
      Activity2 : onCreate->onStart->onResume
      Activity1 : onPause->onStop
    • 不可见不可交互Activity:已经被暂停的Activity,比如已经执行了onStop方法。

    结论:
    onStart执行后,Activity为可见,执行onResume后,Activity为可见可交互。
    onPause执行后,Activity为可见不可交互,执行onStop后,Activity为不可见不可交互。

    注:可见可交互Activity可以称为前台活跃Activity,可见不可交互的Activity可以成为前台不活跃Activity,不可见不可交互Activity为后台Activity。

    屏幕适配

    平时如何有使用屏幕适配吗?原理是什么呢?

    平时的屏幕适配一般采用的头条的屏幕适配方案。简单来说,以屏幕的一边作为适配,通常是宽。

    原理:设备像素px和设备独立像素dp之间的关系是

    px = dp * density
    

    假设UI给的设计图屏幕宽度基于360dp,那么设备宽的像素点已知,即px,dp也已知,360dp,所以density = px / dp,之后根据这个修改系统中跟density相关的知识点即可。

    Handler消息机制

    详细查看gityuan的Android消息机制

    Handler机制的作用:

    负责跨线程通信,这是因为在主线程不能做耗时操作,而子线程不能更新UI,所以当子线程中进行耗时操作后需要更新UI时,通过Handler将有关UI的操作切换到主线程中执行。

    Handler机制的成员:

    • Message(消息):需要被传递的消息,消息分为硬件产生的消息(如按钮、触摸)和软件生成的消息;
    • MessageQueue(消息队列):负责消息的存储与管理,负责管理有Handler发送过来的Message。读取会自动删除消息,单链表维护,插入和删除上有优势。在MessageQueue.next方法中会无线循环,不断判断是否有消息,有就返回这条消息并移除。另外MessageQueue.enqueueMessage会向消息池中投递消息。
    • Handler(消息处理器):负责Message的发送与处理,主要功能向消息池发送各种消息事件(Handler.sendMessage)和处理相应消息事件(Handler.handleMessage),按照先进先出执行,内部使用的是单链表结构。
    • Looper(消息池):负责关联线程以及消息的分发,在该线程下从MessageQueue获取Message,分发给Handler,Looper创建的时候,会创建一个MessageQueue,调用loop()方法的时候,消息循环开始,其中会不断的调用messageQueue的next()方法,当有消息就处理,否则阻塞在messageQueue的next()方法中。当Looper的quit()被调用的时候会调用messageQueue的quit(),此时next()会返回null,然后loop()方法也就跟着退出。

    Handler运行过程:

    Handler运行过程
    • 在主线程创建的时候会创建一个Looper,同时也会在在Looper内部创建一个消息队列。而在创键Handler的时候取出当前线程的Looper,并通过该Looper对象获得消息队列,然后Handler在子线程中通过MessageQueue.enqueueMessage在消息队列中添加一条Message。
    • 通过Looper.loop()开启消息循环不断轮询调用MessageQueue.next(),取得对应的Message并且通过Handler.dispatchMessage传递给Handler,最终调用Handler.handlerMessage处理消息。

    一个线程能否创建多个 Handler,Handler 跟 Looper 之间的对应关系?

    说到这个问题,先了解一下ThreadLocal机制。

    Looper中还有一个ThreadLocal。ThreadLocal不是线程,它的作用是可以在每个线程中存储数据。
    Handler创建的时候会采用当前线程的Looper来构造消息循环系统。

    那么Handler内部如何获取当前线程的Looper呢?
    这就要使用ThreadLocal。Looper类中使用ThreadLocal来存储Looper对象。

    那么为什么需要ThreadLocal来存储Looper呢?
    因为Looper的构造函数是私有的,无法创建Looper进行赋值。只能将Looper的引用存在变量中,而且每个线程都有自己对应的Looper,则需要用到ThreadLocal来存储。因为ThreadLocal可以在不同的线程中互不干扰的存储并提供数据,通过ThreadLocal可以轻松获取每个线程的Looper。

    而Looper中又有这样一段代码:

    private static void prepare(boolean quitAllowed) {
            if (sThreadLocal.get() != null) {
                throw new RuntimeException("Only one Looper may be created per thread");
            }
            sThreadLocal.set(new Looper(quitAllowed));
        }
    

    从上面代码可以看出,如果获取sThreadLocal.get()不为空,就会抛出异常。代表sThreadLocal.set只会执行一次。由此得出结论:

    一个Thread只能有一个Looper,一个MessageQueue,可以有多个Handler。

    线程池的相关知识

    Android 中的线程池都是直接或间接通过配置 ThreadPoolExecutor 来实现不同 特性的线程池.Android 中最常见的类具有不同特性的线程池分别为 FixThreadPool、CachedhreadPool、SingleThreadPool、ScheduleThreadExecutr。

    1. FixThreadPool:只有核心线程,并且数量固定的,也不会被回收,所有线程都活动时,因为队列没有限制大小,新任务会等待执行。优点:更快的响应外界请求。
    2. SingleThreadPool:只有一个核心线程,确保所有的任务都在同一线程中按序完成。因此不需要处理线程同步的问题。
    3. CachedThreadPool:只有非核心线程,最大线程数非常大,所有线程都活动时会为新任务创建新线程, 否则会利用空闲线程(60s 空闲时间,过了就会被回收,所以线程池中有 0 个线程的可能)处理任务。优点:任何任务都会被立即执行(任务队列 SynchronousQuue 相当于一个空集合); 比较适合执行大量的耗时较少的任务。
    4. ScheduledThreadPool:核心线程数固定,非核心线程(闲着没活干会被立即回收数)没有限制。优点:执行定时任务以及有固定周期的重复任务。

    Fragment 加载到 Activity 的 2 种方式

    Fragment 加载到 Activity 的方式分为静态加载和动态加载,下面我们看看这两种加载方 式:
    静态加载:直接在 Activity 布局文件中指定 Fragment。使用指定属性 name 即可,代码 如下所示:

    <fragment
     android:name="com.example.myfragment.MyFragment"
     android:id="@+id/myfragment_1"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"/>
    

    动态加载: 动态加载需要使用到 FragmentManager,这种动态加载的方式在开发中是 非常常见的,示例代码如下:

    FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginT ransaction();
     //将FragmentA从容器中移除掉,减少内存的消耗
     fragmentTransaction.remove(fragmentA);
     fragmentTransaction.add(R.id.fragment_layout,new FragmentB());
     fragmentTransaction.commit();
    

    理解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等。

    View的事件分发机制

    事件分发本质:就是对MotionEvent事件分发的过程。即当一个MotionEvent产生了以后,系统需要将这个点击事件传递到一个具体的View上。
    点击事件的传递顺序:Activity(Window) -> ViewGroup -> View
    三个主要方法:
    dispatchTouchEvent:进行事件的分发(传递)。返回值是 boolean 类型,受当前onTouchEvent和下级view的dispatchTouchEvent影响
    onInterceptTouchEvent:对事件进行拦截。该方法只在ViewGroup中有,View(不包含 ViewGroup)是没有的。一旦拦截,则执行ViewGroup的onTouchEvent,在ViewGroup中处理事件,而不接着分发给View。且只调用一次,所以后面的事件都会交给ViewGroup处理。
    onTouchEvent:进行事件处理。

    View 的绘制过程,自定义View。

    View 的绘制过程

    关于View的绘制过程,可以简单的分成三个步骤:
    1)measure 过程:
    作用:测量View的宽、高
    流程:performMeasure() --- measure() --- onMeasure() --- 子View的measure()
    备注:在onMeasure() 方法中会对所有的子元素进行measure过程
    2)layout 过程:
    作用:通过确定View四个顶点的位置,从而确定View的位置
    流程:performLayout() --- layout() --- onLayout() ---子View的layout过程
    备注:在OnLayout()方法中会对所有子元素进行layout过程
    3)draw 过程:
    作用:将View绘制在屏幕上
    流程:performDraw() --- draw() --- onDraw() --- 子View 的 draw 过程

    自定义View

    自定义View分为以下几种:

    类型 定义
    自定义组合控件 多个控件组合成为一个新的控件,方便多处复用
    继承系统View控件 继承自TextView等系统控件,在系统控件的基础功能上进行扩展
    继承View 不复用系统控件逻辑,继承View进行功能定义
    继承系统ViewGroup 继承自LinearLayout等系统控件,在系统控件的基础功能上进行扩展
    继承ViewViewGroup 不复用系统控件逻辑,继承ViewGroup进行功能定义

    详情参考Android自定义View全解

    Android有哪些序列化方式?

    为了解决Android中内存序列化速度过慢的问题,Android使用了Parcelable

    对比 Serializable Parcelable
    易用性 简单 需要实现特定方法
    效率
    场景 IO、网络和数据库 内存中

    相关文章

      网友评论

          本文标题:面试总结(Android基础)

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