小红书分享踩坑和解决
小红书官方介入链接:
下载sdk文件,位置如下图所示
之后可以按照官方文档进行开发,接入也较简单,这里主要是说明一些隐藏的坑点
一、分享应用内的文件到小红书(这里主要是指应用包名下的文件内容),需要注意setFileProviderAuthority()这个方法。
例如我的代码如下:
AndroidManifest文件 <provider android:name="androidx.core.content.FileProvider" android:authorities="${applicationId}.FileProvider" android:exported="false" android:grantUriPermissions="true" > <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/provider_paths" /> </provider>
res目录下的xml配置文件 <?xml version="1.0" encoding="utf-8"?> <paths> <cache-path name="cache" path="." /> <!--Context.getCacheDir() --> <files-path name="files" path="." /> <!--Context.getFilesDir() --> <external-path name="external" path="." /> <!-- Environment.getExternalStorageDirectory()--> <external-cache-path name="external-cache" path="." /> <!-- Context.getExternalCacheDir() --> <external-files-path name="external-files" path="." /> <!-- Context.getExternalFilesDir() --> <external-files-path name="opensdk_external" path="Images" /> <root-path name="opensdk_root" path="" /> </paths>
像我的项目配置的话,需要设置的代码如下
XhsShareSdk.registerApp(context, XHS_APP_KEY, XhsShareGlobalConfig().setEnableLog(true).setClearCacheWhenShareComplete(true) //重点是下面的这句话,设置为自己应用的 Authority .setFileProviderAuthority("${context.packageName}.FileProvider") , object : XhsShareRegisterCallback { override fun onSuccess() { log { "xhs---onSuccess: 注册成功!" } } override fun onError( errorCode: Int, errorMessage: String, @Nullable exception: Exception? ) { log { "xhs---onError: 注册失败!errorCode: $errorCode errorMessage: $errorMessage exception: $exception" } } })
二、小红书构造方法的坑:
XhsNote().apply { title = getTitleString() // 正文,String content = getContentString() // 标题,String imageInfo = XhsImageInfo(listOf( XhsImageResourceBean.fromUrl("网络图片 url"), XhsImageResourceBean.fromUrl("网络图片 url"))) }
小红书的示例代码和说明,都说的很简单,可以直接使用fromUrl这个方法进行构造,他会自动识别是网络图片还是本地图片。不需要手动处理了。
但是,之后,你就会发现,分享网络资源没有问题,但是如果分享的内容是自己应用内部的文件,就无论如何,都分享不成功,到了小红书APP,就提示未获取到图片或者视频。
请看SDK代码
小红书SDK里面判断了是否是网络地址,然后通过File的构造方法,调用了顶部的Uri.fromFile(filePath),这个方法是存在问题的。
安卓7.0强制启用了striceMode策略,无法直接暴露file://类型的URI了。如果使用的公共目录分享文件,还是可以成功的,但是如果分享的是应用内部的文件,就会出现没有访问权限的问题。所以小红书APP,就会一直报为获取资源的问题。
解决办法:
使用XhsImageResourceBean(Uri)方式去构造视频和图片的对象。示例代码如下:
fun shareXHS( activity: Activity = requireNotNull(SnsHelper.mainActivity), filePath: String//传递过来文件地址 ) { val xhsPackageNames = arrayOf("com.xingin.xhs") //获取赋予权限的URI val uri = getContentUriForFileProvider( filePath = filePath, packages = xhsPackageNames ) log { "xhs--- FilePath=$filePath \n,uri:$uri, " } val title="标题内容" val content="内容文字" try { //获取视频的首帧作为封面图 val bitmap= getThumbnailFromVideo(filePath) val tempFile = File("${activity.cacheDir.absolutePath}/cameraShooting", "tempFileForShare.png") val stream = FileOutputStream(tempFile) bitmap?.compress(Bitmap.CompressFormat.PNG, 100, stream) stream.close() //获取首帧的图片URI val picUri = getContentUriForFileProvider( filePath = tempFile.absolutePath, packages = xhsPackageNames ) val xhsNote= XhsNote().apply { this.title = title this.content = content videoInfo = XhsVideoInfo( //通过URI的方式,构建数据 XhsVideoResourceBean(uri), XhsImageResourceBean(picUri) ) // 封面 } //分享数据 val sessionId = XhsShareSdk.shareNote(activity, xhsNote) }catch (e:Exception){ } } fun getContentUriForFileProvider( filePath: String, packages: Array<String> = emptyArray(), context: Context = CoreApp.getContext(), ): Uri { //根据文件路径,生成关联的 content:// 内容 URI val file = File(filePath) val contentUri = FileProvider.getUriForFile( context, "${context.packageName}.FileProvider", file ) //赋予权限 packages.forEach { context.grantUriPermission( it, contentUri, Intent.FLAG_GRANT_READ_URI_PERMISSION ) } return contentUri } fun getThumbnailFromVideo(path: String, percent: Int = 0): Bitmap? { val retriever = MediaMetadataRetriever() var bitmap: Bitmap? = null try { retriever.setDataSource(path) val duration = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION) ?.toLongOrNull() ?: 0 val timePositionUs = (duration / 100f * percent).toLong() * 1000 bitmap = retriever.getFrameAtTime( timePositionUs, MediaMetadataRetriever.OPTION_CLOSEST ) } catch (e: Exception) { log(type = LogType.E, errorThrowable = e) e.printStackTrace() } finally { retriever.release() } return bitmap }