热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

【0154】【热修复与插件化-2】热修复AndFix详解

1.热修复的基本认识【内容提要】1.1热修复的基本概念-动态更新热修复说白了就是”打补丁”,比如你们公司上线一个app,用户反应有重大bug,需要紧急修复。如果按照通常

1.热修复的基本认识

【内容提要】

1.1 热修复的基本概念-动态更新

热修复说白了就是”打补丁”,比如你们公司上线一个app,用户反应有重大bug,需要紧急修复。如果按照通 
常做法,那就是程序猿加班搞定bug,然后测试,重新打包并发布。这样带来的问题就是成本高,效率低。于是,热 
修复就应运而生.一般通过事先设定的接口从网上下载无Bug的代码来替换有Bug的代码。这样就省事多了,用 
户体验也好。

 

1.2.热修复的好处

【1】开发团队可以避免繁琐的开发流程,使得bug的修改变得容易;

【2】避免用户的流失;

【3】可以轻易的实现小功能的更新;

1.3.热修复的看法

 【说明】高质量的版本加上热修复的技术,才是最好的;

【1】亡羊补牢的手段,不要求轻易的使用;

【2】各家的热修复框架都具有兼容性的问题;

【3】热修复的流程与开发的流程是一样的,也需要全部走一遍;

 1.4.常见的热修复的对比

【技术选型的标准】

【本课程的选择】AndFix使用最简单,维护也来最简单;Tinker开发是最复杂的,维护也是最复杂的;

2.AndFix的详解

【内容提要】

2.1 AndFix的基本介绍

【说明】第一手资料要去gitHub官方去看;

 

【说明】使用的是注解;使用的是native方法打补丁;兼容性不是很好;每套虚拟机都需要去适配;

3.使用AndFix完成bug的修复

3.1 应用中集成AndFix

3.2【添加路径的区别】

【classPath】如果是第三放的代码中还使用到了自定义的gradle脚本文件,我们需要以插件的方式进行使用;需要在project 下的gradle中添加依赖;然后在model中就可以使用第三方的库中的代码了;

【dependingdencies】只是使用第三方的java代码,只是添加此处的app下的gradle代码即可;

3.3 AndFix的初始化

【说明】在使用AndFix第三方库,需要我们先封装;

 【新建管理类】使用双加锁的单例模式;

【封装PatchManager】

【增加load的调用】

【分装之后的使用】

3.4  使用AndFix的准备

【模拟bug】log.e的第2个参数设置为null,会报错空指针异常;

【生成apk】

 

【说明】防止在第二次打包的时候将apk覆盖掉,因此将打包好的apk临时保存;

4. apatch文件的生成

 

【说明】可以从官网下载;

【执行命令生成aptch文件】

【生成文件的输出目录】

【技巧】可以将执行的命令保存成为一个脚本文件,前面记得加sh;

5.补丁文件的安装

 

6. AndFix组件化

6.1 待解决的问题

【说明】【模拟中存在的问题】

【1】实际的应用中需要通过网络下载aptch文件,

【2】实际中不可能让用户点击修复按钮;

【3】可以将此AndFix组件化;

6.2 实际开发中AndFix开发的步骤

 

6.3 组件化实现

【说明】修复的工作创建的是一个serverice服务;

 

【文件目录的构造】

【检查更新请求的发送】

 

【文件的下载】AndFix添加到文件路径中就可以直接修复了,不需要重启设备等;

 

6.4 AndFix组件的调用

【说明】在实际的splash界面启动的时候就开始启动服务;

7.AndFix的源码的讲解

7.1 初始化

 

7.2 init方法

【功能】对patch文件的添加和删除,如果应用的版本升级了,就删除patch,如果没有升级,就将所有的patch添加到mPatch列表中;

【说明】添加patch文件到list中,实际上所有的patch文件都存在到了mPatch中;

【Patch文件的转换】将磁盘上的file转换为patch文件

 7.3 AddPatch方法

 

7.4 loadPatch方法

【说明】具有两个重载的方法,区别是否指定了需要修复的patch文件;

7.5 fix方法

 【说明】AndFixManager其实只是对文件的管理,没有真正的修复操作;

7.6 fixClass执行的方法

【功能】找到class字节码中要替换的方法;

 

7.7 replaceMethod方法

7.8 【总结】

【说明】最终是调用c/c++中的代码进行修复的;

首先将文件都转换为patch类;然后将patch通过名字转换为字节码,然后通过反射找到要替换的方法;最终通过JNI完成方法的替换;

【优劣】

【1】只能修复方法级别的bug,使用的场景受到了限制;如果新增类或者是资源,AndFix是做不到的,需要使用Tinker;

【2】原理简单、集成简单、使用简单、即使生效,不需要启动app;

8.参考博客

【1】Android中热修复框架AndFix原理解析及案例使用

【原文地址】https://blog.csdn.net/u011277123/article/details/53282381

一、前言

最近腾讯弄出一个Tinker热修复框架,那么本文先不介绍这个框架,先来介绍一下阿里的一个热修复框架AndFix,这个框架出来已经很长时间了,但是看网上没有太多非常详细的讲解,这里就来做一次分析。正好项目中要使用到。首先这个框架是开源的:https://github.com/alibaba/AndFix 其实在最早的时候我已经分析了阿里的另外一个热修复框架:Dexposed框架,还不了解的同学可以点击这里查看:Dexposed框架原理解析以及使用 当时介绍这个框架的时候发现他的实现原理很简单:

Android中热修复框架AndFix原理解析及案例使用

他的思想完全来源于Xposed框架,完美诠释了AOP编程,这里用到最核心的知识点就是在native层获取到指定方法的结构体,然后改变他的nativeFunc字段值,而这个值就是可以指定这个方法对应的native函数指针,所以先从Java层跳到native层,改变指定方法的nativeFunc值,然后在改变之后的函数中调用Java层的回调即可。实现了方法的拦截功能。

二、源码分析

那么本文介绍的AndFix框架相对于Dexposed框架来说又有什么区别呢?其实区别就在于AndFix框架更加轻便好用,在进行热修复的过程中更加方便了。当然这个优点在后面的Tinker框架也是能提现出来的。这个框架的原理是:直接在native层进行方法的结构体信息对换,从而实现完美的方法新旧替换,从而实现热修复功能。下面通过分析他的源码来看他的具体实现,下载完源码之后导入工程:

Android中热修复框架AndFix原理解析及案例使用

这里可以看到,因为在native层需要替换新旧方法结构体信息,所以这里肯定要做的工作就是虚拟机的兼容问题,这里做了art和dalvik的分开处理逻辑,下面来看一下这个框架的基本使用规则:

Android中热修复框架AndFix原理解析及案例使用

用法还是很简单的,这里的修复包是直接放在本地的,在实际操作中会从网上去下载。下面就开始分析源码,这里有一个主要的类就是PatchManager:

Android中热修复框架AndFix原理解析及案例使用

第一、PatchManager类初始化

在这个类的构造方法中做了两件事,一件事是初始化AndFixManager类,一件事是创建修复包存放的沙盒目录。这里先来看第二件事创建沙盒目录:

Android中热修复框架AndFix原理解析及案例使用

可以看到这个目录是:/data/data/xxx/files/apatch/xxx.apatch

Android中热修复框架AndFix原理解析及案例使用

当我们从网上下载好修复包apatch文件之后,会调用addPatch方法,这时候会把修复包复制到这个地方,以后再次启动时就会遍历这个目录加载apatch文件。

第二、AndFixManager的初始化

下面继续来看AndFixManager初始化操作:

Android中热修复框架AndFix原理解析及案例使用

在这个初始化中也是干了两件事:一件事是判断当前环境是否支持热修复,一件事是初始化修复包安全校验的工作,先来看一下判断是否支持操作:

Android中热修复框架AndFix原理解析及案例使用

这里支持的条件是:非YunOS系统,Android2.3-7.0系统版本,热修复native层设置是否成功。这里我们看到应该值得关心的是setup操作,这个操作其实是native层进行的,可以直接看一下具体代码:

Android中热修复框架AndFix原理解析及案例使用

这里主要做了一些初始化操作,获取一些函数指针,准备后续的replaceMethod函数中使用:

1、在libdvm.so动态获取dvmDecodeIndirectRef函数指针和获取dvmThreadSelf函数指针。

2、调用dest的 Method.getDeclaringClass方法获取method的类对象clazz。

继续回到AndFixManager的初始化中的第二件事:修复包安全校验工作,其实这里只是做了初始化操作,而真正校验的工作在后面,主要是通过比对应用的签名和修复包的签名信息。后面再看吧!

第三:PatchManager的初始化操作

Android中热修复框架AndFix原理解析及案例使用

这里的初始化做了一件事:是判断当前PatchManager的版本号是否发生变化,如果发生变化就清空本地所有的修复包。如果没有变化,直接调用初始化修复包方法

Android中热修复框架AndFix原理解析及案例使用

这个方法其实就是我们上面提到的逻辑,会遍历沙盒中修复包目录中所有的修复包文件,然后把它添加到修复列表中。

第四:PatchManager的添加修复包操作

这里提供了两个添加修复包的方法,一个是接受文件样式的参数:

Android中热修复框架AndFix原理解析及案例使用

这个方法就是上面的那个initPatchs方法中调用的地方,这里会把目录下所有的修复包文件加到列表中。这里来看一下另外一个主要类Patch的初始化操作:

Android中热修复框架AndFix原理解析及案例使用

这个初始化的主要工作就是通过JarFile类解析修复包文件,读取他的META-INF\PATCH.MF文件内容,获取需要修复类的名称,多个修复类之间用逗号分隔,类似于这样的样式:

Android中热修复框架AndFix原理解析及案例使用

这里的Utils类就是我们需要修复的方法所属的类名,但是这里他又做了处理就是在每个类的后面加了后缀:_CF。这里分析完了所有需要修复的类名之后保存到一个列表中,后面会通过修复包名称获取到他的修复类名称列表。

还有一个添加修复包文件的方法,接受的是文件路径参数:

Android中热修复框架AndFix原理解析及案例使用

这个方法接受的是修复包文件路径,首先会把这个文件拷贝到上面提到的沙盒目录中以便下次进行遍历操作,拷贝之后继续调用上面的addPatch方法添加到列表中,最后就在调用加载修复包操作了。

注意:

第一个方法接受文件样式的方法其实是需要结合上面的initPatchs方法一起使用,他调用的场景是:本地沙盒目录中已经有了修复包文件,并且版本号没有发生变化,这样每次启动程序的时候就会调用初始化操作,在这里会遍历沙盒目录中所有的修复包文件,然后调用这个方法添加到全局文件列表中。

第二个方法接受的是文件路径样式,这个方法使用的场景是版本号发生变化,或者是本地沙盒中没有修复包文件。比如第一次操作的时候,会从网络上下载修复包文件,下载成功之后会把这个文件路径通过这个方法调用即可,执行完之后也会主动调用加载修复包的操作了,比如这里第一次在SD卡中放了一个修复包文件:

Android中热修复框架AndFix原理解析及案例使用

只要调用了这段代码之后就可以走完了所有的流程了:拷贝修复包到沙盒目录中,加载修复包文件。

第五:PatchManager的加载修复包操作

Android中热修复框架AndFix原理解析及案例使用

这个方法有两个地方会调用到:一个是上面提到的那个接受修复包路径的addPatch方法,一个是调用完接受修复文件类型的addPatch方法之后手动调用一次,类似于这样:

Android中热修复框架AndFix原理解析及案例使用

这个方法内部主要是通过Patch类获取修复包所有的修复类名称,之前已经介绍了Patch类的初始化操作,在哪里会解析修复包的MF文件信息,获取到修复包需要修复的类名然后保存到列表中,这里就通过getClasses方法来获取指定修复包名称对应的修复类名称列表,然后在调用AndFixManager的fix方法即可,下面再来看一下AndFixManager的fix方法的实现逻辑:

Android中热修复框架AndFix原理解析及案例使用

这个方法有点长,而且内容也比较多,这里主要做了这么几件事:

第一件事:使用上面初始化完成的校验类进行修复包的校验工作,这里的校验就是比对修复包的签名和应用的签名是否一致:

Android中热修复框架AndFix原理解析及案例使用

这个具体实现逻辑不用介绍了,大家可以下载源码自己分析。

第二件事:使用DexFile和自定义类加载器来加载修复包文件

这个其实和使用DexClassLoader加载原理类似,而且DexClassLoader内部的加载逻辑也是使用了DexFile来进行操作的,而这里为什么要进行加载操作呢?因为我们需要获取修复类中需要修复的方法名称,而这个方法名称是通过修复方法的注解来获取到的,所以咋们得先进行类的加载然后获取到他的方法信息,最后通过分析注解获取方法名,这里用的是反射机制来进行操作的。

Android中热修复框架AndFix原理解析及案例使用

这个加载完类之后就会继续调用fixClass方法,再来看一下fixClass方法实现:

Android中热修复框架AndFix原理解析及案例使用

这里主要是通过反射获取指定类名需要修复类中的所有方法类型,然后在获取到他的注解信息,上面已经分析了通过DexFile加载修复包文件,然后在加载上面Patch类中的getClasses方法获取到的修复类名称列表来进行类的加载,然后在用反射机制获取类中所有的方法对应的注解信息,通过注解信息获取指定修复的方法名称,看一下这个注解的定义:

Android中热修复框架AndFix原理解析及案例使用

这里提供了两个方法,一个是获取当前类名称,一个是获取当前方法名称,可以看一下具体事例:

Android中热修复框架AndFix原理解析及案例使用

上面解析完注解信息之后获取到了方法名称,紧接着就调用了replaceMethod方法开始了方法的替换操作

Android中热修复框架AndFix原理解析及案例使用

这里还会做一件事就是通过上面得到的修复新的方法信息以及需要修复的旧方法名称来操作,不过这里得先获取到旧方法类型,可以看到修复的新旧方法的签名必须一致,所谓签名就是方法的名称,参数个数,参数类型都必须一致,不然这里就报错的。进而也修复不了了。最后在调用了AndFix的addReplaceMethod方法进行native层的修复工作:

Android中热修复框架AndFix原理解析及案例使用

这里会做虚拟机的区分处理,但是他们大致的处理逻辑都是一致的,这里来看一下dalvik的处理机制:

Android中热修复框架AndFix原理解析及案例使用

这里的操作也是非常简单的,主要是通过上层传递过来的新旧方法类型对象,通过JNIEnv的FromReflectedMethod方法获取对应的方法结构体信息,然后将其信息进行替换即可,这里可以看到替换的信息也是非常多的,而且也看到了我们之前介绍Dexposed框架用到的一个字段值nativeFunc,这个就是指定这个Java方法对应的native方法。但是在这之前也会看到有一段代码是用来获取修复方法的类信息的,这里主要是用来做修复方法的类初始化操作,在之前我们看setup方法的时候知道,那里做了这么两件事:

1、在libdvm.so动态获取dvmDecodeIndirectRef函数指针和获取dvmThreadSelf函数指针。

2、调用dest的 Method.getDeclaringClass方法获取method的类对象clazz。

然后在这里就开始获取修复方法对应的类信息,通过调用方法的getDeclaringClass获取方法对应的类对象clazz,然后在调用dvm方法获取到对应的类结构体信息ClassObject,最后在设置他的状态信息标记这个类已经初始化完毕了。

注意:

这里可以看到通过一个类对象clazz类型可以获取到对应的结构体信息ClassObject,这个操作也是非常实用的,因为这个结构信息中有一个字段pDvmDex值,而这个值就是DvmDex结构体信息也就是底层对应的dex文件信息,所以说我们可以通过一个类信息得到他对应的dex文件信息。

三、流程总结

到这里就讲解完了整个框架的所有技术点了,上面可能说的有点乱,下面在来大体总结一下,首先来看一张简单的流程图信息(图片有点大,可以下载看高清大图):

Android中热修复框架AndFix原理解析及案例使用

第一、Patch类负责解析每个修复包apatch文件信息,获取所有需要修复的类名

这个类的初始化操作中会通过传递进来的修复包文件,使用JarFile类进行文件解析,读取他的META-INF\PATCH.MF文件信息,主要通过读取Patch-Classes字段值来获取需要修复的类名称,多个类名称之间用逗号分隔,而且每个类名称都有一个后缀:_CF。解析完成之后就会保存到一个用修复包名称作为key的HashMap中,后面会通过修复包名称参数来调用getClasses方法获取对应修复的类名称列表。

第二、PatchManager负责管理多个Patch类也就是多个修复包信息

主要方法包括初始化,添加修复包,加载修复包,这个类是提供给外界调用的一个入口类,这里有两种方式调用:

一种方式是先调用init方法进行初始化,在这个初始化方法中会判断当前的版本号,如果版本号发生变化就会清空本地所有的修复包文件,如果没有变化就加在所有的本地修复包文件,而这个本地目录就是沙盒中存放修复包文件的目录:/data/data/xxx/apatch/xxx.apatch,然后在调用loadPatch方法进行修复包的加载工作。

一种方式是本地没有修复包文件,也就是第一次操作的时候可能需要从服务器下载修复包文件,这时候会把下载下来的修复包文件路径通过调用addPatch方法进行添加操作,而这里的添加操作包括了:先把修复文件拷贝到上面的沙盒修复包目录中,然后在调用loadPatch方法进行加载工作。

第三、AndFix类主要是和native层交互直接替换方法

这个类主要就是几个native方法用来和底层进行交互的操作,而这些方法都是会被AndFixManager进行调用的。

第四、AndFixManager类主要是负责管理AndFix类

主要方法包括加载每个修复包中需要修复的类,解析出每个类的注解信息获取该类需要修复的方法名称,初始化的时候会进行修复包的校验工作,主要通过对比修复包和应用的签名信息。所以可以知道每个修复包是需要进行签名操作的,然后他的fix方法会使用DexFile类进行加载修复包文件,调用Patch的getClasses方法获取到所有需要修复的类名称进行加载操作。然后在调用fixClass方法,在这个方法中主要通过遍历修复类中所有指定MethodReplace注解信息的方法信息,然后在调用replaceMethod方法进行替换操作,而在这个方法中也会通过新方法的Method类型和注解信息中需要修复方法的名称来得到旧方法的Method类型,最终调用AndFix的native方法replaceMethod进行替换操作,所以这里可以看到替换的新旧方法的签名信息必须一致,不然无效,也就是方法的名称,参数个数,参数类型必须保持一致才可以。

第五、Native层方法

在native层中会做art和dalvik虚拟机的区分处理工作,他们大致的逻辑都是一致的:

dalvik 模式下的Java hook

1、在libdvm.so动态获取dvmDecodeIndirectRef函数指针和获取dvmThreadSelf函数指针。

2、调用dest的 Method.getDeclaringClass方法获取method的类对象clazz。

3、调用dvmDecodeIndirectRef方法,获取clazz的ClassObject*

4、通关 env->FromReflectedMethod方法获取dest的Method结构体函数的指针

5、替换method结构体的成员数据

art模式下的java hook

1、art模式中,我们直接通过 env->FromReflectedMethod获取到ArtMethod函数指针。

2、然后直接替换ArtMethod结构体的成员数据指针

四、框架使用案例

上面介绍完了原理,下面如果不用案例来做分析,那都是白扯淡,这里我们就用一个简单的案例来进行实际操作一下,而且在这个过程中会发现一个神奇的工具apatch,这里的例子很简单,本地定义一个获取版本号的方法:

Android中热修复框架AndFix原理解析及案例使用

这时候我们得到这个值,然后显示在界面上,然后开始出release包,这里直接用eclipse构造签名文件(这个签名文件要记得保存好,后面会使用到)出包了。等包上线发布之后,突然发现这个版本号错了,应该是1.0.2,那么这时候就需要进行热修复了,操作很简单:

第一步:修改这个方法返回值为1.0.2

第二步:继续使用上面的签名文件进行签名得到了一个修复之后的apk包

第三步:使用神器apatch进行线上发布的release包和这次修复的fix包进行比对,获取到修复文件apatch

java -jar apkpatch.jar -f app-release-fix.apk -t app-release-online.apk -o C:\Users\jiangwei1-g\Desktop\apkpatch-1.0.3 -k jiangwei.keystore -p 123456 -a jiangwei -e 123456

这里在使用命令的时候需要用到签名文件,因为在前面分析代码的时候知道会做修复包的签名验证。这里得到了一个修复包文件如下:

Android中热修复框架AndFix原理解析及案例使用

而且会产生一个diff.dex文件和smali文件夹,这个就是修复类的文件对应的dex文件和smali代码:

Android中热修复框架AndFix原理解析及案例使用

Android中热修复框架AndFix原理解析及案例使用

而我们用压缩软件可以打开apatch文件看看:

Android中热修复框架AndFix原理解析及案例使用

可以看到这里的classes.dex文件其实就是上面的diff.dex文件,只是这里更像是Android中的apk文件目录格式,同样有一个META-INF目录,这里存放了签名文件以及需要修复类信息的PATCH.MF文件:

Android中热修复框架AndFix原理解析及案例使用

签名文件就不多说了,来看一下PATCH_MF文件信息:

Android中热修复框架AndFix原理解析及案例使用

Patch_Classes字段包含了需要修复的类的名称信息了。

第四步:这里为了演示方便,直接把上面的修复文件拷贝到sd卡中,然后调用PatchManager的addPatch方法:

Android中热修复框架AndFix原理解析及案例使用

第五步:运行程序

Android中热修复框架AndFix原理解析及案例使用

这时候可以发现版本号已经修复成了1.0.2了。

五、apatch工具原理解析

上面看到案例使用比较简单,但是看到有一个比较牛逼的工具就是apatch,可以生成有方法变动的类所在的dex文件,那下面就来一起看看他的实现原理,没找到源码,直接使用jd-gui查看apatch.jar文件了:

Android中热修复框架AndFix原理解析及案例使用

这里的核心代码就是这部分,会把有方法变动的类信息列表对象DexBackedClassDef借助baksmali类写入到smali文件中,然后在借助DexBuilder和SmaliMod类把smali类变成dex文件,也就是最终的diff.dex文件了。那么下面在来看一下这个变动的DexBackedClassDef类列表信息如何得到的:

Android中热修复框架AndFix原理解析及案例使用

在这里使用DexBackedDexFile类进行加载新旧的dex文件,然后开始比对具体方法实现变动情况,主要是方法:compareMethod的实现:

Android中热修复框架AndFix原理解析及案例使用

这里会调用方法的getImplementation方法来判断新旧方法的实现发生变动了,如果有就把当前的类对象加入变动列表中即可。

所以从这里可以看到这里其实是完全借助了第三方的功能:可以把dex变成smali文件的baksmali工具包、可以把smali变成dex文件的smali工具包。而这两个工具包的源码之前在介绍apktool反编译工具的时候说到了,想查看源码的同学可以查看这篇文章:反编译利器apktool的源码解析。从这里可以看到,我们后续再处理dex,smali等文件格式的时候这两个工具包用的非常多。

六、框架技术总结

到此此次修复操作就完成了。我们的讲解工作和案例演示工作也完成了,下面来总结一下这个框架的知识点,不过先来看一张大图(可以点击下载查看高清大图):

Android中热修复框架AndFix原理解析及案例使用

第一、核心技术点

从上面可以看到AndFix框架的技术点主要包括:

1、使用apatch工具生成修复包文件,主要借助baksmali和smali工具包实现

2、Java层传递新旧方法类型对象,到native层获取其对应的结构体信息实现完美替换新旧方法结构信息

第二、优点和局限性

优点:从上面可以看到这个框架的优点在于轻巧便捷,集成成本低,维护性强。

局限性:从上面的代码分析可以看到这个框架的局限性还是很多的,特别是他只能修复对应已经存在的方法,比如现在我想增加一个方法肯定不行的,如果想给修复方法增加参数信息也是不可以的,这个局限性就非常大了。还有一个局限性就是只能进行代码修复,资源是无法做到的。所以从这里可以看到这个框架更偏重于方法的热修复操作。

项目下载地址:http://download.csdn.net/detail/jiangwei0910410003/9678441

工具下载地址:http://download.csdn.net/detail/jiangwei0910410003/9678885

七、总结

在开发过程中现阶段热修复技术还是很火的,而一些大公司也相继给出了一些热修复的合理方案,每家都有各自的优点和缺点,而我就要做到每家热修复框架的详细原理解析,从中能够学习到更多的技巧和知识,顶着头疼的风险写完了这篇文章,记得多点赞多分享!


推荐阅读
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 在Kubernetes上部署JupyterHub的步骤和实验依赖
    本文介绍了在Kubernetes上部署JupyterHub的步骤和实验所需的依赖,包括安装Docker和K8s,使用kubeadm进行安装,以及更新下载的镜像等。 ... [详细]
  • 目录实现效果:实现环境实现方法一:基本思路主要代码JavaScript代码总结方法二主要代码总结方法三基本思路主要代码JavaScriptHTML总结实 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • t-io 2.0.0发布-法网天眼第一版的回顾和更新说明
    本文回顾了t-io 1.x版本的工程结构和性能数据,并介绍了t-io在码云上的成绩和用户反馈。同时,还提到了@openSeLi同学发布的t-io 30W长连接并发压力测试报告。最后,详细介绍了t-io 2.0.0版本的更新内容,包括更简洁的使用方式和内置的httpsession功能。 ... [详细]
  • 使用Ubuntu中的Python获取浏览器历史记录原文: ... [详细]
  • eclipse学习(第三章:ssh中的Hibernate)——11.Hibernate的缓存(2级缓存,get和load)
    本文介绍了eclipse学习中的第三章内容,主要讲解了ssh中的Hibernate的缓存,包括2级缓存和get方法、load方法的区别。文章还涉及了项目实践和相关知识点的讲解。 ... [详细]
  • 关于我们EMQ是一家全球领先的开源物联网基础设施软件供应商,服务新产业周期的IoT&5G、边缘计算与云计算市场,交付全球领先的开源物联网消息服务器和流处理数据 ... [详细]
  • ZSI.generate.Wsdl2PythonError: unsupported local simpleType restriction ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • Google Play推出全新的应用内评价API,帮助开发者获取更多优质用户反馈。用户每天在Google Play上发表数百万条评论,这有助于开发者了解用户喜好和改进需求。开发者可以选择在适当的时间请求用户撰写评论,以获得全面而有用的反馈。全新应用内评价功能让用户无需返回应用详情页面即可发表评论,提升用户体验。 ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
author-avatar
手机用户2502917325
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有