安卓原生共享到微信、朋友圈,适配Android12

前语

最近作业比较忙,还被人甩了,又逢五一放假,良久没写博客了,okhttp3系列的内容还没写完呢,仍是得打起劲来。这篇博客是最近解决的一个问题,挺有意思,记录下吧!

需求

最近有个APP要上架Google,可是国内的一些SDK无法运用,去除了之后一直在忙适配问题,这儿便是微信SDK不能运用了,导致里边的微信共享都要找替代办法,我这是用的原生自带的。

ps. 202305更新,微博和QQ的SDK也不能上架Google,所以也只能运用体系自带的了,代码已更新。

下面先说下代码,再来讲碰到的各种问题,以及解决办法。

代码

其实代码很简略,便是调用隐式的intent去拜访其他app的页面,传递数据曩昔,我也是拿网上他人的改了改,下面详细说。

共享到微信

共享到微信老友,我这只是传递文字曩昔了,和调用体系共享没什么差异,只是加了包名和类名的一个限定,至于判别运用是否装置,后面会再讲下其中的问题。

    /**
     * 共享到微信老友
     */
    public fun shareTextToWeChat(context: Context, text: String) {
        //判别是否装置微信,如果没有装置微信 又没有判别就直达微信共享是会挂掉的
        if (!isAppInstall(context, "com.tencent.mm")) {
            ToastUtils.show(context, "您还没有装置微信")
            return
        }
        shareText(context, text, "com.tencent.mm", "com.tencent.mm.ui.tools.ShareImgUI")
    }
    fun shareText(context: Context, text: String, pkg: String, cls: String) {
        if (TextUtils.isEmpty(text)) {
            ToastUtils.show(context, "内容不能为空")
            return
        }
        try {
            val intent = Intent()
            intent.component = ComponentName(pkg, cls)
            intent.action = Intent.ACTION_SEND
            intent.putExtra(Intent.EXTRA_TEXT, text)
            intent.type = "text/plain"
            context.startActivity(Intent.createChooser(intent, "共享"))
        }catch (e: Exception) {
            e.printStackTrace()
            ToastUtils.show(context, "共享失利")
        }
    }
    fun isAppInstall(context: Context, appPackageName: String): Boolean {
        val packageManager = context.packageManager // 获取packagemanager
        val info = packageManager.getInstalledPackages(0) // 获取一切已装置程序的包信息
        for (i in info.indices) {
            val pn = info[i].packageName
            if (appPackageName == pn) {
                return true
            }
        }
        return false
    }

共享到微信朋友圈

共享到朋友圈复杂一点,这儿需求带一张图曩昔,否则就提示获取资源失利,再一个便是文字需求放在“Kdescription”里边。这儿的图片需求一个Uri,这个有点费事,下面再讲。

    /**
     * 共享到微信朋友圈,需求带一张图
     */
    public fun shareTextToWeChatFriend(context: Context, message: WechatUtil.Message) {
        if (!isAppInstall(context, "com.tencent.mm")) {
            ToastUtils.show(context, "您还没有装置微信")
            return
        }
        try {
            val intent = Intent()
            intent.component = ComponentName("com.tencent.mm", "com.tencent.mm.ui.tools.ShareToTimeLineUI")
            intent.action = Intent.ACTION_SEND
            intent.type = "image/*";
            val shareStr = """
                ${message.title}
                ${message.description}
                ${message.shareUrl}
                """.trimIndent()
            intent.putExtra(Intent.EXTRA_TEXT, shareStr)
            intent.putExtra("Kdescription", shareStr);
            // 给方针运用一个暂时授权,如同用不到
            // intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
            intent.putExtra(Intent.EXTRA_STREAM, message.thumbnail);
            context.startActivity(Intent.createChooser(intent, "共享"))
        }catch (e: Exception) {
            e.printStackTrace()
            ToastUtils.show(context, "共享失利")
        }
    }
    data class Message(
        var title: String = "", 
        var description: String = "", 
        var shareUrl: String = "", 
        var scene: String = "", 
        var thumbnail: Uri? = null)

共享到微博

这儿说几句,网上搜到的文章里边都是”com.sina.weibo.EditActivity”这个包名,经过检测,我发现是错的,最后仍是靠chatGPT才找到对的共享页面包名。

fun shareToWeibo(context: Context, message: ShareUtil.Message) {
    if (!isAppInstall(context, "com.sina.weibo")) {
        ToastUtils.show(context, "weibo is not install")
        return
    }
    try {
        val intent = Intent()
        intent.component = ComponentName("com.sina.weibo", "com.sina.weibo.composerinde.ComposerDispatchActivity")
        intent.action = Intent.ACTION_SEND
        intent.type = "image/*";
        val shareStr = """
            ${message.title}
            ${message.description}
            ${message.shareUrl}
            """.trimIndent()
        intent.putExtra(Intent.EXTRA_TEXT, shareStr)
        intent.putExtra(Intent.EXTRA_STREAM, message.thumbnail);
        context.startActivity(Intent.createChooser(intent, "共享"))
    }catch (e: Exception) {
        e.printStackTrace()
        ToastUtils.show(context, "共享失利")
    }
}
fun shareToWeibo(context: Context, text: String) {
    if (!isAppInstall(context, "com.sina.weibo")) {
        ToastUtils.show(context, "weibo is not install")
        return
    }
    shareText(context, text, "com.sina.weibo", "com.sina.weibo.composerinde.ComposerDispatchActivity")
}

共享到QQ

这儿有个问题,QQ里边的QQ空间如同不能运用,略微有点坑。共享QQ的代码我试了能够,下面哪个QQ空间的我没装置,不知道行不行。

fun shareToQQ(context: Context, text: String) {
    if (!isAppInstall(context, "com.tencent.mobileqq")) {
        ToastUtils.show(context, "您还没有装置QQ")
        return
    }
    shareText(context, text, "com.tencent.mobileqq", "com.tencent.mobileqq.activity.JumpActivity")
}
fun shareToQzone(context: Context, text: String) {
    if (!isAppInstall(context, "com.qzone")) {
        ToastUtils.show(context, "您还没有装置QQ空间")
        return
    }
    shareText(context, text, "com.qzone", "com.qzonex.module.operation.ui.QZonePublishMoodActivity")
}

问题

无法检测是否装置运用

由于要上架Google,我这把targetSdkVersion调到了31(android12),结果一开始便是微信未装置、微博也未装置,有点坑。下面看解决办法:

  1. 首先在manifest中添加queries标签

    <manifest>
       <application>...</application>
       <queries>
            <package android:name="com.tencent.mm" />
            <package android:name="com.tencent.mobileqq" />
            <package android:name="com.sina.weibo" />
            <package android:name="com.qzone" />
        </queries>
    </manifest>
    

    说到这个,我找了良久都没看到queries应该放在哪个方位,去问chatGPT,竟然不苟言笑告诉我放在application里边,太坑了。

  2. 提高gradle及插件版别

    运用了queries后,或许Android studio还不能辨认,找了半天后发现是gradle插件要4.0.1才干支撑,同时gradle的版别也要关于晋级到6.1.1.

  3. 处理gradle晋级后的问题

    我这晋级gradle版别后出了挺多问题,什么不能在manifest里边写minSdkVersion,项目里不能有任何androidx字样,最坑的是gradle里边的minSdkVersion竟然不能用变量设置:

    // 错误
    defaultConfig.minSdkVersion config.minSdkVersion
    // 正确
    defaultConfig.minSdkVersion 19
    

    我这儿还有一个旧版别的butterknife由于这个用不了了,好在代码不过直接去掉了,其他的看读者自己解决吧。

共享到朋友圈失利

上面现已说到过了共享到朋友圈要带一张图片,文字要经过”Kdescription”去共享,可是这儿还有一个问题,便是Uri的问题。在微信共享SDK中是直接传递bitmap曩昔的(thumbnail),可是我们要传递图片,在Android12就得适配了。

一开始我也觉得直接用拍照的方法供给Uri,7.0以下从文件取得,7.0以上用FileProvider,可是如同不太行:

Uri data;
if (Build.VERSION.SDK_INT >= 24) {
    data = FileProvider.getUriForFile(this, PhoneUtils.getAppProcessName(this) + ".opener.provider", cameraFile);
    // 给方针运用一个暂时授权
    cameraIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
} else {
    data = Uri.fromFile(cameraFile);
}

由于贮存适配我这儿图片放到了外部私有目录,仍是在沙盒里边,没办法只得写到公有目录去,好在我之前写了个博客Android 不请求权限贮存、删去相册图片,下面就简略改了改,把文件写到相册去了:

import android.content.ContentValues
import android.content.Context
import android.graphics.Bitmap
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.provider.MediaStore
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.InputStream
object BitmapFileUtil {
    // 保存到外部贮存-公有目录-Picture内,而且无需贮存权限
    public fun insert2Pictures(context: Context, bitmap: Bitmap): Uri {
        val baos = ByteArrayOutputStream()
        bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos)
        val bais = ByteArrayInputStream(baos.toByteArray())
        return insert2Album(context, bais, "Media")
    }
    // 运用MediaStore方法将流写入相册
    @Suppress("SameParameterValue")
    private fun insert2Album(context: Context, inputStream: InputStream, type: String): Uri {
        val fileName = "${type}_${System.currentTimeMillis()}.jpg"
        val contentValues = ContentValues()
        contentValues.put(MediaStore.Images.ImageColumns.DISPLAY_NAME, fileName)
        // Android 10,路径保存在RELATIVE_PATH
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            //RELATIVE_PATH 字段表示相对路径,Fundark为相册下专有目录
            contentValues.put(
                MediaStore.Images.ImageColumns.RELATIVE_PATH,
                Environment.DIRECTORY_PICTURES + File.separator + "YOUR_PATH"
            )
        } else {
            val dstPath = StringBuilder().let { sb->
                sb.append(context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)!!.path)
                sb.append(File.separator)
                sb.append("YOUR_PATH")
                sb.append(File.separator)
                sb.append(fileName)
                sb.toString()
            }
            //DATA字段在Android 10.0 之后现已废弃(Android 11又启用了,可是只读)
            contentValues.put(MediaStore.Images.ImageColumns.DATA, dstPath)
        }
        // 刺进相册
        val uri =  context.contentResolver
            .insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)
        // 写入文件
        uri?.let {
            write2File(context, it, inputStream)
        }
        return uri!!
    }
    private fun write2File(context: Context, uri: Uri, inputStream: InputStream) {
        // 从Uri结构输出流
        context.contentResolver.openOutputStream(uri)?.use { outputStream->
            val byteArray = ByteArray(1024)
            var len: Int
            do {
                //从输入流里读取数据
                len = inputStream.read(byteArray)
                if (len != -1) {
                    outputStream.write(byteArray, 0, len)
                    outputStream.flush()
                }
            } while (len != -1)
        }
    }
}

这儿把你的bitmap传进去,就能拿到Uri了,不过这儿的名字我是随机命名的,有需求能够传递进去,用这个Uri就能把图片传递到微信了,不过要注意上面这些是IO操作,最好发动个线程履行。

结语

大致内容便是这样了,我这微信老友和微信朋友圈都能共享曩昔,撒花!下面贴一张chatGPT胡言乱语的图:

使用系统自带分享,分享到微信、朋友圈、微博、QQ,适配Android12