集册 Android&Java 技术笔记 《Android源码设计模式解析与实战》

《Android源码设计模式解析与实战》

—— 《Android源码设计模式解析与实战》一书

欢马劈雪     最近更新时间:2020-08-04 05:37:59

208

面向对象的六大原则

单例模式

  • 建议实现方式

    • 无参数时,static inner holder class方式:
    public class Singleton {
        private Singleton() {
            // singleton
        }
    
        public static getInstance() {
            return InstanceHolder.sInstance;
        }
    
        private static class InstanceHolder {
            private static final Singleton sInstance = new Singleton();
        }
    }
    • 有参数时,优化的double check lock (DCL)方式,注意volatile关键字,在JDK1.5及以后,用于解决this指针逃逸问题:
    public class Singleton {
        private static volatile Singleton sInstance;
    
        private final Context mContext;
    
        private Singleton(Context context) {
              mContext = context;
        }
    
        public static getInstance(Context context) {
              if (sInstance == null) {
                  synchronized(Singleton.class) {
                      if (sInstance == null) {
                          sInstance = new Singleton(context);
                      }
                  }
              }
    
              return sInstance;
        }
    }
    • this指针逃逸问题

      在DCL单例实现中,sInstance = new Singleton(context);实际上大致会进行三个操作:

      1. Singleton的实例分配内存;
      2. 调用Singleton的构造函数,初始化成员变量;
      3. sInstance指向新分配的内存空间(此时sInstance就不是null了);

      由于Java memory model的内存缓存模型(寄存器,cache,主存),以及允许指令重排,在缓存层面,上述三步中后两步顺序是无法保证的,有可能是1-2-3,也有可能是1-3-2。如果是1-3-2,在A线程执行,第三步执行完之后,切换到B线程,则B线程执行getInstance时将直接返回sInstance,因为B线程将直接命中缓存,但是它的成员变量仍未初始化,一旦使用就会出现问题。

      volatile关键字则能保证每次读数据都从主存读取,不会受到缓存的影响,所以B线程从主存读到的仍是null,就不会出现问题了。

  • 安卓系统的实践(部分)
    • 各种system service就是单例,虽然他们使用binder机制和真正的service跨进程通信,但是在本地的proxy就是单例的(APP进程中的单例);
    • 每个APP都有各种各样的Context,它的实现类是ComtextImpl,它在static代码块中初始化各种system service,并保存在一个map中,后续获取的时候将直接从map中获取;android 23起,初始化system service、缓存、单例逻辑的代码从ContextImpl转移到了SystemServiceRegistry中;
    • LayoutInflater工作原理
      • 实现类是PhoneLayoutInflater
      • 内置view,在xml定义时不用声明完整包名,因为PhoneLayoutInflateronCreateView函数中会自动为其添加包名前缀
      • 最终view的创建都是通过createView函数完成,在其中通过反射创建view实例
      • inflate函数会解析xml布局文件,深度优先遍历,逐层创建view(通过createViewFromTag函数),并最终形成一棵view的树

Builder模式

  • AutoValue及其在Android平台的版本AutoParcel,不仅支持immutable,还支持builder模式
  • WindowManager
    • 实现类为WindowManagerImpl
    • Activity, Fragment, Dialog等组建的view显示,都是通过的Window::setContentView()方法来实现的
    • Window是抽象类,其实现类为PhoneWindow它的setContentView方法,怎么和WindowManager关联起来的??
    • WindowManager添加、移除view的实现工作在WindowManagerGlobal类中
    • ViewRootImpl是framework层与native层通信的桥梁,继承自Handler
    • WMS只负责管理手机屏幕上View的z-order,即View的图层顺序,WMS管理的是属于某个window下的view

原型模式

  • 在已有对象的基础上构造新对象,通常是clone;clone的效率通常比new的效率高,但不绝对;需要注意深浅拷贝的问题。
  • Intent的查找与匹配
    • 系统启动之后,PackageManagerService会扫描系统内所有的APP,解析其manifest文件,得到所有APP注册的所有各类组件,保存在系统中(内存);后续使用Intent进行跳转时,通过查找保存的组件信息,得知应该启动哪个APP(组件);
    • 显式Intent:直接指定了响应Activity;隐式Intent:只指定了Action;
    • 启动Activity时,会向PackageManagerService查询intent对应的activity,在PackageManagerService::queryIntentActivities(...)方法中;

工厂方法

  • 用工厂去创建产品(对象),工厂和产品都可以提供抽象类,具体类,以达到实现解耦的目的
  • Activity的启动过程
    • ActivityThread::main()是app的执行起点 ==>
    • thread.attach(false)把ActivityThread绑定到ActivityManagerService,它调用了ActivityManagerService::attachApplication方法 ==>
    • ActivityManagerService::attachApplication方法中间接调用了ActivityThread.ApplicationThread::bindApplicationActivityManagerService::attachApplicationLocked,前者把ApplicationThread对象绑定到ActivityManagerService ==>
    • ActivityManagerService::attachApplicationLocked ==>
    • ActivityManagerService::realStartActivityLocked ==>
    • ActivityThread.ApplicationThread::scheduleLaunchActivity ==>
    • ActivityThread.H::handleMessage ==>
    • ActivityThread::handleLaunchActivity ==>
    • ActivityThread::performLaunchActivity ==>
    • Instrumentation::callActivityOnCreate ==>
    • Activity::performCreate ==>
    • Activity::onCreate

抽象工厂

四种角色:抽象的工厂类,定义工厂的接口;具体的工厂类,实现生产产品;抽象的产品类,定义产品的接口;具体的产品类,实现产品的功能;

策略模式

  • 某一需求可以有多种实现算法,将每种算法独立封装,且它们之间可以相互替换(实现相同的接口)。策略模式让算法独立于使用它们的客户端而独立变化。
  • 安卓系统动画原理
    • view.startAnimation(),动画是怎么实现的?随时间改变view属性的值;
    • 整个过程是怎样的?绘制时利用TimeInterpolator获取绘制的时间百分比,再利用TypeEvaluator把百分比计算为属性值,设置给view;
    • 怎么做到随时间流逝持续进行上述过程?
      • View::startAnimation(Animation animation)在设置了animation之后,调用invalidate,invalidate最终调用到ViewGroup::drawChild(Canvas canvas, View child, long drawingTime) ==>
      • View::draw(Canvas canvas, ViewGroup parent, long drawingTime) ==>
      • View::applyLegacyAnimation(ViewGroup parent, long drawingTime, Animation a, boolean scalingRequired) ==>
      • applyLegacyAnimation函数中,完成了动画初始化、动画操作、界面刷新(本次动画操作完成后,再次invalidate)
      • 动画操作在Animator::getTransformation(long currentTime, Transformation outTransformation)函数中实现
    • ValueAnimator原理
      • 动画属性都保存在PropertyValuesHolder类中
      • ValueAnimator::start()调用后,将会把动画指令发送给内部的ValueAnimator.AnimationHandler类,而handler则使用Choreographer来进行定时的刷新
      • 刷新时调用了ValueAnimator::doAnimationFrame(long frameTime) ==>
      • ValueAnimator::animationFrame(long currentTime) ==>
      • ValueAnimator::animateValue(float fraction) ==>
      • 具体实现类(例如ObjectAnimator)的重载中,ObjectAnimator::animateValue(float fraction) ==>
      • PropertyValuesHolder::setAnimatedValue(Object target),在其中通过反射,为view设置属性
    • ValueAnimator的代码使用反射工作,设置动画属性时传入的是字符串,容易产生错误,有两个不错的库对这一点进行了优化:ViewAnimator, AnimatorCompat

状态模式

一个对象的行为取决于它的状态,最直接的实现是各个函数中对状态进行判断(if-else或switch),采取不同的行为。状态模式则是把状态抽象为一个类,不同状态下的行为封装为不同的状态实现类。改变对象的状态时,只需要修改其状态对象,即可达到修改其行为的目的。

安卓事件输入系统

  • InputReader,从硬件的事件(CPU中断)中,读取输入事件,并封装为Event对象,然后分发给InputDispatcher
  • InputDispatcher负责接收来自InputReader的事件,并分发给合适的窗口,同时监控ANR
  • InputReaderThreadInputDispatcherThreadInputManagerInputManagerServiceWindowManagerService

观察者模式

又称订阅模式,定义了对象间一对多的依赖关系,当特定的对象(subject)变化时,所有依赖它的对象(observer)都会得到通知并自动更新。

展开阅读全文