Android 面试之Glide做了哪些优化?
前言
Glide可以说是最常用的图片加载框架了,Glide链式调用使用方便,性能上也可以满足大多数场景的使用,Glide源码与原理也是面试中的常客。
但是Glide的源码内容比较多,想要学习它的源码往往千头万绪,一时抓不住重点.
本文以Glide做了哪些优化为切入点,介绍与学习Glide的源码与原理,如果对您有所帮助,欢迎点赞.
Glide做了哪些优化?
要想要回答这个问题,我们可以先想一想,如果我们自己要实现一个图片加载框架,我们会思考什么问题?
1.图片下载是个耗时过程,我们首先需要考虑的就是图片缓存的问题
2.图片加载也是个耗内存的操作,很多OOM都是图片加载导致的,所以我们也要考虑内存优化问题
3.图片加载到一半,页面关闭了,图片加载也应该中止,这又牵扯到了生命周期管理的问题
4.还有就是图片加载框架是否支持大图加载?大图情况下会有什么问题?
以上就是我们提出的有关于Glide的几个问题了,这样我们可以轻松得出本文主要包括的内容
1.Glide图片加载的总体流程介绍
2.Glide缓存机制做了哪些优化?
3.Glide做了哪些内存优化?
4.Glide如何管理生命周期?
5.Glide怎么做大图加载?
下面就带着问题进入正文~
1.Glide图片加载总体流程介绍
在开始了解Glide具体做了哪些优化之前,我们先对Glide图片加载的总体流程做一个简单的介绍,让大家首先有个整体概念。
同时在后面对Glide做的优化具体发生在哪一步也可以方便的知道.
概括来说,图片加载包含封装,解析,下载,解码,变换,缓存,显示等操作,如下图所示:

- 1.封装参数:从指定来源,到输出结果,中间可能经历很多流程,所以第一件事就是封装参数,这些参数会贯穿整个过程;
- 2.解析路径:图片的来源有多种,格式也不尽相同,需要规范化;
- 3.读取缓存:为了减少计算,通常都会做缓存;同样的请求,从缓存中取图片(
Bitmap)即可; - 4.查找文件/下载文件:如果是本地的文件,直接解码即可;如果是网络图片,需要先下载;
- 5.解码:这一步是整个过程中最复杂的步骤之一,有不少细节;
- 6.变换:解码出
Bitmap之后,可能还需要做一些变换处理(圆角,滤镜等); - 7.缓存:得到最终bitmap之后,可以缓存起来,以便下次请求时直接取结果;
- 8.显示:显示结果,可能需要做些动画(淡入动画,crossFade等)。
以上就是Glide图片加载的总体流程,这里只做了简单的介绍,详情可见:聊一聊关于Glide在面试中的那些事
2.Glide缓存机制做了哪些优化?
我们知道,下载图片是非常耗费资源的,所以图片缓存机制是图片加载框架很重要的一部分,下面就以一张表格来说明下 Glide 缓存。
| 缓存类型 | 缓存代表 | 说明 |
|---|---|---|
| 活动缓存 | ActiveResources | 如果当前对应的图片资源是从内存缓存中获取的,那么会将这个图片存储到活动资源中。 |
| 内存缓存 | LruResourceCache | 图片解析完成并最近被加载过,则放入内存中 |
| 磁盘缓存-资源类型 | DiskLruCacheWrapper | 被解码后的图片写入磁盘文件中 |
| 磁盘缓存-原始数据 | DiskLruCacheWrapper | 网络请求成功后将原始数据在磁盘中缓存 |
在介绍具体缓存前,先来看一张加载缓存执行顺序,有个大概的印象

Glide的缓存机制,主要分为2种缓存,一种是内存缓存,一种是磁盘缓存。
之所以使用内存缓存的原因是:防止应用重复将图片读入到内存,造成内存资源浪费。
之所以使用磁盘缓存的原因是:防止应用重复的从网络或者其他地方下载和读取数据。
正式因为有着这两种缓存的结合,才构成了Glide极佳的缓存效果。
2.1 内存缓存
Glide默认开启内存缓存,我们也可以通过skipMemoryCache关闭
上面我们可以看到内存缓存其实分两个部分,ActiveResource缓存与LRU缓存
ActiveResources 就是一个弱引用的 HashMap ,用来缓存正在使用中的图片,使用 ActiveResources 来缓存正在使用中的图片,可以保护这些图片不会被 LruCache 算法回收掉
内存缓存加载顺序如下:
1.根据图片地址,宽高,变换,签名等生成key
2.第一次加载没有获取到活动缓存。
3.接着加载内存资源缓存,先清理掉内存缓存,在添加进行活动缓存。
4.第二次加载活动缓存已经存在。
5.当前图片引用为 0 的时候,清理活动资源,并且添加进内存资源。
6.又回到了第一步,然后就这样环环相扣。
总结为流程图如下:

这里没有贴出源码,如果想要看源码的同学可参考:从源码的角度分析 Glide 缓存策略
我们上面总结了Glide内存缓存加载的流程,看到这里我们很容易有个疑问,为什么Glide要设计两种内存缓存?
2.1.1 为什么设计两种内存缓存?
LruCache算法的实现,你会发现它其实是用一个Set来缓存对象的,每次内存超出缓存设定触发trim操作的时候,其实是对这个Set进行遍历,然后移除缓存。但是我们都知道Set是无序的,因此遍历的时候有可能会把正在使用的缓存给误伤了,我还在用着它呢就给移出去了。因此这个弱引用可能是对正在使用中的图片的一种保护,使用的时候先从LruCache里面移出去,用完了再把它重新加到缓存里面。
举个例子

比如我们 Lru 内存缓存 size 设置装 99 张图片,在滑动 RecycleView 的时候,如果刚刚滑动到 100 张,那么就会回收掉我们已经加载出来的第一张,这个时候如果返回滑动到第一张,会重新判断是否有内存缓存,如果没有就会重新开一个 Request 请求,很明显这里如果清理掉了第一张图片并不是我们要的效果。所以在从内存缓存中拿到资源数据的时候就主动添加到活动资源中,并且清理掉内存缓存中的资源。这么做很显然好处是 保护不想被回收掉的图片不被 LruCache 算法回收掉,充分利用了资源。
2.1.1 小结
本节主要总结了Glide内存缓存加载的流程
1.首先去获取活动缓存,如果加载到则直接返回,没有则进入下一步
2.接着去获取LRU缓存,在获取时会将其从LRU中删除并添加到活动缓存中
3.下次加载就可以直接加载活动缓存了
4.当图片引用为0时,会从活动缓存中清除并添加到LRU缓存中
5.之所以要设计两种内存缓存的原因是为了防止加载中的图片被LRU回收
2.2 磁盘缓存
首先了解一下磁盘缓存策略
DiskCacheStrategy.NONE: 表示不缓存任何内容。DiskCacheStrategy.RESOURCE: 在资源解码后将数据写入磁盘缓存,即经过缩放等转换后的图片资源。DiskCacheStrategy.DATA: 在资源解码前将原始数据写入磁盘缓存。DiskCacheStrategy.ALL: 使用DATA和RESOURCE缓存远程数据,仅使用RESOURCE来缓存本地数据。DiskCacheStrategy.AUTOMATIC:它会尝试对本地和远程图片使用最佳的策略。当你加载远程数据时,AUTOMATIC策略仅会存储未被你的加载过程修改过的原始数据,因为下载远程数据相比调整磁盘上已经存在的数据要昂贵得多。对于本地数据,AUTOMATIC策略则会仅存储变换过的缩略图,因为即使你需要再次生成另一个尺寸或类型的图片,取回原始数据也很容易。默认使用这种缓存策略
在了解磁盘缓存时我们主要需要明确一个概念,是当我们使用 Glide 去加载一张图片的时候,Glide 默认并不会将原始图片展示出来,而是会对图片进行压缩和转换,总之就是经过种种一系列操作之后得到的图片,就叫转换过后的图片。
我们既可以缓存变换之前的原始图片,也可以缓存变换后的图片
2.2.1 为什么需要两种磁盘缓存
上文已经说了,DiskCacheStrategy.RESOURCE缓存的是变换后的资源,DiskCacheStrategy.DATA缓存的是变换前的资源
举个例子,同一张图片,我们先在100*100的View是展示,再在200*200的View上展示
如果不缓存变换后的类型相当于每次都要进行一次变换操作,如果不缓存原始数据则每次都要去重新下载数据
如下可以看出,两种缓存的key不一样
DiskCacheStrategy.RESOURCE
currentKey = new ResourceCacheKey(helper.getArrayPool(),sourceId,helper.getSignature(),helper.getWidth(),helper.getHeight(),transformation,resourceClass,helper.getOptions());DiskCacheStrategy.DATA
DataCacheKey newOriginalKey = new DataCacheKey(loadData.sourceKey, helper.getSignature());
2.2.2 小结
本节主要介绍了Glide磁盘缓存的几种策略并介绍了为什么需要两种磁盘缓存的原因
这里也没有贴什么源码,如果想要看源码的同学可参考:从源码的角度分析 Glide 缓存策略
3.Glide做了哪些内存优化?
Glide的内存优化主要也是对Bitmap的优化,在回答这个问题前,我们可以想想有哪些常见的Bitmap优化手段
1.当图片大小与View大小不一致时,可以用inSampleSize进行尺寸优化
2.图片所占内存即宽高每像素所占内存大小,不同的模式每个像素所占的内存大小不同,我们可以利用inpreferredconfig配置
3.Bitmpa所占内存比较大,如果频繁创建回收Bitmap内存可能造成内存抖动,我们可以利用inBitmap利用Bitmap内存
4.内存缓存,上文我们已经介绍了Glide的弱引用缓存与LRU缓存
其实常见的Bitmap内存优化也就这么几种了,不过我们在工作中比较少直接使用他们。
下面我们就介绍下Glide中具体是怎么使用他们的.
3.1 尺寸优化
当装载图片的容器例如ImageView只有100*100,而图片的分辨率为800 * 800,这个时候将图片直接放置在容器上,很容易OOM,同时也是对图片和内存资源的一种浪费。当容器的宽高都很小于图片的宽高,其实就需要对图片进行尺寸上的压缩,将图片的分辨率调整为ImageView宽高的大小,一方面不会对图片的质量有影响,同时也可以很大程度上减少内存的占用
我们通常使用inSampleSize对Bitmap进行尺寸缩放
如果
inSampleSize设置的值大于1,则请求解码器对原始的bitmap进行子采样图像,然后返回较小的图片来减少内存的占用,例如inSampleSize== 4,则采样后的图像宽高为原图像的1/4,而像素值为原图的1/16,也就是说采样后的图像所占内存也为原图所占内存的1/16;当inSampleSize<=1时,就当作1来处理也就是和原图一样大小。另外最后一句还注明,inSampleSize的值一直为2的幂,如1,2,4,8。任何其他的值也都是四舍五入到最接近2的幂。
//1int widthScaleFactor = orientedSourceWidth / outWidth;int heightScaleFactor = orientedSourceHeight / outHeight;//2int scaleFactor =rounding == SampleSizeRounding.MEMORY? Math.max(widthScaleFactor, heightScaleFactor): Math.min(widthScaleFactor, heightScaleFactor);int powerOfTwoSampleSize;//3if (Build.VERSION.SDK_INT <= 23&& NO_DOWNSAMPLE_PRE_N_MIME_TYPES.contains(options.outMimeType)) {powerOfTwoSampleSize = 1;} else {//4powerOfTwoSampleSize = Math.max(1, Integer.highestOneBit(scaleFactor));//5if (rounding == SampleSizeRounding.MEMORY// exactScaleFactor由各个裁剪策略如CenterCrop重写得到,详情可见代码&& powerOfTwoSampleSize < (1.f / exactScaleFactor)) {powerOfTwoSampleSize = powerOfTwoSampleSize << 1;}}options.inSampleSize = powerOfTwoSampleSize;
如上就是Glide图片进行尺寸缩放相关的代码
1.首先计算出图片与View的宽高比
2.根据缩放策略是省内存还是高品质,决定取宽高比的最大值还是最小值
3.当Build.VERSION.SDK_INT<=23时,一些格式的图片不能缩放
4.highestOneBit的功能是把我们计算的比例四舍五入到最接近2的幂
5.如果缩放策略为省内存,并且我们计算的SampleSize<exactScaleFactor,将inSampleSize*2
如上就是Glide图片加载时做尺寸优化的大概逻辑
3.2 图片格式优化
我们知道,Bitmap所占内存大小,由宽*高*每像素所占内存决定
上面的尺寸优化决定宽高,图片格式优化决定每像素所占内存
在API29中,将Bitmap分为ALPHA_8, RGB_565, ARGB_4444, ARGB_8888, RGBA_F16, HARDWARE六个等级。
ALPHA_8:不存储颜色信息,每个像素占1个字节;RGB_565:仅存储RGB通道,每个像素占2个字节,对Bitmap色彩没有高要求,可以使用该模式;ARGB_4444:已弃用,用ARGB_8888代替;ARGB_8888:每个像素占用4个字节,保持高质量的色彩保真度,默认使用该模式;RGBA_F16:每个像素占用8个字节,适合宽色域和HDR;HARDWARE:一种特殊的配置,减少了内存占用同时也加快了Bitmap的绘制。
每个等级每个像素所占用的字节也都不一样,所存储的色彩信息也不同。同一张100像素的图片,ARGB_8888就占了400字节,RGB_565才占200字节,RGB_565在内存上取得了优势,但是Bitmap的色彩值以及清晰度却不如ARGB_8888模式下的Bitmap
值得注意的是在Glide4.0之前,Glide默认使用RGB565格式,比较省内存
但是Glide4.0之后,默认格式已经变成了ARGB_8888格式了,这一优势也就不存在了。
这本身也就是质量与内存之间的取舍,如果应用所需图片的质量要求不高,也可以修改默认格式
//默认格式修改为了ARGB_8888public static final Option<DecodeFormat> DECODE_FORMAT =Option.memory("com.bumptech.glide.load.resource.bitmap.Downsampler.DecodeFormat", DecodeFormat.DEFAULT);
3.3 内存复用优化
Bitmap所占内存比较大,如果我们频繁创建与回收Bitmap,那么很容易造成内存抖动,所以我们应该尽量复用Bitmap内存
Glide主要使用了inBitmap与BitmapPool来实现内存的复用
3.3.1 inBitmap介绍
在 Android 3.0(API 级别 11)开始,系统引入了 BitmapFactory.Options.inBitmap 字段。如果设置了此选项,那么采用 Options 对象的解码方法会在生成目标 Bitmap 时尝试复用 inBitmap,这意味着 inBitmap 的内存得到了重复使用,从而提高了性能,同时移除了内存分配和取消分配。不过 inBitmap 的使用方式存在某些限制,在 Android 4.4(API 级别 19)之前系统仅支持复用大小相同的位图,4.4 之后只要 inBitmap 的大小比目标 Bitmap 大即可
3.3.2 BitmapPool介绍
通过上文我们知道了可以通过inBitmap复用内存,但是还需要一个地方存储可复用的Bitmap,这就是BitmapPool
JDK 中的 ThreadPoolExecutor 相信大多数开发者都很熟悉,我们一般将之称为“线程池”。池化是一个很常见的概念,其目的都是为了实现对象复用,例如 ThreadPoolExecutor 就实现了线程的复用机制
BitmapPool即实现了Bitmap的池化
3.3.3 Glide的应用
private static void setInBitmap(BitmapFactory.Options options, BitmapPool bitmapPool, int width, int height) {@Nullable Bitmap.Config expectedConfig = null;if (expectedConfig == null) {expectedConfig = options.inPreferredConfig;}// BitmapFactory will clear out the Bitmap before writing to it, so getDirty is safe.options.inBitmap = bitmapPool.getDirty(width, height, expectedConfig);}
如上即是Glide设置inBitmap的代码,向BitmapPool中传入宽高与格式,得到一个可复用的对象,这样就实现了Bitmap的内存复用
由于篇幅原因,详细的源码这里没有贴出来,想要了解更多的读者可参考:Coil 和 Glide 的 Bitmap 缓存复用机制
4.Glide如何管理生命周期?
当我们在做一个网络请示时,页面退出时应该中止请示,不然容易造成内存泄漏
对于图片加载也是如此,我们在页面退出时应该中止请示,销毁资源。
但是我们使用Glide的时候却不需要在页面退出时做什么操作,说明Glide可以做到在页面关闭时自动释放资源
下面我们一起看下Glide是如何实现的
主要是两步:
1.调用时通过Glide.with传入context,利用context构建一个Fragment
2.监听Fragment生命周期,销毁时释放Glide资源
4.1 传入context构建Fragment
//通过Activity拿到RequestManager
public RequestManager get(@NonNull Activity activity) {//拿到当前Activity的FragmentManagerandroid.app.FragmentManager fm = activity.getFragmentManager();//生成一个Fragment去绑定一个请求管理RequestManagerreturn fragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));}private RequestManager fragmentGet(@NonNull Context context,@NonNull android.app.FragmentManager fm,@Nullable android.app.Fragment parentHint,boolean isParentVisible) {//①在当前Activity添加一个Fragment用于管理请求的生命周期RequestManagerFragment current = getRequestManagerFragment(fm, parentHint, isParentVisible);//获取RequestManagerRequestManager requestManager = current.getRequestManager();//如果不存在RequestManager,则创建if (requestManager == null) {Glide glide = Glide.get(context);//②构建RequestManager //current.getGlideLifecycle()就是ActivityFragmentLifecycle,也就是构建RequestManager时会传入fragment中的ActivityFragmentLifecyclerequestManager =factory.build(glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);//将构建出来的RequestManager绑定到fragment中current.setRequestManager(requestManager);}//返回当前请求的管理者return requestManager;}
如上所示:
1.在当前Activity添加一个透明Fragment用于管理请示生命周期
2.构建RequestManager并传入Fragment生命周期
4.2 RequestManager监听生命周期
public class RequestManager implements LifecycleListener,ModelTypes<RequestBuilder<Drawable>> { RequestManager(Glide glide,Lifecycle lifecycle,RequestManagerTreeNode treeNode,RequestTracker requestTracker,ConnectivityMonitorFactory factory,Context context) {... //将当前对象注册到ActivityFragmentLifecyclelifecycle.addListener(this);}//...//RequestManager实现了fragment生命周期回调@Overridepublic synchronized void onStart() {resumeRequests();targetTracker.onStart();}@Overridepublic synchronized void onStop() {pauseRequests();targetTracker.onStop();}@Overridepublic synchronized void onDestroy() {targetTracker.onDestroy();}}public class RequestManagerFragment extends Fragment {//生命周期的关键就在ActivityFragmentLifecycleprivate final ActivityFragmentLifecycle lifecycle;public RequestManagerFragment() {this(new ActivityFragmentLifecycle());}RequestManagerFragment(@NonNull ActivityFragmentLifecycle lifecycle) {this.lifecycle = lifecycle;}@Overridepublic void onStart() {super.onStart();lifecycle.onStart();}@Overridepublic void onStop() {super.onStop();lifecycle.onStop();}@Overridepublic void onDestroy() {super.onDestroy();lifecycle.onDestroy();unregisterFragmentWithRoot();}//...
}
逻辑很简单:Fragment生命周期变化会回调RequestManager生命周期,然后在进行相关的资源释放工作
4.3 小结

Glide.with(this)绑定了Activity的生命周期。在Activity内新建了一个无UI的Fragment,这个Fragment持有一个Lifecycle,通过Lifecycle在Fragment关键生命周期通知RequestManager进行相关从操作。在生命周期onStart时继续加载,onStop时暂停加载,onDestory时停止加载任务和清除操作。
5.Glide怎么做大图加载
对于图片加载还有种情况,就是单个图片非常巨大,并且还不允许压缩。比如显示:世界地图、清明上河图、微博长图等
首先不压缩,按照原图尺寸加载,那么屏幕肯定是不够大的,并且考虑到内存的情况,不可能一次性整图加载到内存中
所以这种情况的优化思路一般是局部加载,通过BitmapRegionDecoder来实现
这种情况下通常Glide只负责将图片下载下来,图片的加载由我们自定义的ImageView来实现
5.1 BitmapRegionDecoder介绍
BitmapRegionDecoder主要用于显示图片的某一块矩形区域,如果你需要显示某个图片的指定区域,那么这个类非常合适。
对于该类的用法,非常简单,既然是显示图片的某一块区域,那么至少只需要一个方法去设置图片;一个方法传入显示的区域即可
举个例子:
//设置显示图片的中心区域
BitmapRegionDecoder bitmapRegionDecoder = BitmapRegionDecoder.newInstance(inputStream, false);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.RGB_565;
Bitmap bitmap = bitmapRegionDecoder.decodeRegion(new Rect(width / 2 - 100, height / 2 - 100, width / 2 + 100, height / 2 + 100), options);
mImageView.setImageBitmap(bitmap);
更详细的实现可见:Android 高清加载巨图方案 拒绝压缩图片
不过这种方法虽然也能加载大图,但做的还不够,滑动时内存抖动,卡顿现象比较明显,不能用于线上

下面介绍一种可以用于线上的大图加载方案
5.2 可用于线上的大图加载方案
介绍一个开源库:subsampling-scale-image-view
SubsamplingScaleImageView将大图切片,再判断是否可见,如果可见则加入内存中,否则回收,减少了内存占用与抖动 同时根据不同的缩放比例选择合适的采样率,进一步减少内存占用 同时在子线程进行decodeRegion操作,解码成功后回调至主线程,减少UI卡顿.
总结
本文主要以Glide做了哪些优化为切入点,回答了如下几个问题
1.说一下Glide图片加载的总体流程
2.Glide缓存机制做了哪些优化?
3.Glide做了哪些内存优化?
4.Glide如何管理生命周期?
5.Glide怎么做大图加载?
下面我针对Android 中不同的技术模块,整理了一些相关的 学习文档笔记 及相应的 面试习题(含答案),大家可以针对性的进行参考学习与复习。

相关文章:
Android 面试之Glide做了哪些优化?
前言 Glide可以说是最常用的图片加载框架了,Glide链式调用使用方便,性能上也可以满足大多数场景的使用,Glide源码与原理也是面试中的常客。 但是Glide的源码内容比较多,想要学习它的源码往往千头万绪,一时抓不住重点.…...
【韩顺平 零基础30天学会Java】数组、排序和查找(2days)
数组、排序、查找和多维数组 数组可以存放多个同一类型的数据。数组也是一种数据类 型,是引用数据类型。 定义一个数组 double[] hens {3,5,1,3.4,2,50} 遍历数组得到数组所有元素的和 hens[下标],下标是从0开始编号的。 可以通过数组名.lenght得到数组…...
VUE笔记(一)初识vue
一、vue的简介 1、什么是vue 官网地址:Vue.js Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。 构建用户界面:之前在学习vue之前通过原生js对DOM操作进行构建用户界面的 使用原生js构建用户界面的不足 - 没有规范,…...
3D点云处理:学习总结(更新整理中)
文章目录 开发工具个人看法 微信:dhlddx B站演示视频 前置说明:仅是个人在使用pcl开发过程中的总结(点云处理顺序或比较实用的功能),不喜勿喷; 开发工具 开发IDE:Qt Creator(Windo…...
Day45|leetcode 70. 爬楼梯、322. 零钱兑换、279.完全平方数
leetcode 70. 爬楼梯 题目链接:70. 爬楼梯 - 力扣(LeetCode) 本题可以用背包问题来解决,就相当于楼顶是背包,台阶是物品,相当于之前写法的进阶版。 代码实现 class Solution { public:int climbStairs(in…...
arm:day9
1。思维导图 2..I2C实验,检测温度和湿度 iic.h #ifndef __IIC_H__ #define __IIC_H__ #include "stm32mp1xx_gpio.h" #include "stm32mp1xx_rcc.h" #include "gpio.h" /* 通过程序模拟实现I2C总线的时序和协议* GPIOF ---> AHB4…...
【大模型AIGC系列课程 1-2】创建并部署自己的ChatGPT机器人
OpenAI API 调用 获取 openai api api-key https://platform.openai.com/account/api-keys 利用 python requests 请求 openai 参考 openai 接口说明:https://platform.openai.com/docs/api-reference/chat/create import json # 导入json包 import requests # 导入req…...
启动metastore服务报错
启动Metastore的时候报错: 简略的报错信息: MetaException(message:Error creating transactional connection factory)Caused by: MetaException(message:Error creating transactional connection factory)Caused by: javax.jdo.JDOFatalInternalExce…...
c 语言 算法 技巧 之 用移位来代替乘除
除法 当你需要计算一个数的一半时,通常我们会考虑使用除法运算(/)来实现。然而,计算机内部的运算中,除法通常比加法和乘法运算慢得多,因为除法需要更多的处理步骤。 位运算在这种情况下可以提供一个快速的…...
python爬虫实战零基础(3)——某云音乐
爬取某些云网页音乐,无需app 分析网页第二种方式批量爬取 声明:仅供参考学习,参考,若有不足,欢迎指正 你是不是遇到过这种情况,在pc端上音乐无法下载,必须下载客户端才能下载? 那么&…...
渗透测试漏洞原理之---【XSS 跨站脚本攻击】
文章目录 1、跨站 脚本攻击1.1、漏洞描述1.2、漏洞原理1.3、漏洞危害1.4、漏洞验证1.5、漏洞分类1.5.1、反射性XSS1.5.2、存储型XSS1.5.3、DOM型XSS 2、XSS攻防2.1、XSS构造2.1.1、利用<>2.1.2、JavaScript伪协议2.1.3、时间响应 2.2、XSS变形方式2.2.1、大小写转换2.2.2…...
【浮点数二分】
数的三次方根 #include<iostream> using namespace std;double n;int main(){cin>>n;double l -10000;double r 10000;while((r-l)>1e-8){double mid (lr)/2;if((mid*mid*mid)>n) r mid;else l mid;}printf("%lf",l);return 0; }...
基于FPGA的FIR低通滤波器实现(附工程源码),matlab+vivado19.2+simulation
基于FPGA的FIR低通滤波器实现(附工程源码) 文章目录 基于FPGA的FIR低通滤波器实现(附工程源码)前言一、matlab设计FIR滤波器,生成正弦波1.设计FIR滤波器1.生成正弦波.coe 二、vivado1.fir滤波器IP核2.正弦波生成IP核3.时钟IP核设置4.顶层文件/测试文件代码 三.simul…...
c++ qt--事件(第六部分)
c qt–事件(第六部分) 一.编辑伙伴,编辑顺序(按TAB进行切换) 1.编辑伙伴 此功能在设计界面如下的位置 1.设置伙伴关系 鼠标左键长按一个Label组件然后把鼠标移到另一个组件上 2.伙伴关系的作用 伙伴关系的作用就是…...
嵌入式系统入门实战:探索基本概念和应用领域
嵌入式系统是一种专用的计算机系统,它是为了满足特定任务而设计的。这些系统通常具有较低的硬件资源(如处理器速度、内存容量和存储容量),但具有较高的可靠性和实时性。嵌入式系统广泛应用于各种领域,如家用电器、汽车、工业控制、医疗设备等。 嵌入式系统的基本概念 微控…...
关于hive sql进行调优的理解
这是一个面试经常面的问题,很不幸,在没有准备的时候,我面到了这个题目,反思了下,将这部分的内容进行总结,给大家一点分享。 hive其实是基于hadoop的数据库管理工具,底层是基于MapReduce实现的&a…...
十大排序算法
一、冒泡排序 冒泡排序(Bubble Sort)是一种简单直观的排序算法。它重复地走访要排序的数列,一次比 较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经…...
PIP 常用操作汇总
1. 升级 python -m pip install --upgrade pip2. 列出所有安装包 pip list3. 查找特定包 pip list | findstr xxx4. 查看特定包 pip show xxx5. 安装软件包 pip install pyzmq24.0.16. 卸载软件包 pip uninstall -y pyzmq7. 查看配置 # 生效的配置(global -&…...
线性代数的本质笔记(3B1B课程)
文章目录 前言向量矩阵行列式线性方程非方阵点积叉积基变换特征向量与特征值抽象向量空间 前言 最近在复习线代,李永乐的基础课我刷了一下,感觉讲的不够透彻,和我当年学线代的感觉一样,就是不够形象。 比如,行列式为…...
快速掌握MQ消息中间件rabbitmq
快速掌握MQ消息中间件rabbitmq 目录概述需求: 设计思路实现思路分析1.video 参考资料和推荐阅读 Survive by day and develop by night. talk for import biz , show your perfect code,full busy,skip hardness,make a better result,wait for change,c…...
挑战杯推荐项目
“人工智能”创意赛 - 智能艺术创作助手:借助大模型技术,开发能根据用户输入的主题、风格等要求,生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用,帮助艺术家和创意爱好者激发创意、提高创作效率。 - 个性化梦境…...
java_网络服务相关_gateway_nacos_feign区别联系
1. spring-cloud-starter-gateway 作用:作为微服务架构的网关,统一入口,处理所有外部请求。 核心能力: 路由转发(基于路径、服务名等)过滤器(鉴权、限流、日志、Header 处理)支持负…...
抖音增长新引擎:品融电商,一站式全案代运营领跑者
抖音增长新引擎:品融电商,一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中,品牌如何破浪前行?自建团队成本高、效果难控;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...
1.3 VSCode安装与环境配置
进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件,然后打开终端,进入下载文件夹,键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...
MySQL 8.0 OCP 英文题库解析(十三)
Oracle 为庆祝 MySQL 30 周年,截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始,将英文题库免费公布出来,并进行解析,帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...
【JavaSE】绘图与事件入门学习笔记
-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角,以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向,距离坐标原点x个像素;第二个是y坐标,表示当前位置为垂直方向,距离坐标原点y个像素。 坐标体系-像素 …...
人工智能(大型语言模型 LLMs)对不同学科的影响以及由此产生的新学习方式
今天是关于AI如何在教学中增强学生的学习体验,我把重要信息标红了。人文学科的价值被低估了 ⬇️ 转型与必要性 人工智能正在深刻地改变教育,这并非炒作,而是已经发生的巨大变革。教育机构和教育者不能忽视它,试图简单地禁止学生使…...
华为OD最新机试真题-数组组成的最小数字-OD统一考试(B卷)
题目描述 给定一个整型数组,请从该数组中选择3个元素 组成最小数字并输出 (如果数组长度小于3,则选择数组中所有元素来组成最小数字)。 输入描述 行用半角逗号分割的字符串记录的整型数组,0<数组长度<= 100,0<整数的取值范围<= 10000。 输出描述 由3个元素组成…...
ubuntu系统文件误删(/lib/x86_64-linux-gnu/libc.so.6)修复方案 [成功解决]
报错信息:libc.so.6: cannot open shared object file: No such file or directory: #ls, ln, sudo...命令都不能用 error while loading shared libraries: libc.so.6: cannot open shared object file: No such file or directory重启后报错信息&…...
【Linux】Linux安装并配置RabbitMQ
目录 1. 安装 Erlang 2. 安装 RabbitMQ 2.1.添加 RabbitMQ 仓库 2.2.安装 RabbitMQ 3.配置 3.1.启动和管理服务 4. 访问管理界面 5.安装问题 6.修改密码 7.修改端口 7.1.找到文件 7.2.修改文件 1. 安装 Erlang 由于 RabbitMQ 是用 Erlang 编写的,需要先安…...
