1.fileloader Դ?源码?
2.webpack5loader和plugin原理解析
3.运用ptcms搭建自用无广告网站(附采集规则和源码)
4.SpringBoot全局配置文件加载原理详解(万字源码分析)
5.DexClassLoaderåPathClassLoaderçåºå«
fileloader Դ??
Loader学习:深入解析babel-loader的功能与实现
Webpack中的Loader作为模块转换器,其核心职责是源码将源文件内容转换为所需格式。通常,源码Loader通过链式处理,源码以单一功能的源码形式依次作用于源文件,确保输出符合预期。源码dlna源码Loader作为Node.js模块,源码其核心是源码导出一个函数,利用webpack提供的源码API进行操作,如处理返回内容、源码sourceMap、源码AST等。源码
除了基本的源码代码转换,Loader还可以返回额外信息。源码通过webpack的源码callback API,开发者可以控制返回结果的处理方式。Loader的同步与异步特性,异步Loader如file-loader,需要明确告诉webpack处理的是二进制数据。此外,为了优化性能,Loader可以利用cacheable API缓存转换结果,当依赖不变时,可以显著提升加载速度。
学习Loader的深入理解,需要掌握各种API,如获取options、处理二进制数据、缓存策略等。以babel-loader为例,javapc源码其源码中体现了这些概念。首先,它依赖@babel/core,并根据用户配置进行定制化处理。loader函数的核心部分,涉及options解析、配置加载、source处理、缓存判断以及元数据订阅等步骤,最后返回转换后的结果。
总结来说,Loader的核心是处理输入和输出,通过调用webpack提供的API实现各种功能。了解并掌握这些API,我们就可以开始编写自己的定制Loader,如babel-loader那样,为项目提供强大的代码转换能力。现在,对Loader有了更直观的认识,对编写Loader的步骤和技巧也更加清晰了。
webpack5loader和plugin原理解析
大家好,今天为大家解析下loader和plugin一、区别loader是文件加载器,能够加载资源文件,并对这些文件进行一些处理,诸如编译、压缩等,最终一起打包到指定的文件中
plugin赋予了Webpack各种灵活的功能,例如打包优化、openamp源码资源管理、环境变量注入等,目的是解决loader无法实现的其他事从整个运行时机上来看,如下图所示:
可以看到,两者在运行时机上的区别:
loader运行在打包文件之前plugins在整个编译周期都起作用在Webpack运行的生命周期中会广播出许多事件,Plugin可以监听这些事件,在合适的时机通过Webpack提供的API改变输出结果
对于loader,实质是一个转换器,将A文件进行编译形成B文件,操作的是文件,比如将A.scss或A.less转变为B.css,单纯的文件转换过程
下面我们来看看loader和plugin实现的原理
Loader原理loader概念帮助webpack将不同类型的文件转换为webpack可识别的模块。
loader执行顺序分类
pre:前置loader
normal:普通loader
inline:内联loader
post:后置loader
执行顺序
4类loader的执行优级为:pre>normal>inline>post。
相同优先级的loader执行顺序为:从右到左,从下到上。
例如:
//此时loader执行顺序:loader3-loader2-loader1module:{ rules:[{ test:/\.js$/,loader:"loader1",},{ test:/\.js$/,loader:"loader2",},{ test:/\.js$/,loader:"loader3",},],},//此时loader执行顺序:loader1-loader2-loader3module:{ rules:[{ enforce:"pre",test:/\.js$/,loader:"loader1",},{ //没有enforce就是normaltest:/\.js$/,loader:"loader2",},{ enforce:"post",test:/\.js$/,loader:"loader3",},],},使用loader的方式
配置方式:在webpack.config.js文件中指定loader。(pre、normal、postloader)
内联方式:在每个import语句中显式指定loader。(inlineloader)
开发一个loader1.最简单的loader//loaders/loader1.jsmodule.exports=functionloader1(content){ console.log("hellofirstloader");returncontent;};它接受要处理的源码作为参数,输出转换后的js代码。
2.loader接受的参数content源文件的内容
mapSourceMap数据
meta数据,可以是任何内容
loader分类1.同步loadermodule.exports=function(content,map,meta){ returncontent;};this.callback方法则更灵活,因为它允许传递多个参数,而不仅仅是content。
module.exports=function(content,map,meta){ //传递map,让source-map不中断//传递meta,让下一个loader接收到其他参数this.callback(null,content,map,meta);return;//当调用callback()函数时,总是tvvod源码返回undefined};2.异步loadermodule.exports=function(content,map,meta){ constcallback=this.async();//进行异步操作setTimeout(()=>{ callback(null,result,map,meta);},);};由于同步计算过于耗时,在Node.js这样的单线程环境下进行此操作并不是好的方案,我们建议尽可能地使你的loader异步化。但如果计算量很小,同步loader也是可以的。
3.RawLoader默认情况下,资源文件会被转化为UTF-8字符串,然后传给loader。通过设置raw为true,loader可以接收原始的Buffer。
module.exports=function(content){ //content是一个Buffer数据returncontent;};module.exports.raw=true;//开启RawLoader4.PitchingLoadermodule.exports=function(content){ returncontent;};module.exports.pitch=function(remainingRequest,precedingRequest,data){ console.log("dosomethings");};webpack会先从左到右执行loader链中的每个loader上的pitch方法(如果有),然后再从右到左执行loader链中的每个loader上的普通loader方法。
在这个过程中如果任何pitch有返回值,则loader链被阻断。webpack会跳过后面所有的的pitch和loader,直接进入上一个loader。
loaderAPI方法名含义用法this.async异步回调loader。返回this.callbackconstcallback=this.async()this.callback可以同步或者异步调用的并返回多个结果的函数this.callback(err,content,sourceMap?,meta?)this.getOptions(schema)获取loader的optionsthis.getOptions(schema)this.emitFile产生一个文件this.emitFile(name,content,sourceMap)this.utils.contextify返回一个相对路径this.utils.contextify(context,request)this.utils.absolutify返回一个绝对路径this.utils.absolutify(context,request)更多文档,请查阅webpack官方loaderapi文档
手写clean-log-loader作用:用来清理js代码中的console.log
//loaders/clean-log-loader.jsmodule.exports=functioncleanLogLoader(content){ //将console.log替换为空returncontent.replace(/console\.log\(.*\);?/g,"");};手写banner-loader作用:给js代码添加文本注释
loaders/banner-loader/index.js
constschema=require("./schema.json");module.exports=function(content){ //获取loader的options,同时对options内容进行校验//schema是options的校验规则(符合JSONschema规则)constoptions=this.getOptions(schema);constprefix=`/**Author:${ options.author}*/`;return`${ prefix}\n${ content}`;};loaders/banner-loader/schema.json
//此时loader执行顺序:loader1-loader2-loader3module:{ rules:[{ enforce:"pre",test:/\.js$/,loader:"loader1",},{ //没有enforce就是normaltest:/\.js$/,loader:"loader2",},{ enforce:"post",test:/\.js$/,loader:"loader3",},],},0手写babel-loader作用:编译js代码,将ES6+语法编译成ES5-语法。
下载依赖
//此时loader执行顺序:loader1-loader2-loader3module:{ rules:[{ enforce:"pre",test:/\.js$/,loader:"loader1",},{ //没有enforce就是normaltest:/\.js$/,loader:"loader2",},{ enforce:"post",test:/\.js$/,loader:"loader3",},],},1loaders/babel-loader/index.js
//此时loader执行顺序:loader1-loader2-loader3module:{ rules:[{ enforce:"pre",test:/\.js$/,loader:"loader1",},{ //没有enforce就是normaltest:/\.js$/,loader:"loader2",},{ enforce:"post",test:/\.js$/,loader:"loader3",},],},2loaders/banner-loader/schema.json
//此时loader执行顺序:loader1-loader2-loader3module:{ rules:[{ enforce:"pre",test:/\.js$/,loader:"loader1",},{ //没有enforce就是normaltest:/\.js$/,loader:"loader2",},{ enforce:"post",test:/\.js$/,loader:"loader3",},],},3手写file-loader作用:将文件原封不动输出出去
下载包
//此时loader执行顺序:loader1-loader2-loader3module:{ rules:[{ enforce:"pre",test:/\.js$/,loader:"loader1",},{ //没有enforce就是normaltest:/\.js$/,loader:"loader2",},{ enforce:"post",test:/\.js$/,loader:"loader3",},],},4loaders/file-loader.js
//此时loader执行顺序:loader1-loader2-loader3module:{ rules:[{ enforce:"pre",test:/\.js$/,loader:"loader1",},{ //没有enforce就是normaltest:/\.js$/,loader:"loader2",},{ enforce:"post",test:/\.js$/,loader:"loader3",},],},5loader配置
//此时loader执行顺序:loader1-loader2-loader3module:{ rules:[{ enforce:"pre",test:/\.js$/,loader:"loader1",},{ //没有enforce就是normaltest:/\.js$/,loader:"loader2",},{ enforce:"post",test:/\.js$/,loader:"loader3",},],},6手写style-loader作用:动态创建style标签,插入js中的样式代码,使样式生效。
loaders/style-loader.js
//此时loader执行顺序:loader1-loader2-loader3module:{ rules:[{ enforce:"pre",test:/\.js$/,loader:"loader1",},{ //没有enforce就是normaltest:/\.js$/,loader:"loader2",},{ enforce:"post",test:/\.js$/,loader:"loader3",},],},7Plugin原理Plugin的作用通过插件我们可以扩展webpack,加入自定义的构建行为,使webpack可以执行更广泛的任务,拥有更强的构建能力。
Plugin工作原理webpack就像一条生产线,要经过一系列处理流程后才能将源文件转换成输出结果。emlogPro源码这条生产线上的每个处理流程的职责都是单一的,多个流程之间有存在依赖关系,只有完成当前处理后才能交给下一个流程去处理。插件就像是一个插入到生产线中的一个功能,在特定的时机对生产线上的资源做处理。webpack通过Tapable来组织这条复杂的生产线。webpack在运行过程中会广播事件,插件只需要监听它所关心的事件,就能加入到这条生产线中,去改变生产线的运作。webpack的事件流机制保证了插件的有序性,使得整个系统扩展性很好。——「深入浅出Webpack」
站在代码逻辑的角度就是:webpack在编译代码过程中,会触发一系列Tapable钩子事件,插件所做的,就是找到相应的钩子,往上面挂上自己的任务,也就是注册事件,这样,当webpack构建的时候,插件注册的事件就会随着钩子的触发而执行了。
Webpack内部的钩子什么是钩子钩子的本质就是:事件。为了方便我们直接介入和控制编译过程,webpack把编译过程中触发的各类关键事件封装成事件接口暴露了出来。这些接口被很形象地称做:hooks(钩子)。开发插件,离不开这些钩子。
TapableTapable为webpack提供了统一的插件接口(钩子)类型定义,它是webpack的核心功能库。webpack中目前有十种hooks,在Tapable源码中可以看到,他们是:
//此时loader执行顺序:loader1-loader2-loader3module:{ rules:[{ enforce:"pre",test:/\.js$/,loader:"loader1",},{ //没有enforce就是normaltest:/\.js$/,loader:"loader2",},{ enforce:"post",test:/\.js$/,loader:"loader3",},],},8Tapable还统一暴露了三个方法给插件,用于注入不同类型的自定义构建行为:
tap:可以注册同步钩子和异步钩子。
tapAsync:回调方式注册异步钩子。
tapPromise:Promise方式注册异步钩子。
Plugin构建对象Compilercompiler对象中保存着完整的Webpack环境配置,每次启动webpack构建时它都是一个独一无二,仅仅会创建一次的对象。
这个对象会在首次启动Webpack时创建,我们可以通过compiler对象上访问到Webapck的主环境配置,比如loader、plugin等等配置信息。
它有以下主要属性:
compiler.options可以访问本次启动webpack时候所有的配置文件,包括但不限于loaders、entry、output、plugin等等完整配置信息。
compiler.inputFileSystem和compiler.outputFileSystem可以进行文件操作,相当于Nodejs中fs。
compiler.hooks可以注册tapable的不同种类Hook,从而可以在compiler生命周期中植入不同的逻辑。
compilerhooks文档
Compilationcompilation对象代表一次资源的构建,compilation实例能够访问所有的模块和它们的依赖。
一个compilation对象会对构建依赖图中所有模块,进行编译。在编译阶段,模块会被加载(load)、封存(seal)、优化(optimize)、分块(chunk)、哈希(hash)和重新创建(restore)。
它有以下主要属性:
compilation.modules可以访问所有模块,打包的每一个文件都是一个模块。
compilation.chunkschunk即是多个modules组成而来的一个代码块。入口文件引入的资源组成一个chunk,通过代码分割的模块又是另外的chunk。
compilation.assets可以访问本次打包生成所有文件的结果。
compilation.hooks可以注册tapable的不同种类Hook,用于在compilation编译模块阶段进行逻辑添加以及修改。
compilationhooks文档
生命周期简图开发一个插件最简单的插件plugins/test-plugin.js
//此时loader执行顺序:loader1-loader2-loader3module:{ rules:[{ enforce:"pre",test:/\.js$/,loader:"loader1",},{ //没有enforce就是normaltest:/\.js$/,loader:"loader2",},{ enforce:"post",test:/\.js$/,loader:"loader3",},],},9注册hook//loaders/loader1.jsmodule.exports=functionloader1(content){ console.log("hellofirstloader");returncontent;};0启动调试通过调试查看compiler和compilation对象数据情况。
package.json配置指令
//loaders/loader1.jsmodule.exports=functionloader1(content){ console.log("hellofirstloader");returncontent;};1运行指令
//loaders/loader1.jsmodule.exports=functionloader1(content){ console.log("hellofirstloader");returncontent;};2此时控制台输出以下内容:
PSC:\Users\\Desktop\source>//loaders/loader1.jsmodule.exports=functionloader1(content){ console.log("hellofirstloader");returncontent;};2>source@1.0.0debug>node--inspect-brk./node_modules/webpack-cli/bin/cli.jsDebuggerlisteningonws://.0.0.1:/ea-7b--a7-fccForhelp,see:/post/开发思路:
我们需要借助html-webpack-plugin来实现
在html-webpack-plugin输出index.html前将内联runtime注入进去
删除多余的runtime文件
如何操作html-webpack-plugin?官方文档
实现:
//loaders/loader1.jsmodule.exports=functionloader1(content){ console.log("hellofirstloader");returncontent;};7运用ptcms搭建自用无广告网站(附采集规则和源码)
运用ptcms搭建自用无广告网站的方法,附带采集规则和源码,解决小说阅读中的广告困扰。首先,需要服务器,推荐阿里云免费服务器,确保网站搭建顺利。通过阿里云界面,购买服务器并配置IP地址,连接服务器后,安装宝塔面板,选择适合的操作系统,安装必要的软件包,如Nginx、PHP版本7.3、PHP扩展(fileinfo、memcached、swoole或swoole4、swoole_serialize,禁用shell_exec),以及MySQL版本5.6。完成软件安装后,将ptcms源码压缩文件上传至服务器。
在服务器中,新建ptcms文件夹,复制loader.so和license文件到新建目录中。通过命令行进行目录切换,安装扩展和配置文件,确保php环境正确运行。添加加密Loader代码到配置文件,重启PHP服务以使更改生效。
配置corn任务,通过php脚本执行检查任务,确保网站运行无误。接下来,设置伪静态,使用特定的重写规则,以优化网站性能和搜索引擎友好度。编辑网站目录结构,确保文件正确放置。在网站设置中,更改运行目录至“public”,并关闭访问日志,以节省存储空间。
完成上述步骤后,网站基本搭建完成。若需安装数据库,使用宝塔面板创建数据库,配置数据库名、用户名和密码,以及访问权限。安装过程中,注意填写网站名称和相关配置信息,确保数据安全。完成数据库和网站基础设置后,导入采集规则,如小说数据,以实现自动采集功能。
最后,确保网站的统计代码数字进行替换,以防止官方发现和避免商业用途,保持合法合规。ptcms的使用,不仅限于小说网站搭建,根据需求,可以扩展至其他内容平台的搭建,探索更多的应用可能性。
SpringBoot全局配置文件加载原理详解(万字源码分析)
SpringBoot通过全局配置文件实现项目部署和修改的便利性,以application.properties或application.yaml为核心,配置文件加载遵循特定的优先级规则:从classpath:/config/开始,依次是file:./config/、classpath:/、file:./,且越靠前的路径优先级越高。
配置文件的生效依赖于ApplicationListener实现类,如ConfigFileApplicationListener,它监听ApplicationEnvironmentPreparedEvent事件。当项目启动时,会从spring.factories文件中加载并实例化这些监听器,如ConfigFileApplicationListener负责加载配置文件。
启动过程中的关键步骤包括:首先,从spring.factories中获取监听器,如EventPublishingRunListener,然后通过事件广播机制触发加载配置文件的步骤。当遇到ApplicationEnvironmentPreparedEvent时,ConfigFileApplicationListener的load方法开始检索配置文件,优先级高的配置文件会覆盖低的。
加载过程涉及PropertySourceLoader,如PropertiesPropertySourceLoader和YamlPropertySourceLoader,它们根据文件扩展名(properties或yaml)检索并加载配置。具体操作包括读取配置文件、解析键值对,然后以Document对象的形式返回给上层处理。
总结来说,SpringBoot的全局配置文件加载是一个从配置文件路径查找、内容读取、解析到加载的完整流程,确保项目的配置能够在启动时正确生效。
DexClassLoaderåPathClassLoaderçåºå«
å¨ä½¿ç¨Javaèææºæ¶ï¼æ们ç»å¸¸èªå®ä¹ç»§æ¿èªClassLoaderçç±»å è½½å¨ãç¶åéè¿defineClassæ¹æ³æ¥ä»ä¸ä¸ªäºè¿å¶æµä¸å è½½Classãèå¨Androidä¸æ们æ æ³è¿ä¹ä½¿ç¨ï¼Androidä¸ClassLoaderçdefineClassæ¹æ³å ·ä½æ¯è°ç¨VMClassLoaderçdefineClassæ¬å°éææ¹æ³ãèè¿ä¸ªæ¬å°æ¹æ³ä»ä¹é½æ²¡åï¼åªæ¯æåºäºä¸ä¸ªâUnsupportedOperationExceptionâå¼å¸¸ã
æ¢ç¶å¨Dalvikèææºéï¼ClassLoaderä¸å¥½ç¨ï¼é£ä¹Androidå®æ¹ä¸ºäºè§£å³è¿ä¸ªé®é¢ï¼å¸®æ们ä»ClassLoaderä¸æ´¾çåºäºä¸¤ä¸ªç±»ï¼DexClassLoaderåPathClassLoaderãåä¸ç两è å¾åï¼é£ä¹ç©¶ç«äºè å¨ä½¿ç¨ä¸é¢æä½ä¸åï¼è¿éæå大家ä¸èµ·æ¢è®¨ä¸ä¸ã
é¦å æ¥çä¸ä¸äºè çæé æ¹æ³
DexClassLoader
public DexClassLoader (String dexPath, String dexOutputDir, String libPath, ClassLoader parent)
åæ°è¯¦è§£ï¼
dexPathï¼dexæ件路å¾å表ï¼å¤ä¸ªè·¯å¾ä½¿ç¨â:âåé
dexOutputDirï¼ç»è¿ä¼åçdexæ件ï¼odexï¼æ件è¾åºç®å½
libPathï¼å¨æåºè·¯å¾ï¼å°è¢«æ·»å å°appå¨æåºæ索路å¾å表ä¸ï¼
parentï¼è¿æ¯ä¸ä¸ªClassLoaderï¼è¿ä¸ªåæ°ç主è¦ä½ç¨æ¯ä¿çjavaä¸ClassLoaderçå§ææºå¶ï¼ä¼å ç¶ç±»å è½½å¨å è½½classesï¼ç±ä¸èä¸çå è½½æºå¶ï¼é²æ¢éå¤å 载类åèç ï¼
DexClassLoaderæ¯ä¸ä¸ªå¯ä»¥ä»å å«classes.dexå®ä½ç.jaræ.apkæ件ä¸å è½½classesçç±»å è½½å¨ãå¯ä»¥ç¨äºå®ç°dexçå¨æå è½½ã代ç çæ´æ°ççãè¿ä¸ªç±»å è½½å¨å¿ é¡»è¦ä¸ä¸ªappçç§æãå¯åç®å½æ¥ç¼åç»è¿ä¼åçclassesï¼odexæ件ï¼ï¼ä½¿ç¨Context.getDir(String, int)æ¹æ³å¯ä»¥å建ä¸ä¸ªè¿æ ·çç®å½ï¼ä¾å¦ï¼
File dexOutputDir = context.getDir(âdexâ, 0);
PathClassLoader
PathClassLoaderæä¾ä¸¤ä¸ªå¸¸ç¨æé æ¹æ³
public PathClassLoader (String path, ClassLoader parent)
public PathClassLoader (String path, String libPath, ClassLoader parent)
åæ°è¯¦è§£ï¼
pathï¼æ件æè ç®å½çå表
libPathï¼å å«libåºçç®å½å表
parentï¼ç¶ç±»å è½½å¨
PathClassLoaderæä¾ä¸ä¸ªç®åçClassLoaderå®ç°ï¼å¯ä»¥æä½å¨æ¬å°æ件系ç»çæ件å表æç®å½ä¸çclassesï¼ä½ä¸å¯ä»¥ä»ç½ç»ä¸å è½½classesã
为äºä¾¿äºç解ï¼æ们æ¥çä¸ä¸äºè çæºç ï¼
è¿éåå¾çæè¿°
// DexClassLoader.java
public class DexClassLoader extends BaseDexClassLoader {
public DexClassLoader(String dexPath, String optimizedDirectory,
String libraryPath, ClassLoader parent) {
super(dexPath, new File(optimizedDirectory), libraryPath, parent);
}
}
// çæææï¼ç´åæ¬æ¥çæå µ/mynameishuangshuai
// PathClassLoader.java
public class PathClassLoader extends BaseDexClassLoader {
public PathClassLoader(String dexPath, ClassLoader parent) {
super(dexPath, null, null, parent);
}
public PathClassLoader(String dexPath, String libraryPath,
ClassLoader parent) {
super(dexPath, null, libraryPath, parent);
}
}
å¾ææ¾ä¸¤è é½ç»§æ¿äºBaseDexClassLoaderç±»ï¼å¹¶åäºä¸ä¸å°è£ ï¼å ·ä½çå®ç°è¿æ¯å¨ç¶ç±»éãä¸é¾çåºï¼ä¸»è¦çåºå«å¨äºPathClassLoaderçoptimizedDirectoryåæ°åªè½æ¯nullï¼é£ä¹optimizedDirectoryæ¯åä»ä¹ç¨çå¢ï¼æ们è¿BaseDexClassLoaderå»ççè¿ä¸ªåæ°ã
public BaseDexClassLoader(String dexPath, File optimizedDirectory,
String libraryPath, ClassLoader parent) {
super(parent);
this.originalPath = dexPath;
this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory);
}
代ç ä¸ä¸optimizedDirectoryæå ³çå°æ¹æ¯new ä¸ä¸ªDexPathListå®ä¾ã
public DexPathList(ClassLoader definingContext, String dexPath,
String libraryPath, File optimizedDirectory) {
â¦â¦
this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory);
}
private static Element[] makeDexElements(ArrayList<File> files,
File optimizedDirectory) {
ArrayList<Element> elements = new ArrayList<Element>();
for (File file : files) {
ZipFile zip = null;
DexFile dex = null;
String name = file.getName();
if (name.endsWith(DEX_SUFFIX)) {
dex = loadDexFile(file, optimizedDirectory);
} else if (name.endsWith(APK_SUFFIX) || name.endsWith(JAR_SUFFIX)
|| name.endsWith(ZIP_SUFFIX)) {
zip = new ZipFile(file);
}
â¦â¦
if ((zip != null) || (dex != null)) {
elements.add(new Element(file, zip, dex));
}
}
return elements.toArray(new Element[elements.size()]);
}
private static DexFile loadDexFile(File file, File optimizedDirectory)
throws IOException {
if (optimizedDirectory == null) {
return new DexFile(file);
} else {
String optimizedPath = optimizedPathFor(file, optimizedDirectory);
return DexFile.loadDex(file.getPath(), optimizedPath, 0);
}
}
/
*** Converts a dex/jar file path and an output directory to an
* output file path for an associated optimized dex file.
*/
private static String optimizedPathFor(File path,
File optimizedDirectory) {
String fileName = path.getName();
if (!fileName.endsWith(DEX_SUFFIX)) {
int lastDot = fileName.lastIndexOf(".");
if (lastDot < 0) {
fileName += DEX_SUFFIX;
} else {
StringBuilder sb = new StringBuilder(lastDot + 4);
sb.append(fileName, 0, lastDot);
sb.append(DEX_SUFFIX);
fileName = sb.toString();
}
}
File result = new File(optimizedDirectory, fileName);
return result.getPath();
}
optimizedDirectoryæ¯ç¨æ¥ç¼åæ们éè¦å è½½çdexæ件çï¼å¹¶å建ä¸ä¸ªDexFile对象ï¼å¦æå®ä¸ºnullï¼é£ä¹ä¼ç´æ¥ä½¿ç¨dexæ件åæçè·¯å¾æ¥å建DexFile
对象ã
optimizedDirectoryå¿ é¡»æ¯ä¸ä¸ªå é¨åå¨è·¯å¾ï¼æ 论åªç§å¨æå è½½ï¼å è½½çå¯æ§è¡æ件ä¸å®è¦åæ¾å¨å é¨åå¨ãDexClassLoaderå¯ä»¥æå®èªå·±çoptimizedDirectoryï¼æ以å®å¯ä»¥å è½½å¤é¨çdexï¼å 为è¿ä¸ªdexä¼è¢«å¤å¶å°å é¨è·¯å¾çoptimizedDirectoryï¼èPathClassLoader没æoptimizedDirectoryï¼æ以å®åªè½å è½½å é¨çdexï¼è¿äºå¤§é½æ¯åå¨ç³»ç»ä¸å·²ç»å®è£ è¿çapkéé¢çã
éè¿ä»¥ä¸çåæï¼æ们å¯ä»¥å¾åºäºè åè½ä¸çåºå«
DexClassLoaderï¼è½å¤å è½½æªå®è£ çjar/apk/dex
PathClassLoaderï¼åªè½å 载系ç»ä¸å·²ç»å®è£ è¿çapk