FyListen 在 MVP 架构中的内存优化表现
FyListen 在 MVP 中的内存优化表现
本文只是分享个人开源框架的内存优化测试,你可以直接跳到最后,参考内存泄漏的分析过程!
项目地址:
https://github.com/StudyNoteOfTu/fylisten2-alpha1
由于使用到 AOP,所以直接导入依赖是是用不了的。需要将该模块和主模块进行合并,并做AspectJ的配置。
FyListen是否能够应用到项目中,需要测试。本文测试的结果为:【可以使用,而且符合开闭原则】
本次实验分析,我选择分析它在MVP架构中的内存优化表现,我将通过以下场景过程进行模拟:
-
Activity 实现了 LifecyclePublisher 接口,并将 onDestroy() 生命周期暴露出去
-
Presenter 层使用 RxJava 来控制 Model层和View层
-
Presenter 层的 Rxjava 利用 FyListen 对传入的 View层进行生命周期监听
- compose()操作符,发现生命周期结束,立马执行 dispose(),停止上游的异步任务,最快速度释放资源
-
View的任务是:从网络上下载图片,并保存到本地
- Model层1:retrofit从网络下载图片(模拟卡顿10s)
- Model层2:引用Context,进行本地文件写入
-
RxJava 极大地化简了 Model 层代码
-
如果让Model层自行做异步,再通过接口回调,则需要在Model层的接口中加上一个类似:
public interface IDownloadModel{public interface OnDownloadListener{void onFinish(String path);} }
-
由于使用RxJava,自动切换到子线程执行,所以完全可以让 Model 层的方法进行同步的返回,只需要由 Presenter 层将最后的结果回调给 View 层(如果View层没有销毁的话/dispose()没有被调用)
-
同时,为了尽可能地简化代码,以表现出 FyListen 生命周期监听的优越性,我们不做:
- 不在View层对Presenter进行解绑 - 减少了 View 层 onDestroy() 中的代码量
- 不在Presenter层对FyListen做发布者释放
- 和 unregisterPublisher() 方法的设计初衷保持一致:这个方法只用来发布者主动要求不再发布,不代表发布者生命周期结束!
1. 代码结构:
Model层
-
FileStoreModelImpl - 本地文件写入工具,负责将下载图片的byte[]写入本地文件 - 模拟本地io
public class FileStoreModelImpl implements IFileStoreModel {FileOutputStream fos;/*** 取消文件存储或者读取的工作*/@Overridepublic void cancel() {try {if (fos != null) {fos.close();}} catch (IOException e) {e.printStackTrace();}}/*** 开始文件存储*/@Overridepublic String storeFile(Context context, String filename, String filepath, byte[] filebytes) {try {//由于读写暂时没用到context,我们使用是GCROOT的局部变量,来对context作一个强引用Context c = context;File file = new File(filepath+File.separator+filename);if (file.exists()){file.delete();}File parentFile = file.getParentFile();if (!parentFile.exists()){parentFile.mkdirs();}//本地文件写入file.createNewFile();fos = new FileOutputStream(file);fos.write(filebytes);fos.flush();fos.close();} catch (IOException e) {e.printStackTrace();}return filepath+File.separator+filename;} }
-
PictureDownloadImpl - 网络图片下载工具 - 模拟耗时的网络请求
public class PictureDownloadImpl implements IPictureDownloadModel {//将 Observable交出去,让presenter来调度该异步任务@Overridepublic Observable<ResponseBody> download(String url) {//使用retrofit进行下载Retrofit retrofit = new Retrofit.Builder().baseUrl("https://img-blog.csdnimg.cn/").addConverterFactory(GsonConverterFactory.create()).addCallAdapterFactory(RxJava2CallAdapterFactory.create()).build();IPictureDownloadService downloadServiceProxy = retrofit.create(IPictureDownloadService.class);//模拟网络卡顿,等待时间长达1000秒try {Thread.sleep(1000_000);} catch (InterruptedException e) {e.printStackTrace();}return downloadServiceProxy.download(url);}//由于将异步任务交出有 Presenter层来管理,所以observable任务的停止有rxjava来控制,这里不作实现@Overridepublic void cancel() {} }
Presenter层
-
BasePresenter - 绑定的同时开启生命周期监听,当LifecyclePublisher生命周期来到 onDestroy()的时候,进行解绑:
public class BasePresenter<T> implements Listener {//所有presenter都需要传入IView 所以我们抽出Base层 并且用弱引用来绑定IView//解决内存泄漏问题,我们暂时采用弱引用方式// 还是得等到内存不足的时候才让gc来收protected WeakReference<T> mViewRef;//根除内存泄漏 --- 绑定//进行绑定public void attachView(T view){mViewRef = new WeakReference<T>(view);//进行监听,监听目标退出时,自动回调并进行解绑!!!if (view instanceof LifecyclePublisher){//如果是个生命周期发布者,就监听它//如果项目中并没有使用LifecyclePublisher,即view没有实现LifecyclePublisher,就不会进到这里FyListen.registerListenerAnyway((LifecyclePublisher)view,this);}}//进行解绑public void detachView(){//手动地打断弱引用,而非让gc来mViewRef.clear();}/*** 监听到onDestroy()的时候,自动解绑* @param p 生命周期发布者*/@Overridepublic void onDestroy(LifecyclePublisher p) {detachView();}@Overridepublic void onError(LifecyclePublisher p, String error) {//一般不会使用到,这是LifecyclePublisher绑定出问题时候的回调} }
-
PictureDownloadPresenter - 具体表现层,负责控制Model层进行图片下载与保存,并将结果回调给View层。
- Model层分别为下载器和文件存储工具,将任务细化解耦,便于后期维护
- 使用 RxJava 按顺序执行下载和存储这两个步骤,如果任务成功完成,将回调,如果任务中断,则直接结束方法
public class PictureDownloadPresenter<T extends IPictureDownloadView> extends BasePresenter<T> {//下载器IPictureDownloadModel pictureDownloadModel = new PictureDownloadImpl();//文件工具-存储byte[]或者stream到本地,再次之前,请自行处理权限申请IFileStoreModel fileModel = new FileStoreModelImpl();//presenter发起电影下载以及文件存储工作,任务分工明确,使用两个model//rxjava将两个先后执行的异步任务整合起来,得到最终结果后,回到主线程,回调UIpublic void downloadPicture(String url) {if (mViewRef.get() != null) {mViewRef.get().beginDownload();if (pictureDownloadModel != null && fileModel != null) {//开启监听,以及线程切换//1.执行下载任务//2.下载任务执行之后,保存文件//3.主线程回调任务结果// compose(CommonTransformer.listen(mViewRef.get())):// 传入的mViewRef.get()如果是LifecyclePublisher,就会进行生命周期监听注册//当view的onDestroy()被监听到时,会自动调用dispose()停止rxjava的工作//如果不是,就自行根据原项目结构进行内存泄漏处理Disposable subscribe = Observable.create((ObservableOnSubscribe<String>) emitter -> emitter.onNext(url)).flatMap(new Function<String, Observable<String>>() {@Overridepublic Observable<String> apply(String s) throws Exception {return pictureDownloadModel.download(s).map(new Function<ResponseBody, String>() {@Overridepublic String apply(ResponseBody responseBody) throws Exception {//获取返回结果中的文件,通过filemodel进行存储,分工明确byte[] bytes = responseBody.bytes();//这些model都同步执行任务,提交异步交给rxjava来完成String filePath = Environment.getExternalStorageDirectory().getPath();String fileName = "test.jpg";return fileModel.storeFile((Context) mViewRef.get(), fileName, filePath, bytes);}});}}).compose(CommonTransformer.listen(mViewRef.get())).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Consumer<String>() {@Overridepublic void accept(String s) throws Exception {if (mViewRef.get() != null) {//主线程回调下载结果(返回文件下载路径)mViewRef.get().pictureDownloaded(s);}}});}}}}
-
RxJava使用 compose()操作符来插入生命周期监听
public class CommonTransformer<T> implements Listener, ObservableTransformer<T,T> {CompositeDisposable compositeDisposable = new CompositeDisposable();//rxjava监听到生命周期发布者发起了 onDestroy//就会回调到这里,将rxjava的任务取消!@Overridepublic void onDestroy(LifecyclePublisher p){Log.e("TAG","publisher ondestroyed");if (!compositeDisposable.isDisposed()){compositeDisposable.dispose();}}@Overridepublic void onError(LifecyclePublisher p, String error) {Log.e("TAG","publisher error");}@Overridepublic ObservableSource<T> apply(Observable<T> upstream) {return upstream.doOnSubscribe(new Consumer<Disposable>() {@Overridepublic void accept(Disposable disposable) throws Exception {compositeDisposable.add(disposable);}});}public static <T> CommonTransformer<T> listen(Object o){CommonTransformer<T> transformer = new CommonTransformer<>();if (o instanceof LifecyclePublisher){//如果object是lifecyclePublisher,就注册,否则什么也不做Log.e("TAG","listen to lifecycle publisher");FyListen.registerListenerAnyway((LifecyclePublisher) o,transformer);}return transformer;} }
View层
常见MVP架构,通常会使用BaseActivity来进行Presenter的绑定和解绑,由于 FyListen 符合设计思想:对拓展开放,对修改关闭。这表现在:
- 只需要在原项目的BaseActivity中实现LifecyclePublisher这个空接口
- 在onDestry()上填上@LifecycleTrace(Status.ON_DESTROY) 注解
对原本项目几乎没有任何修改!
-
BaseActivity - MVP架构下的Activity基类
public abstract class BaseLifecyclePublisherActivity<V, T extends BasePresenter<V>> extends AppCompatActivity implements LifecyclePublisher {//表示层的引用public T mPresenter;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);//由于监听者使用的是 registerListenerAnyway()// 所以甚至不需要发布者主动进行注册// 更加符合了对修改关闭,对拓展开放的设计原则 // FyListen.registerPublisher(this);mPresenter = createPresenter();mPresenter.attachView((V)this);}protected abstract T createPresenter();//对外暴露onDestroy()的生命周期回调@LifecycleTrace(Status.ON_DESTROY)@Overrideprotected void onDestroy() {super.onDestroy();//没有FyListen的时候,未解决内存泄漏//你需要手动在这里解绑//mPresenter.detachView();}}
-
MainActivity - 下载图片并展示的活动
一个View对应一个Presenter,一个Presenter可以根据业务数据要求,找到不同的Model层进行数据获取,并将获取到的数据处理完后,回调View层更新UI的接口。通过RxJava,我们不再需要在View层写类似 runOnUIThread()的代码。
public class MainActivity extends BaseLifecyclePublisherActivity<IPictureDownloadView, PictureDownloadPresenter<IPictureDownloadView>> implements IPictureDownloadView,LifecyclePublisher {Button btnDownload;ImageView ivPicture;//图片地址String picUrl = "eb102bfc59ae457798bbf13c381a4af1.jpeg";@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initViews();}//绑定表示层//如果未来表示层逻辑需要替换,在这里替换需要的表示层即可@Overrideprotected PictureDownloadPresenter<IPictureDownloadView> createPresenter() {return new PictureDownloadPresenter<>();}private void initViews() {btnDownload = findViewById(R.id.btn_download);btnDownload.setOnClickListener(v->{mPresenter.downloadPicture(picUrl);});ivPicture = findViewById(R.id.imageview);}//----------UI 回调---------------@Overridepublic void pictureDownloaded(String filepath) {//假设我们下载了一个图片,我们把它展现出来Glide.with(this).load(filepath).into(ivPicture);}@Overridepublic void beginDownload() {//通知用户开始下载了!Toast.makeText(this, "开始下载图片,并存储到本地", Toast.LENGTH_SHORT).show();} }
2. 测试开始 - 测试成功
我们从LauncherActivity进入到MainActivity后,点击按钮进行下载图片,如果一直等待,图片将会展示出来,这是正常运行的情况:
为了测试在图片下载过程中,点击返回键,Activity是否能够正常被释放,我将图片下载的延时设置为100s,我们来做以下操作:
- 点击下载图片
- 点击返回键
- 查看Profiler中内存情况:
- 点击下载图片时候的内存情况
- 点击返回后的内存情况
- 主动GC后的内存情况
1. 点击下载图片时候的内存情况:
由于MainActivity在前台运行,所以自然没有被释放
2. 在图片还在下载过程中(模拟1000秒等待),点击返回
我们观察到,虽然点了返回,但是MainActivity仍然没有被释放:
我们注意到,如果一个对象只被弱引用所持有的,也不会立即被释放,需要等到GC到来的时候,才被释放。我们来看一下 FyListen 是否成功将其他引用全部断开:
可以看到,现在剩下唯一的GCROOT只有 WeakHashMap 对它持有引用,而且是弱引用。我们可以等待GC的到来,也可以通过下面这个按钮让GC提前到来:(我在这里卡了很久,一直在想为什么WeakHashMap没有释放对MainActivity的引用,后来才想起来,这只是个弱引用,只是GC还没来而已!)
通知GC清理战场后,图片还没下载好,但是我们惊喜的发现,Activity被释放了!
本人又进行了额外强度大一点的测试:连续开关MainActivity多次,导致内存中有很多的MainActivity实例,通知GC时,全都被回收,过程同上,不贴图了。
3. 为什么会这样呢?
WeakHashMap的原理我后续会另外整理一份文档和大家分享。我们只需要知道,WeakHashMap对key是个弱引用,key被回收后,value会在 get()、put()等方法中被主动置空清除。
我们主要来分析 FyListen 作为核心角色,在这里面发挥的作用,具体使用参考FyListen 的接口文档:
1. 代码优化
FyListen 可以被用在原本就构建好的MVP工程,你只需要做两件事:
- 让BaseActivity或者Activity实现 LifecyclePublisher 接口
- 在Presenter层实现一个监听者,这里的可能性很多,我上面是在一个RxJava的项目中的表现,但这并不影响没有使用 RxJava 框架的项目,你只需要做到:实现一个Listener接口的实例,让它监听View层的LifecyclePublisher实例
- 例如本文分析时,通过Listener实例,通过dispose()让RxJava的异步任务停止,从而释放引用
- 你也可以在Presenter层任意一个合适的地方,对View层注册监听。
- 如果你的项目比较特殊,View和Presenter是多对多,FyListen的回调中也明确了是谁发来的生命周期通知。
- 你不再需要在 BaseActivity 的 onDestroy() 中对 Presenter层进行解绑,所有解绑工作都放在了 Presenter层的Listener实例的 onDestroy(LifecyclePublisher p)生命周期回调之中
2. 优化原理
首先,FyListen的监听者可以主动要求实现了 LifecyclePublisher接口的发布者进行生命周期发布注册,这使得你不用在 BaseActivity 的onCreate() 中加入registerPublisher()这串代码进行发布者注册。(对修改关闭设计)
//监听者在注册监听时,只需要有发布者的引用,就可以让发布者被动的去注册生命周期发布。
//如果这个发布者此时已经被释放了,将不会注册成功,并回调监听者的 onError()方法
FyListen.registerListenerAnyway((LifecyclePublisher) o,listener);
其次,FyListen由于监听到了LifecyclePublisher的onDestroy()生命周期,会通知所有仍然存活着的监听者。监听者们可以通 onDestroy(LifecyclePublisher p)的回调,判断是否需要退出,如果要退出,执行一些任务关闭的方法:
例如本例中,监听到onDestroy,主动调用dispose()关闭 RxJava 的工作
public class CommonTransformer<T> implements Listener, ObservableTransformer<T,T> {//作为Listener,发布者通知 ON_DESTROY 的时候,回调到这个方法@Overridepublic void onDestroy(LifecyclePublisher p){//通过 dispose() 来关闭rxjava的工作if (!compositeDisposable.isDisposed()){compositeDisposable.dispose();}}
}
除了关闭执行的任务,还需要在FyListen管理着中,将View层的引用断开,但是这个步骤,由WeakHashMap帮我们完成了,因为本身就是弱引用。
需要注意的是,本项目中,认为Model层是长生命周期,View层是短生命周期,所以不需要处理View对Model层的引用(准确来说,是不需要处理 FyListen的map对Model层的强引用,Model层的引用会在View层释放之后的某一个时间被回收。如果有性能要求,你也可以在监听者的onDestroy()中,进行解绑监听,提前释放引用)。如果你的项目中,View层是长生命周期,而Model层工具是短声明周期,请注意调用 unregisterListener(LifecyclePublisher target, Listener listener)
,将Model层解放出来,使之可以被回收!
相关文章:
FyListen 在 MVP 架构中的内存优化表现
FyListen 在 MVP 中的内存优化表现 本文只是分享个人开源框架的内存优化测试,你可以直接跳到最后,参考内存泄漏的分析过程! 项目地址: https://github.com/StudyNoteOfTu/fylisten2-alpha1 由于使用到 AOP,所以直接…...
Qt代码单元测试以及报告生成
简介 单元测试是所有测试中最底层的一类测试,是第一个环节,也是最重要的一个环节,是唯一一次有保证能够代码覆盖率达到100%的测试,是整个软件测试过程的基础和前提,单元测试防止了开发的后期因bug过多而失控࿰…...
vscode构建Vue3.0项目(vite,vue-cli)
构建Vue3.0项目构建Vue3.0项目1.使用Vite构建vue项目的方法以及步骤1. 安装vite2. 运行vite vue 项目3.说明2.使用vue-cli构建vue项目的方法以及步骤1.安装全局vue cli —— 脚手架2、VSCode3.报错4.运行构建Vue3.0项目 1.使用Vite构建vue项目的方法以及步骤 1. 安装vite n…...
【2023】华为OD机试真题Java-题目0215-优雅数组
优雅数组 题目描述 如果一个数组中出现次数最多的元素出现大于等于 k k k 次,被称为k-优雅数组, k k k 也可以被称为优雅阈值。 例如,数组[1, 2, 3, 1, 2, 3, 1],它是一个3-优雅数组,因为元素1出现次数大于等于3次...
通过Prowork每日自动提醒待处理工作任务
对于中小团队来说,由于不需要繁琐的流程和高频的异地沟通,需要一款更适合中小团队的日程和项目管理工具。而Prowork就是这样一款敏捷高效的协同平台。Prowork与以往各种项目管理⼯具最⼤的不同在于,其弱化流程和弱化权限的特性,不…...
Linux自定义系统服务
文章目录一. Linux系统服务二. 自定义系统服务一. Linux系统服务 Linux 系统服务有时也称为守护程序,是在Linux启动时自动加载并在Linux退出时自动停止的系统任务,CentOS 7.x开始,CentOS开始使用 systemd服务来代替 daemon ,原来…...
mongodb lambda 查询插件
需求背景需要一个像mybatis plus 一样的基于lambda, 且面向对象的查询mongo数据的插件。在网上找了很久,没有发现有类似功能的插件。于是自己手写了一个,借助mongoTemplate屏蔽了底层查询语句的实现细节。在此基础上,实现了查询的统一封装。技…...
C++设计模式(16)——责任链模式
亦称: 职责链模式、命令链、CoR、Chain of Command、Chain of Responsibility 意图 责任链模式是一种行为设计模式, 允许你将请求沿着处理者链进行发送。 收到请求后, 每个处理者均可对请求进行处理, 或将其传递给链上的下个处理…...
springmvc+jsp电影院购票售票选座推荐网站java ssm
本电影购票推荐网站以SSM作为框架,B/S模式以及MySql作为后台运行的数据库。本系统主要包括以下功能模块:个人中心、用户管理、电影信息管理、电影类型管理、影院信息管理、系统管理、订单管理等模块,通过这些模块的实现能够基本满足日常电影购…...
ASEMI高压MOS管4N65SE,4N65SE参数,4N65SE特征
编辑-Z ASEMI高压MOS管4N65SE参数: 型号:4N65SE 漏极-源极电压(VDS):650V 栅源电压(VGS):30V 漏极电流(ID):4A 功耗(PD…...
第46章 自定义静态与数据库动态授权依赖注入的定义实现
1 数据库动态授权表授权原理 2 准备工作 2.1 重构Program.cs using Framework.Infrastructure.Extensions; var builder WebApplication.CreateBuilder(args); //如果启动项中不存在“appsettings.json”文件,则通过.Net(Core)的内置方法自动新建“appsettings.…...
Go语言面试题
请解释 Go 语言中的 goroutine 是什么。请解释 Go 语言中的 channel 是什么,并举例说明它的用途。请解释 Go 语言中的 interface 是什么,并举例说明它的用途。请解释 Go 语言中的 map 和 slice 是什么,并举例说明它们的用途。请解释 Go 语言中…...
Kubernetes入门级教程
Kubernetes入门级教程1. Introduction1.1 概述1.2 关键字介绍2. Cluster Install2.1 Big Data -- Postgres3. 基础知识3.1 Pod3.2 控制器3.3 通讯模式3.4 服务发现4. Command4.0 编辑文件4.1 在宿主机执行命令4.2 创建资源对象4.3 查询资源对象4.4 查询资源描述4.5 修改资源4.6…...
15个顶级思维模型
今天给大家分享几个思维模型,无论是数分、数开还是其他岗位,都会有所启发。 查理芒格认为,每个学科都是从一个独特的角度去切入了解这个世界,都是一个摸象的瞎子。 要超越普通人的认知决策,就必须掌握多个核心思维模…...
外贸谷歌优化,外贸google SEO优化费用是多少?
本文主要分享关于做外贸网站的谷歌seo成本到底需要投入多少这一件事。 本文由光算创作,有可能会被剽窃和修改,我们佛系对待这种行为吧。 那么外贸google SEO优化费用是多少? 答案是:2w~25w。 好,看到这个答案先别激…...
华为OD机试 - 统计匹配的二元组个数(Python) | 机试题算法思路
最近更新的博客 华为OD机试 - 招聘(Python) | 备考思路,刷题要点,答疑 【新解法】华为OD机试 - 五键键盘 | 备考思路,刷题要点,答疑 【新解法】华为OD机试 - 热点网络统计 | 备考思路,刷题要点,答疑 【新解法】华为OD机试 - 路灯照明 | 备考思路,刷题要点,答疑 【新解…...
Java 日志简介
目录1、Slf4j2、Log4j3、LogBack4、Logback 优点5、ELK1、Slf4j slf4j 的全称是 Simple Loging Facade For Java,即它仅仅是一个为 Java 程序提供日志输出的统一接口,并不是一个具体的日志实现方案,就比如 JDBC 一样,只是一种规则…...
HTTPS协议原理---详解六个加密方案
目录 一、HTTPS 1.加密与解密 2.我们为什么要加密? 3.常见加密方式 ①对称加密 ②非对称加密 4.数据摘要 5.数字签名 二、HTTPS的加密方案 1.只是用对称加密 2.只使用非对称加密 3.双方都使用非对称加密 4.非对称加密+对称加密 中间人攻…...
纯css实现坤坤经典动作-“铁山靠”
背景 2023年2月16日,晴,今天没有工作,一直在掘金摸鱼,摸的我好累。 不行!我得找点有意义的事情做! 此时间,我发的一条沸点竟然有小黑子给我评论,\ 我看到之后气不打一处来ÿ…...
Linux 操作系统原理 — NUMA 体系结构
目录 文章目录 目录NUMA 体系结构NUMA 的基本概念查看 Host 的 NUMA TopologyBash 脚本DPDK 脚步NUMA 体系结构 NUMA(Non-Uniform Memory Access,非一致性存储器访问)的设计理念是将 CPU 和 Main Memory 进行分区自治(Local NUMA node),又可以跨区合作(Remote NUMA nod…...
cesium学习记录01
1,将右弦GISer的cesium实战系列的大部分功能(25-110) 都又跟着走了一遍(大部分是CTRL CCTRL V) 2,代码SVN地址(用户名:liu 密码:123) (如果我没有遗漏上传…...
Linux延时队列工作原理与实现
当进程要获取某些资源(例如从网卡读取数据)的时候,但资源并没有准备好(例如网卡还没接收到数据),这时候内核必须切换到其他进程运行,直到资源准备好再唤醒进程。 waitqueue (等待队列) 就是内核…...
【Python】scipy稀疏矩阵的奇异值分解svds
文章目录基本原理scipy实现测试基本原理 当AAA是方阵时,可以很容易地进行特征分解:AWΣW−1AW\Sigma W^{-1}AWΣW−1,其中Σ\SigmaΣ是AAA的特征值组成的对角矩阵。如果WWW由标准正交基组成,则W−1WTW^{-1}W^TW−1WT,…...
网络安全等级保护基础知识汇总
等保 全称是网络安全等级保护,分为两个阶段 等保1.0 1994年国务院147令《中华人民共和国计算机信息系统安全保护条例》 等保2.0 2017年 网络安全法,21条规定的 国家实行网络安全等级保护制度,等保进入了有法可依阶段。 2019年国标22239-2019版…...
ros1使用过程中遇到的问题记录
Failed to fetch current robot state如果使用的是moveit助手生成的demo.launch文件启动机械臂的话,应该是其他在运行的自己写的节点代码中少了spin函数,因为getCurrentPose函数依赖于spin,也可以使用AsyncSpinner。具体看下面这个链接https:…...
centos7给已有分区进行扩容
1、背景 最近我在虚拟机上安装软件,发现磁盘空间不足,通过上网查找资料,发现可以通过如下方法进行磁盘扩容,此处进行记录一下。 2、实现扩容 1、虚拟机上添加一个新的硬盘 2、查看我们刚刚加入的硬盘 此处我们可以看到/dev/nvm…...
package.json
{"name": "project-name", 项目名字"version": "0.1.0", 版本号"private": true, 项目包,不需要发版"scripts": { 脚本"serve": "vue-cli-service serve", 运行命令后缀是 se…...
【项目精选】户籍管理系统(视频+论文+源码)
点击下载源码 当今社会人们生活质量越来越高,人们对生活品质的追求不断提升,对于孩子求学,变更住所等情况时有发生,因此对于户籍变动管理就显得十分重要,管理用户的户籍信息可以有效防止信息错乱,信息管理过…...
【IP技术】网络安全防护措施
网络安全威胁造成的形式主要包含运用系统软件缺点或侧门,运用网络防火墙安全隐患,内部结构客户的泄密、泄露和毁坏,动态口令进攻和拒绝服务式攻击等。针对该网络安全威胁,现阶段的预防措施主要有五种:1.访问控制技术&a…...
基于AIOT技术的智慧教室智能物联管控系统设计与实现(提纲)
摘要随着物联网技术的不断发展和智能化的不断推进,智慧教室已经成为现代教育中不可或缺的一部分。本文提出了一种基于AIOT技术的智慧教室智能物联管控系统设计与实现方案,该方案集成了物联网技术、人工智能技术、大数据技术和云计算技术等先进技术&#…...
南宁建网站公司就去云尚网络/百度站长工具数据提交
今天开始慢慢的吧挖沙啦的开发经验给大家分享,哈哈,很高兴的向大家推荐了。如有不才的地方,还请大家多多指点 把汉字转换成拼音(全拼) - 挖沙啦代码库【1】 比如说以下标题,使用的就是URL中文转拼音的技术,很简单,给大家一点点的讲…...
网站建设课程设计/seo推广绩效考核指标是什么
 1、Android-Universal-Image-Loader 可以高度配置的网络图片缓存库,非常灵活,用户量最多 缓存过期实现: File cacheDir StorageUtils.getCacheDirectory(context); // or any other folder MemoryCacheAware&l…...
用github做网站/优化技术基础
关于前端性能优化相关的技术知识,网上随便搜一些就有很多,本文将系统性的从初级到高级的思路,总结移动前端性能优化各个方面的相关技术点,内容来自笔者以往经验的总结,希望读者可以花些时间看看。在目前大多数刚从事前…...
做兼职比较专业靠谱的网站/seo成功案例分析
>[warning]Vue访问data修改****思路: 访问属性时, 默认去data里访问对应的key, 设置个data代理即可代码如下:~~~function Vue(options, exp){this.data options.data();let el document.querySelector(options[el]);// 2. 给获取到的data下的属性, 每个key都调用代理proxy…...
wordpress 新建侧边栏/seo自学网站
中国目前正处于由熟人社会向陌生人社会转型的阶段,陌生人社会带来的孤独和挫折以及文化心理的惯性,让中国人对人情和关系的依赖依然存在,凭借熟人关系开展工作,运用人情关系的法则对抗社会制度和规范的现象依然很普遍。 人情、…...
商业网站建设规划书/中国站长站
2019独角兽企业重金招聘Python工程师标准>>> 一、前言 Activiti 5对表单的支持目前还是比较弱的,表现在对表单的开发还需要写Freemark模板,并且它的模板还需要跟class文件一起打包发布。这使得流程的表单设计必须由开发人员来开发处理…...