Android学习之路(23)组件化框架ARouter的使用

一、功能介绍

  1. 支持直接解析标准URL进行跳转,并自动注入参数到目标页面中
  2. 支持多模块工程使用
  3. 支持添加多个拦截器,自定义拦截顺序
  4. 支持依赖注入,可单独作为依赖注入框架使用
  5. 支持InstantRun
  6. 支持MultiDex(Google方案)
  7. 映射关系按组分类、多级管理,按需初始化
  8. 支持用户指定全局降级与局部降级策略
  9. 页面、拦截器、服务等组件均自动注册到框架
  10. 支持多种方式配置转场动画
  11. 支持获取Fragment
  12. 完全支持Kotlin以及混编(配置见文末 其他#5)
  13. 支持第三方 App 加固(使用 arouter-register 实现自动注册)
  14. 支持生成路由文档
  15. 提供 IDE 插件便捷的关联路径和目标类
  16. 支持增量编译(开启文档生成后无法增量编译)
  17. 支持动态注册路由信息

二、典型应用

  1. 从外部URL映射到内部页面,以及参数传递与解析
  2. 跨模块页面跳转,模块间解耦
  3. 拦截跳转过程,处理登陆、埋点等逻辑
  4. 跨模块API调用,通过控制反转来做组件解耦

三、基础功能

  1. 添加依赖和配置

    android {
        defaultConfig {
            ...
            javaCompileOptions {
                annotationProcessorOptions {
                    arguments = [AROUTER_MODULE_NAME: project.getName()]
                }
            }
        }
    }
    
    dependencies {
        // 替换成最新版本, 需要注意的是api
        // 要与compiler匹配使用,均使用最新版可以保证兼容
        compile 'com.alibaba:arouter-api:x.x.x'
        annotationProcessor 'com.alibaba:arouter-compiler:x.x.x'
        ...
    }
    // 旧版本gradle插件(< 2.2),可以使用apt插件,配置方法见文末'其他#4'
    // Kotlin配置参考文末'其他#5'
    
  2. 添加注解

    // 在支持路由的页面上添加注解(必选)
    // 这里的路径需要注意的是至少需要有两级,/xx/xx
    @Route(path = "/test/activity")
    public class YourActivity extend Activity {
        ...
    }
    
  3. 初始化SDK

    if (isDebug()) {           // 这两行必须写在init之前,否则这些配置在init过程中将无效
        ARouter.openLog();     // 打印日志
        ARouter.openDebug();   // 开启调试模式(如果在InstantRun模式下运行,必须开启调试模式!线上版本需要关闭,否则有安全风险)
    }
    ARouter.init(mApplication); // 尽可能早,推荐在Application中初始化
    
  4. 发起路由操作

    // 1. 应用内简单的跳转(通过URL跳转在'进阶用法'中)
    ARouter.getInstance().build("/test/activity").navigation();
    
    // 2. 跳转并携带参数
    ARouter.getInstance().build("/test/1")
                .withLong("key1", 666L)
                .withString("key3", "888")
                .withObject("key4", new Test("Jack", "Rose"))
                .navigation();
    
  5. 添加混淆规则(如果使用了Proguard)

    -keep public class com.alibaba.android.arouter.routes.**{*;}
    -keep public class com.alibaba.android.arouter.facade.**{*;}
    -keep class * implements com.alibaba.android.arouter.facade.template.ISyringe{*;}
    
    # 如果使用了 byType 的方式获取 Service,需添加下面规则,保护接口
    -keep interface * implements com.alibaba.android.arouter.facade.template.IProvider
    
    # 如果使用了 单类注入,即不定义接口实现 IProvider,需添加下面规则,保护实现
    # -keep class * implements com.alibaba.android.arouter.facade.template.IProvider
    
  6. 使用 Gradle 插件实现路由表的自动加载 (可选)

    apply plugin: 'com.alibaba.arouter'
    
    buildscript {
        repositories {
            mavenCentral()
        }
    
        dependencies {
            classpath "com.alibaba:arouter-register:?"
        }
    }
    

    可选使用,通过 ARouter 提供的注册插件进行路由表的自动加载(power by AutoRegister), 默认通过扫描 dex 的方式 进行加载通过 gradle 插件进行自动注册可以缩短初始化时间解决应用加固导致无法直接访问 dex 文件,初始化失败的问题,需要注意的是,该插件必须搭配 api 1.3.0 以上版本使用!

  7. 使用 IDE 插件导航到目标类 (可选)

    在 Android Studio 插件市场中搜索 ARouter Helper, 或者直接下载文档上方 最新版本 中列出的 arouter-idea-plugin zip 安装包手动安装,安装后 插件无任何设置,可以在跳转代码的行首找到一个图标 (navigation) 点击该图标,即可跳转到标识了代码中路径的目标类

四、进阶用法

  1. 通过URL跳转

    // 新建一个Activity用于监听Scheme事件,之后直接把url传递给ARouter即可
    public class SchemeFilterActivity extends Activity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    
        Uri uri = getIntent().getData();
        ARouter.getInstance().build(uri).navigation();
        finish();
        }
    }
    

    AndroidManifest.xml

    <activity android:name=".activity.SchemeFilterActivity">
        <!-- Scheme -->
        <intent-filter>
            <data
            android:host="m.aliyun.com"
            android:scheme="arouter"/>
    
            <action android:name="android.intent.action.VIEW"/>
    
            <category android:name="android.intent.category.DEFAULT"/>
            <category android:name="android.intent.category.BROWSABLE"/>
        </intent-filter>
    </activity>
    
  2. 解析参数

    // 为每一个参数声明一个字段,并使用 @Autowired 标注
    // URL中不能传递Parcelable类型数据,通过ARouter api可以传递Parcelable对象
    @Route(path = "/test/activity")
    public class Test1Activity extends Activity {
        @Autowired
        public String name;
        @Autowired
        int age;
        
        // 通过name来映射URL中的不同参数
        @Autowired(name = "girl") 
        boolean boy;
        
        // 支持解析自定义对象,URL中使用json传递
        @Autowired
        TestObj obj;      
        
        // 使用 withObject 传递 List 和 Map 的实现了
        // Serializable 接口的实现类(ArrayList/HashMap)
        // 的时候,接收该对象的地方不能标注具体的实现类类型
        // 应仅标注为 List 或 Map,否则会影响序列化中类型
        // 的判断, 其他类似情况需要同样处理        
        @Autowired
        List<TestObj> list;
        @Autowired
        Map<String, List<TestObj>> map;
        
        @Override
        protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ARouter.getInstance().inject(this);
    
        // ARouter会自动对字段进行赋值,无需主动获取
        Log.d("param", name + age + boy);
        }
    }
    
    
    // 如果需要传递自定义对象,新建一个类(并非自定义对象类),然后实现 SerializationService,并使用@Route注解标注(方便用户自行选择序列化方式),例如:
    @Route(path = "/yourservicegroupname/json")
    public class JsonServiceImpl implements SerializationService {
        @Override
        public void init(Context context) {
    
        }
    
        @Override
        public <T> T json2Object(String text, Class<T> clazz) {
            return JSON.parseObject(text, clazz);
        }
    
        @Override
        public String object2Json(Object instance) {
            return JSON.toJSONString(instance);
        }
    }
    
  3. 声明拦截器(拦截跳转过程,面向切面编程)

    // 比较经典的应用就是在跳转过程中处理登陆事件,这样就不需要在目标页重复做登陆检查
    // 拦截器会在跳转之间执行,多个拦截器会按优先级顺序依次执行
    @Interceptor(priority = 8, name = "测试用拦截器")
    public class TestInterceptor implements IInterceptor {
        @Override
        public void process(Postcard postcard, InterceptorCallback callback) {
        ...
        callback.onContinue(postcard);  // 处理完成,交还控制权
        // callback.onInterrupt(new RuntimeException("我觉得有点异常"));      // 觉得有问题,中断路由流程
    
        // 以上两种至少需要调用其中一种,否则不会继续路由
        }
    
        @Override
        public void init(Context context) {
        // 拦截器的初始化,会在sdk初始化的时候调用该方法,仅会调用一次
        }
    }
    
  4. 处理跳转结果

    // 使用两个参数的navigation方法,可以获取单次跳转的结果
    ARouter.getInstance().build("/test/1").navigation(this, new NavigationCallback() {
        @Override
        public void onFound(Postcard postcard) {
        ...
        }
    
        @Override
        public void onLost(Postcard postcard) {
        ...
        }
    });
    
  5. 自定义全局降级策略

    // 实现DegradeService接口,并加上一个Path内容任意的注解即可
    @Route(path = "/xxx/xxx")
    public class DegradeServiceImpl implements DegradeService {
    @Override
    public void onLost(Context context, Postcard postcard) {
        // do something.
    }
    
    @Override
    public void init(Context context) {
    
    }
    }
    
  6. 为目标页面声明更多信息

    // 我们经常需要在目标页面中配置一些属性,比方说"是否需要登陆"之类的
    // 可以通过 Route 注解中的 extras 属性进行扩展,这个属性是一个 int值,换句话说,单个int有4字节,也就是32位,可以配置32个开关
    // 剩下的可以自行发挥,通过字节操作可以标识32个开关,通过开关标记目标页面的一些属性,在拦截器中可以拿到这个标记进行业务逻辑判断
    @Route(path = "/test/activity", extras = Consts.XXXX)
    
  7. 通过依赖注入解耦:服务管理(一) 暴露服务

    // 声明接口,其他组件通过接口来调用服务
    public interface HelloService extends IProvider {
        String sayHello(String name);
    }
    
    // 实现接口
    @Route(path = "/yourservicegroupname/hello", name = "测试服务")
    public class HelloServiceImpl implements HelloService {
    
        @Override
        public String sayHello(String name) {
        return "hello, " + name;
        }
    
        @Override
        public void init(Context context) {
    
        }
    }
    
  8. 通过依赖注入解耦:服务管理(二) 发现服务

    public class Test {
        @Autowired
        HelloService helloService;
    
        @Autowired(name = "/yourservicegroupname/hello")
        HelloService helloService2;
    
        HelloService helloService3;
    
        HelloService helloService4;
    
        public Test() {
        ARouter.getInstance().inject(this);
        }
    
        public void testService() {
        // 1. (推荐)使用依赖注入的方式发现服务,通过注解标注字段,即可使用,无需主动获取
        // Autowired注解中标注name之后,将会使用byName的方式注入对应的字段,不设置name属性,会默认使用byType的方式发现服务(当同一接口有多个实现的时候,必须使用byName的方式发现服务)
        helloService.sayHello("Vergil");
        helloService2.sayHello("Vergil");
    
        // 2. 使用依赖查找的方式发现服务,主动去发现服务并使用,下面两种方式分别是byName和byType
        helloService3 = ARouter.getInstance().navigation(HelloService.class);
        helloService4 = (HelloService) ARouter.getInstance().build("/yourservicegroupname/hello").navigation();
        helloService3.sayHello("Vergil");
        helloService4.sayHello("Vergil");
        }
    }
    
  9. 预处理服务

    // 实现 PretreatmentService 接口,并加上一个Path内容任意的注解即可
    @Route(path = "/xxx/xxx")
    public class PretreatmentServiceImpl implements PretreatmentService {
        @Override
        public boolean onPretreatment(Context context, Postcard postcard) {
            // 跳转前预处理,如果需要自行处理跳转,该方法返回 false 即可
        }
    
        @Override
        public void init(Context context) {
    
        }
    }
    
  10. 动态注册路由信息 适用于部分插件化架构的App以及需要动态注册路由信息的场景,可以通过 ARouter 提供的接口实现动态注册 路由信息,目标页面和服务可以不标注 @Route 注解,注意:同一批次仅允许相同 group 的路由信息注册

        ARouter.getInstance().addRouteGroup(new IRouteGroup() {
            @Override
            public void loadInto(Map<String, RouteMeta> atlas) {
                atlas.put("/dynamic/activity",      // path
                    RouteMeta.build(
                        RouteType.ACTIVITY,         // 路由信息
                        TestDynamicActivity.class,  // 目标的 Class
                        "/dynamic/activity",        // Path
                        "dynamic",                  // Group, 尽量保持和 path 的第一段相同
                        0,                          // 优先级,暂未使用
                        0                           // Extra,用于给页面打标
                    )
                );
            }
        });
    

五、更多功能

  1. 初始化中的其他设置

    ARouter.openLog(); // 开启日志
    ARouter.openDebug(); // 使用InstantRun的时候,需要打开该开关,上线之后关闭,否则有安全风险
    ARouter.printStackTrace(); // 打印日志的时候打印线程堆栈
    
  2. 详细的API说明

    // 构建标准的路由请求
    ARouter.getInstance().build("/home/main").navigation();
    
    // 构建标准的路由请求,并指定分组
    ARouter.getInstance().build("/home/main", "ap").navigation();
    
    // 构建标准的路由请求,通过Uri直接解析
    Uri uri;
    ARouter.getInstance().build(uri).navigation();
    
    // 构建标准的路由请求,startActivityForResult
    // navigation的第一个参数必须是Activity,第二个参数则是RequestCode
    ARouter.getInstance().build("/home/main", "ap").navigation(this, 5);
    
    // 直接传递Bundle
    Bundle params = new Bundle();
    ARouter.getInstance()
        .build("/home/main")
        .with(params)
        .navigation();
    
    // 指定Flag
    ARouter.getInstance()
        .build("/home/main")
        .withFlags();
        .navigation();
    
    // 获取Fragment
    Fragment fragment = (Fragment) ARouter.getInstance().build("/test/fragment").navigation();
                        
    // 对象传递
    ARouter.getInstance()
        .withObject("key", new TestObj("Jack", "Rose"))
        .navigation();
    
    // 觉得接口不够多,可以直接拿出Bundle赋值
    ARouter.getInstance()
            .build("/home/main")
            .getExtra();
    
    // 转场动画(常规方式)
    ARouter.getInstance()
        .build("/test/activity2")
        .withTransition(R.anim.slide_in_bottom, R.anim.slide_out_bottom)
        .navigation(this);
    
    // 转场动画(API16+)
    ActivityOptionsCompat compat = ActivityOptionsCompat.
        makeScaleUpAnimation(v, v.getWidth() / 2, v.getHeight() / 2, 0, 0);
    
    // ps. makeSceneTransitionAnimation 使用共享元素的时候,需要在navigation方法中传入当前Activity
    
    ARouter.getInstance()
        .build("/test/activity2")
        .withOptionsCompat(compat)
        .navigation();
            
    // 使用绿色通道(跳过所有的拦截器)
    ARouter.getInstance().build("/home/main").greenChannel().navigation();
    
    // 使用自己的日志工具打印日志
    ARouter.setLogger();
    
    // 使用自己提供的线程池
    ARouter.setExecutor();
    
  3. 获取原始的URI

    String uriStr = getIntent().getStringExtra(ARouter.RAW_URI);
    
  4. 重写跳转URL

    // 实现PathReplaceService接口,并加上一个Path内容任意的注解即可
    @Route(path = "/xxx/xxx") // 必须标明注解
    public class PathReplaceServiceImpl implements PathReplaceService {
        /**
        * For normal path.
        *
        * @param path raw path
        */
        String forString(String path) {
        return path;    // 按照一定的规则处理之后返回处理后的结果
        }
    
    /**
        * For uri type.
        *
        * @param uri raw uri
        */
    Uri forUri(Uri uri) {
        return url;    // 按照一定的规则处理之后返回处理后的结果
    }
    }
    
  5. 生成路由文档

    // 更新 build.gradle, 添加参数 AROUTER_GENERATE_DOC = enable
    // 生成的文档路径 : build/generated/source/apt/(debug or release)/com/alibaba/android/arouter/docs/arouter-map-of-${moduleName}.json
    android {
        defaultConfig {
            ...
            javaCompileOptions {
                annotationProcessorOptions {
                    arguments = [AROUTER_MODULE_NAME: project.getName(), AROUTER_GENERATE_DOC: "enable"]
                }
            }
        }
    }
    

六、其他

  1. 路由中的分组概念

    • SDK中针对所有的路径(/test/1 /test/2)进行分组,分组只有在分组中的某一个路径第一次被访问的时候,该分组才会被初始化
    • 可以通过 @Route 注解主动指定分组,否则使用路径中第一段字符串(/*/)作为分组
    • 注意:一旦主动指定分组之后,应用内路由需要使用 ARouter.getInstance().build(path, group) 进行跳转,手动指定分组,否则无法找到
    @Route(path = "/test/1", group = "app")
    
  2. 拦截器和服务的异同

    • 拦截器和服务所需要实现的接口不同,但是结构类似,都存在 init(Context context) 方法,但是两者的调用时机不同
    • 拦截器因为其特殊性,会被任何一次路由所触发,拦截器会在ARouter初始化的时候异步初始化,如果第一次路由的时候拦截器还没有初始化结束,路由会等待,直到初始化完成。
    • 服务没有该限制,某一服务可能在App整个生命周期中都不会用到,所以服务只有被调用的时候才会触发初始化操作
  3. 旧版本gradle插件的配置方式

    apply plugin: 'com.neenbedankt.android-apt'
    
    buildscript {
        repositories {
            mavenCentral()
        }
    
        dependencies {
            classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4'
        }
    }
    
    apt {
        arguments {
            AROUTER_MODULE_NAME project.getName();
        }
    }
    
    dependencies {
        compile 'com.alibaba:arouter-api:x.x.x'
        apt 'com.alibaba:arouter-compiler:x.x.x'
        ...
    }
    
  4. Kotlin项目中的配置方式

    // 可以参考 module-kotlin 模块中的写法
    apply plugin: 'kotlin-kapt'
    
    kapt {
        arguments {
            arg("AROUTER_MODULE_NAME", project.getName())
        }
    }
    
    dependencies {
        compile 'com.alibaba:arouter-api:x.x.x'
        kapt 'com.alibaba:arouter-compiler:x.x.x'
        ...
    }
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/338337.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

计组原理:系统概论与基本组成

系统概论与基本组成 系统概论硬件软件 计算机系统的层次结构系统复杂性的管理方法1&#xff1a;抽象 计算机的基本组成冯诺依曼计算机系统复杂性的管理方法 2&#xff1a;&#xff08;3’Y&#xff09; 计算机的工作步骤上机前的准备&#xff1a;计算机的解题过程存储器的基本组…

Java List集合使用 Comparator.comparing 排序报空指针异常问题

前言 有时候对一个List集合的某个字段进行排序的时候会报错&#xff0c;问题就是排序的那个字段可能是个空值&#xff0c;那么下面就是处理这种问题的方式&#xff0c;亲自测试有效。 参考 点击可跳转&#xff1a;Java List集合使用 Comparator.comparing 排序报空指针异常问…

TPM模拟器安装

目录 TPM模拟器安装 1&#xff09;安装配置所需依赖 2&#xff09;从官网下载TPM模拟器程序ibmtpm1332.tar.gz 3&#xff09;创建安装目录并将源码解压到对应目录 4&#xff09;进入解压后的目录&#xff0c;然后执行安装命令 5&#xff09;将tpm服务器添到Linux系统执行目…

gradle各版本下载地址

IDEA如何配置 Gradle&#xff08;详细版&#xff09;_idea gradle配置-CSDN博客 Gradle | Releases 参考以上文档

Python中函数的4种参数形式

默认参数的特点是在声明函数时使用“”来指定默认值。缺省参数指因为程序使用了默认值&#xff0c;使得函数调用时不必写出全部参数。 关键字参数可以摆脱位置匹配的限制&#xff0c;直接用变量名匹配。可变参数用于处理任意数量的参数&#xff0c;形参中带一个*&#xff0c;将…

小蓝和小桥的挑战*

题目 import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner sc new Scanner(System.in);int t sc.nextInt();sc.nextLine();while(t-- > 0) {int n sc.nextInt();sc.nextLine();int[] a new int[n];for(int i0;i<n;i)a[i…

Web server failed to start.Port xxxx was already in use.

目录 一、报错截图&#xff1a;二、解决方式 一、报错截图&#xff1a; 某端口被占用,导致出现如下报错&#xff1a; 二、解决方式 windowsR 输入cmd—>回车 如下图所示 查看被占用的端口的进程&#xff0c;如下图&#xff1a; netstat -ano |findstr 端口号结束这个进程…

恭喜CSDN,www.csdn.com域名买回来了!

摘要&#xff1a;www.csdn.com已经可以访问了&#xff0c;恭喜CSDN&#xff0c;迈出国际化的重要一步。 主页面浓浓的国际简约范 静态展示特点 著名的1024 day day up OpenStack & Open-Source 最后附上的是report 已经有众多支持者了&#xff0c;看排序。 博客不多&#…

重拾计网-第三弹 你不知道的计算机的秘密

&#x1f389;欢迎您来到我的MySQL基础复习专栏 ☆* o(≧▽≦)o *☆哈喽~我是小小恶斯法克&#x1f379; ✨博客主页&#xff1a;小小恶斯法克的博客 &#x1f388;该系列文章专栏&#xff1a;重拾计算机网络 &#x1f379;文章作者技术和水平很有限&#xff0c;如果文中出现错…

【MATLAB源码-第120期】基于matlab的GFSK系统调制解调仿真,输出各个节点的波形以及功率谱。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 GFSK&#xff08;高斯频移键控&#xff09;是一种数字调制技术&#xff0c;广泛应用于无线通信领域&#xff0c;尤其是在低功率和短距离通信系统中&#xff0c;例如蓝牙技术。GFSK是频移键控&#xff08;FSK&#xff09;的一…

Vue3+ElementUI 多选框中复选框和名字点击方法效果分离

现在的需求为 比如我点击了Option A &#xff0c;触发点击Option A的方法&#xff0c;并且复选框不会取消勾选&#xff0c;分离的方法。 <el-checkbox-group v-model"mapWork.model_checkArray.value"> <div class"naipTypeDom" v-for"item …

AI+量化02_金融市场的基础概念

文章目录 问答之纯小白 vs GPT4Q1. 请用尽可能简短的语句或例子&#xff0c;给小白讲解宏观经济Q2. 给小白讲解资本边际效率Q3. 如果证券没有风险是否意味着没有收益&#xff1f;Q4. 芒格&#xff1a;教人炒股&#xff0c;犹如引人吸毒Q5. 给小白快速讲解&#xff1a;卡房统计量…

IP 地址如何进行动态分配?

由于 IP 地址资源的有限性&#xff0c;大部分用户上网都是使用动态 IP 地址&#xff0c;而不是静态 IP 地址。动态 IP 地址指的是在需要的时候才进行 IP 地址分配的方式&#xff0c;而静态 IP 地址是固定分配一个 IP 地址&#xff0c;每次都用这一个地址。因此&#xff0c;IP 地…

docker常用基础命令

文章目录 1、Docker 环境信息命令1.1、docker info1.2、docker version 2、系统日志信息常用命令2.1、docker events2.2、docker logs2.3、docker history 3、容器的生命周期管理命令3.1、docker create3.2、docker run 总结 1、Docker 环境信息命令 1.1、docker info 显示 D…

硬件-11-服务器的基础知识

参考服务器基础知识大科普 1 电视剧背景 服务器被誉为互联网之魂。 电视剧《创业年代》是一部有冯绍峰和袁姗姗等人联手主演的一部讲述我国第一批科技创业者创业故事的电视剧&#xff0c;可以说是他们铲下了建设中关村的第一捧土。 电视剧《创业年代》中的潮信公司并没有…

Ubuntu上安装部署Qt

首先需要下载对应的虚拟机软件和ubuntu镜像&#xff0c;虚拟机软件使用VMware或者Virtual Box都行&#xff0c;我用的是前者&#xff0c;这里是VMware的下载链接&#xff1a;下载 VMware Workstation Pro | CN。Ubuntu镜像推荐去清华的网站下载&#xff1a;Index of /ubuntu-re…

不管过程有多难,也想做好送给你

大家好&#xff0c;我是小悟 春节临近&#xff0c;很多品牌都各自定制了专属红包封面&#xff0c;相信也有小伙伴也抢到了一些品牌主分发的红包封面&#xff0c;微信也在各种入口比如通过搜索、摇一摇、看视频帮助用户“薅”红包封面。作为“代码撬动改变世界”的实践者&#x…

stm32h7内部flash与qspi flash异同与差异

内部flash.与qspi flash区别 在STM32H7微控制器中&#xff0c;QSPI闪存&#xff08;Quad SPI Flash&#xff09;和内部闪存之间存在一些具体的差异。以下是一些常见的差异点&#xff1a; 访问速度&#xff1a;内部闪存通常具有更快的访问速度&#xff0c;可以以更高的速率读取…

Webpack 基本使用 - 1

Webpack 是什么 webpack 的核心目的是打包&#xff0c;即把源代码一个一个的 js 文件&#xff0c;打包汇总为一个总文件 bundle.js。 基本配置包括mode指定打包模式&#xff0c;entry指定打包入口&#xff0c;output指定打包输出目录。 另外&#xff0c;由于 webpack默认只能打…

计量属性和会计报表

目录 历史成本计价基础的优点重置成本计价可变现净值计价现值公允价值会计报表 \quad \quad 会计要素的计量属性简单的来说就是用什么样的方法来进行计量。 \quad \quad 历史成本计价基础的优点 (比如用发票) 1、数据客观 2、随时可以查证 3、防止随意更改 4、核算手续简化 历…