Android 14适配

最近刚刚做了Android 14的适配(即targetSdkVersion 升级到 34 ),通过此博客整理下相关注意点。

前台服务类型

        当targetSdkVersion >= 34 ,应用内的前台服务(Foreground Service)需要指定至少一种前台服务类型。

        在Android系统中,前台服务(Foreground Service)是一种特殊的服务,它可以在后台执行操作,同时向用户显示一个通知,这表示该服务正在执行任务。像音乐播放器、健身程序、定位程序,通过前台服务显示状态栏通知,让用户知道APP执行任务并且正在消耗系统资源。。Android10 引入了前台服务类型。(android:foregroundServiceType 属性)。

        Android 8 (targetSdkVersion 28) 以后要用startForegroundService来启动"前台服务"。

 private fun startLocationService() {val intent = Intent(this, LocationService::class.java)if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O) {startForegroundService(intent)} else {startService(intent)}}

        Android 9及以后前台服务要在manifest中配置以下权限

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

        前台应用启动服务后,要在service里面通过startForeground方法向通知栏发送一个通知:

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {val notification = NotificationCompat.Builder(this, "channel_id").setContentTitle("前台服务").setContentText("前台服务正在运行中......").setSmallIcon(R.drawable.ic_launcher_background).build()startForeground(1, notification)return super.onStartCommand(intent, flags, startId)}
 <serviceandroid:name=".service.LocationService"android:foregroundServiceType="camera"android:enabled="true"android:exported="true"></service>

        

        常用的前台服务类型有:

camera
connectedDevice
dataSync
health
location
mediaPlayback
mediaProjection
microphone
phoneCall
remoteMessaging
shortService
specialUse
systemExempted

注意:如果您调用 startForeground() 但未声明适当的前台服务类型权限,系统会抛出 SecurityException。

安全相关

限制隐式Intent和PendingIntent

        当targetSdk为34时,通过隐式Intent或隐式Intent创建的PendingIntent只能打开设置了android:exported="true"的组件,如果android:exported属性值为false,系统会抛出异常。(即应用必须使用明确的intent来打开(android:exported="false")的组件)通过上述限制可防止恶意应用拦截只供给用内部组件使用的隐式 intent。

       如果应用创建一个PendingIntent ,但 intent 未指定组件或包,系统现在会抛出异常。

        <activityandroid:name=".NormalIntentMainActivity"android:exported="false"><intent-filter><action android:name="com.transsion.targetsdk34android.APP_ACTION"/><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity>

错误写法:(注意上面Activity的exported属性是false)

   startActivity(Intent("com.transsion.targetsdk34android.APP_ACTION"))

 报错信息如下:

E  FATAL EXCEPTION: mainProcess: com.transsion.targetsdk34android, PID: 6361android.content.ActivityNotFoundException: No Activity found to handle Intent { act=com.transsion.targetsdk34android.APP_ACTION }

正确写法:(注意上面Activity的exported属性是false)

val intent = Intent(MainActivity2@ this, NormalIntentMainActivity::class.java)
startActivity(intent)

运行时动态注册广播接收器必须指定导出行为

在 Android 14 上,运行时通过 Context的registerReceiver() 动态注册广播接收器,需要设置标记  RECEIVER_EXPORTED 或 RECEIVER_NOT_EXPORTED ,标识是否导出该广播,避免应用程序出现安全漏洞,如果注册的是系统广播,则不需要指定标记。(如果应用仅通过 Context的registerReceiver 方法为系统广播注册接收器时,那么它可以不在注册接收器时指定标志。)

选择广播接收器是否应导出并对设备上的其他应用可见。如果此接收器正在监听从系统或其他应用(甚至是您拥有的其他应用)发送的广播,请使用 RECEIVER_EXPORTED 标志。如果此接收器仅监听应用发送的广播,请使用 RECEIVER_NOT_EXPORTED 标志。

一些系统广播来自具有较高特权 这类应用(例如蓝牙和电话应用), 但不在系统的唯一进程 ID (UID) 下运行。接收者 接收所有系统广播,包括来自具有较高特权的广播 应用,请使用 RECEIVER_EXPORTED 标记您的接收器。

如果您使用 RECEIVER_NOT_EXPORTED 标记接收器, 接收器可以接收来自您的服务器 应用,但不从具有较高特权的应用进行广播。

如果您的应用要监听多个广播,但只应监听部分广播 已将RECEIVER_NOT_EXPORTED和部分标记为 RECEIVER_EXPORTED,将广播划分到不同的 广播接收器。

val listenToBroadcastsFromOtherApps = false
val receiverFlags = if (listenToBroadcastsFromOtherApps) {ContextCompat.RECEIVER_EXPORTED
} else {ContextCompat.RECEIVER_NOT_EXPORTED
}

注意 :如果导出广播接收器,其他应用可能会向您的应用发送不受保护的广播。

调用 registerReceiver() 注册接收器:

ContextCompat.registerReceiver(context, br, filter, receiverFlags)

安全的动态代码加载

如果应用以 Android 14 为目标平台并使用动态代码加载 (DCL),则所有动态加载的文件都必须标记为只读,否则,系统会抛出异常。

我们建议应用尽可能避免动态加载代码,因为这样做会大大增加应用因代码注入或代码篡改而受到危害的风险。

如果必须动态加载代码,请使用以下方法将动态加载的文件(例如 DEX、JAR 或 APK 文件)在文件打开后和写入任何内容之前立即设置为只读:

val jar = File("DYNAMICALLY_LOADED_FILE.jar")
val os = FileOutputStream(jar)
os.use {// Set the file to read-only first to prevent race conditionsjar.setReadOnly()// Then write the actual file content
}
val cl = PathClassLoader(jar, parentClassLoader)

处理已存在的动态加载文件


为防止系统对现有动态加载的文件抛出异常,我们建议您先删除并重新创建文件,然后再尝试在应用中重新动态加载这些文件。重新创建文件时,请按照上述指南在写入时将文件标记为只读。或者,您可以将现有文件重新标记为只读,但在这种情况下,我们强烈建议您先验证文件的完整性(例如,对照可信值检查文件的签名)以保护应用免遭恶意操作的影响。

针对从后台启动 activity 的其他限制

对于以 Android 14(API 级别 34)或更高版本为目标平台的应用,系统会进一步限制允许应用何时从后台启动 activity:

  • 当应用使用 PendingIntent#send() 或类似方法发送 PendingIntent 时,如果它想要授予自己的后台 activity 启动待处理 intent 的启动特权,则必须选择启用。如需选择启用,应用应通过 setPendingIntentBackgroundActivityStartMode(MODE_BACKGROUND_ACTIVITY_START_ALLOWED) 传递 ActivityOptions 软件包。(使用PendingIntent从后台打开Activity时,必须设置setPendingIntentBackgroundActivityStartMode(MODE_BACKGROUND_ACTIVITY_START_ALLOWED)。)
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)private fun startPendingActivity() {val options = ActivityOptions.makeBasic().apply {pendingIntentBackgroundActivityStartMode =ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED}PendingIntent.getActivity(this,0,Intent(this, NormalIntentMainActivity::class.java),PendingIntent.FLAG_IMMUTABLE,options.toBundle())}

  • 当一个可见应用使用 bindService() 绑定另一个在后台运行的应用的服务时,如果该可见应用想要将其自己的后台 activity 启动权限授予绑定服务,则它现在必须选择加入 BIND_ALLOW_ACTIVITY_STARTS 标志。(即后台服务想要启动前台Activity)
private fun bindService() {bindService(intent, serviceConnection, Context.BIND_ALLOW_ACTIVITY_STARTS)}

这些更改扩展了现有的一组限制 ,通过防止恶意应用滥用 API 从后台启动破坏性活动来保护用户。

压缩路径遍历

对于以 Android 14(API 级别 34)或更高版本为目标平台的应用,Android 会通过以下方式防止 Zip 路径遍历漏洞:如果 zip 文件条目名称包含“..”或以“/”开头,则 ZipInputStream.getNextEntry() 会抛出 ZipException。ZipFile(String)

应用可以通过调用 dalvik.system.ZipPathValidator.clearCallback() 选择停用此验证。

OpenJDK 17

Android 14 将继续更新 Android 的核心库,以与最新 OpenJDK LTS 版本中的功能保持一致,包括适合应用和平台开发者的库更新和 Java 17 语言支持。

以下变更可能会影响应用兼容性:

  • 对正则表达式的更改:现在,为了更严格地遵循 OpenJDK 的语义,不允许无效的组引用。您可能会看到 java.util.regex.Matcher 类抛出 IllegalArgumentException 的新情况,因此请务必测试应用中使用正则表达式的情形。如需在测试期间启用或停用此变更,请使用兼容性框架工具切换 DISALLOW_INVALID_GROUP_REFERENCE 标志。
  • UUID 处理:现在,验证输入参数时,java.util.UUID.fromString() 方法会执行更严格的检查,因此您可能会在反序列化期间看到 IllegalArgumentException。如需在测试期间启用或停用此变更,请使用兼容性框架工具切换 ENABLE_STRICT_VALIDATION 标志。
  • ProGuard 问题:有时,在您尝试使用 ProGuard 缩减、混淆和优化应用时,添加 java.lang.ClassValue 类会导致问题。问题源自 Kotlin 库,该库会根据 Class.forName("java.lang.ClassValue") 是否会返回类更改运行时行为。如果您的应用是根据没有 java.lang.ClassValue 类的旧版运行时开发的,则这些优化可能会将 computeValue 方法从派生自 java.lang.ClassValue 的类中移除。

应用只能杀死自己的后台进程

从 Anroid 14 开始,调用 ActivityManager的killBackgroundProcesses() 方法时,只能终止自己的应用程序,如果传入其他应用程序的包名,不会对其他应用程序产生影响。

public void killBackgroundProcesses (String packageName)

在运行Android 14或更高版本的设备上,第三方应用只能使用此API来终止自己的进程。

最低可安装目标 API 级别

从 Android 14 开始,targetSdkVersion 低于 23 的应用无法安装。要求应用满足这些最低目标 API 级别要求有助于提高用户的安全性和隐私性。

恶意软件通常会以较旧的 API 级别为目标平台,以绕过在较新版本 Android 中引入的安全和隐私保护机制。例如,有些恶意软件应用使用 targetSdkVersion 22,以避免受到 Android 6.0 Marshmallow(API 级别 23)在 2015 年引入的运行时权限模型的约束。这项 Android 14 变更使恶意软件更难以规避安全和隐私权方面的改进限制。尝试安装以较低 API 级别为目标平台的应用将导致安装失败,并且 Logcat 中会显示以下消息:

INSTALL_FAILED_DEPRECATED_SDK_VERSION: App package must target at least SDK version 23, but found 7

授予对照片和视频的部分访问权限  (从荣耀开发者平台搬运)

Android 14 引入了所选照片访问权限,可让用户授予应用对其库中特定图片和视频的访问权限,而不是授予对给定类型的所有媒体内容的访问权限。在 Android 13(targetSDK 33)中引入了图片或视频文件的访问权限时,在 Android 14 设备上与应用交互的用户现在可以授予对其照片或视频文件的部分访问权限,即用户可选择只授予应用访问本地媒体文件中的部分图片和视频,具体授予哪些文件由用户自己指定 ,其他文件则不允许应用访问,此变更有利于保护用户的隐私数据。

请求权限

首先,根据操作系统版本在 Android 清单中请求正确的存储权限:

<!-- Devices running Android 12L (API level 32) or lower  -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" /><!-- Devices running Android 13 (API level 33) or higher -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" /><!-- To handle the reselection within the app on devices running Android 14or higher if your app targets Android 14 (API level 34) or higher.  -->
<uses-permission android:name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED" />

U 版本(Android 14)在原有图片和视频的权限弹框 允许 和 不允许 的基础上,增加 选择照片和视频 的按钮,用户点击这个按钮后可以授予 App 访问部分图片和视频的权限

新的授权对话框会展示如下的权限选项:

  • 选择照片和视频:Android 14中的新特性,用户选择他们想要提供给应用的特定的照片和视频。
  • 全部允许:用户授予对设备上所有照片和视频的完整访问权限。
  • 不允许:用户拒绝所有访问。

如果用户选择了 选择照片和视频 选项,并且应用程序稍后再次请求 READ_MEDIA_IMAGES 或 READ_MEDIA_VIDEO 权限,系统将显示一个不同的对话框,含有 选择更多 的选项,让用户有机会访问其他照片和视频。

为了帮助应用程序支持新的更改,系统引入了新的权限:READ_MEDIA_VISUAL_USER_SELECTED。根据应用程序是否使用新权限,系统的行为会有所不同。

注意:

如果应用已使用了系统 照片选择器,则不需要再做适配修改。否则,可以考虑使用 照片选择器 。

兼容性影响

该特性只会对 targetSdk>=33 ,且声明使用 READ_MEDIA_VIDEO 和 READ_MEDIA_IMAGES 读取图片和视频文件的应用产生影响。

  1. targetSdk>=33 并且在 Manifest 中声明使用 U 版本定义的 READ_MEDIA_VISUAL_USER_SELECTED 权限的应用表示App已主动适配该特性,此时应用应该已经按照 “适配指导” 部分的操作完成了特性的适配。

<uses-permission android:name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED" />

如果应用声明 READ_MEDIA_VISUAL_USER_SELECTED 权限,并且用户在系统权限对话框中选择 选择照片和视频 ,则会发生以下行为:

  • READ_MEDIA_IMAGES 和 READ_MEDIA_VIDEO 两个权限都会被系统拒绝。

  • 应用会被授予 READ_MEDIA_VISUAL_USER_SELECTED 权限,向应用提供部分的、临时的访问用户照片和视频的权限。

  • 如果应用需要访问其他照片或者视频(用户授权外的),必须再一次手动请求 READ_MEDIA_IMAGES 或者 READ_MEDIA_VIDEO 权限(或者两个都申请)。

注意:
READ_MEDIA_IMAGES 和 READ_MEDIA_VIDEO 是访问用户的照片和视频照片库所需的唯一其他权限。声明 READ_MEDIA_VISUAL_USER_SELECTED 是让权限控制器知道您的应用程序支持手动重新请求选择更多照片和视频。

  1. targetSdk>=33 未适配授予对照片和视频的部分访问权限的 App,U 版本会使用兼容性方案让用户也能在系统提供的 UX 中选择部分照片和视频。这时候应用的功能可能受到影响,如用户授权 App 读取部分照片和视频的权限后, 在 APP 中发送照片时可能会因为在 App 的照片选择器找不到需要的照片而产生疑惑(因为此时用户只给 App 授予了部分照片和视频的访问权限)。

    如果应用不声明 READ_MEDIA_VISUAL_USER_SELECTED 权限,并且用户在系统权限对话框中选择 选择照片和视频 ,则会发生以下行为:

    • 在应用程序会话期间授予 READ_MEDIA_IMAGES 和 READ_MEDIA_VIDEO 权限,提供临时授权,应用可以临时访问用户选择的照片和视频。当应用移动到后台,或者用户主动关闭应用时,系统最终会拒绝这些权限,此行为与其他一次性权限一样。

    • 如果应用程序稍后需要访问其他照片和视频,则必须再次手动请求 READ_MEDIA_IMAGES 或者 READ_MEDIA_VIDEO 权限,流程与最初的权限申请流程一样。

U 版本新增应用授予对照片和视频的部分访问权限的逻辑梳理如下:

适配指导

照片和视频的部分访问权限既可以用作读取图片权限(READ_MEDIA_IMAGES),也可以用作读取视频权限(READ_MEDIA_VIDEO)。下面以读取图片(READ_MEDIA_IMAGES)为示例进行说明。

注意:

以下内容只针对 targetSdk>=33 并且在 Manifest 中声明使用U版本定义的 READ_MEDIA_VISUAL_USER_SELECTED 权限,主动适配该特性的操作步骤。

应用第一次读取图片

用户交互旅程的关键适配代码:

  1. 首次读取图片前申请 READ_MEDIA_IMAGES 权限。

final int PERMISSION_REQUEST_CODE = 100;
......
context.requestPermissions(new String[]{"android.permission.READ_MEDIA_IMAGES"}, PERMISSION_REQUEST_CODE);

      2.在 App 的图片选择器中判断当前获取的是所有文件权限还是读取部分图片和视频的权限。

if (context.checkSelfPermission("android.permission.READ_MEDIA_VISUAL_USER_SELECTED") == PackageManager.PERMISSION_GRANTED){// 表示用户授予了App读取部分图片和视频的权限// App从系统MediaStore或者公共存储中读取到的图片和视频是不全的,App只能看到用户在系统UX中授予的部分图片和视频......// App需要适配新流程......
} else if  (context.checkSelfPermission("android.permission.READ_MEDIA_IMAGES") == PackageManager.PERMISSION_GRANTED) {// 表示用户授予了App读取所有图片和视频的权限// App从系统MediaStore或者公共存储中读取到的所有图片和视频文件// 这时候应用的处理逻辑和基础版本保持不变......
} else {// 表示用户拒绝了App读取所有图片和视频的权限// 这时候应用的处理逻辑和基础版本保持不变......
}

3.App 在自己的图片选择器中列出应用能够读取到的所有图片,供用户选择。

应用第二次读取图片

用户交互旅程的关键适配代码:

  1. 判断当前用户是否只授予了 App 读取部分图片和视频的权限。

if (context.checkSelfPermission("android.permission.READ_MEDIA_VISUAL_USER_SELECTED") == PackageManager.PERMISSION_GRANTED
&& context.checkSelfPermission("android.permission.READ_MEDIA_IMAGES") != PackageManager.PERMISSION_GRANTED ) {// 表示用户授予了App读取部分图片和视频的权限// App从系统MediaStore或者公共存储中读取到的图片和视频是不全的,App只能看到用户在系统UX中授予的部分图片和视频// App需要适配新流程......
}

        2.在 App 的图片选择器中列出所有能够选择的图片(用户提供 App 的部分图片),并提供 添加可访问的照片 的能力,方便用户在需要的时候申请 App 读取更多图片的权限。

        3.在 App 的 UX 上,用户选择 添加可访问的照片 时,请求 READ_MEDIA_IMAGES 权限,通过系统 UX 添加更多的提供 App 访问的图片。

final int PERMISSION_REQUEST_CODE = 100;
......
context.requestPermissions(new String[]{"android.permission.READ_MEDIA_IMAGES"}, PERMISSION_REQUEST_CODE);

        4.App 在自己的图片选择器中列出 App 能够读取到的最新的所有图片,供用户选择。

应用第2+N次读取图片

在 U 版本上,如果用户在第一次交互和第二次交互中都只提供 App 读取部分图片权限,系统认为用户这个决定以后不再更改。此时 App 申请 READ_MEDIA_IMAGES 权限时的弹框就可以省略,以减少用户交互的层次。

  1. 判断当前用户只授予了 App 读取部分图片和视频的权限。

if (context.checkSelfPermission("android.permission.READ_MEDIA_VISUAL_USER_SELECTED") == PackageManager.PERMISSION_GRANTED     && context.checkSelfPermission("android.permission.READ_MEDIA_IMAGES") != PackageManager.PERMISSION_GRANTED ) {// 表示用户授予了App读取部分图片和视频的权限// App从系统MediaStore或者公共存储中读取到的图片和视频是不全的,App只能看到用户在系统UX中授予的部分图片和视频// App需要适配新流程......
}

        2.在 App 的图片选择器中列出所有能够选择的图片(用户提供 App 的部分图片),并提供 添加可访问的照片 的能力,方便用户在需要的时候申请 App 读取更多图片的权限。

        3.在 App 的 UX 上,用户选择 添加可访问的照片 时,请求 READ_MEDIA_IMAGES 权限,通过系统 UX 添加更多的提供 App 访问的图片。

final int PERMISSION_REQUEST_CODE = 100;
......
context.requestPermissions(new String[]{"android.permission.READ_MEDIA_IMAGES"}, PERMISSION_REQUEST_CODE);

        4.App 在自己的图片选择器中列出 App 能够读取到的最新的所有图片,供用户选择。

最佳实践

在官方文档中,Google 推荐了使用 READ_MEDIA_VISUAL_USER_SELECTED 权限的几个最佳实践。

  • 后台媒体处理实质上需要获得新权限

    如果应用进行媒体处理(例如在后台压缩或上传媒体),请注意,READ_MEDIA_IMAGES 和 READ_MEDIA_VIDEO 权限最终会再次被拒绝。我们强烈建议您添加对 READ_MEDIA_VISUAL_USER_SELECTED 的支持。或者您的应用应打开 InputStream 或使用 ContentResolver 进行查询,检查自己是否有权访问特定照片或视频。

  • 请勿永久存储权限状态

    请勿永久存储权限状态(包括 SharedPreferences 或 DataStore )。存储的状态可能与实际状态不同步。权限状态可能会在以下情况下发生更改:权限重新设置、应用程序休眠、用户在应用设置中发起更改,或者应用进入后台。请应用使用 ContextCompat.checkSelfPermission() 检查存储权限。

  • 不要假装拥有对照片和视频的完整访问权限

    根据 Android 14 中引入的更改,您的应用可能只对设备的照片库进行部分访问权限。如果应用在使用 ContentResolver 进行查询时存储了 MediaStore 数据,则存储可能不是最新的。

    • 应始终使用 ContentResolver 查询 MediaStore,而不是依赖于存储的缓存。
    • 当应用在前台运行时,将结果保存在内存中。
  • 将 URI 访问权限视为临时访问权限

    如果用户在系统权限对话框中选择 选择照片和视频 ,您的应用对所选择照片和视频的访问权限最终将过期。无论产生何种授权,应用应始终处理无法访问任何 URI 的情况。

参考:https://developer.android.com/develop/background-work/services/foreground-services?hl=zh-cn

照片和视频的部分访问权限  从荣耀开发者服务平台搬运,我感觉他们写的比较完整以及详细。

适配/设计规范-Google U版本(Android 14)应用兼容性适配指导 | 荣耀开发者服务平台 (honor.com)

官网:

授予对照片和视频的部分访问权限  |  Android Developers

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/406804.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

k8s - Secret实践练习

参考文档&#xff1a;https://kubernetes.io/zh-cn/docs/concepts/configuration/secret/ 这个和ConfigMap很相似&#xff0c;这里选两个做下测试&#xff0c;就不过多赘述了 简介 Secret 类似于 ConfigMap 但专门用于保存机密数据。 Secret 是一种包含少量敏感信息例如密码…

qt creator自动运行单元测试

qt creator自动运行单元测试 工具-选项-Testing-General&#xff0c;找到Automatically run&#xff0c;选项卡选择All。

[C语言]-基础知识点梳理-编译、链接、预处理

前言 各位师傅大家好&#xff0c;我是qmx_07,今天来给大家讲解以下程序运行会经历哪些事情 翻译环境和运⾏环境 在ANSIC的任何⼀种实现中&#xff0c;存在两个不同的环境 第1种是翻译环境&#xff0c;在这个环境中源代码被转换为可执⾏的机器指令&#xff08;⼆进制指令&a…

Linux下opencv报错 undefined reference to cv::imread cv::Mat

如果你是和libtorch一起使用&#xff0c;那么请你继续&#xff0c;否则该篇文章不适合你。 正文 在https://pytorch.org/下 下载的时候要选择Cxx11 ABI版 随后正常配置就可以了

Langchain-Chatchat

模型能力定制 微调 智能设备 微调 响应有要求 微调 动态数据 RAG 幻觉 RAG 可解释性 RAG 成本 RAG 依赖生成能力 微调 微调需要几万、几十万条好的数据&#xff0c;否者白调&#xff0c;所以是否需要微调&#xff0c;需要视情况而定。 RAG的落地&#xff0c;可以使用 https:/…

wangeditor编辑器自定义按钮和节点,上传word转换html,文本替换

vue3ts 需求&#xff1a;在编辑器插入图片和视频时下方会有一个输入框填写描述&#xff0c;上传word功能 wangeditor文档wangEditor开源 Web 富文本编辑器&#xff0c;开箱即用&#xff0c;配置简单https://www.wangeditor.com/ 安装&#xff1a;npm install wangeditor/edit…

Datawhale X 李宏毅苹果书 AI夏令营-深度学习入门班-task1

机器学习就是去拟合一种函数&#xff0c;它可能在高维上&#xff0c;十分抽象&#xff0c;但是却可以有丰富的语义含义&#xff0c;从而完成一系列任务 回归任务是预测一个准确的值&#xff0c;例如拟合一条直线的时候&#xff0c;我们希望每一个点的值都能对应上 分类任务则…

java多线程(六)关键字Volatile可见性、有序性以及单个变量的原子性

volatile关键字 作用 volatile 是 Java 虚拟机提供的轻量级的同步机制&#xff0c;主要用来确保变量被线程安全地读取和写入。 当一个变量定义为 volatile 后&#xff0c;它具备以下特性&#xff1a; 可见性&#xff1a;确保不同线程对这个变量操作的可见性&#xff0c;即一…

深入学习SQL优化的第三天

目录 聚合函数 排序和分组 聚合函数 1251. 平均售价 表&#xff1a;Prices------------------------ | Column Name | Type | ------------------------ | product_id | int | | start_date | date | | end_date | date | | price | int …

K8S - Java微服务配置 - 使用ConfigMap配置redis

参考文档&#xff1a;https://v1-27.docs.kubernetes.io/zh-cn/docs/tutorials/configuration/configure-redis-using-configmap/ cat <<EOF >./example-redis-config.yaml apiVersion: v1 kind: ConfigMap metadata:name: example-redis-config data:redis-config: …

“解决Windows电脑无法投影到其他屏幕的问题:尝试更新驱动程序或更换视频卡“

目录 背景: 解决方法1: 解决方法2: 什么是驱动程序&#xff1a; 背景: 今天在日常的工作中&#xff0c; 我想将笔记本分屏到另一个显示屏&#xff0c;我这电脑Windows10系统&#xff0c;当我按下Windows键P键&#xff0c;屏幕信息上提示我"你的电脑不能投影到其他屏幕…

C++:继承(protected、隐藏、不能被继承的类、)

目录 继承的概念 继承的使用 继承方式 protected 继承类模板 赋值兼容转换 隐藏 子类的默认成员函数 构造函数 拷贝构造函数 赋值重载函数 析构函数 不能被继承的类 方法1&#xff1a;父类的构造函数私有 方法2&#xff1a;final 继承与友元 继承与静态成员 …

Redis 键值型数据库

一、Redis是什么 Redis&#xff1a;REmote DIctionary Server&#xff08;远程字典服务器&#xff09; 是完全开源免费的&#xff0c;用C语言编写的&#xff0c;遵守BSD协议&#xff0c;是一个高性能的&#xff08;Key/Value&#xff09;分布式内存数据 库&#xff0c;基于内存…

tcp 和udp通信

一.recvfrom recvfrom函数是一个系统调用&#xff0c;用于从套接字接收数据。该函数通常与无连接的数据报服务&#xff08;如 UDP&#xff09;一起使用&#xff0c;但也可以与其他类型的套接字使用。与简单的 recv() 函数不同&#xff0c;recvfrom() 可以返回数据来源的地址信息…

FreeRTOS学习:内存管理

FreeRTOS内存管理简介 在使用 FreeRTOS 创建任务、队列、信号量等对象的时候&#xff0c; FreeRTOS 一般都提供了两种方法&#xff0c; 动态方法创建&#xff1a;自动地从 FreeRTOS 管理的内存堆中申请所创建对象所需的内存&#xff0c;在对象被删除后&#xff0c;又可以将这…

Vue3开始

1.创建 Vue3 工程 1.基于 vue-cli创建 2.基于vite创建工程 Home | Vite中文网 (vitejs.cn) nodejs 环境可以 使用 nvm 来管理 nodejs 的版本 3.开始创建 4.目录结构 5.创建完 执行 安装依赖 npm i 6.运行项目 为什么命令是 dev 就是从这里配置的 npm run dev

大白话社融中M1和M2的学习笔记

一、背景 为什么我看社融数据呢&#xff1f;因为作为一个码农&#xff0c;我不清楚当前个人所处的样本情况是否真实。我所处的环境是在深圳南山&#xff0c;身边一些同事有不少找工作不怎么顺利&#xff0c;我所在的公司今年也没招大学生了&#xff0c;人员也只出不进为主&…

本地生活服务商系统如何利用本地推获得更多曝光?

随着本地生活赛道中的竞争愈演愈烈&#xff0c;越来越多的本地生活服务商和本地生活商家开始计划着通过在本地推等平台投放相关信息&#xff0c;以提高品牌店铺的曝光量和知名度。不过&#xff0c;就目前的情况来看&#xff0c;绝大多数人都陷入了一种“投入多&#xff0c;转化…

教育部-华为产学合作协同育人项目 | 仓颉编程语言专项

为响应《教育部高等教育司关于调整产学合作协同育人项目运行模式及征集2024年产学合作协同育人项目的通知》号召&#xff0c;华为公司2024年第二批70个项目已发布&#xff0c;其中仓颉编程语言领域共计10个项目&#xff0c;如下所示&#xff0c;通过新工科建设项目&#xff0c;…

U盘安装Ubuntu24.04,乌邦图,UltralISO

文章目录 前言通过UltraISO&#xff0c;制作启动U盘下载镜像制作工具UltraISO(软碟通)下载ubuntu镜像文件制作启动U盘 安装ubuntu设置root密码&#xff0c;并登陆root 前言 在Ubuntu作为主流的linux系统&#xff0c;有时候使用VMware安装使用&#xff0c;总归有一定的性能损耗…