tips

单例模式

1. 饿汉式 上来就new对象
public class IdGenerator { 
  private static final IdGenerator instance = new IdGenerator();
  private IdGenerator() {}
  public static IdGenerator getInstance() {
    return instance;
  }
}
2. 懒汉式 支持延迟加载(双重检测 支持高并发)
public class IdGenerator { 
  private static IdGenerator instance;
  private IdGenerator() {}
  public static IdGenerator getInstance() {
    if (instance == null) {
      synchronized(IdGenerator.class) { // 此处为类级别的锁
        if (instance == null) {
          instance = new IdGenerator();
        }
      }
    }
    return instance;
  }
}
3.静态内部类
SingletonHolder 是一个静态内部类,当外部类 IdGenerator 被加载的时候,并不会创建 SingletonHolder 实例对象。只有当调用 getInstance() 方法时,
SingletonHolder 才会被加载,这个时候才会创建 instance。instance 的唯一性、创建过程的线程安全性,都由 JVM 来保证。所以,这种实现方法既保证了线程安全,又能做到延迟加载
public class IdGenerator { 
  private IdGenerator() {}

  private static class SingletonHolder{
    private static final IdGenerator instance = new IdGenerator();
  }
  
  public static IdGenerator getInstance() {
    return SingletonHolder.instance;
  }
}

工厂模式

比如 Java 中的 DateFormat、Calender。String 类的valueOf() 函数
    public static String valueOf(char data[], int offset, int count) {
        // Android-changed: Replace constructor call with call to new StringFactory class.
        // return new String(data, offset, count);
        return StringFactory.newStringFromChars(data, offset, count);
    }

1.简单工厂 一个工厂类,if-else 创建对象
public class RuleConfigParserFactory {
  private static final Map cachedParsers = new HashMap<>();

  static {
    cachedParsers.put("json", new JsonRuleConfigParser());
    cachedParsers.put("xml", new XmlRuleConfigParser());
    cachedParsers.put("yaml", new YamlRuleConfigParser());
    cachedParsers.put("properties", new PropertiesRuleConfigParser());
  }

  public static IRuleConfigParser createParser(String configFormat) {
    if (configFormat == null || configFormat.isEmpty()) {
      return null;//返回null还是IllegalArgumentException全凭你自己说了算
    }
    IRuleConfigParser parser = cachedParsers.get(configFormat.toLowerCase());
    return parser;
  }
}
2.工厂方法 单独的工厂类创建相应的对象
public interface IRuleConfigParserFactory {
  IRuleConfigParser createParser();
}

public class JsonRuleConfigParserFactory implements IRuleConfigParserFactory {
  @Override
  public IRuleConfigParser createParser() {
    return new JsonRuleConfigParser();
  }
}

public class XmlRuleConfigParserFactory implements IRuleConfigParserFactory {
  @Override
  public IRuleConfigParser createParser() {
    return new XmlRuleConfigParser();
  }
}

public class YamlRuleConfigParserFactory implements IRuleConfigParserFactory {
  @Override
  public IRuleConfigParser createParser() {
    return new YamlRuleConfigParser();
  }
}

public class PropertiesRuleConfigParserFactory implements IRuleConfigParserFactory {
  @Override
  public IRuleConfigParser createParser() {
    return new PropertiesRuleConfigParser();
  }
}
3.抽象工厂就是针对这种非常特殊的场景而诞生的。我们可以让一个工厂负责创建多个不同类型的对象(IRuleConfigParser、ISystemConfigParser 等),而不是只创建一种 parser 对象。这样就可以有效地减少工厂类的个数。

public interface IConfigParserFactory {
  IRuleConfigParser createRuleParser();
  ISystemConfigParser createSystemParser();
  //此处可以扩展新的parser类型,比如IBizConfigParser
}

public class JsonConfigParserFactory implements IConfigParserFactory {
  @Override
  public IRuleConfigParser createRuleParser() {
    return new JsonRuleConfigParser();
  }

  @Override
  public ISystemConfigParser createSystemParser() {
    return new JsonSystemConfigParser();
  }
}
}

// 省略YamlConfigParserFactory和PropertiesConfigParserFactory代码

建造者模式

如dialog、retrofit
1.防止构造函数参数列表太长
2.对参数进行统一校验(如果类的属性之间有一定的依赖关系或者约束条件)
3.如果我们希望创建不可变对象,也就是说,对象在创建好之后,就不能再修改内部的属性值

原型模式

如果对象的创建成本比较大,而同一个类的不同对象之间差别不大(大部分字段都相同),在这种情况下,我们可以利用对已有对象(原型)进行复制(或者叫拷贝)的方式来创建新对象,以达到节省创建时间的目的。这种基于原型来创建对象的方式就叫作原型设计模式(Prototype Design Pattern),简称原型模式。

代理模式

代理模式(Proxy Design Pattern)它在不改变原始类(或叫被代理类)代码的情况下,通过引入代理类来给原始类附加功能。
动态代理的原理与实现静态代理需要针对每个类都创建一个代理类,并且每个代理类中的代码都有点像模板式的“重复”代码,增加了维护成本和开发成本。对于静态代理存在的问题,我们可以通过动态代理来解决。我们不事先为每个原始类编写代理类,而是在运行的时候动态地创建原始类对应的代理类,然后在系统中用代理类替换掉原始类。

桥接模式

一个类存在两个(或多个)独立变化的维度,我们通过组合的方式,让这两个(或多个)维度可以独立进行扩展。“抽象”和“实现”独立开发,通过对象之间的组合关系,组装在一起。
它非常类似我们之前讲过的“组合优于继承”设计原则,通过组合关系来替代继承关系,避免继承层次的指数级爆炸
举个很简单的例子,现在有两个纬度
Car 车 (奔驰、宝马、奥迪等)
Transmission 档位类型 (自动挡、手动挡、手自一体等)
按照继承的设计模式,Car是一个Abstract基类,假设有M个车品牌,N个档位一共要写M*N个类去描述所有车和档位的结合。
而当我们使用桥接模式的话,我首先new一个具体的Car(如奔驰),再new一个具体的Transmission(比如自动档)。然后奔驰.set(手动档)就可以了。
那么这种模式只有M+N个类就可以描述所有类型,这就是M*N的继承类爆炸简化成了M+N组合。

沙盒路径

Environment.getDataDirectory() = /data
Environment.getDownloadCacheDirectory() = /cache
Environment.getExternalStorageDirectory() = /mnt/sdcard
Environment.getExternalStoragePublicDirectory(“test”) = /mnt/sdcard/test
Environment.getRootDirectory() = /system
getPackageCodePath() = /data/app/com.my.app-1.apk
getPackageResourcePath() = /data/app/com.my.app-1.apk
getCacheDir() = /data/data/com.my.app/cache
getDatabasePath(“test”) = /data/data/com.my.app/databases/test
getDir(“test”, Context.MODE_PRIVATE) = /data/data/com.my.app/app_test
getExternalCacheDir() = /mnt/sdcard/Android/data/com.my.app/cache
getExternalFilesDir(“test”) = /mnt/sdcard/Android/data/com.my.app/files/test
getExternalFilesDir(null) = /mnt/sdcard/Android/data/com.my.app/files
getFilesDir() = /data/data/com.my.app/files

复制文件夹

    /**
     *
     * @param srcPath  当前文件夹路径
     * @param toPath  目标文件夹路径
     * @return true 成功
     */
    public boolean copyDirs(String srcPath,String toPath){
        File srcFile = new File(srcPath);
        File toFile = new File(toPath);
        if (!srcFile.isDirectory() || !toFile.isDirectory()){
            return false;
        }
        for (File file : srcFile.listFiles()) {
            if (file.isFile()){
                File toFileWithName = new File(toFile, file.getName());
                copyFile(file.getAbsolutePath(), toFileWithName.getAbsolutePath());
            }else {
                copyDirs(file.getAbsolutePath(),toPath);
            }
        }
        return true;
    }

    /**
     *
     * @param srcPath  当前文件路径
     * @param toPath  目标文件路径
     * @return true 成功
     */
    public boolean copyFile(String srcPath,String toPath){
        File srcFile = new File(srcPath);
        File toFile = new File(toPath);
        if (!srcFile.exists() || !srcFile.isFile()){
            return false;
        }
        File parentFile = toFile.getParentFile();
        if (parentFile != null){
            parentFile.mkdirs();
        }
        try {
            FileInputStream fileInputStream = new FileInputStream(srcFile);
            FileOutputStream fileOutputStream = new FileOutputStream(toFile);
            byte[] buffer = new byte[1024 * 8];
            int byteRead;
            while (-1 != (byteRead = fileInputStream.read(buffer))) {
                fileOutputStream.write(buffer, 0, byteRead);
            }
            fileInputStream.close();
            fileOutputStream.flush();
            fileOutputStream.close();
            return true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

dpi 和drawable-xhdpi[Bitmap这个“内存刺客”你也要小心~ (qq.com)

为了简化不同的配置,Android针对不同像素密度范围进行了归纳分组,如下:

android dpi范围.png

适用于不同像素密度的配置限定符

我们通常选取中密度 (mdpi) 作为基准密度(1倍图),并保持ldpi~xxxhdpi这六种主要密度之间 「3:4:6:8:12:16」 的缩放比,来放置相应尺寸的图片资源。

例如,在创建Android工程时IDE默认为我们添加的ic_launcher图标,就遵循了这个规则。该图标在中密度 (mdpi)目录下的大小为48x48,在其他各种密度的目录下的大小则分别为:

• 36x36 (0.75x) - 低密度 (ldpi)• 48x48(1.0x 基准)- 中密度 (mdpi)• 72x72 (1.5x) - 高密度 (hdpi)• 96x96 (2.0x) - 超高密度 (xhdpi)• 144x144 (3.0x) - 超超高密度 (xxhdpi)• 192x192 (4.0x) - 超超超高密度 (xxxhdpi)

当我们引用该图标时,系统就会「根据所运行设备屏幕的dpi,与不同密度目录名称中的限定符进行比较,来选取最符合当前设备的图片资源」。如果在该密度目录下没有找到合适的图片资源,系统会有对应的规则查找另外一个可能的匹配资源,并「对其进行相应的缩放,以适配屏幕,由此可能造成图片有明显的模糊失真」。

[图片上传失败...(image-438730-1660274940112)]

不同密度大小的ic_launcher图标
adnroid dpi对应缩放比.png

那么,具体的查找规则是怎样的呢?

8Android查找最佳匹配资源的规则

一般来说,Android会「更倾向于缩小较大的原始图像,而非放大较小的原始图像」。在此前提下:

• 假设最接近设备屏幕密度的目录选项为xhdpi,如果图片资源存在,则匹配成功;• 如果不存在,系统就会从更高密度的资源目录下查找,依次为xxhdpi、xxxhdpi;• 如果还不存在,系统就会从「像素密度无关的资源目录nodpi」下查找;• 如果还不存在,系统就会向更低密度的资源目录下查找,依次为hdpi、mdpi、ldpi。

你可能感兴趣的:(tips)