Android 组件化 路由跳转(上)

2023-11-04

一.简介

 

组件化中,每个Module之间的页面跳转是个问题。因为每个Module不可能知道其他Module的Activity路径。所以我们没有办法像普通的工程一样使用Intent方式跳转。那么组件化开发中每个Module之间如何完成页面跳转呢?

路由,对的我们可以使用路由完成这个工作。市面上常见的路由有阿里的ARouter。

 

 

 

 

 

 

 

二.ARouter集成

 

1.组件的Module的Gradle文件添加一下依赖

apply plugin: 'com.android.application'

android {

    compileSdkVersion 28

    defaultConfig {

        ...

        javaCompileOptions {
            annotationProcessorOptions {
                //ARouter编译的时候需要的module名字
                arguments = [AROUTER_MODULE_NAME: project.getName()]
            }
        }

    }

    

}



dependencies {
    ...

    implementation 'com.alibaba:arouter-api:1.5.1'
    annotationProcessor 'com.alibaba:arouter-compiler:1.5.1'
}

 

注意:kotlin配置

plugins {
    ...
    id 'kotlin-kapt'
}

dependencies {
    ...
    implementation 'com.alibaba:arouter-api:1.5.1'
    kapt 'com.alibaba:arouter-compiler:1.5.1'
}

 

 

 

2.初始化(一般在Application中初始化)

public class MyApplication extends Application {


    @Override
    public void onCreate() {
        super.onCreate();
        ...

        ARouter.init(this);

        ...
    }

    ...

}

 

 

 

3.目标页配置跳转注解

@Route(path = "/anim/AnimActivity")
public class AnimActivity extends AppCompatActivity implements View.OnClickListener {

    ....

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ....
    }


    ....
    
}

 

 

 

4.跳转页面跳转

 

<1> 普通跳转

ARouter.getInstance().build("/anim/AnimActivity").navigation();

 

 

<2> 基本传值跳转

ARouter.getInstance().build("/anim/AnimActivity").withInt("IntKey", 12).withString("StringKey", "张三").navigation();

接收

Intent intent = getIntent();
int i = intent.getIntExtra("IntKey", -1);
String s = intent.getStringExtra("StringKey");
Log.d("AnimActivity", "i----:" + i);
Log.d("AnimActivity", "s----:" + s);

结果

D/AnimActivity: i----:12


D/AnimActivity: s----:张三

 

 

 

<3> Bundle传值

Bundle bundle = new Bundle();
bundle.putInt("IntKey", 20);
bundle.putString("StringKey", "李四");
bundle.putLong("LongKey", 123456);
bundle.putDouble("DoubleKey", 123.456);
ARouter.getInstance().build("/anim/AnimActivity").withBundle("BundleKey", bundle).navigation();

接收

Intent intent = getIntent();
Bundle bundle = intent.getBundleExtra("BundleKey");
int i = bundle.getInt("IntKey");
String s = bundle.getString("StringKey");
long l = bundle.getLong("LongKey");
double d = bundle.getDouble("DoubleKey");
Log.d("AnimActivity", "i----:" + i);
Log.d("AnimActivity", "s----:" + s);
Log.d("AnimActivity", "l----:" + l);
Log.d("AnimActivity", "d----:" + d);

结果

D/AnimActivity: i----:20


D/AnimActivity: s----:李四


D/AnimActivity: l----:123456


D/AnimActivity: d----:123.456

 

说明:

ARouter页面跳转时通过.withXXX方法给我们提供了类似于原生Intent的所有带参数的传值。接收的方式和原生Intent跳转一样。

 

 

 

5.ARouter的其他注解

刚刚配置目标Activity时使用了ARouter的@Route注解,使用path配置目标Activity的路径。其实ARouter为我们配置了很多的注解。

 

<1> @Route注解

Route注解是作用在类上面,里面会携带path路径。

 

 

<2> @Interceptor注解

它能拦截你的路由,什么时候让路由通过什么时候让路由不通过,项目中如果需要拦截,可以使用此注解。

 

 

<3> @Autowired注解

Autowired注解是定义在目标页的属性上,通常用来定义目标页接收的值,还可以定义上面说到的接收服务方。

 

 

<4> ...

 

 

 

6.总结

到此,ARouter的基本使用就完成了,注意是基本使用。因为ARouter的功能还有很多,比如ARouter实现了Gradle插件路由的自动加载功能(它主要是在编译期通过gradle插装把需要依赖arouter注解的类自动扫描到arouter的map管理器里面),比如配置混淆规则等等。

 

详情参考官网:https://github.com/alibaba/ARouter

 

 

 

 

 

 

 

 

三.ARouter源码解析

 

1.简介

在上述使用Arouter过程中,我们的Module依赖了arouter-api、arouter-compiler,两个Jar包。在 编译期 arouter-compiler通过扫描项目中用到的Route、Autowired、Interceptor等注解来生成对应的class文件。然后将这些Class文件放到Map中。跳转的时候根据Key(就是我们@Route时使用path确定的路径,比如path = "/anim/AnimActivity")。

 

其实,Activity,Service,等等四大组件的跳转,使用系统Intent的方式完全可以。比如

Intent intent=new Intent(MainActivity.this, RxBusActivity.class);
intent.putExtra("bean",student);
startActivity(intent);

但是,组件化中。不同的Module之间一般没有相互依赖。所以拿不到目标的Class<?>对象。所以我们在组件化中不可能使用Intent的方式跳转。下面我们从源码层面看一下ARouter是如何做到的。

 

 

 

 

2.源码解析之 初始化

ARouter.init(this);

调用ARouter类的init方法

 

public static void init(Application application) {
    if (!hasInit) {
        logger = _ARouter.logger;
        _ARouter.logger.info("ARouter::", "ARouter init start.");
        hasInit = _ARouter.init(application);
        if (hasInit) {
            _ARouter.afterInit();
        }

        _ARouter.logger.info("ARouter::", "ARouter init over.");
    }

}

 

我们重点看下面一行代码

hasInit = _ARouter.init(application);
protected static synchronized boolean init(Application application) {
    mContext = application;
    LogisticsCenter.init(mContext, executor);
    logger.info("ARouter::", "ARouter init success!");
    hasInit = true;
    mHandler = new Handler(Looper.getMainLooper());
    return true;
}

 

接下来,我们看下面一行代码

LogisticsCenter.init(mContext, executor);
public static synchronized void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
    mContext = context;
    executor = tpe;

    try {
        long startInit = System.currentTimeMillis();
        loadRouterMap();
         
        //如果使用了Gradle配置路由列表 直接打印不继续执行
        if (registerByPlugin) {
            ARouter.logger.info("ARouter::", "Load router map by arouter-auto-register plugin.");
        } else {

            //没有使用Gradle配置路由列表 也就是使用我们上面的方法
            Object routerMap;
            
            //debuggable=false或者没有新的版本 SharedPreferences取出HashSet
            if (!ARouter.debuggable() && !PackageUtils.isNewVersion(context)) {
                ARouter.logger.info("ARouter::", "Load router map from cache.");
                routerMap = new HashSet(context.getSharedPreferences("SP_AROUTER_CACHE", 0).getStringSet("ROUTER_MAP", new HashSet()));
            } else {

                //创建新的集合存放到SharedPreferences中
                ARouter.logger.info("ARouter::", "Run with debug mode or new install, rebuild router map.");
                routerMap = ClassUtils.getFileNameByPackageName(mContext, "com.alibaba.android.arouter.routes");
                if (!((Set)routerMap).isEmpty()) {
                    context.getSharedPreferences("SP_AROUTER_CACHE", 0).edit().putStringSet("ROUTER_MAP", (Set)routerMap).apply();
                }

                PackageUtils.updateVersion(context);
            }

            ARouter.logger.info("ARouter::", "Find router map finished, map size = " + ((Set)routerMap).size() + ", cost " + (System.currentTimeMillis() - startInit) + " ms.");
            startInit = System.currentTimeMillis();
            Iterator var5 = ((Set)routerMap).iterator();

            while(var5.hasNext()) {
                String className = (String)var5.next();
                if 
            (className.startsWith("com.alibaba.android.arouter.routes.ARouter$$Root")) {// 将com.alibaba.android.arouter.routes.ARouter$$Root前缀的class类放到Warehouse.groupsIndex中
                   ((IRouteRoot)((IRouteRoot)Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
                } else if // 将com.alibaba.android.arouter.routes.ARouter$$Interceptors前缀的class类放到Warehouse.interceptorsIndex中
        
   (className.startsWith("com.alibaba.android.arouter.routes.ARouter$$Interceptors")) {
                    ((IInterceptorGroup)((IInterceptorGroup)Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
                } else if // 将com.alibaba.android.arouter.routes.ARouter$$Providers前缀的class类放到Warehouse.providersIndex中
            (className.startsWith("com.alibaba.android.arouter.routes.ARouter$$Providers")) {
                    ((IProviderGroup)((IProviderGroup)Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
                }
            }
        }

        ARouter.logger.info("ARouter::", "Load root element finished, cost " + (System.currentTimeMillis() - startInit) + " ms.");
        if (Warehouse.groupsIndex.size() == 0) {
            ARouter.logger.error("ARouter::", "No mapping files were found, check your configuration please!");
        }

        if (ARouter.debuggable()) {
            ARouter.logger.debug("ARouter::", String.format(Locale.getDefault(), "LogisticsCenter has already been loaded, GroupIndex[%d], InterceptorIndex[%d], ProviderIndex[%d]", Warehouse.groupsIndex.size(), Warehouse.interceptorsIndex.size(), Warehouse.providersIndex.size()));
        }

    } catch (Exception var7) {
        throw new HandlerException("ARouter::ARouter init logistics center exception! [" + var7.getMessage() + "]");
    }
}

 

 

小结1

不是通过Gradle自动加载路由列表的情况下:

<1> 通过ClassUtils.getFileNameByPackageName获取到apk中前缀为com.alibaba.android.arouter.routes的类,然后压缩成zip文件,然后通过DexFile.loadDex转化成DexFile对象,然后过滤出com.alibaba.android.arouter.routes前缀的class并返回。

 

<2> 拿到了需要的class类后,放到sp里面,方便下次不去扫描apk拿class,更新版本号。

      <2.1> 将com.alibaba.android.arouter.routes.ARouter$$Root前缀的class类放到Warehouse.groupsIndex中。

      <2.2> 将com.alibaba.android.arouter.routes.ARouter$$Interceptors前缀的class类放到Warehouse.interceptorsIndex中。

      <2.3> 将com.alibaba.android.arouter.routes.ARouter$$Providers前缀的class类放到Warehouse.providersIndex中。

 

 

 

3.源码解析之 配置目标页面路径

@Route(path = "/anim/AnimActivity")
public class AnimActivity extends AppCompatActivity {

   ...

}

配置后,编译后,在具体Module的build目录下会自动生成ARouter相关的类。如图

 

自动生成的ARouter$$Group$$anim类

/**
 * DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class ARouter$$Group$$anim implements IRouteGroup {
  @Override
  public void loadInto(Map<String, RouteMeta> atlas) {
    atlas.put("/anim/AnimActivity", RouteMeta.build(RouteType.ACTIVITY, AnimActivity.class, "/anim/animactivity", "anim", null, -1, -2147483648));
  }
}

也就是说,ARouter自动把使用@Route注解中配置的path当成Key,然后把目标Activity当成Value存放在Map中。

 

 

小结2

ARouter会在apt阶段,生成的class类有Arouter$$Root$$模块名,模块名是在gradle中配置的arg("AROUTER_MODULE_NAME", "${project.getName()}")属性,把所有组的信息放到传进来的map中,这个组是通过我们在Route注解的path属性拆分的,比如定义/anim/AnimActivity,会认为组名是anim,Arouter$$Root$$组名放的是该组下,所有的路由表信息,包括route、provider注解通过RouteMeta包装对应的class类信息,provider注解会放在Arouter$$Providers$$模块名下面。

 

 

 

 

4.源码解析之 跳转页面

ARouter.getInstance().build("/anim/AnimActivity").withBundle("BundleKey", bundle).navigation();

 

public Object navigation() {
    return this.navigation((Context)null);
}

 

最终调用

public Object navigation(Context context, NavigationCallback callback) {
    return ARouter.getInstance().navigation(context, this, -1, callback);
}
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
    PretreatmentService pretreatmentService = (PretreatmentService)ARouter.getInstance().navigation(PretreatmentService.class);
    if (null != pretreatmentService && !pretreatmentService.onPretreatment(context, postcard)) {
        return null;
    } else {
        try {
            LogisticsCenter.completion(postcard);
        } catch (NoRouteFoundException var8) {
            logger.warning("ARouter::", var8.getMessage());
            if (debuggable()) {
                this.runInMainThread(new Runnable() {
                    public void run() {
                        Toast.makeText(_ARouter.mContext, "There's no route matched!\n Path = [" + postcard.getPath() + "]\n Group = [" + postcard.getGroup() + "]", 1).show();
                    }
                });
            }

            if (null != callback) {
                callback.onLost(postcard);
            } else {
                DegradeService degradeService = (DegradeService)ARouter.getInstance().navigation(DegradeService.class);
                if (null != degradeService) {
                    degradeService.onLost(context, postcard);
                }
            }

            return null;
        }

        if (null != callback) {
            callback.onFound(postcard);
        }

        if (!postcard.isGreenChannel()) {
            interceptorService.doInterceptions(postcard, new InterceptorCallback() {
                public void onContinue(Postcard postcardx) {
                    _ARouter.this._navigation(context, postcardx, requestCode, callback);
                }

                public void onInterrupt(Throwable exception) {
                    if (null != callback) {
                        callback.onInterrupt(postcard);
                    }

                    _ARouter.logger.info("ARouter::", "Navigation failed, termination by interceptor : " + exception.getMessage());
                }
            });
            return null;
        } else {
            return this._navigation(context, postcard, requestCode, callback);
        }
    }
}

 

private Object _navigation(Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
    final Context currentContext = null == context ? mContext : context;
    switch(postcard.getType()) {
    case ACTIVITY:
        final Intent intent = new Intent(currentContext, postcard.getDestination());
        intent.putExtras(postcard.getExtras());
        int flags = postcard.getFlags();
        if (-1 != flags) {
            intent.setFlags(flags);
        } else if (!(currentContext instanceof Activity)) {
            intent.setFlags(268435456);
        }

        String action = postcard.getAction();
        if (!TextUtils.isEmpty(action)) {
            intent.setAction(action);
        }

        this.runInMainThread(new Runnable() {
            public void run() {
                _ARouter.this.startActivity(requestCode, currentContext, intent, postcard, callback);
            }
        });
        return null;
    case PROVIDER:
        return postcard.getProvider();
    case BOARDCAST:
    case CONTENT_PROVIDER:
    case FRAGMENT:
        Class fragmentMeta = postcard.getDestination();

        try {
            Object instance = fragmentMeta.getConstructor().newInstance();
            if (instance instanceof Fragment) {
                ((Fragment)instance).setArguments(postcard.getExtras());
            } else if (instance instanceof android.support.v4.app.Fragment) {
                    ((android.support.v4.app.Fragment)instance).setArguments(postcard.getExtras());
            }

            return instance;
        } catch (Exception var11) {
            logger.error("ARouter::", "Fetch fragment instance error, " + TextUtils.formatStackTrace(var11.getStackTrace()));
            }
    case METHOD:
    case SERVICE:
    default:
        return null;
    }
}

 

 

小结3

最终,还是通过Intent的方式完成传值跳转。通过上述ARouter在编译期间的Map集合,获取相应的Class文件。最终完成跳转。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android 组件化 路由跳转(上) 的相关文章

  • qt操作excel表

    https blog csdn net cannon qi article details 79972258
  • day-37 代码随想录算法训练营(19)贪心part06

    738 单调递增的数字 思路 在给的数字中找到第一个开始递减的两个数字 将前一个数字减1 后面的数字全部变为最大值9 968 监控二叉树 思路 分三种状态 0无覆盖 1有监控 2有覆盖 分四种情况 1 两边都有覆盖 返回0 2 两边有一边无
  • 在关系数据库中。存放在数据库中的逻辑结构以什么为主 (4选一)

    C 哈希表
  • 笔试题2:如何用八进制和十六进制来表示整型数据

    八进制的含义在于每位数字的进位大小为8 也就是0 8的9个数字 十六进制的进位大小为16 除了0 9的10个数字 还包括a b c d e f来表示10 11 12 13 14 15 答案 Java的八进制采用0开头 十六进制采用0x开头
  • iOS宏定义的黑魔法 - 宏菜鸟起飞手册

    转自 OneV s Den的博客 宏定义在C系开发中可以说占有举足轻重的作用 底层框架自不必说 为了编译优化和方便 以及跨平台能力 宏被大量使用 可以说底层开发离开define将寸步难行 而在更高层级进行开发时 我们会将更多的重心放在业务逻
  • 计算机虚拟化+网络

    计算机虚拟化 网络 cookie 什么是 Cookie cookie的生命周期 cookie Cookie 用于存储 web 页面的用户信息 什么是 Cookie Cookie 是一些数据 存储于你电脑上的文本文件中 当 web 服务器向浏
  • C++像素游戏

    我的作品 鼠标板 黑科技之橡素 代码 include
  • Verilog语言实现FPGA上的计数器

    Verilog语言实现FPGA上的计数器 计数器是数字电路中经常使用的基本元素之一 它用于生成指定脉冲数量或者指定计数范围内的计数信号 在现代数字电路设计中 FPGA Field Programmable Gate Array 作为一种可编
  • QT+Opencv 时报错Failed to load module “canberra-gtk-module“

    解决方案 sudo apt get install libcanberra gtk module
  • 二维数组作为参数,传入函数(最好用的)

    二维数组作为参数 传入函数 最好用的 很多时候我都是直接通过传入一个 固定的数字来传递一个二维数组 比如这样子定义函数 int fun int a 3 int n 调用函数是 fun a n 这样子调用的二维数组只能是固定已经知道的 不够灵
  • 使用Kettle实现数据排序

    一 Kettle的安装 1 下载Kettle的安装包文件 在Windows系统中打开浏览器 访问Kettle官网 https sourceforge net projects pentaho 下载Kettle安装文件pdi ce 9 1 0
  • 最大公约数、最小公倍数、辗转相除法的求解和证明

    两个正整数的最大公约数 Greatest Common Divisor GCD 在计算机中通常使用辗转相除法计算 最小公倍数 Least Common Multiple LCM 可以使用GCD来计算 下面首先介绍GCD和LCM 然后介绍辗转
  • node.js解析xml(xmlreader)

    博客搬家 由于各种原因 我现在的博客将首发于blog mojijs com 可以百度搜索 姜哥的墨迹技术博客 或者 点击这里 本文地址 http blog mojijs com post 19 html xml作为一种重要的数据交换格式 我
  • 图书库毕业设计网页增删改查源码

    介绍 使用HTML VUE PHP MYSQL写的一个简单图书库 实现了简单的数据库增删改查 以及数据列表的展示 源码里包含了前端文件 和api文件 还有数据库表文件 搭建好环境 导入数据库 配置好数据库链接即可直接运行 学习资料地址 ht
  • javaswing基本使用

    package exam test1 import javax swing import java awt import java awt event ActionEvent import java awt event ActionList
  • 三态门——概念,作用,原理

    介绍一下三态门的概念 作用 原理 目录 三态门的概念 三态门的作用 实现总线结构 实现双向数据传输 三态门的原理 三态门的概念 三态门是指逻辑门的输出有三种状态 高电平状态 低电平状态 高阻状态 其中 高阻状态相当于隔离状态 因为高阻状态电
  • linux x64 asm 参数传递,x64 ASM 常用汇编指令

    语法习惯 立即数 开头 寄存器 开头 取地址里面的值 偏移量 寄存器 除了 lea 取地址指令 外 lea就是取地址 load effecive address 整形操作通用后缀 后缀 b w l q 1 2 4 8 byte word l
  • spring源码之@Autowired属性注入

    注入现象 当我们在属性上面加上 Autowired的时候 spring就要根据Type来注入实例了 那么到底会找哪个实例的如果有多个怎么办 今天就来实验一下 多接口注入 当注入的属性接口下有多个实现 这个时候运行的话是 public cla
  • npm link实操详细指南

    准备 首先我们需要有两个npm包 一个作为依赖包 一个作为应用包 依赖包 deps 应用包 app 然后仔仔细细的看一下依赖包的包名和输出路径 如main 好几次用法不对都是因为main字段配置的路径有问题 如我的依赖包package js

随机推荐

  • 多线程(Threading)和多进程(Multiprocessing)

    多线程和多进程 线程和进程是什么 进程间通信方式 线程间通信方式 死锁 Python多线程 Threading 什么是多线程 基本方法函数 join Queue 继承使用线程 同步 GIL锁 锁 Python多进程 多核 Multiproc
  • java.lang.ClassCastException的java类型转换异常解决方案

    在项目中 需要使用XStream将xml string转成相应的对象 却报出了java lang ClassCastException com model test cannot be cast to com model test的错误 原
  • 钟汉良日记:什么副业最好?可以持续带来复利效应的技能,写作!

    2022年9月3日 周六 大晴天 看到一些人做抖音 做自媒体发财了 你是不是也挺羡慕的 可是真要你也去拍摄短视频 做直播真人出镜 大多数人又做不到 怎么办 我觉得 大家还是要认识到一点 打铁还需自身硬 你如果没有足够的对应的能力 只能做一个
  • 【从零开始的Java开发】2-10-2 Servlet入门:Servlet开发步骤、请求参数的发送与接收、Get和Post、注解

    文章目录 概述 软件结构发展史 Tomcat与Servlet Servlet 第一个Servlet JavaWeb工程结构 Servlet开发步骤 请求参数的发送与接收 Get和Post请求方法 Servlet生命周期 使用注解简化配置 启
  • 在Unity中双击打不开vs脚本文件

    从官网下载软件后 创建新项目后 容易漏掉这个设置 导致双击脚本打不开 解决方法 点击找到Edit gt Preferences gt External Tool gt External Script Editor 将对应设置改成你所下载的v
  • 11. TypeScript 条件类型

    TypeScript 条件类型 1 条件类型基本使用 可以使用extends关键字和三元表达式 实现条件判断 interface Fish name1 string interface Water name2 string interfac
  • River Jumping【贪心+模拟】

    题目链接 我们可以贪心的从前往后 每次选最接近的且满足条件的这样的贪心 然后从后往前的时候 就是直接用倒着一个个判断是否合法即可 include
  • 2023百度云智大会:科技与创新的交汇点

    这次的百度云智大会 可谓是亮点云集 发布了包含42个大模型 41个数据集 10个精选应用范式的全新升级千帆大模型平台2 0 发布首个大模型生态伙伴计划 而且也预告了文心大模型4 0的发布 大模型服务的成绩单也非常秀 月活企业数已近万家 覆盖
  • 【已解决】Docker启动失败,报错Cannot connect to the Docker daemon at unix:///var/run/docker.sock.

    报错原因 无法与Docker守护进程建立连接 守护进程负责管理Docker容器和镜像 并提供对Docker API的访问 解决措施 输入以下代码 重启docker服务 service docker start 验证成功 1 输入以下代码 则
  • 如何避免爬虫IP被屏蔽

    各位爬友们好 作为一名专业的爬虫代理提供者 我要和大家分享一些避免爬虫IP被屏蔽的实用技巧 你知道吗 当我们爬取数据的时候 很容易被目标网站识别出来并封禁我们的IP地址 导致无法继续爬取数据 这个问题困扰了很多爬虫程序员 但别担心 今天我就
  • jetbrains IDE设置 phpstorm

    PhpStorm插件 进入 File gt Settings gt Plugins gt Browse repositories 搜索你想要的插件 PHP插件 Symfony Plugin 支持 Symfony 2 3 4 Laravel
  • vue+element-ui el-table组件二次封装实现虚拟滚动,解决数据量大渲染DOM过多而卡顿问题

    一 此功能已集成到TTable组件中 二 最终效果 三 需求 某些页面不做分页时 当数据过多 会导致页面卡顿 甚至卡死 四 虚拟滚动 一 固定一个可视区域的大小并且其大小是不变的 那么要做到性能最大化就需要尽量少地渲染 DOM 元素 而这个
  • stm32电机控制之控制两路直流电机!看完你会了吗

    小车使用的电机是12v供电的直流电机 带编码器反馈 这样就可以采用闭环速度控制 这里电机使用PWM驱动 速度控制框图如下 由以上框图可知 STM32通过定时器模块输出PWM波来控制两个直流电机的转动 通过改变PWM占空比的大小可以改变电机的
  • 【分享】科大讯飞星火认知大模型(初体验)

    前言 哈喽 大家好 我是木易巷 随着人工智能技术的迅猛发展 自然语言处理 NLP 成为了热门话题 在众多NLP模型中 科大讯飞星火认知大模型成为了一个备受瞩目的新秀 今天我们来了解一下这个由科大讯飞公司开发的人工智能模型 内测方法在文末 5
  • 智齿科技:更坚定地迈出“全面国际化”战略步伐

    2月28日 智齿科技召开了以 领航出海 联络世界 为主题的媒体沟通会 近二十家出海媒体 ToB行业媒体代表参与了此次会议 会上 智齿科技联合创始人 CEO徐懿公布了国际化战略实施情况 自2021年底开展国际化战略至今 智齿科技已初步构建了国
  • java异常处理

    1 概念 在我们的程序运行时经常会出现一些操作会使我们的程序无法继续运行下去 用户的非正常输入或者程序本身逻辑上的错误等 这些都会导致我们程序无法继续进行下去 但我们想要这个程序继续执行 就需要进行处理 1 1 在java中 运行时的错误会
  • Qt-QLabel的创建使用和修改属性

    一 QLabel的 创建 方法1 QLabel lable1 new QLabel 默认位置 0 0 lable1 gt setText 这是一个QLabel1 设置文本 lable1 gt setParent this 设置parent
  • 3.基于Arduino的循迹小车

    前言 长期以来一直把arduino看为一款比较弱智简单的单片机 好像事实也是如此 小学生基本不会去学51单片机 但是却在学习arduino单片机 因为相当于傻瓜编程 网上程序一扎一大堆 甚至小学生比有的大学生玩的还溜 arduino使用的是
  • QT的常用的数据类型

    QT的基本数据类型 QT的数据类型其实也是C 的数据类型 两者用法基本一致 因此对照C 的基本数据类型介绍QT的基本类型比较 C vector list hash map QT QVector QList QHash 一 QVector 1
  • Android 组件化 路由跳转(上)

    一 简介 组件化中 每个Module之间的页面跳转是个问题 因为每个Module不可能知道其他Module的Activity路径 所以我们没有办法像普通的工程一样使用Intent方式跳转 那么组件化开发中每个Module之间如何完成页面跳转