Android笔记(二十五):两种sdk热更插件资源加载方案
背景
在研究sdk插件化热更新方式的过程中总结出了两套插件资源加载方案,在此记录下
资源热更方式
方式一:合并所有插件资源
需要解决资源id冲突问题
资源ID值一共4个字段,由三部分组成:PackageId+TypeId+EntryId
- PackageId:是包的Id值,Android 中如果第三方应用的话,这个默认值是 0x7f,系统应用的话就是 0x01 ,插件的话那么就是给插件分配的id值,占用1个字节。
- TypeId:是资源的类型Id值,一般 Android 中有这几个类型:attr,drawable,layout,anim,raw,dimen,string,bool,style,integer,array,color,id,menu 等。【应用程序所有模块中的资源类型名称,按照字母排序之后。值是从1开支逐渐递增的,而且顺序不能改变(每个模块下的R文件的相同资源类型id值相同)。比如:anim=0x01占用1个字节,那么在这个编译出的所有R文件中anim 的值都是 0x01】
- EntryId:是在具体的类型下资源实例的id值,从0开始,依次递增,他占用2个字节。
两种解决方式
- gradle3.5以上可以动态配置aapt的package-id参数修改插件apk的packageId,可在插件模块的build.gradle配置如下:
android {compileSdkVersion 32defaultConfig {applicationId "xxx"minSdkVersion 21targetSdkVersion 32versionCode 1versionName "1.0"}buildTypes {release {minifyEnabled falseproguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'}}compileOptions {sourceCompatibility JavaVersion.VERSION_1_8targetCompatibility JavaVersion.VERSION_1_8}//为模块定一个唯一的package-id,如0x80aaptOptions {additionalParameters '--allow-reserved-package-id','--package-id', '0x80'}
}
- gradle3.5以下,可以将打包出来的插件apk包进行解包,修改public.xml文件内资源id的package-id,再扫描每一个R$xxx.smali文件,纠正代码中R类的值,与public.xml中的对应,重新打包。
反射替换Application中的mResources字段
注意:在启动service和receiver的时候,会使用application的resource
fun mergePatchResources(application: Application, apkPaths : List<String>) {val newAssetManagerObj = AssetManager::class.java.newInstance()//todo 需要注意这里的addAssetPath被标志为废弃了val addAssetPath = AssetManager::class.java.getMethod("addAssetPath", String::class.java)// 插入宿主的资源addAssetPath.invoke(newAssetManagerObj, application.baseContext.packageResourcePath)// 插入插件的资源apkPaths.forEach {addAssetPath.invoke(newAssetManagerObj, it)}val newResourcesObj = Resources(newAssetManagerObj,application.baseContext.resources.displayMetrics,application.baseContext.resources.configuration)newHoldResources = newResourcesObjval resourcesField = application.baseContext.javaClass.getDeclaredField("mResources")resourcesField.isAccessible = trueresourcesField[application.baseContext] = newResourcesObjval packageInfoField = application.baseContext.javaClass.getDeclaredField("mPackageInfo")packageInfoField.isAccessible = trueval packageInfoObj = packageInfoField[application.baseContext]// 获取 mPackageInfo 变量对象中类的Resources类型的mResources 变量,并替换它的值为新的Resources对象// 注意:这是最主要的需要替换的,如果不需要支持插件运行时更新,只留这一个就可以了val resourcesField2 = packageInfoObj.javaClass.getDeclaredField("mResources")resourcesField2.isAccessible = trueresourcesField2[packageInfoObj] = newResourcesObj// 获取 ContextImpl 中的 Resources.Theme 类型的 mTheme 变量,并至空它// 注意:清理mTheme对象,否则通过inflate方式加载资源会报错, 如果是activity动态加载插件,则需要把activity的mTheme对象也设置为nullval themeField = application.baseContext.javaClass.getDeclaredField("mTheme")themeField.isAccessible = truethemeField[application.baseContext] = null
}
更新Activity的资源
- 监听activity的onCreate回调
((Application)context).registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {@Overridepublic void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) {Log.d("zbm111", "doMonitorActivity---onCreate");Resources resources = "含宿主和所有插件资源的完整resource对象"ResHookUtil.monkeyPatchExistingResources(activity, resources);}@Overridepublic void onActivityStarted(@NonNull Activity activity) {}@Overridepublic void onActivityResumed(@NonNull Activity activity) {}@Overridepublic void onActivityPaused(@NonNull Activity activity) {}@Overridepublic void onActivityStopped(@NonNull Activity activity) {}@Overridepublic void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) {}@Overridepublic void onActivityDestroyed(@NonNull Activity activity) {}});
- 将要启动的activity反射替换mResources字段
public static void monkeyPatchExistingResources(Activity activity, Resources newResources) {try {Class<?> contextThemeWrapperClass = null;try {contextThemeWrapperClass = Class.forName("android.view.ContextThemeWrapper");} catch (ClassNotFoundException e) {e.printStackTrace();}// 反射获取 ContextThemeWrapper 类的 mResources 字段Field mResourcesField = null;try {mResourcesField = contextThemeWrapperClass.getDeclaredField("mResources");} catch (NoSuchFieldException e) {e.printStackTrace();}// 设置字段可见性mResourcesField.setAccessible(true);// 将插件资源设置到插件 Activity 中try {mResourcesField.set(activity, newResources);} catch (IllegalAccessException e) {e.printStackTrace();}Method mTheme = contextThemeWrapperClass.getDeclaredMethod("setTheme", Resources.Theme.class);mTheme.setAccessible(true);mTheme.invoke(activity, (Object) null);} catch (Exception e) {e.printStackTrace();}}
方式二:封装含插件资源的Resource对象
注意:插件apk的包名须与宿主的包名保持一致
需要解决资源id冲突问题
参考方式一,可以通过修改type id与宿主区分开
hook ActivityThread的handler设置callback
- 给ActivityThread中mH(Hander类型)对象的mCallback字段设置一个代理对象ProxyHandlerCallback
public static void doHandlerHook(Context context) {try {HookUtils.context = context;Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");Method currentActivityThread = activityThreadClass.getDeclaredMethod("currentActivityThread");Object activityThread = currentActivityThread.invoke(null);Field mHField = activityThreadClass.getDeclaredField("mH");mHField.setAccessible(true);Handler mH = (Handler) mHField.get(activityThread);Field mCallbackField = Handler.class.getDeclaredField("mCallback");mCallbackField.setAccessible(true);mCallbackField.set(mH, new ProxyHandlerCallback(mH));} catch (Exception e) {e.printStackTrace();}}
- 系统启动service前,进行拦截,将ActivityThread的mPackages新增一个service的LoadedApk,该LoadedApk的mResources字段替换为含插件+宿主资源的SingleMixRes对象;
系统启动receiver前,进行拦截,将application的mResources字段替换为含插件+宿主资源的SingleMixRes对象。
public class ProxyHandlerCallback implements Handler.Callback {private Handler mBaseHandler;private Map<String, String> mPathToPluginNameMap = new HashMap<>();public ProxyHandlerCallback(Handler mBaseHandler) {this.mBaseHandler = mBaseHandler;}@Overridepublic boolean handleMessage(Message msg) {Log.d("zbm111", "接受到消息了msg:" + msg);if (msg.what == 113){//启动receiver的时候走这里try {Object object = msg.obj;Field infoField = object.getClass().getDeclaredField("info");infoField.setAccessible(true);ActivityInfo activityInfo = (ActivityInfo) infoField.get(object);String hostReceiverName = activityInfo.name;Resources resources = "含插件+宿主资源的SingleMixRes对象";Field resourcesField = ((Application)HookUtils.getContext()).getBaseContext().getClass().getDeclaredField("mResources");resourcesField.setAccessible(true);resourcesField.set(((Application)HookUtils.getContext()).getBaseContext(), resources);}catch (Exception e){Log.e("zbm111", "handle create receiver failed");}} else if (msg.what == 114) {//启动service的时候走这里try {Object object = msg.obj;Field infoField = object.getClass().getDeclaredField("info");infoField.setAccessible(true);ServiceInfo serviceInfo = (ServiceInfo) infoField.get(object);String hostServiceName = serviceInfo.name;String path = "插件apk本地存储路径"//todo dex热修复必须同时进行资源热更if (path != null) {replaceLoadApk(hostServiceName, path, false);Log.i("zbm111", "replaced to plugin service success");}} catch (Exception e) {Log.e("zbm111", "handle create service failed");}}mBaseHandler.handleMessage(msg);return true;}private void replaceLoadApk(String componentName, String path, boolean isUseActivity) throws Exception {Log.i("zbm111", "start replaceLoadApk");Field activityThreadField = Class.forName("android.app.ActivityThread").getDeclaredField("sCurrentActivityThread");activityThreadField.setAccessible(true);Object sCurrentActivityThread = activityThreadField.get(null);Field mPackagesField = sCurrentActivityThread.getClass().getDeclaredField("mPackages");mPackagesField.setAccessible(true);ArrayMap mPackages = (ArrayMap) mPackagesField.get(sCurrentActivityThread);if (null == mPackages) {Log.i("zbm111", "can not get mPackages");return;}ApplicationInfo applicationInfo = generateApplicationInfo(path);if (null != applicationInfo) {Field compatibilityInfoField = Class.forName("android.content.res.CompatibilityInfo").getDeclaredField("DEFAULT_COMPATIBILITY_INFO");compatibilityInfoField.setAccessible(true);Object defaultCompatibilityInfo = compatibilityInfoField.get(null);Object loadedApk;if (isUseActivity){Method getPackageInfoMethod = sCurrentActivityThread.getClass().getDeclaredMethod("getPackageInfo", ApplicationInfo.class, Class.forName("android.content.res.CompatibilityInfo"), int.class);getPackageInfoMethod.setAccessible(true);loadedApk = getPackageInfoMethod.invoke(sCurrentActivityThread, applicationInfo, defaultCompatibilityInfo, Context.CONTEXT_INCLUDE_CODE);}else {Method getPackageInfoMethod = sCurrentActivityThread.getClass().getDeclaredMethod("getPackageInfoNoCheck", ApplicationInfo.class, Class.forName("android.content.res.CompatibilityInfo"));getPackageInfoMethod.setAccessible(true);loadedApk = getPackageInfoMethod.invoke(sCurrentActivityThread, applicationInfo, defaultCompatibilityInfo);}String pluginName = applicationInfo.packageName;if (!TextUtils.isEmpty(pluginName)) {Log.i("zbm111", "plugin pkg name is " + pluginName);Resources resources = "含插件+宿主资源的SingleMixRes对象";setResource(loadedApk, resources);mPackages.put(pluginName, new WeakReference<>(loadedApk));mPackagesField.set(sCurrentActivityThread, mPackages);mPathToPluginNameMap.put(path, pluginName);} else {Log.i("zbm111", "get plugin pkg name failed");}} else {Log.i("zbm111", "can not get application info");}}private void setResource(Object loadedApk, Resources resources) throws Exception{Field mResourcesField = loadedApk.getClass().getDeclaredField("mResources");mResourcesField.setAccessible(true);mResourcesField.set(loadedApk, resources);}public ApplicationInfo generateApplicationInfo(String pluginPath) {try {ApplicationInfo applicationInfo = getApplicationInfoByPackageArchiveInfo(pluginPath);if (null == applicationInfo) {LogUtil.i("zbm111", "get applicationInfo failed");return null;}applicationInfo.sourceDir = pluginPath;applicationInfo.publicSourceDir = pluginPath;return applicationInfo;} catch (Exception e) {LogUtil.i("zbm111", "generateApplicationzInfo failed " + e.getMessage());}return null;}private ApplicationInfo getApplicationInfoByPackageArchiveInfo(String pluginPath) {PackageManager packageManager = HookUtils.getContext().getPackageManager();if (null == packageManager) {LogUtil.i("zbm111", "get PackageManager failed");return null;}PackageInfo packageInfo = packageManager.getPackageArchiveInfo(pluginPath, 0);if (null == packageInfo) {LogUtil.i("zbm111", "get packageInfo failed");return null;}return packageInfo.applicationInfo;}
}
- 自定义ResourcesWrapper
public class ResourcesWrapper extends Resources {private Resources mBase;public ResourcesWrapper(Resources base){super(base.getAssets(),base.getDisplayMetrics(),base.getConfiguration());mBase = base;}@Overridepublic CharSequence getText(int id) throws NotFoundException {return mBase.getText(id);}@TargetApi(Build.VERSION_CODES.O)@Overridepublic Typeface getFont(int id) throws NotFoundException {return mBase.getFont(id);}@Overridepublic CharSequence getQuantityText(int id, int quantity) throws NotFoundException {return mBase.getQuantityText(id, quantity);}@Overridepublic String getString(int id) throws NotFoundException {return mBase.getString(id);}@Overridepublic String getString(int id, Object... formatArgs) throws NotFoundException {return mBase.getString(id, formatArgs);}@Overridepublic String getQuantityString(int id, int quantity, Object... formatArgs) throws NotFoundException {return mBase.getQuantityString(id, quantity, formatArgs);}@Overridepublic String getQuantityString(int id, int quantity) throws NotFoundException {return mBase.getQuantityString(id, quantity);}@Overridepublic CharSequence getText(int id, CharSequence def) {return mBase.getText(id, def);}@Overridepublic CharSequence[] getTextArray(int id) throws NotFoundException {return mBase.getTextArray(id);}@Overridepublic String[] getStringArray(int id) throws NotFoundException {return mBase.getStringArray(id);}@Overridepublic int[] getIntArray(int id) throws NotFoundException {return mBase.getIntArray(id);}@Overridepublic TypedArray obtainTypedArray(int id) throws NotFoundException {return mBase.obtainTypedArray(id);}@Overridepublic float getDimension(int id) throws NotFoundException {return mBase.getDimension(id);}@Overridepublic int getDimensionPixelOffset(int id) throws NotFoundException {return mBase.getDimensionPixelOffset(id);}@Overridepublic int getDimensionPixelSize(int id) throws NotFoundException {return mBase.getDimensionPixelSize(id);}@Overridepublic float getFraction(int id, int base, int pbase) {return mBase.getFraction(id, base, pbase);}@Overridepublic Drawable getDrawable(int id) throws NotFoundException {return mBase.getDrawable(id);}@TargetApi(Build.VERSION_CODES.LOLLIPOP)@Overridepublic Drawable getDrawable(int id, Theme theme) throws NotFoundException {return mBase.getDrawable(id, theme);}@Overridepublic Drawable getDrawableForDensity(int id, int density) throws NotFoundException {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {return mBase.getDrawableForDensity(id, density);} else {return null;}}@TargetApi(Build.VERSION_CODES.LOLLIPOP)@Overridepublic Drawable getDrawableForDensity(int id, int density, Theme theme) {return mBase.getDrawableForDensity(id, density, theme);}@Overridepublic Movie getMovie(int id) throws NotFoundException {return mBase.getMovie(id);}@Overridepublic int getColor(int id) throws NotFoundException {return mBase.getColor(id);}@TargetApi(Build.VERSION_CODES.M)@Overridepublic int getColor(int id, Theme theme) throws NotFoundException {return mBase.getColor(id, theme);}@Overridepublic ColorStateList getColorStateList(int id) throws NotFoundException {return mBase.getColorStateList(id);}@TargetApi(Build.VERSION_CODES.M)@Overridepublic ColorStateList getColorStateList(int id, Theme theme) throws NotFoundException {return mBase.getColorStateList(id, theme);}@Overridepublic boolean getBoolean(int id) throws NotFoundException {return mBase.getBoolean(id);}@Overridepublic int getInteger(int id) throws NotFoundException {return mBase.getInteger(id);}@Overridepublic XmlResourceParser getLayout(int id) throws NotFoundException {return mBase.getLayout(id);}@Overridepublic XmlResourceParser getAnimation(int id) throws NotFoundException {return mBase.getAnimation(id);}@Overridepublic XmlResourceParser getXml(int id) throws NotFoundException {return mBase.getXml(id);}@Overridepublic InputStream openRawResource(int id) throws NotFoundException {return mBase.openRawResource(id);}@Overridepublic InputStream openRawResource(int id, TypedValue value) throws NotFoundException {return mBase.openRawResource(id, value);}@Overridepublic AssetFileDescriptor openRawResourceFd(int id) throws NotFoundException {return mBase.openRawResourceFd(id);}@Overridepublic void getValue(int id, TypedValue outValue, boolean resolveRefs) throws NotFoundException {mBase.getValue(id, outValue, resolveRefs);}@Overridepublic void getValueForDensity(int id, int density, TypedValue outValue, boolean resolveRefs) throws NotFoundException {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {mBase.getValueForDensity(id, density, outValue, resolveRefs);}}@Overridepublic void getValue(String name, TypedValue outValue, boolean resolveRefs) throws NotFoundException {mBase.getValue(name, outValue, resolveRefs);}@Overridepublic TypedArray obtainAttributes(AttributeSet set, int[] attrs) {return mBase.obtainAttributes(set, attrs);}@Overridepublic DisplayMetrics getDisplayMetrics() {return mBase.getDisplayMetrics();}@Overridepublic Configuration getConfiguration() {return mBase.getConfiguration();}@Overridepublic int getIdentifier(String name, String defType, String defPackage) {return mBase.getIdentifier(name, defType, defPackage);}@Overridepublic String getResourceName(int resid) throws NotFoundException {return mBase.getResourceName(resid);}@Overridepublic String getResourcePackageName(int resid) throws NotFoundException {return mBase.getResourcePackageName(resid);}@Overridepublic String getResourceTypeName(int resid) throws NotFoundException {return mBase.getResourceTypeName(resid);}@Overridepublic String getResourceEntryName(int resid) throws NotFoundException {return mBase.getResourceEntryName(resid);}@Overridepublic void parseBundleExtras(XmlResourceParser parser, Bundle outBundle) throws XmlPullParserException, IOException {mBase.parseBundleExtras(parser, outBundle);}@Overridepublic void parseBundleExtra(String tagName, AttributeSet attrs, Bundle outBundle) throws XmlPullParserException {mBase.parseBundleExtra(tagName, attrs, outBundle);}
}
- SingleMixRes优先从插件加载资源,找不到则从宿主加载资源
public class SingleMixRes extends ResourcesWrapper{private Resources mHostResources;public SingleMixRes(Resources hostResources, Resources pluginResources) {super(pluginResources);mHostResources = hostResources;}@Overridepublic CharSequence getText(int id) throws NotFoundException {try {return super.getText(id);} catch (NotFoundException e) {return mHostResources.getText(id);}}@Overridepublic String getString(int id) throws NotFoundException {try {return super.getString(id);} catch (NotFoundException e) {return mHostResources.getString(id);}}@Overridepublic String getString(int id, Object... formatArgs) throws NotFoundException {try {return super.getString(id,formatArgs);} catch (NotFoundException e) {return mHostResources.getString(id,formatArgs);}}@Overridepublic float getDimension(int id) throws NotFoundException {try {return super.getDimension(id);} catch (NotFoundException e) {return mHostResources.getDimension(id);}}@Overridepublic int getDimensionPixelOffset(int id) throws NotFoundException {try {return super.getDimensionPixelOffset(id);} catch (NotFoundException e) {return mHostResources.getDimensionPixelOffset(id);}}@Overridepublic int getDimensionPixelSize(int id) throws NotFoundException {try {return super.getDimensionPixelSize(id);} catch (NotFoundException e) {return mHostResources.getDimensionPixelSize(id);}}@Overridepublic Drawable getDrawable(int id) throws NotFoundException {try {return super.getDrawable(id);} catch (NotFoundException e) {return mHostResources.getDrawable(id);}}@TargetApi(Build.VERSION_CODES.LOLLIPOP)@Overridepublic Drawable getDrawable(int id, Theme theme) throws NotFoundException {try {return super.getDrawable(id, theme);} catch (NotFoundException e) {return mHostResources.getDrawable(id,theme);}}@Overridepublic Drawable getDrawableForDensity(int id, int density) throws NotFoundException {try {return super.getDrawableForDensity(id, density);} catch (NotFoundException e) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {return mHostResources.getDrawableForDensity(id, density);} else {return null;}}}@TargetApi(Build.VERSION_CODES.LOLLIPOP)@Overridepublic Drawable getDrawableForDensity(int id, int density, Theme theme) {try {return super.getDrawableForDensity(id, density, theme);} catch (Exception e) {return mHostResources.getDrawableForDensity(id,density,theme);}}@Overridepublic int getColor(int id) throws NotFoundException {try {return super.getColor(id);} catch (NotFoundException e) {return mHostResources.getColor(id);}}@TargetApi(Build.VERSION_CODES.M)@Overridepublic int getColor(int id, Theme theme) throws NotFoundException {try {return super.getColor(id,theme);} catch (NotFoundException e) {return mHostResources.getColor(id,theme);}}@Overridepublic ColorStateList getColorStateList(int id) throws NotFoundException {try {return super.getColorStateList(id);} catch (NotFoundException e) {return mHostResources.getColorStateList(id);}}@TargetApi(Build.VERSION_CODES.M)@Overridepublic ColorStateList getColorStateList(int id, Theme theme) throws NotFoundException {try {return super.getColorStateList(id,theme);} catch (NotFoundException e) {return mHostResources.getColorStateList(id,theme);}}@Overridepublic boolean getBoolean(int id) throws NotFoundException {try {return super.getBoolean(id);} catch (NotFoundException e) {return mHostResources.getBoolean(id);}}@Overridepublic XmlResourceParser getLayout(int id) throws NotFoundException {try {return super.getLayout(id);} catch (NotFoundException e) {return mHostResources.getLayout(id);}}@Overridepublic String getResourceName(int resid) throws NotFoundException {try {return super.getResourceName(resid);} catch (NotFoundException e) {return mHostResources.getResourceName(resid);}}@Overridepublic int getInteger(int id) throws NotFoundException {try {return super.getInteger(id);} catch (NotFoundException e) {return mHostResources.getInteger(id);}}@Overridepublic CharSequence getText(int id, CharSequence def) {try {return super.getText(id,def);} catch (NotFoundException e) {return mHostResources.getText(id,def);}}@Overridepublic InputStream openRawResource(int id) throws NotFoundException {try {return super.openRawResource(id);} catch (NotFoundException e) {return mHostResources.openRawResource(id);}}@Overridepublic XmlResourceParser getXml(int id) throws NotFoundException {try {return super.getXml(id);} catch (NotFoundException e) {return mHostResources.getXml(id);}}@TargetApi(Build.VERSION_CODES.O)@Overridepublic Typeface getFont(int id) throws NotFoundException {try {return super.getFont(id);} catch (NotFoundException e) {return mHostResources.getFont(id);}}@Overridepublic Movie getMovie(int id) throws NotFoundException {try {return super.getMovie(id);} catch (NotFoundException e) {return mHostResources.getMovie(id);}}@Overridepublic XmlResourceParser getAnimation(int id) throws NotFoundException {try {return super.getAnimation(id);} catch (NotFoundException e) {return mHostResources.getAnimation(id);}}@Overridepublic InputStream openRawResource(int id, TypedValue value) throws NotFoundException {try {return super.openRawResource(id,value);} catch (NotFoundException e) {return mHostResources.openRawResource(id,value);}}@Overridepublic AssetFileDescriptor openRawResourceFd(int id) throws NotFoundException {try {return super.openRawResourceFd(id);} catch (NotFoundException e) {return mHostResources.openRawResourceFd(id);}}
}
更新Activity的资源
参考方式一
两种方式注意事项
- 打包出的插件apk需要去除第三方smali代码,否则可能会报资源问题
- 代码更新与资源更新最好同步,预防id对应不上
附录
修改资源id冲突及去除第三方smali文件工具
相关文章:

Android笔记(二十五):两种sdk热更插件资源加载方案
背景 在研究sdk插件化热更新方式的过程中总结出了两套插件资源加载方案,在此记录下 资源热更方式 方式一:合并所有插件资源 需要解决资源id冲突问题 资源ID值一共4个字段,由三部分组成:PackageIdTypeIdEntryId PackageId&…...

spring框架--全面详解(学习笔记)
目录 1.Spring是什么 2.Spring 框架特点 3.Spring体系结构 4.Spring开发环境搭建 5.spring中IOC和DI 6.Spring中bean的生命周期 7.Spring Bean作用域 8.spring注解开发 9.Spring框架中AOP(Aspect Oriented Programming) 10.AOP 实现分类 11.A…...

2023年CDGA考试模拟题库(401-500)
2023年CDGA考试模拟题库(401-500) 401.数据管理战略的SMART原则指的是哪项? [1分] A.具体 、高质量、可操作 、现实、有时间限制 B.具体、可衡量、可检验、现实、有时间限制 C.具体、可衡量、可操作、现实、有时间限制 D.具体、高质量、可检验、现实12-24个月的目标 答…...

软件设计师备考文档
cpu 计算机的基本硬件系统:运算器、控制器、存储器、输入设备、输出设备 cpu负责获取程序指令,对指令进行译码并加以执行 * cpu的功能控制器(保证程序正常执行,处理异常事件) 程序控制操作控制运算器(只能…...

Javascript的API基本内容(一)
一、获取DOM对象 querySelector 满足条件的第一个元素 querySelectorAll 满足条件的元素集合 返回伪数组 document.getElementById 专门获取元素类型节点,根据标签的 id 属性查找 二、操作元素内容 通过修改 DOM 的文本内容,动态改变网页的内容。 inn…...

10、最小公倍数
法一: #include <stdio.h>int main(){int a,b;scanf("%d%d",&a,&b);int m a>b?a:b;//m表示a,b之间的较大值while(1){if(m%a0&&m%b0){break;}m;}printf("%d",m);return 0; }法二:a*i%b0成立 #include &…...

【vue】vue2.x项目中使用md文件
一、Vue项目展示md文件的三种方式 1、将md文件 导入为 html 生成的标题标签自带具有id属性,值为标题内容; <h2 id"测试">测试</h2> # 处理md为html字符串 yarn add markdown-loader # 处理字符串,用于导出显示 yarn a…...

操作系统权限提升(十三)之绕过UAC提权-MSF和CS绕过UAC提权
系列文章 操作系统权限提升(十二)之绕过UAC提权-Windows UAC概述 注:阅读本编文章前,请先阅读系列文章,以免造成看不懂的情况!! MSF和CS绕过UAC提权 CS绕过UAC提权 拿到一个普通管理员的SHELL,在CS中没有*号代表有…...

快速排序+快速定位
快速排序算法采用了分治法以及递归作为解决问题的思想。在计算机科学中,分治法是一种很重要的算法。字面上的解释是“分而治之”,就是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题……直到最后子问题可以…...

nginx http rewrite module 详解
大家好,我是 17。 今天和大家聊聊 nginx http rewrite module 。 简单来说, ngx_http_rewrite_module module 用正则匹配请求,改写请求,然后做跳转。可以是内部跳转,也可以是外部跳转。 学习这个模块的时候…...

机器学习可解释性一(LIME)
随着深度学习的发展,越来越多的模型诞生,并且在训练集和测试集上的表现甚至于高于人类,但是深度学习一直被认为是一个黑盒模型,我们通俗的认为,经过训练后,机器学习到了数据中的特征,进而可以正…...

CV学习笔记-MobileNet
MobileNet 文章目录MobileNet1. MobileNet概述2. 深度可分离卷积(depthwise separable convolution)2.1 深度可分离卷积通俗理解2.2 深度可分离卷积对于参数的优化3. MobileNet网络结构4. 代码实现4.1 卷积块4.2 深度可分离卷积块4.3 MobileNet定义4.4 完…...

C++进阶——继承
C进阶——继承 1.继承的概念及定义 面向对象三大特性:封装、继承、多态。 概念: 继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特 性的基础上进行扩展,增加功能,这…...

数据结构---单链表
专栏:数据结构 个人主页:HaiFan. 专栏简介:从零开始,数据结构!! 单链表前言顺序表的缺陷链表的概念以及结构链表接口实现打印链表中的元素SLTPrintphead->next!NULL和phead!NULL的区别开辟空间SLTNewNod…...

redis数据结构的底层实现
文章目录一.引言二.redis的特点三.Redis的数据结构a.字符串b.hashc.listd.sete.zset(有序集合)一.引言 redis是一个开源的使用C语言编写、支持网络、可基于内存亦可持久化的日志型、key-value的NoSQL数据库。 通常使用redis作为缓存中间件来降低数据库的压力,除此…...

【JavaSE】复习(进阶)
文章目录1.final关键字2.常量3.抽象类3.1概括3.2 抽象方法4. 接口4.1 接口在开发中的作用4.2类型和类型之间的关系4.3抽象类和接口的区别5.包机制和import5.1 包机制5.2 import6.访问控制权限7.Object7.1 toString()7.2 equals()7.3 String类重写了toString和equals8.内部类8.1…...

Java 主流日志工具库
日志系统 java.util.logging (JUL) JDK1.4 开始,通过 java.util.logging 提供日志功能。虽然是官方自带的log lib,JUL的使用确不广泛。 JUL从JDK1.4 才开始加入(2002年),当时各种第三方log lib已经被广泛使用了JUL早期存在性能问题&#x…...

产品经理有必要考个 PMP吗?(含PMP资料)
现在基本上做产品的都有一个PMP证件,从结果导向来说,不对口不会有这么大范围的人来考,但是需要因地制宜,在公司内部里,标准程序并不流畅,产品和项目并不规范,关系错综复杂。 而产品经理的职能又…...

什么是原型、原型链?原型和原型链的作用
1、ES6之前,继承都用构造函数来实现;对象的继承,先申明一个对象,里面添加实例成员<!DOCTYPE html> <html><head><meta charset"utf-8" /><title></title></head><body><script…...

条件期望4
条件期望例题----快排算法的分析 快速排序算法的递归定义如下: 有n个数(n≥2n\geq 2n≥2), 一开始随机选取一个数xix_ixi, 并将xix_ixi和其他n-1个数进行比较, 记SiS_iSi为比xix_ixi小的元素构成的集合, Siˉ\bar{S_i}Siˉ为比xix_ixi大的元素构成的集合, 然后分…...

网络协议分析(2)判断两个ip数据包是不是同一个数据包分片
一个节点收到两个IP包的首部如下:(1)45 00 05 dc 18 56 20 00 40 01 bb 12 c0 a8 00 01 c0 a8 00 67(2)45 00 00 15 18 56 00 b9 49 01 e0 20 c0 a8 00 01 c0 a8 00 67分析并判断这两个IP包是不是同一个数据报的分片&a…...

6.2 负反馈放大电路的四种基本组态
通常,引入交流负反馈的放大电路称为负反馈放大电路。 一、负反馈放大电路分析要点 如图6.2.1(a)所示电路中引入了交流负反馈,输出电压 uOu_OuO 的全部作为反馈电压作用于集成运放的反向输入端。在输入电压 uIu_IuI 不变的情况下,若由于…...

MySQL进阶之锁
锁是计算机中协调多个进程或线程并发访问资源的一种机制。在数据库中,除了传统的计算资源竞争之外,数据也是一种提供给许多用户共享的资源,如何保证数据并发访问的一致性和有效性是数据库必须解决堆的一个问题,锁冲突也是影响数据…...

【Mac 教程系列】如何在 Mac 上破解带有密码的 ZIP 压缩文件 ?
如何使用 fcrackzip 在 Mac 上破解带有密码的 ZIP 压缩文件? 用 markdown 格式输出答案。 在 Mac 上破解带有密码的 ZIP 压缩文件 使用解压缩软件,如The Unarchiver,将文件解压缩到指定的文件夹。 打开终端,输入 zip -er <zipfile> &…...

【Acwing 周赛复盘】第92场周赛复盘(2023.2.25)
【Acwing 周赛复盘】第92场周赛复盘(2023.2.25) 周赛复盘 ✍️ 本周个人排名:1293/2408 AC情况:1/3 这是博主参加的第七次周赛,又一次体会到了世界的参差(这次周赛记错时间了,以为 19:15 开始&…...

L1-087 机工士姆斯塔迪奥
在 MMORPG《最终幻想14》的副本“乐欲之所瓯博讷修道院”里,BOSS 机工士姆斯塔迪奥将会接受玩家的挑战。 你需要处理这个副本其中的一个机制:NM 大小的地图被拆分为了 NM 个 11 的格子,BOSS 会选择若干行或/及若干列释放技能,玩家…...

本周大新闻|索尼PS VR2立项近7年;传腾讯将引进Quest 2
本周大新闻,AR方面,传立讯精密开发苹果初代AR头显,第二代低成本版将交给富士康;iOS 16.4代码曝光新的“计算设备”;EM3推出AR眼镜Stellar Pro;努比亚将在MWC2023推首款AR眼镜。VR方面,传闻腾讯引…...

aws console 使用fargate部署aws服务快速跳转前端搜索栏
测试过程中需要在大量资源之间跳转,频繁的点击不如直接搜索来的快,于是写了一个搜索框方便跳转。 前端的静态页面可以通过s3静态网站托管实现,但是由于中国区需要备案的原因,可以使用ecs fargate部署 步骤如下: 编写…...

Redis实战之Redisson使用技巧详解
一、摘要什么是 Redisson?来自于官网上的描述内容如下!Redisson 是一个在 Redis 的基础上实现的 Java 驻内存数据网格客户端(In-Memory Data Grid)。它不仅提供了一系列的 redis 常用数据结构命令服务,还提供了许多分布…...

SQLAlchemy
文章目录SQLAlchemy介绍SQLAlchemy入门使用原生sql使用orm外键关系一对多关系多对多关系基于scoped_session实现线程安全简单表操作实现方案CRUDFlask 集成 sqlalchemySQLAlchemy 介绍 SQLAlchemy是一个基于Python实现的ORM框架。该框架建立在 DB API之上,使用关系…...