小红书分享踩坑和解决
小红书官方介入链接:
下载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
}