tips

作者: 霁逸lei | 来源:发表于2022-09-09 17:33 被阅读0次

    单例模式

    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<String, RuleConfigParser> 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

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