移动架构01-常见设计模式与AOP编程

移动架构01-设计模式

设计模式是一套设计标准,用来指导实际的设计工作。作用是提高工作效率。

Android常见的设计模式:工厂模式、建造者模式、单例模式、观察者模式、代理模式、AOP。

一、工厂模式

工厂,顾名思义就是批量生成产品的车间。

工厂模式是指根据要求生成相同或相似的对象的设计模式。作用是统一管理相同接口的对象的实例化。具体来说,一个接口通常有很多实现类,如果创建这些实现类需要先做一些逻辑判断或初始化操作,这个时候就可以用工厂模式来统一创建。

1.工厂模式的分类

关于工厂模式的分类,有两种说法:

  1. 分成简单工厂模式和抽象工厂模式,比如:百度百科;
  2. 分成简单工厂模式、工厂方法模式和抽象工厂模式,比如:各大博客网站;

个人比较倾向与第一张说法,理由有二:

  1. 工厂方法模式中工厂类使用的是抽象类,却一定要叫工厂方法模式,太过牵强;
  2. 工厂方法模式中只能生成一种对象,可是连简单工厂模式都能生产多种对象,工厂方法模式却不能,不太合理。

下面,按照第一种说法来介绍工厂模式!

2.简单工厂模式

简单工厂模式就是只有一个工厂类的工厂模式。

//工厂类
public class Factory {
    public static Api create(int type){
        switch (type){
            case 1:
                return new ImplA();
            case 2:
                return new ImplB();
            default:
                return new ImplA();
        }
    }
}

//产品接口
public interface Api {
    void operator();
}
//具体的产品A
public class ImplA implements Api {
    @Override
    public void operator() {
        System.out.println("完成了一种操作A");
    }
}
//具体的产品B
public class ImplB implements Api {
    @Override
    public void operator() {
        System.out.println("完成了一种操作B");
    }
}

public class Test {
    public static void main(String[] args) {
        //通过简单工厂模式创造ImplB对象
        Api api = Factory.create(2);
        api.operator();
    }
}

3.抽象工厂模式

抽象工厂模式就是工厂类抽象化的工厂模式。

为什么要使用抽象工厂模式呢?当出现新的产品时,如果使用简单工厂模式,就需要修改原先的工厂类,这样就违反了Java的开闭原则。这个时候,就使用抽象工厂模式,创意一个新的工厂类来生产新的产品。

//工厂类接口(抽象的工厂类)
public interface IFactory {
   Api create(int type);
}
//新的工厂类
public class Factory2 implements IFactory{
    public Api create(int type){
        switch (type){
            case 1:
                return new ImplA();
            case 2:
                return new ImplB();
            case 3:
                return new ImplC();
            default:
                return new ImplC();
        }
    }
}

public class Test {
    public static void main(String[] args) {
        //通过抽象工厂模式创造ImplC对象
        Api api = new Factory2().create(3);
        api.operator();
    }
}

二、建造者模式

建造者模式是用来生产复杂对象的设计模式。作用是将一个复杂对象的创建过程转化为多个简单的创建过程。

//建造者:先分步设置临时对象的属性,然后创建实际的对象,并统一设置属性。
public class WorkBuilder {
    private RoomParams params;

    public WorkBuilder() {
        this.params = new RoomParams();
    }

    public WorkBuilder makeWindow(String window) {
        params.window = window;
        return this;
    }

    public WorkBuilder makFloor(String floor) {
        params.floor = floor;
        return this;
    }

    public Room build() {
        Room room = new Room();
        room.apply(params);
        return room;
    }

    // 用来设置Room的属性
    class RoomParams {
        public String window;
        public String floor;
    }
}
//需要创建的复杂对象
public class Room {
    private String window;
    private String floor;

    public void apply(WorkBuilder.RoomParams parmas) {
        window = parmas.window;
        floor = parmas.floor;
    }

    @Override
    public String toString() {
        return "Room{" + "window='" + window + '\'' + ", floor='" + floor + '\'' + '}';
    }
}
public class Test {
    public void test(){
        //使用建造者模式创建复杂对象
        Room room=new WorkBuilder().makeWindow("法式").makFloor("英式").build();
        System.out.println(room);
    }
}

三、单例模式

单例模式是内存中只有一个实例的设计模式。作用有2点:

  1. 减小频繁创建与销毁对象带来的开销;
  2. 保证核心对象的唯一性。

单例模式的分类:饿汉式、懒汉式、双重检查式、静态内部类。

另外,可以通过Enum(枚举)来实现单例模式,不过Android一般不这么用。

1.饿汉式

饿汉式是在类加载的时候就创建对象。使用懒汉式创建对象时,无法传递参数。

public class SingletonHungry {
    //持有私有静态实例,防止被引用,此处直接赋值
    private static SingletonHungry instance = new SingletonHungry();  

    // 私有构造方法,防止被实例化  
    private SingletonHungry() {  
    }  

    // 静态工程方法,创建实例  
    public static SingletonHungry getInstance() {  
        return instance;  
    }  
}

2.懒汉式

懒汉式是在获取对象时创建对象。如果一个单例对象从来都不使用,这个时候使用饿汉式(类加载的时候就会创造对象)就会浪费内存,就需要使用懒汉式。

public class SingletonLazy {
    //持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加载 
    private static SingletonLazy instance = null;  

    // 私有构造方法,防止被实例化  
    private SingletonLazy() {  
    }  

    // 静态工程方法,创建实例  
    public static SingletonLazy getInstance() {  
        if (instance == null) {  
            instance = new SingletonLazy();  
        }  
        return instance;  
    }  
}

3.双重检查式

在多线程使用懒汉式,会出现安全问题。如果直接对getInstance方法加synchronized关键字,那么每次调用getInstance()时都会降低性能,所以需要使用双重检查式。

双重检查式是在获取对象时,先判空,然后使用synchronized代码块来创建对象。synchronized代码块内部也需要判空,因为可能在外部判空之后,对象刚好创建好了。

/**
 * 双重检查式
 */
public class SingletonLazy2 {
    // 持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加载
    private static SingletonLazy2 instance = null;

    // 私有构造方法,防止被实例化
    private SingletonLazy2() {
    }

    // 静态工程方法,创建实例
    public static SingletonLazy2 getInstance() {
        if (instance == null) {
            synchronized (instance) {
                if (instance == null) {
                    instance = new SingletonLazy2();
                }
            }
        }
        return instance;
    }
}

使用双重检查式也存在隐患,看下面的情况:在Java指令中创建对象和赋值操作是分开进行的,也就是说instance = new Singleton();语句是分两步执行的。但是由于JVM的指令重排机制,JVM并不保证这两个操作的先后顺序,也就是说有可能JVM会为新的Singleton实例分配空间,然后直接赋值给instance成员,然后再去初始化这个Singleton实例。这样就可能出错了,我们以A、B两个线程为例:

  1. A、B线程同时进入了第一个if判断
  2. A首先进入synchronized块,由于instance为null,所以它执行instance = new Singleton();
  3. 由于JVM内部的优化机制,JVM先画出了一些分配给Singleton实例的空白内存,并赋值给instance成员(注意此时JVM没有开始初始化这个实例),然后A离开了synchronized块。
  4. B进入synchronized块,由于instance此时不是null,因此它马上离开了synchronized块并将结果返回给调用该方法的程序。
  5. 此时B线程打算使用Singleton实例,却发现它没有被初始化,于是错误发生了。

为了解决上面的问题,需要使用volatile关键字,来保证对象的可见性和有序性(禁止该对象的指令重排)。

private static volatile SingletonLazy2 instance = null;

4.静态内部类

使用volatile关键字的双重检查式存在一个问题:性能差。

所以使用静态内部类来实现,JVM内部的机制能够保证当一个类被加载的时候,这个类的加载过程是线程互斥的。这样当我们第一次调用getInstance的时候,JVM能够帮我们保证instance只被创建一次,并且会保证把赋值给instance的内存初始化完毕,这样我们就不用担心上面的问题。同时该方法也只会在第一次调用的时候使用互斥机制,这样就解决了低性能问题。这样我们暂时总结一个完美的单例模式:

public class Singleton {  

    /* 私有构造方法,防止被实例化 */  
    private Singleton() {  
    }  

    /* 此处使用一个内部类来维护单例 */  
    private static class SingletonFactory {  
        private static Singleton instance = new Singleton();  
    }  

    /* 获取实例 */  
    public static Singleton getInstance() {  
        return SingletonFactory.instance;  
    }  
}  

四、观察者模式

观察者模式是一种订阅与通知的设计模式。作用是当对象变化时,统一通知其依赖对象。

观察者模式,是一种被动的观察,类似于邮件订阅。观察者先订阅被观察者,当被观察者发生变化,就会通知观察者。

//观察者接口
public interface IObserver {
    public void update(); 
}

//观察者1
public class Observer1 implements IObserver {
    public void update() {
        System.out.println("this is Observer1");
    }
}

//观察者接口2
public class Observer2 implements IObserver {
    public void update() {
        System.out.println("this is Observer2");
    }
}

//被观察者接口
public interface ISubject {  
    /*增加观察者*/  
    public void add(IObserver observer);  

    /*删除观察者*/  
    public void del(IObserver observer);  

    /*通知所有的观察者*/  
    public void notifyObservers();  
} 

//被观察者
public  class Subject implements ISubject {  
    private Vector<IObserver> vector = new Vector<IObserver>();  

    @Override  
    public void add(IObserver observer) {  
        vector.add(observer);  
    }  

    @Override  
    public void del(IObserver observer) {  
        vector.remove(observer);  
    }  

    @Override  
    public void notifyObservers() {  
        Enumeration<IObserver> enumo = vector.elements();  
        while(enumo.hasMoreElements()){  
            enumo.nextElement().update();  
        }  
    }  
} 

public class Test {
    public static void main(String[] args) {
        ISubject sub = new Subject();
        //观察者订阅被观察者
        sub.add(new Observer1());
        sub.add(new Observer2());
        //被观察者通知观察者
        sub.notifyObservers();
    }
}

五、代理模式

代理模式是通过代理对象对原对象进行扩展的设计模式。优点是不用直接修改原对象。

代理模式分为两种:静态代理和动态代理。

1.静态代理

静态代理是代理对象和被代理对象要实现统一接口的代理模式。

//代理接口
public interface IUserDao {
    void save();
}
//被代理对象
public class UserDao implements IUserDao {
    @Override
    public void save() {
        System.out.println("----已经保存数据!----");      
    }
}
//代理对象
public class UserDaoProxy implements IUserDao {

    // 接收保存目标对象
    private IUserDao target;

    public UserDaoProxy(IUserDao target) {
        this.target = target;
    }

    public void save() {
        System.out.println("开始事务...");
        target.save();// 执行目标对象的方法
        System.out.println("提交事务...");
    }

}
public class Test {
    public static void main(String[] args) {
        // 被代理对象
        UserDao target = new UserDao();
        // 代理对象,把被代理对象传给代理对象,建立代理关系
        UserDaoProxy proxy = new UserDaoProxy(target);
        // 执行的是代理的方法
        proxy.save();
    }
}

2.动态代理

动态代理是是通过反射实现对原对象扩展的代理模式。

// 创建动态代理对象 动态代理不需要实现接口,但是需要指定接口类型
public class ProxyFactory {

    // 维护一个被代理对象
    private Object target;

    public ProxyFactory(Object target) {
        this.target = target;
    }

    // 给被代理对象生成代理对象
    // Proxy.newProxyInstance()有3个参数,1是ClassLoader,用来创建对象;2是被代理对象的所有实现接口,用来给代理对象实现;3是InvocationHandler,用来对被代理的方法进行扩展
    public Object getProxyInstance() {
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("开始事务2");
                        // 执行被代理对象方法
                        Object returnValue = method.invoke(target, args);
                        System.out.println("提交事务2");
                        return returnValue;
                    }
                });
    }
}

public class Test {
    public static void main(String[] args) {
        // 被代理对象
        IUserDao target = new UserDao();
        // 给被代理对象创建代理对象
        IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance();
        // 执行方法   【代理对象】
        proxy.save();
    }
}

六、AOP

AOP(面向切面编程)是一种对多个类统一扩展的设计模式。AOP通过预编译方式和运行期动态代理实现程序功能的统一维护,优点是扩展性比代理模式更强,也更加灵活。

AOP使用aspectjrt框架来实现。

实现步骤

  1. 引入aspectjrt框架,将aspectjrt.jar添加到libs目录,并在gradle中添加引用:implementation files('libs/aspectjrt.jar');

  2. 配置gradle,使用aspectjrt作为类编译器,从而在编译修改class文件,实现动态扩展;

    buildscript {
       repositories {
           mavenCentral()
       }
       dependencies {
           classpath 'org.aspectj:aspectjtools:1.8.8'
           classpath 'org.aspectj:aspectjweaver:1.8.8'
       }
    }
    import org.aspectj.bridge.IMessage
    import org.aspectj.bridge.MessageHandler
    import org.aspectj.tools.ajc.Main
    
    final def log = project.logger
    final def variants = project.android.applicationVariants
    
    variants.all { variant ->
       if (!variant.buildType.isDebuggable()) {
           log.debug("Skipping non-debuggable build type '${variant.buildType.name}'.")
           return;
       }
    
       JavaCompile javaCompile = variant.javaCompile
       javaCompile.doLast {
           String[] args = ["-showWeaveInfo",
                            "-1.8",
                            "-inpath", javaCompile.destinationDir.toString(),
                            "-aspectpath", javaCompile.classpath.asPath,
                            "-d", javaCompile.destinationDir.toString(),
                            "-classpath", javaCompile.classpath.asPath,
                            "-bootclasspath", project.android.bootClasspath.join(File.pathSeparator)]
           log.debug "ajc args: " + Arrays.toString(args)
    
           MessageHandler handler = new MessageHandler(true);
           new Main().run(args, handler);
           for (IMessage message : handler.getMessages(null, true)) {
               switch (message.getKind()) {
                   case IMessage.ABORT:
                   case IMessage.ERROR:
                   case IMessage.FAIL:
                       log.error message.message, message.thrown
                       break;
                   case IMessage.WARNING:
                       log.warn message.message, message.thrown
                       break;
                   case IMessage.INFO:
                       log.info message.message, message.thrown
                       break;
                   case IMessage.DEBUG:
                       log.debug message.message, message.thrown
                       break;
               }
           }
       }
    }
  3. 自定义注解

    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface BehaviorTrace {
       String value();
    }
  4. 配置切面

    /**
    * 使用Aspect注解来定义切面,
    */
    @Aspect
    public class BehaviorTraceAspect {
       /**
        * 使用Pointcut注解来定义切入点,指定需要进行切面处理的方法
        * Pointcut的参数必须使用指定格式execution(@注释名   注释所在的类 注释所在的方法).*代表所有
        */
       @Pointcut("execution(@gsw.aop.annotation.BehaviorTrace * *(..))")
       public void methodAnnotatedWithBehaviorTrace() {
       }
    
       /**
        * 对切入点进行处理,可以使用三种方式注解(参数为上面使用Pointcut注解的方法):
        * Before注解:自动在切入点之前运行,不能调用ProceedingJoinPoint.process();
        * After注解: 自动在切入点之后运行,不能调用ProceedingJoinPoint.process();
        * Around注解:必须调用ProceedingJoinPoint.process()来执行切入点的方法,process()之前的代码在切入点之前执行,process()之后的代码在切入点之后执行;
        *
        * @param joinPoint 切入点
        */
       @Around("methodAnnotatedWithBehaviorTrace()")
       public Object weaveJoinPoint(ProceedingJoinPoint joinPoint) throws Throwable {
           MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
           String className = methodSignature.getDeclaringType().getSimpleName();
           String methodName = methodSignature.getName();
           String funName = methodSignature.getMethod().getAnnotation(BehaviorTrace.class).value();
    
           //统计时间
           long begin = System.currentTimeMillis();
           //执行切入点的方法
           Object result = joinPoint.proceed();
           long duration = System.currentTimeMillis() - begin;
           Log.d("jett", String.format("功能:%s,%s类的%s方法执行了,用时%d ms", funName, className, methodName, duration));
           return result;
       }
    }
  5. 给需要扩展的方法添加自定义注解,然后编译运行即可。

    /**
    * 添加自定义注解,aspect框架会自动进行切入处理。可以添加多个注解。
    */
    @BehaviorTrace("摇一摇")
    public void mShake(View view) {
    SystemClock.sleep(new Random().nextInt(2000));
    }

    运行结果:功能:摇一摇,ActivityAOP类的mShake方法执行了,用时647 ms

代码已上传gitee,喜欢的同学可以下载,顺便点个赞!

上一篇

下一篇:移动架构02-Tinker热修复的分析与使用

移动架构01-设计模式

设计模式是一套设计标准,用来指导实际的设计工作。作用是提高工作效率。

Android常见的设计模式:工厂模式、建造者模式、单例模式、观察者模式、代理模式、AOP。

一、工厂模式

工厂,顾名思义就是批量生成产品的车间。

工厂模式是指根据要求生成相同或相似的对象的设计模式。作用是统一管理相同接口的对象的实例化。具体来说,一个接口通常有很多实现类,如果创建这些实现类需要先做一些逻辑判断或初始化操作,这个时候就可以用工厂模式来统一创建。

1.工厂模式的分类

关于工厂模式的分类,有两种说法:

  1. 分成简单工厂模式和抽象工厂模式,比如:百度百科;
  2. 分成简单工厂模式、工厂方法模式和抽象工厂模式,比如:各大博客网站;

个人比较倾向与第一张说法,理由有二:

  1. 工厂方法模式中工厂类使用的是抽象类,却一定要叫工厂方法模式,太过牵强;
  2. 工厂方法模式中只能生成一种对象,可是连简单工厂模式都能生产多种对象,工厂方法模式却不能,不太合理。

下面,按照第一种说法来介绍工厂模式!

2.简单工厂模式

简单工厂模式就是只有一个工厂类的工厂模式。

//工厂类
public class Factory {
    public static Api create(int type){
        switch (type){
            case 1:
                return new ImplA();
            case 2:
                return new ImplB();
            default:
                return new ImplA();
        }
    }
}

//产品接口
public interface Api {
    void operator();
}
//具体的产品A
public class ImplA implements Api {
    @Override
    public void operator() {
        System.out.println("完成了一种操作A");
    }
}
//具体的产品B
public class ImplB implements Api {
    @Override
    public void operator() {
        System.out.println("完成了一种操作B");
    }
}

public class Test {
    public static void main(String[] args) {
        //通过简单工厂模式创造ImplB对象
        Api api = Factory.create(2);
        api.operator();
    }
}

3.抽象工厂模式

抽象工厂模式就是工厂类抽象化的工厂模式。

为什么要使用抽象工厂模式呢?当出现新的产品时,如果使用简单工厂模式,就需要修改原先的工厂类,这样就违反了Java的开闭原则。这个时候,就使用抽象工厂模式,创意一个新的工厂类来生产新的产品。

//工厂类接口(抽象的工厂类)
public interface IFactory {
   Api create(int type);
}
//新的工厂类
public class Factory2 implements IFactory{
    public Api create(int type){
        switch (type){
            case 1:
                return new ImplA();
            case 2:
                return new ImplB();
            case 3:
                return new ImplC();
            default:
                return new ImplC();
        }
    }
}

public class Test {
    public static void main(String[] args) {
        //通过抽象工厂模式创造ImplC对象
        Api api = new Factory2().create(3);
        api.operator();
    }
}

二、建造者模式

建造者模式是用来生产复杂对象的设计模式。作用是将一个复杂对象的创建过程转化为多个简单的创建过程。

//建造者:先分步设置临时对象的属性,然后创建实际的对象,并统一设置属性。
public class WorkBuilder {
    private RoomParams params;

    public WorkBuilder() {
        this.params = new RoomParams();
    }

    public WorkBuilder makeWindow(String window) {
        params.window = window;
        return this;
    }

    public WorkBuilder makFloor(String floor) {
        params.floor = floor;
        return this;
    }

    public Room build() {
        Room room = new Room();
        room.apply(params);
        return room;
    }

    // 用来设置Room的属性
    class RoomParams {
        public String window;
        public String floor;
    }
}
//需要创建的复杂对象
public class Room {
    private String window;
    private String floor;

    public void apply(WorkBuilder.RoomParams parmas) {
        window = parmas.window;
        floor = parmas.floor;
    }

    @Override
    public String toString() {
        return "Room{" + "window='" + window + '\'' + ", floor='" + floor + '\'' + '}';
    }
}
public class Test {
    public void test(){
        //使用建造者模式创建复杂对象
        Room room=new WorkBuilder().makeWindow("法式").makFloor("英式").build();
        System.out.println(room);
    }
}

三、单例模式

单例模式是内存中只有一个实例的设计模式。作用有2点:

  1. 减小频繁创建与销毁对象带来的开销;
  2. 保证核心对象的唯一性。

单例模式的分类:饿汉式、懒汉式、双重检查式、静态内部类。

另外,可以通过Enum(枚举)来实现单例模式,不过Android一般不这么用。

1.饿汉式

饿汉式是在类加载的时候就创建对象。使用懒汉式创建对象时,无法传递参数。

public class SingletonHungry {
    //持有私有静态实例,防止被引用,此处直接赋值
    private static SingletonHungry instance = new SingletonHungry();  

    // 私有构造方法,防止被实例化  
    private SingletonHungry() {  
    }  

    // 静态工程方法,创建实例  
    public static SingletonHungry getInstance() {  
        return instance;  
    }  
}

2.懒汉式

懒汉式是在获取对象时创建对象。如果一个单例对象从来都不使用,这个时候使用饿汉式(类加载的时候就会创造对象)就会浪费内存,就需要使用懒汉式。

public class SingletonLazy {
    //持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加载 
    private static SingletonLazy instance = null;  

    // 私有构造方法,防止被实例化  
    private SingletonLazy() {  
    }  

    // 静态工程方法,创建实例  
    public static SingletonLazy getInstance() {  
        if (instance == null) {  
            instance = new SingletonLazy();  
        }  
        return instance;  
    }  
}

3.双重检查式

在多线程使用懒汉式,会出现安全问题。如果直接对getInstance方法加synchronized关键字,那么每次调用getInstance()时都会降低性能,所以需要使用双重检查式。

双重检查式是在获取对象时,先判空,然后使用synchronized代码块来创建对象。synchronized代码块内部也需要判空,因为可能在外部判空之后,对象刚好创建好了。

/**
 * 双重检查式
 */
public class SingletonLazy2 {
    // 持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加载
    private static SingletonLazy2 instance = null;

    // 私有构造方法,防止被实例化
    private SingletonLazy2() {
    }

    // 静态工程方法,创建实例
    public static SingletonLazy2 getInstance() {
        if (instance == null) {
            synchronized (instance) {
                if (instance == null) {
                    instance = new SingletonLazy2();
                }
            }
        }
        return instance;
    }
}

使用双重检查式也存在隐患,看下面的情况:在Java指令中创建对象和赋值操作是分开进行的,也就是说instance = new Singleton();语句是分两步执行的。但是由于JVM的指令重排机制,JVM并不保证这两个操作的先后顺序,也就是说有可能JVM会为新的Singleton实例分配空间,然后直接赋值给instance成员,然后再去初始化这个Singleton实例。这样就可能出错了,我们以A、B两个线程为例:

  1. A、B线程同时进入了第一个if判断
  2. A首先进入synchronized块,由于instance为null,所以它执行instance = new Singleton();
  3. 由于JVM内部的优化机制,JVM先画出了一些分配给Singleton实例的空白内存,并赋值给instance成员(注意此时JVM没有开始初始化这个实例),然后A离开了synchronized块。
  4. B进入synchronized块,由于instance此时不是null,因此它马上离开了synchronized块并将结果返回给调用该方法的程序。
  5. 此时B线程打算使用Singleton实例,却发现它没有被初始化,于是错误发生了。

为了解决上面的问题,需要使用volatile关键字,来保证对象的可见性和有序性(禁止该对象的指令重排)。

private static volatile SingletonLazy2 instance = null;

4.静态内部类

使用volatile关键字的双重检查式存在一个问题:性能差。

所以使用静态内部类来实现,JVM内部的机制能够保证当一个类被加载的时候,这个类的加载过程是线程互斥的。这样当我们第一次调用getInstance的时候,JVM能够帮我们保证instance只被创建一次,并且会保证把赋值给instance的内存初始化完毕,这样我们就不用担心上面的问题。同时该方法也只会在第一次调用的时候使用互斥机制,这样就解决了低性能问题。这样我们暂时总结一个完美的单例模式:

public class Singleton {  

    /* 私有构造方法,防止被实例化 */  
    private Singleton() {  
    }  

    /* 此处使用一个内部类来维护单例 */  
    private static class SingletonFactory {  
        private static Singleton instance = new Singleton();  
    }  

    /* 获取实例 */  
    public static Singleton getInstance() {  
        return SingletonFactory.instance;  
    }  
}  

四、观察者模式

观察者模式是一种订阅与通知的设计模式。作用是当对象变化时,统一通知其依赖对象。

观察者模式,是一种被动的观察,类似于邮件订阅。观察者先订阅被观察者,当被观察者发生变化,就会通知观察者。

//观察者接口
public interface IObserver {
    public void update(); 
}

//观察者1
public class Observer1 implements IObserver {
    public void update() {
        System.out.println("this is Observer1");
    }
}

//观察者接口2
public class Observer2 implements IObserver {
    public void update() {
        System.out.println("this is Observer2");
    }
}

//被观察者接口
public interface ISubject {  
    /*增加观察者*/  
    public void add(IObserver observer);  

    /*删除观察者*/  
    public void del(IObserver observer);  

    /*通知所有的观察者*/  
    public void notifyObservers();  
} 

//被观察者
public  class Subject implements ISubject {  
    private Vector<IObserver> vector = new Vector<IObserver>();  

    @Override  
    public void add(IObserver observer) {  
        vector.add(observer);  
    }  

    @Override  
    public void del(IObserver observer) {  
        vector.remove(observer);  
    }  

    @Override  
    public void notifyObservers() {  
        Enumeration<IObserver> enumo = vector.elements();  
        while(enumo.hasMoreElements()){  
            enumo.nextElement().update();  
        }  
    }  
} 

public class Test {
    public static void main(String[] args) {
        ISubject sub = new Subject();
        //观察者订阅被观察者
        sub.add(new Observer1());
        sub.add(new Observer2());
        //被观察者通知观察者
        sub.notifyObservers();
    }
}

五、代理模式

代理模式是通过代理对象对原对象进行扩展的设计模式。优点是不用直接修改原对象。

代理模式分为两种:静态代理和动态代理。

1.静态代理

静态代理是代理对象和被代理对象要实现统一接口的代理模式。

//代理接口
public interface IUserDao {
    void save();
}
//被代理对象
public class UserDao implements IUserDao {
    @Override
    public void save() {
        System.out.println("----已经保存数据!----");      
    }
}
//代理对象
public class UserDaoProxy implements IUserDao {

    // 接收保存目标对象
    private IUserDao target;

    public UserDaoProxy(IUserDao target) {
        this.target = target;
    }

    public void save() {
        System.out.println("开始事务...");
        target.save();// 执行目标对象的方法
        System.out.println("提交事务...");
    }

}
public class Test {
    public static void main(String[] args) {
        // 被代理对象
        UserDao target = new UserDao();
        // 代理对象,把被代理对象传给代理对象,建立代理关系
        UserDaoProxy proxy = new UserDaoProxy(target);
        // 执行的是代理的方法
        proxy.save();
    }
}

2.动态代理

动态代理是是通过反射实现对原对象扩展的代理模式。

// 创建动态代理对象 动态代理不需要实现接口,但是需要指定接口类型
public class ProxyFactory {

    // 维护一个被代理对象
    private Object target;

    public ProxyFactory(Object target) {
        this.target = target;
    }

    // 给被代理对象生成代理对象
    // Proxy.newProxyInstance()有3个参数,1是ClassLoader,用来创建对象;2是被代理对象的所有实现接口,用来给代理对象实现;3是InvocationHandler,用来对被代理的方法进行扩展
    public Object getProxyInstance() {
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("开始事务2");
                        // 执行被代理对象方法
                        Object returnValue = method.invoke(target, args);
                        System.out.println("提交事务2");
                        return returnValue;
                    }
                });
    }
}

public class Test {
    public static void main(String[] args) {
        // 被代理对象
        IUserDao target = new UserDao();
        // 给被代理对象创建代理对象
        IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance();
        // 执行方法   【代理对象】
        proxy.save();
    }
}

六、AOP

AOP(面向切面编程)是一种对多个类统一扩展的设计模式。AOP通过预编译方式和运行期动态代理实现程序功能的统一维护,优点是扩展性比代理模式更强,也更加灵活。

AOP使用aspectjrt框架来实现。

实现步骤

  1. 引入aspectjrt框架,将aspectjrt.jar添加到libs目录,并在gradle中添加引用:implementation files('libs/aspectjrt.jar');

  2. 配置gradle,使用aspectjrt作为类编译器,从而在编译修改class文件,实现动态扩展;

    buildscript {
       repositories {
           mavenCentral()
       }
       dependencies {
           classpath 'org.aspectj:aspectjtools:1.8.8'
           classpath 'org.aspectj:aspectjweaver:1.8.8'
       }
    }
    import org.aspectj.bridge.IMessage
    import org.aspectj.bridge.MessageHandler
    import org.aspectj.tools.ajc.Main
    
    final def log = project.logger
    final def variants = project.android.applicationVariants
    
    variants.all { variant ->
       if (!variant.buildType.isDebuggable()) {
           log.debug("Skipping non-debuggable build type '${variant.buildType.name}'.")
           return;
       }
    
       JavaCompile javaCompile = variant.javaCompile
       javaCompile.doLast {
           String[] args = ["-showWeaveInfo",
                            "-1.8",
                            "-inpath", javaCompile.destinationDir.toString(),
                            "-aspectpath", javaCompile.classpath.asPath,
                            "-d", javaCompile.destinationDir.toString(),
                            "-classpath", javaCompile.classpath.asPath,
                            "-bootclasspath", project.android.bootClasspath.join(File.pathSeparator)]
           log.debug "ajc args: " + Arrays.toString(args)
    
           MessageHandler handler = new MessageHandler(true);
           new Main().run(args, handler);
           for (IMessage message : handler.getMessages(null, true)) {
               switch (message.getKind()) {
                   case IMessage.ABORT:
                   case IMessage.ERROR:
                   case IMessage.FAIL:
                       log.error message.message, message.thrown
                       break;
                   case IMessage.WARNING:
                       log.warn message.message, message.thrown
                       break;
                   case IMessage.INFO:
                       log.info message.message, message.thrown
                       break;
                   case IMessage.DEBUG:
                       log.debug message.message, message.thrown
                       break;
               }
           }
       }
    }
  3. 自定义注解

    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface BehaviorTrace {
       String value();
    }
  4. 配置切面

    /**
    * 使用Aspect注解来定义切面,
    */
    @Aspect
    public class BehaviorTraceAspect {
       /**
        * 使用Pointcut注解来定义切入点,指定需要进行切面处理的方法
        * Pointcut的参数必须使用指定格式execution(@注释名   注释所在的类 注释所在的方法).*代表所有
        */
       @Pointcut("execution(@gsw.aop.annotation.BehaviorTrace * *(..))")
       public void methodAnnotatedWithBehaviorTrace() {
       }
    
       /**
        * 对切入点进行处理,可以使用三种方式注解(参数为上面使用Pointcut注解的方法):
        * Before注解:自动在切入点之前运行,不能调用ProceedingJoinPoint.process();
        * After注解: 自动在切入点之后运行,不能调用ProceedingJoinPoint.process();
        * Around注解:必须调用ProceedingJoinPoint.process()来执行切入点的方法,process()之前的代码在切入点之前执行,process()之后的代码在切入点之后执行;
        *
        * @param joinPoint 切入点
        */
       @Around("methodAnnotatedWithBehaviorTrace()")
       public Object weaveJoinPoint(ProceedingJoinPoint joinPoint) throws Throwable {
           MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
           String className = methodSignature.getDeclaringType().getSimpleName();
           String methodName = methodSignature.getName();
           String funName = methodSignature.getMethod().getAnnotation(BehaviorTrace.class).value();
    
           //统计时间
           long begin = System.currentTimeMillis();
           //执行切入点的方法
           Object result = joinPoint.proceed();
           long duration = System.currentTimeMillis() - begin;
           Log.d("jett", String.format("功能:%s,%s类的%s方法执行了,用时%d ms", funName, className, methodName, duration));
           return result;
       }
    }
  5. 给需要扩展的方法添加自定义注解,然后编译运行即可。

    /**
    * 添加自定义注解,aspect框架会自动进行切入处理。可以添加多个注解。
    */
    @BehaviorTrace("摇一摇")
    public void mShake(View view) {
    SystemClock.sleep(new Random().nextInt(2000));
    }

    运行结果:功能:摇一摇,ActivityAOP类的mShake方法执行了,用时647 ms

代码已上传gitee,喜欢的同学可以下载,顺便点个赞!

上一篇

下一篇:移动架构02-Tinker热修复的分析与使用

猜你喜欢

转载自blog.csdn.net/dshdhsd/article/details/80147331