微信朋友圈多图分享方案

石朝辉2018-11-03 19:00AndroidAndroid 微信分享

转载请注明出处,点击此处open in new window 查看更多精彩内容

Github 查看源码open in new window

本库经过几个版本的升级,当前的分享逻辑和本文中略有不同,但整体思想是一致的,实际方案请阅读源码open in new window

无法分享到微信

前些日子微信更新到了 v6.7.3 版本,传统的多图分享方式已经无效了。

微信 v6.7.3 版本以前的方案

微信在 v6.7.3 以前的版本提供了从系统相册分享多图到朋友圈的接口,具体使用如下:

val intent = Intent()
intent.action = Intent.ACTION_SEND_MULTIPLE
intent.component = ComponentName("com.tencent.mm", "com.tencent.mm.ui.tools.ShareToTimeLineUI")
intent.type = "image/*"
intent.putStringArrayListExtra(Intent.EXTRA_TEXT, arrayListOf(text))
intent.putExtra("Kdescription", text)
// 传递多张图片的 Uri 。
intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uriList)
startActivity(intent)

使用以上代码,在微信 v6.7.3 以前可以正常分享,但是如果用户把微信更新到 v6.7.3 版本,再使用该分案分享多图就只能看到上面那张图了。

跳转微信 v6.7.3 图文分享界面

虽然 v6.7.3 不能直接分享多图了,但是接口其实还是保留了的,只要在传递参数 Intent.EXTRA_STREAM 时 urilList 中只携带 1 张图片,还是可以跳转到朋友圈图文分享界面的。如下:

微信图文分享界面

微信 v7.0.x 不能使用 Intent.ACTION_SEND_MULTIPLE 分享了,只能使用 Intent.ACTION_SEND,具体解决方案请阅读源码open in new window

此界面和从朋友圈中通过选择图片后跳转的界面是一模一样的,不仅可以分享图片,还可以发送文字、定位、同步到QQ空间。

但是,仅仅打开图文分享界面是不行的,因为我们要的是多图分享,而这里只传入了 1 张图片。为了解决这个问题,需要用到 AccessibilityService

使用无障碍服务

AccessibilityService 官方文档open in new window

无障碍服务是一个为残疾人或可能暂时无法与设备完全互动的人提供用户界面扩展功能的服务。

从Android4.0(API级别14)开始,无障碍服务可以代表用户操作,包含改变输入焦点和选择(激活)用户界面元素。在Android4.1(API级别16),操作的范围被扩展至包含滚动列表和与文本域交互。无障碍服务也可采取全局操作,如导航到主界面、按返回按钮、打开屏幕通知和最近应用列表。Android4.1也包含新焦点类型,无障碍焦点,该焦点类型可让所有视觉元素能够被无障碍服务所选择。

这些新的能力让开发者能够开发替代导航模式,如手势导航,提高残障用户对Android设备的控制。

通过使用无障碍服务,可以监听用户手机界面和事件,并在特定事件产生时代替用户执行一些操作。因此我们只要知道分析朋友圈图文分享界面的事件,找到合适的事件帮助用户自动填写文字、自动选择图片即可。

注意:由于无障碍服务功能强大,因此 Android 系统对其进行了限制,必须用户手动打开 APP 开发的无障碍服务,才能产生作用。

事件分析

class WXShareMultiImageService : AccessibilityService() {

    override fun onAccessibilityEvent(event: AccessibilityEvent) {
        // 当窗口发生的事件是我们配置监听的事件时,会回调此方法.会被调用多次
    }

    override fun onInterrupt() {
        //当服务要被中断时调用.会被调用多次
    }
}

每次产生的事件都会发送到 onAccessibilityEvent 函数,通过打印 AccessibilityEvent 的内容分析事件,发现以下事件:

  • 打开图文分享界面时产生的事件:
{
    eventType : AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
    className : com.tencent.mm.plugin.sns.ui.SnsUploadUI
    ...
}
  • 点击添加图片的 + 号产生的事件:
{
    eventType : AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
    className : android.widget.ListView
    ...
}
  • 打开图片选择界面的事件:
{
    eventType : AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
    className : com.tencent.mm.plugin.gallery.ui.AlbumPreviewUI
    ...
}

分析图文分享界面的 UI

使用 Android SDK 的 uiautomatorviewer 工具分析 UI,找到如下视图:

  • 文字编辑框(EditText)。
  • 已选图片列表(GridView),最后一个 Item 是【添加图片按钮】。
  • 添加图片方式列表(ListView),最后一个 Item 是【从相册选择】。
  • 待选图片列表(GridView)。
  • 复选框(CheckBox),其实点击的不是 CheckBox,而是另一个不可见的 View。
  • 【完成】选择按钮。

AccessibilityService 中获取视图节点的 3 种方式:

// 通过 id  获取节点。
rootNodeInfo.findAccessibilityNodeInfosByViewId(idString)

// 通过文本获取节点。
rootNodeInfo.findAccessibilityNodeInfosByText(text)

// 遍历子节点获取指定节点。
fun findNodeInfo(rootNodeInfo: AccessibilityNodeInfo?, className: String): AccessibilityNodeInfo? {
    // 遍历,具体内容请查看源码。
}

微信每个版本的视图 id 都不一样,为了版本兼容,不能使用 id 获取节点。

通过 text 文本获取节点只对有 text 属性且有值的节点有效。

因此大多数节点通过遍历子节点的形式获取。

自动粘贴

将待分享的文字复制到系统剪切板,如果用户打开了无障碍服务,服务将自动粘贴文字到输入框,否则用户也可以手动长按输入框粘贴。

val editText = findNodeInfo(rootInActiveWindow, EditText::class.java.name)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
    // 粘贴剪切板内容
    editText?.performAction(AccessibilityNodeInfo.ACTION_FOCUS)
    editText?.performAction(AccessibilityNodeInfo.ACTION_PASTE)
} else {
    editText?.text = ClipboardUtil.getPrimaryClip(this)
}

自动选图

用户点击分享时下载并记录图片,如果用户打开了无障碍服务,服务将代替用户自动选择图片,否则用户也可以进入相册手动选择图片。

// 以下几个步骤是异步的,这里只是为了方便才写在了一起。

// 第一步: 自动点击添加图片的 + 号按钮。
val gridView = findNodeInfo(rootInActiveWindow, GridView::class.java.name)
gridView?.getChild(gridView.childCount - 1)?.performAction(AccessibilityNodeInfo.ACTION_CLICK)

// 第二步:点击【从相册选择】按钮。
listView.findAccessibilityNodeInfosByText("从相册选择")
        ?.get(0)
        ?.parent
        ?.performAction(AccessibilityNodeInfo.ACTION_CLICK)

// 第三步:选择图片。
val gridView = findNodeInfo(rootInActiveWindow, GridView::class.java.name)
for (i in ShareInfo.getSelectedImageCount()..ShareInfo.getWaitingImageCount()) {
    findNodeInfo(gridView.getChild(i), View::class.java.name)
            ?.performAction(AccessibilityNodeInfo.ACTION_CLICK)
}

// 第四步:点击【完成】按钮。
rootInActiveWindow.findAccessibilityNodeInfosByText("完成")
        ?.get(0)
        ?.performAction(AccessibilityNodeInfo.ACTION_CLICK)

详细内容请 查看源码open in new window

为了方便大家使用新的分享方案,我这里已经封装成组件 ,并发布源码到 Github。

https://github.com/shichaohui/WXShareMultiImage

完整的多图分享流程

流程图

下载 Demoopen in new window

Github 查看源码open in new window

优缺点

优点

  1. 可自动粘贴分享文字。
  2. 可自动选择指定的多张图片。
  3. 服务不可用时会自动降级,由用户手动选择图片。

缺点

  1. 无法获取分享结果。
  2. 需用户手动打开服务。
  3. 部分机型在关闭APP时会自动关闭服务。
  4. 微信更新朋友圈图文分享界面可能导致方案失效。
最后更新于 2024-02-04 02:20:03