ETJava Beta | Java    注册   登录
  • 搜索:
  • 小红书分享踩坑和解决

    发表于      阅读(1)     博客类别:Crawler     转自:https://www.cnblogs.com/zxxiaoxia/p/18373731
    如有侵权 请联系我们删除  (页面底部联系我们)  
    小红书官方介入链接:小红书分享开放平台

    下载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
            }