当前位置: 首页 > news >正文

Android Handler机制(四) Message源码分析

一. 简介

        接上一篇文章:Android Handler机制(三) Looper源码分析 ,我们来继续分析一下Message源码

这一系列文章都是为了深入理解Handler机制. Message 作为消息传递的载体,源码主要分为以下

几个部分:

1. 操作数据相关,类似 getter()和 setter()这种方法还有之前提到过的 what 和 obj 这类属性。

2. 创建与回收对象实例相关,除了用关键字 new 外,其他得到对象实例的方法。

3. 其他工具类性质的扩展方法。

整个类代码大概600多行, 接下来就开始一起分析下

二. Message数据属性与方法

我们先看看 Message 源码有哪些可供我们使用的属性

public int what :开发者可自定义的消息标识代码,用于区分不同的消息。

public int arg1: 如果要传递的消息只有少量的 integer 型数据,可以使用 这个属性。

public int arg2 :同上面 arg1。

public Object obj :开发者可自定义类型的传输数据。

上面四个属性作为常用的消息传递的数据载体可直接赋值,例如 msg.arg1 = 100;。基本可以满足我们日常开发中简单消息传递。


如果上面几个数据属性不能满足我们的需求,可以使用扩展数据:Bundle 来传递

源码中见Bundle的方法:

  public Bundle getData() {if (data == null) {data = new Bundle();}return data;}public void setData(Bundle data) {this.data = data;}

这段代码也没什么逻辑好分析的,值得一提就是 Bundle data 不是 public 的,
所以我们不能直接操作这个属性,需要通过上面二个方法操作数据, 举个例子:

Bundle bundle = new Bundle(); 
bundle.putString("String","value"); 
bundle.putFloat("float",0.1f); 
Message msg = Message.obtain(); 
msg.setData(bundle);

关于Bundle的API使用

请查看参考此链接:Bundle API 

三. 创建Message 对象的基本方法

首先来看下Message类的构造方法

/** Constructor (but the preferred way to get a Message is to call {@link #obtain() Message.obtain()}).*/public Message() {}

从注释来看, 官方推荐我们使用Message.obtain()来获取Message对象

那这个 obtain()方法干了什么呢?其实就是内部维持了一个链表形式的 Meesage 对象 缓存池,这样会节省重复实例化对象产生的开销成本。

数据结构中的链表一个单元有两个值,当前单元的值 (head)和下一个单元的地址指针(next),如果下一个单元不存在那么 next 就是 null 

 所以,想要实现 Message 对象链表式缓存池就需要额外的两个 Message 类型的 引用 head 和 next,都说了叫缓存池,所以把 head 叫 pool 更合适一点。

有了链表的基础结构我们再想实例化对象的时候就可以先去链表缓存池中看看 有没有,有的话直接从缓存池中拿出来用,没有再 new 一个.

我们主要来分析一下 Message.obtain()方法

// 用于标识当前对象是否存在于缓存池,0 代表不在缓存池中 
int flags;/*
这个常量是供上面的 flags 使用的,它表示 in use(正在使用)状态
如果 Message 对象被存入了 MessageQueue 消息队列排队等待 Looper 
处理或者被回收到缓存池中等待重复利用时,那么它就是 in use(正在使 用)状态
只有在 new Message()和 Message.obtain()时候才可以清除掉 flags 上 的 in use 状态
*/
static final int FLAG_IN_USE = 1 << 0;/*
静态常量对象,通过 synchronized (sPoolSync)让它作为线程并发操作时的锁确保同一时刻只有一个线程可以访问当前对象的引用
*/
private static final Object sPoolSync = new Object();//当前链表缓存池的入口,装载着缓存池中第一个可用的对象 
private static Message sPool;// 链表缓存池中指向下一个对象引用的 next 指针 
Message next;// 当前链表缓存池中对象的数量 
private static int sPoolSize = 0;/*** Return a new Message instance from the global pool. Allows us to* avoid allocating new objects in many cases.
从缓存池中拿出来一个 Message 对象给你, 可以让我们在许多情况下避免分配新对象。*/public static Message obtain() {
// 上锁,这期间只有一个线程可以执行这段代码synchronized (sPoolSync) {
// pool 不等于空就说明缓存池中还有可用的对象,直接取出来if (sPool != null) {
// 声明一个 Message 引用指向缓存池中的 pool 对象Message m = sPool;
// 让缓存池中 pool 引用指向它的 next 引用的对象sPool = m.next;
// 因为该对象已经从缓存池中被取出,所以将 next 指针置空m.next = null;
// 将从缓存池中取出的对象的 flags 的 in use 标识清除掉m.flags = 0; // clear in-use flag
// 缓存池中 Message 对象数量减去一个sPoolSize--;return m;}}
// 如果缓存池中没有可用的对象就 new一个return new Message();}

理论上我们希望 sPool 引用指向了链表缓存池中的第一个对象,让它作为整个缓存池的出入口。所以我们把它设置成 static 的,这样它就与实例化出来的对象 无关,也就是说无论我们在哪个 Message 对象中进行操作,sPool 还是 sPool。

再来梳理一下:

静态方法 obtain()的代码逻辑流程:

先判断缓存池是不是空的:if(sPool != null),如果是空的就直接:return new Message();,

不是空的就声明一个引用让它指向缓存池第一个对象:Message m = sPool;,

而缓存池的链表头部 sPool 引用就指向了链表中下一个对象:sPool = m.next;,因为这个时候缓存池中第一个对象已经取出交给了引用 Message m, 所以需要清除掉这个对象身上的特殊标识,

包括缓存池中的 next 引用和用来标 记对象状态的 flags值:m.next = null;  m.flags = 0;,

最后将缓存池中的对象 数量减一:sPoolSize--;

用图表示更简洁一点:

阶段一:   初始状态, sPool 指向链表头部


阶段二: 声明一个Message 引用 m 也指向链表头部

 

阶段三:  让sPool 指向链表下一个对象

 阶段四: 让m引用指向对象的next=null,  flag 清零.

 到这里,就把消息缓存池中的头部消息取出来了

分析到这里我们知道了为什么官方推荐我们使用 Message.obtain()得到对象 了,因为它是在缓存池中取出来重复利用的,但是通过上面代码也看可以看到,只有缓存池里有东西时也就是 sPool != null 的时候才可以取,Message 是怎么 把对象回收到缓存池中的呢?

四. 回收 Message 对象到缓存池的方法

阅读源码后发现有一个 public void recycle()方法用于回收 Message 对象,但 是它也牵扯出了一堆其他方法与属性:

// 缓存池最大存储值 
private static final int MAX_POOL_SIZE = 50;// 区分当前 Android 版本是否大于或者等于 LOLLIPOP 版本的全局静态变量,默认初始值为 true 
private static boolean gCheckRecycle = true;/*** 用于区分当前 Android 版本是否大于或者等于 LOLLIPOP 版本 * 内部隐藏方法,在 APP启动时就会执行该方法,开发者是不可见的 * @hide */ 
public static void updateCheckRecycle(int targetSdkVersion) {if (targetSdkVersion < Build.VERSION_CODES.LOLLIPOP) {gCheckRecycle = false;}}//* 判断当前对象的 flags 是否为 in-use 状态/*package*/ boolean isInUse() {return ((flags & FLAG_IN_USE) == FLAG_IN_USE);}/*
调用这个方法后,当前对象就会被回收入缓存池中。 
你不能回收一个在 MessageQueue 排队等待处理或者正在交付给 Handler 处理的 Message 对象 
说白了就是 in-use 状态的不可回收
*/
public void recycle() {
// 判断当前对象是否为 in-use 状态if (isInUse()) {
// 如果当前版本大于或者等于 LOLLIPOP 则抛出异常if (gCheckRecycle) {throw new IllegalStateException("This message cannot be recycled because it "+ "is still in use.");}
// 如果当前版本小于 LOLLIPOP 什么也不干直接结束方法return;}// 回收 Message 对象recycleUnchecked();}/*** Recycles a Message that may be in-use.* Used internally by the MessageQueue and Looper when disposing of queued Messages.*/@UnsupportedAppUsagevoid recycleUnchecked() {// Mark the message as in use while it remains in the recycled object pool.//回收缓存池中消息,标记flag为FLAG_IN_USE // Clear out all other details.    //其他属性全部清除flags = FLAG_IN_USE;what = 0;arg1 = 0;arg2 = 0;obj = null;replyTo = null;sendingUid = UID_NONE;workSourceUid = UID_NONE;when = 0;target = null;callback = null;data = null;// 上锁synchronized (sPoolSync) {// 如果当前缓存池对象中的数量小于缓存池最大存储值(50)就存入缓存池中if (sPoolSize < MAX_POOL_SIZE) {// 存入缓存池next = sPool;sPool = this;// 缓存池数量加 1sPoolSize++;}}}

上面代码的逻辑很清晰,执行 recycle()方法后先判断当前对象是否为 in-use 状 态:if (isInUse()),如果是 in-use 状态的话当前 Android 版本是 LOLLIPOP(5.0) 版本之前直接结束程序,LOLLIPOP及之后版本抛出异常。

如果当前对象不是in-use 状态,那么就执行 recycleUnchecked()方法先将它切换到 in-use 状态:flags = FLAG_IN_USE;   

再把所有的数据属性全部清除,最后把对象存入缓存池链表中。

回收 Message对象 流程图:

 

五. 我们需要手动回收消息嘛?

        现在我们知道了通过执行 recycle()方法回收 Message 对象,但是如果要为每个 Message 对象都进行手动回收岂不是很麻烦?

        庆幸的是开发人员也想到了这一点,从源码中可以看到其实最终真正执行回收操 作的调用 recycleUnchecked()方法,且注释中告诉我们 MessageQueue 和 Looper 内部也会调用该方法执行回收。

         这里先说一个结论,MessageQueue 和 Looper 内部分发处理消息时,当它们得知 当前这个 Message 对象已经使用完毕后就会直接调用 recycleUnchecked()方法 将它回收掉.

MessageQueue中的相关代码:

//MessageQueue.java中 removeMessagexxx 都会调用recycleUnchecked()方法
private void removeAllMessagesLocked() {Message p = mMessages;while (p != null) {Message n = p.next;//消息回收p.recycleUnchecked();p = n;}mMessages = null;}

Looper 中的相关代码:

public static void loop() {.....for (;;) {//从MessageQueue中取出消息Message msg = queue.next(); // might blocktry {//分发消息msg.target.dispatchMessage(msg);if (observer != null) {observer.messageDispatched(token, msg);}.....//消息分发完之后,回收消息msg.recycleUnchecked();}}

         所以,如果我们用实例化 Message 对象是放入 Handler 中去传消息的,那么我们 就不需要手动回收,他们内部自己就回收了。

六. 包含 Handler 参数的 obtain()方法

给 Message 内部装了一个 Handler 起了什么作用呢?首先,我们可以通过上面讲 解我们可以得出以下已知的结论:

1. 表面上看我们使用 Handler 发送消息后,消息直接传回到了 Handler 内部的 handleMessage(Message msg)方法中。

2. 实际上是先把消息传入了 MessageQueue 中,Looper 再从 MessageQueue 依次取出 消息分发给 Handler。

3. Looper 是线程独立的, Looper 和 MessageQueue 是一对一的。 但是,你有没有想过 Looper 和 Handler 是不是一对一的?答案:不是, MessageQueue 只负责队列消息,Looper 只负责取出消息分发。他们的功能很明 确而且通用。 所以,无论当前线程有多少个 Handler,同样都只有一个 Lopper 和一个 MessageQueue。

解释图:

 提个问题:

Question:  既然每个线程只有一个 Looper 和 MessageQueue 的话那么 Looper 分发消息的时 候要如何判断当前这个 Message 是哪个 Handler 的呢?

Answer:  所以开发人员就给 Message内部配置了一个Handler属性,这样Looper分发消息时直接调用Messgae 内部的 Handler 属性就能找到它对应的 handleMessage(Message msg)接收消息的 方法了.

源码很简单,就是在空参方法 obtain()基础上加了个 Handler 属性,还有它的 getter()和 setter():

//handler对象
Handler target;/*** Same as {@link #obtain()}, but sets the value for the <em>target</em> member on the Message returned.* @param h  Handler to assign to the returned Message object's <em>target</em> member.* @return A Message object from the global pool.*/public static Message obtain(Handler h) {Message m = obtain();//把handler与Message关联上m.target = h;return m;}public void setTarget(Handler target) {this.target = target;}/*** Retrieve the {@link android.os.Handler Handler} implementation that* will receive this message. The object must implement* {@link android.os.Handler#handleMessage(android.os.Message)* Handler.handleMessage()}. Each Handler has its own name-space for* message codes, so you do not need to* worry about yours conflicting with other handlers.*/public Handler getTarget() {return target;}

还有一个地方也可以找到他们 Message 与 Handler 关联的代码, 就是Handler发送消息的时候就已经关联上了,在 Handler.java文件中:

 //1.发送消息public final boolean sendMessage(@NonNull Message msg) {return sendMessageDelayed(msg, 0);}//2. 接着调用
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {MessageQueue queue = mQueue;if (queue == null) {RuntimeException e = new RuntimeException(this + " sendMessageAtTime() called with no mQueue");Log.w("Looper", e.getMessage(), e);return false;}return enqueueMessage(queue, msg, uptimeMillis);}// 3.消息关联上handlerprivate boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,long uptimeMillis) {//这句话是两者关联msg.target = this;msg.workSourceUid = ThreadLocalWorkSource.getUid();if (mAsynchronous) {msg.setAsynchronous(true);}return queue.enqueueMessage(msg, uptimeMillis);}

这样子就知道 这个消息的源头是与哪个Handler相关联的,  当然处理消息时,自然就回到了对应线程的Hanlder的 handleMessage方法中了. 这样就保证消息发送和处理的准确性了.


 

相关文章:

Android Handler机制(四) Message源码分析

一. 简介 接上一篇文章:Android Handler机制(三) Looper源码分析 ,我们来继续分析一下Message源码 这一系列文章都是为了深入理解Handler机制. Message 作为消息传递的载体&#xff0c;源码主要分为以下 几个部分: 1. 操作数据相关&#xff0c;类似 getter()和 setter()这种…...

【Git】git命令(全)

Git1、本地操作2、版本管理3、远端仓库4、分支管理5、缓存stash6、遗留rebase7、标签管理8、解决冲突9、参考教程10、示例代码1、本地操作 Linux安装git&#xff1a;yum install git查看git版本 git version查看git设置 git config --list设置git属性 git config --global初始…...

软考论文-成本管理(1)

成本管理 1.成本管理的主要内容&#xff1f; 规划成本&#xff1a;制定一个成本管理的计划。估算成本&#xff1a;根据项目范围说明书&#xff0c;项目管理计划和wbs等文档&#xff0c;采用xxx方法进行估算成本成本预算&#xff1a;可以算工作包的费用&#xff0c;制定预算和…...

Java 多线程 --- 锁的概念和类型划分

Java 多线程 --- 锁的概念和类型划分锁的概念乐观锁与悲观锁公平锁与非公平锁什么是可重入锁独占锁与共享锁轻量级锁和重量级锁自旋锁 (Spinlock)锁的概念 锁可以将多个线程对共享数据的并发访问转换为串行访问, 这样一个共享数据一次只能被一个线程访问, 该线程访问结束后其他…...

python程序员狂飙上头——京海市大嫂单推人做个日历不过分吧?

嗨害大家好鸭&#xff01;我是小熊猫~ 这个反黑剧其实火了很久了&#xff0c; 但是我现在才有空开始看 该说不说&#xff0c;真的很上头&#xff01;&#xff01;&#xff01; 大嫂简直就像是干枯沙漠里的玫瑰 让人眼前一亮哇~~ 我小熊猫此时此刻就成为大嫂的单推人&…...

浅谈子网掩码、IP地址、网络地址之间关系

文章目录一、什么是子网掩码二、给定IP地址&#xff0c;如何求网络地址网络标识&#xff08;net-id&#xff09;和主机标识&#xff08;host-id&#xff09;计算步骤三、CIDR地址表示方法(Classless Inter Domain Routing)四、IP地址与MAC地址一、什么是子网掩码 在TCP/IP协议…...

前端优化的解决方案

能缓存的&#xff0c;尽量强缓存。减少HTTP请求数 使用外部引入的css和js文件&#xff0c;并且引入的css和js越少越好使用雪碧图&#xff08;精灵图&#xff09;img计算缩放也需要时间&#xff0c;使用base64编码将较小图片嵌入到样式表中&#xff0c;减少请求数因为iframe会阻…...

PYthon组合数据类型的简单使用

Python的数据类型有两种&#xff0c;基本数据类型和组合数据类型&#xff0c;组合数据类型在Python的使用中特别重要。 1.组合数据类型的分类&#xff1a; 2.序列类型 序列类型中元素存在顺序关系&#xff0c;可以存在数值相同但位置不同的元素。序列类型支持成员关系操作符&…...

【Java】P2 基础语法与运算符

Java 基础语法 运算符Java注释方法基本数据类型驼峰命名法Scanner类基本运算除法隐式转换逻辑运算符 以及 短路逻辑运算符三元运算符前言 上一节内容涵盖Java的基础知识&#xff0c;包含安装下载&#xff0c;JDK与JRE等。 链接&#xff1a;https://blog.csdn.net/weixin_43098…...

【并发基础】Java中线程的创建和运行以及相关源码分析

目录 一、线程的创建和运行 1.1 创建和运行线程的三种方法 1.2 三者之间的继承关系 二、Thread类和Runnable接口的区别 2.1 Runnable接口可以实现线程之间资源共享&#xff0c;而Thread类不能 2.2 实现Runnable接口相对于继承Thread类的优点 三、实现 Runnable 接口和实现 Call…...

Spark Shuffle

Shuffle : 集群范围内跨节点、跨进程的数据分发 分布式数据集在集群内的分发&#xff0c;会引入大量的磁盘 I/O 与网络I/O在 DAG 的计算中&#xff0c;Shuffle 环节的执行性能是最差的 , 会消耗所有类型的硬件资源 (CPU、内存、磁盘、网络) Spark 2.0 后&#xff0c;将 Shuff…...

Linux/MacOS 生成双击可执行文件

双击可执行文件包含两种&#xff1a;终端shell脚本 Unix可执行文件 1.终端shell脚本 随意新建一个文件&#xff08;可使用command键N&#xff0c;前提是有已打开的文件&#xff09;&#xff0c;输入shell格式的测试代码&#xff0c;比如&#xff1a; #! /bin/sh echo “h…...

Ubuntu三种拨号方法

1.宽带拨号(PPPoE) (1)打开连接。关闭以太网连接&#xff0c;打开有线连接设置&#xff0c;取消勾选“自动连接”选项。 (2)配置连接。在终端输入命令sudo pppoeconf&#xff0c;会看到一系列配置信息&#xff0c;包括用户名、密码&#xff0c;配置完成后会有一些提示信息&…...

Vue-router的引入和安装

什么是Vue-Router&#xff1f;Vue路由器是Vue.js的官方路由器&#xff0c;它与Vue.js核心深度集成&#xff0c;使用Vue轻松构建单页应用程序变得轻而易举。功能包括&#xff1a;嵌套路线映射动态路由模块化&#xff0c;基于组件的路由器配置路由参数&#xff0c;查询&#xff0…...

无线WiFi安全渗透与攻防(四)之kismet的使用

系列文章 无线WiFi安全渗透与攻防(一)之无线安全环境搭建 无线WiFi安全渗透与攻防(二)之打造专属字典 无线WiFi安全渗透与攻防(三)之Windows扫描wifi和破解WiFi密码 kismet 如果要进行无线网络渗透测试&#xff0c;则必须先扫描所有有效的无线接入点。刚好在Kali Linux中&am…...

2023新版PMP考试有哪些变化?

对于2022年很多事情也都在发生&#xff0c;疫情也都没有完全结束&#xff0c;基金会已经开始通知下一场考试了&#xff0c;很多人也会担心新的考纲会不会给自己带来难度&#xff0c;其实这次六月份的考试很多人都内心已经知道了结果&#xff0c;所以这里也详细说一下新考纲的改…...

P8074 [COCI2009-2010#7] SVEMIR 最小生成树

[COCI2009-2010#7] SVEMIR 题目描述 太空帝国要通过建造隧道来联通它的 NNN 个星球。 每个星球用三维坐标 (xi,yi,zi)(x_i,y_i,z_i)(xi​,yi​,zi​) 来表示&#xff0c;而在两个星球 A,BA,BA,B 之间建造隧道的价格为 min⁡{∣xA−xB∣,∣yA−yB∣,∣zA−zB∣}\min\{|x_A-x_…...

10种常见网站安全攻击手段及防御方法

在某种程度上&#xff0c;互联网上的每个网站都容易遭受安全攻击。从人为失误到网络罪犯团伙发起的复杂攻击均在威胁范围之内。 网络攻击者最主要的动机是求财。无论你运营的是电子商务项目还是简单的小型商业网站&#xff0c;潜在攻击的风险就在那里。 知己知彼百战不殆&…...

为什么我选择收费的AdsPower指纹浏览器?

在决定开始用指纹浏览器之前&#xff0c;龙哥让我们团队的运营小哥找了市面上很多产品去测试。最后&#xff0c;还是决定用AdsPower。每个人的使用感受都不一样&#xff0c;龙哥就说说几个用得顺手的几个点。一、指纹环境强大 双内核引擎 市面上指纹浏览器内核都是基于谷歌Chro…...

Java输入输出和数组

一、问答题 1. 如何声明和创建一个一维数组&#xff1f; Int i[]new int[3] 2. 如何访问数组的元素&#xff1f; Int a[]new int a[3] for (int x0;x<a.length;x){ System.out.print(i[x]); } System.out.println(); 3.数组下标的类型是什么&#xff1f;最小的下标是什…...

这些免费API帮你快速开发,工作效率杠杠滴

一、短信发送 短信的应用可以说是非常的广泛了&#xff0c;短信API也是当下非常热门的API~ 短信验证码&#xff1a;可用于登录、注册、找回密码、支付认证等等应用场景。支持三大运营商&#xff0c;3秒可达&#xff0c;99.99&#xff05;到达率&#xff0c;支持大容量高并发。…...

干货|最全PCB布线教程总结,14条PCB布线原则技巧,保姆级搞定PCB布线

1、坚持手动布线&#xff0c;慎用自动布线2、了解制造商的规格3、合适的走线宽度4、迹线之间留出足够的空间5、元器件放置6、保持模拟和数字走线分开7、接地层8、走线和安装孔留有足够的空间9、交替走线方向10、避免电容耦合11、放置散热孔和焊盘12、接地和电源走线13、利用丝印…...

编程快捷键和markdown语法小计

Data Structure FQA文章目录1.idea快捷键汇总2.markdown一些常用语法1.idea快捷键汇总 altenter  快捷生成变量 altInsert可以新建类&#xff0c;文件&#xff0c;get或set方法&#xff0c;此快捷键又名创造一切 编辑区和文件区的跳转。 alt 1  &#xff1a;编辑区跳转至…...

内网vCenter部署教程二,最全的了!

一、组网说明 vCenter组网最佳实践 每台服务器需要6个网口,需要三个分布式交换机,每个交换机分配2个物理网卡做冗余,分别做为管理网络、业务网络、高可用网络使用。另vsan网络和vmotion网络可以复用业务网络或管理网络,vcenter HA需要单独用一个网络。 二、创建管理网络…...

2023-3-2 刷题情况

迷宫 题目描述 这天, 小明在玩迷宫游戏。 迷宫为一个 nn 的网格图, 小明可以在格子中移动, 左上角为 (1,1), 右 下角 (n,n) 为终点。迷宫中除了可以向上下左右四个方向移动一格以外, 还有 m 个双向传送门可以使用, 传送门可以连接两个任意格子。 假如小明处在格子 (x1,y1)(…...

Docker SYS_ADMIN 权限容器逃逸

1.漏洞原理Docker容器不同于虚拟机&#xff0c;它共享宿主机操作系统内核。宿主机和容器之间通过内核命名空间&#xff08;namespaces&#xff09;、内核Capabilities、CGroups&#xff08;control groups&#xff09;等技术进行隔离。若启动docker容器时给主机一个--cap-addSY…...

【Kotlin】 yyyy-MM-dd HH:mm:ss 时间格式 时间戳 全面解读超详细

时间格式 时间格式(协议)描述gg时期或纪元。y不包含纪元的年份。不具有前导零。yy不包含纪元的年份。具有前导零。yyyy包含纪元的四位数的年份。M月份数字。一位数的月份没有前导零。MM月份数字。一位数的月份有一个前导零。MMM月份的缩写名称&#xff0c;在AbbreviatedMonthN…...

git repack多包使用及相关性能测试

1、git数据结构 git 中存在四种数据结构&#xff0c;即object包含四种&#xff0c;分别是tree对象、blob对象、commit对象、tag对象 1.1 blob对象 存储文件内容&#xff0c;内容是二进制的形式&#xff0c;通过SHA-1算法对文件内容和头信息进行计算得到key(文件名)。 如果一…...

QT获取dll库文件详细信息

一、需求背景获取软件下依赖的dll库的版本信息&#xff0c;如下图所示版本为1.0.7.1018二、实现方法2.1步骤windows下实现&#xff0c;基于version.lib(version.dll)提供的函数获取这些信息首先使用GetFileVersionInfoSizeA(W)获取VersionInfo的大小&#xff0c;申请缓冲区&…...

常见的电脑运行卡顿原因及解决方法

大家在日常使用电脑过程中&#xff0c;会发现多开几个文件就卡顿&#xff0c;其实很多时候都跟C盘长期不清理有关&#xff0c;C盘的内存被下载的软件安装包、页面文件、休眠文件、更新文件等一系列的文件占据。大的文件甚至能占到20-30G&#xff0c;驱动人生就为大家带来几种解…...

佛山专业做网站/seo关键词工具

展开全部采用对接拼缝&#xff0c;可以处理明显接缝&#xff0c;对接拼缝是将壁纸的边缘互相紧靠在一起。既不重叠32313133353236313431303231363533e58685e5aeb931333431353936&#xff0c;也不留缝隙。这种拼缝方法优点是平整、光滑、无痕迹&#xff0c;给人的感觉是完整流畅…...

潍坊企业宣传片制作公司/杭州seo网站建设靠谱

Bootstrap Method:在统计学中&#xff0c;Bootstrap从原始数据中抽取子集&#xff0c;然后分别求取各个子集的统计特征&#xff0c;最终将统计特征合并。例如求取某国人民的平均身高&#xff0c;不可能测量每一个人的身高&#xff0c;但却可以在10个省市&#xff0c;分别招募10…...

wordpress调用导航菜单的内容/b站免费版入口

问题:txt进行编程操作时出现乱码-2020-6-21 场景:进行java编程时文件读入时乱码 解决:更改编码,就是另存为。 我的话修改成ANSI编码格式即可。...

api网站模板/资源搜索引擎

在网上看了几篇AdaBoost的介绍后&#xff0c;感觉网上介绍的都不好&#xff0c;不能够让人完全理解&#xff0c;因此就下载了一个外国人写的代码&#xff0c;总算透彻的理解了AdaBoost&#xff0c;可以向Transfer开进了&#xff0c;现在分享一下代码&#xff1a;主函数代码[pla…...

深圳龙华网站公司/seo分析师

在本文中&#xff0c;我们将介绍如何清除Linux终端。1)clear命令让我们假设你的终端填满了命令和输出&#xff0c;如下所示&#xff0c;在终端提示符的底部运行clear。结果终端屏幕被清除&#xff0c;如下所示2)reset命令就像上面的例子一样&#xff0c;你需要在终端的底部运行…...

做摘抄的网站/seo建站技术

推荐一波我写的任务管理软件(持续扩展完善) 主要功能可以每天规划任务,并查看今天还剩下多少秒,以及总完成率. CSDN下载:https://download.csdn.net/download/qq_41806966/12545118 需要源码可以去我的Github:https://github.com/1711680493/Application 里面的ShendiTaskM…...