小红书分享踩坑和解决
小红书官方介入链接:
下载sdk文件,位置如下图所示
之后可以按照官方文档进行开发,接入也较简单,这里主要是说明一些隐藏的坑点
一、分享应用内的文件到小红书(这里主要是指应用包名下的文件内容),需要注意setFileProviderAuthority()这个方法。
例如我的代码如下:
AndroidManifest文件<providerandroid:name="androidx.core.content.FileProvider"android:authorities="${applicationId}.FileProvider"android:exported="false"android:grantUriPermissions="true" > <meta-dataandroid:name="android.support.FILE_PROVIDER_PATHS"android:resource="@xml/provider_paths" /> </provider>
res目录下的xml配置文件<?xml version="1.0" encoding="utf-8"?> <paths> <cache-pathname="cache"path="." /> <!--Context.getCacheDir()--> <files-pathname="files"path="." /> <!--Context.getFilesDir()--> <external-pathname="external"path="." /> <!--Environment.getExternalStorageDirectory()--> <external-cache-pathname="external-cache"path="." /> <!--Context.getExternalCacheDir()--> <external-files-pathname="external-files"path="." /> <!--Context.getExternalFilesDir()--> <external-files-pathname="opensdk_external"path="Images" /> <root-pathname="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 =titlethis.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
)
}returncontentUri
}
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() ?: 0val timePositionUs= (duration / 100f * percent).toLong() * 1000bitmap=retriever.getFrameAtTime(
timePositionUs, MediaMetadataRetriever.OPTION_CLOSEST
)
}catch(e: Exception) {
log(type= LogType.E, errorThrowable =e)
e.printStackTrace()
}finally{
retriever.release()
}returnbitmap
}