Android 通过AccessibilityService实现微信聊天记录导出

使用Android辅助功能AccessibilityService实现微信自动聊天【外挂插件】 本文是使用Android辅助功能AccessibilityService实现微信自动聊天demo;只是为了跟深入的了解Android辅助功能, 提高自身的动手能力。 请勿用于商用,或非法用途 阅读详情
AI摘要

本文介绍了一种使用AccessibilityService在Android设备上自动备份微信聊天记录的方法。通过解析微信的界面元素,可以滚动聊天窗口,抓取聊天记录并保存。代码详细展示了如何定位聊天列表、聊天对象和聊天内容。

接上Android 微信聊天记录、联系人备份并导出为表格继续讲

不太了解AccessibilityService可以看看这篇文章

基本原理:

首先打开 DDMS 捕捉界面元素

拿到resourceid,调用方法

List<AccessibilityNodeInfo> mListView = rootNode.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/a-c");

能拿到一个 listview 的 list, 事实上 mListView 的长度只有1再通过

myListView.get(0)

就可以准确的拿到 listview了,然后可以调用滚动的方法

/* 滚动 */
listNode.performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD) //向上滚动
listNode.performAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD) //向下滚动

每滚动一次,根据 resourceId 获取当前屏幕的textview 集合,再遍历集合,调用 textview.getText() 方法,就能拿到聊天记录了; 

下面直接贴代码,讲的很详细了,1:1的注释

package com.cxk.wechatlog;

import android.accessibilityservice.AccessibilityService;
import android.content.Intent;
import android.graphics.Rect;
import android.os.Environment;
import android.text.TextUtils;
import android.util.Log;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.Toast;

import java.util.List;

/**
 * Created by 曾轲
 * <p>
 * 获取即时微信聊天记录服务类,该功能在微信6.5.8上可正常使用
 */

public class WeChatLogService extends AccessibilityService {

    /**
     * 聊天对象
     */
    private String ChatName;

    /**
     * 小视频的秒数,格式为00:00
     */
    private String VideoSecond;


    private String ChatRecord;

    /**
     *  根据聊天列表两次滚动后最底部的人是不是同一个,来判断聊天列表是否到了底部
     */
    private boolean listIsBottom = false;
    private String bottomName = "";

    /**
     *  根据聊天消息滚动前后三条是否重复,用来判断消息界面是否滚动到了最顶部
     */
    private boolean chatMessageIsTop = false;
    private String chatMessage1 = "";
    private String chatMessage2 = "";
    private String chatMessage3 = "";
    String topMessage1= "";
    String topMessage2= "";
    String topMessage3= "";
    /**
     * 聊天人的姓名
     */
    String chatName= "";

    /**
     * 聊天信息保存目录
     */
    private String mCurrApkPath =  Environment.getExternalStorageDirectory().getPath() + "/";

    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        int eventType = event.getEventType();
        switch (eventType) {
            case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED: {
                String currentActivity = event.getClassName().toString();
                //如果在微信界面
                if (currentActivity.equals(WeChatTextWrapper.WechatClass.WECHAT_CLASS_LAUNCHUI)) {
                    Log.e("界面","微信主页");
                    AccessibilityNodeInfo rootNodeInfo = getRootInActiveWindow();
                    //获取聊天列表的 listview
                    List<AccessibilityNodeInfo> listview = rootNodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/bny");
                    if (listview!=null&&listview.size() != 0) {
                        //如果 listview 没有滑动到最底部,就一直无限循环遍历聊天记录
                        while (!listIsBottom) {
                            //获取当前页面聊天 list的单个条目和聊天人名字
                            List<AccessibilityNodeInfo> ChatList = rootNodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/agu");
                            List<AccessibilityNodeInfo> NameList = rootNodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/agw");
                            //如果没有聊天信息,直接跳出,并终止循环
                            if(NameList.size()==0||NameList==null){
                                listIsBottom=true;
                               return;
                            }
                            //获取当前list 最后一个人员的名字,与上一次滑动比较,如果相同说明到了最底部
                            String lastName = NameList.get(NameList.size() - 1).getText().toString();
                            if (bottomName.equals(lastName)) {
                                listIsBottom = true;
                            } else {
                                bottomName = lastName;
                                listIsBottom = false;
                                //遍历循环列表,进入聊天详情页进行备份
                                if (ChatList!=null&ChatList.size() != 0) {
                                    for (int j = 0; j < ChatList.size(); j++) {
                                        //页面停顿防止过快无法找到目标
                                        try {
                                            Thread.sleep(500);
                                        } catch (InterruptedException e) {
                                            e.printStackTrace();
                                        }
                                        //进入聊天界面
                                        ChatList.get(j).performAction(AccessibilityNodeInfo.ACTION_CLICK);
                                        //延迟防止页面还没完全载入,拿不到根节点信息
                                        try {
                                            Thread.sleep(500);
                                        } catch (InterruptedException e) {
                                            e.printStackTrace();
                                        }
                                        AccessibilityNodeInfo chatRootNodeInfo = getRootInActiveWindow();
                                        //遍历下一个人的聊天记录,需要重置定置状态
                                        chatMessageIsTop=false;
                                        getWeChatLog(chatRootNodeInfo);
                                    }
                                }
                                //循环遍历完当前页面的列表后滑动遍历下一个页面
                                try {
                                    Thread.sleep(500);
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }
                                listview.get(0).performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
                                try {
                                    Thread.sleep(1000);
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }
                            }

                           }
                        }else{

                    }

                    } else if (currentActivity.equals(WeChatTextWrapper.WechatClass.WECHAT_CLASS_CONTACTINFOUI)) {
                        Log.e("界面","联系人界面");
                    } else if (currentActivity.equals(WeChatTextWrapper.WechatClass.WECHAT_CLASS_CHATUI)) {
                        Log.e("界面","聊天界面");
                    }
                }
                break;
            }


    }

    /**
     * 遍历获取聊天消息
     * @param rootNode
     */
    private void getWeChatLog(AccessibilityNodeInfo rootNode) {
        if (rootNode != null) {
            //获取聊天人的姓名
            List<AccessibilityNodeInfo> listName = rootNode.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/gp");
            if(listName!=null&&listName.size()!=0){
               chatName = listName.get(0).getText().toString();
                Log.e("name",chatName);
            }

            //获取聊天详情页的listview
            List<AccessibilityNodeInfo> listChatRecord = rootNode.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/a3e");
            //如果没有聊天记录,直接返回
            if(listChatRecord!=null&&listChatRecord.size()==0){
                Log.e("消息类型","当前没有聊天记录");
                performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK);
                return;
            }
            //利用姓名创建文件


            //当消息详情不在最顶部时无限循环遍历消息记录
            while(!chatMessageIsTop){
            //有聊天记录,开始遍历循环
            //获取聊天头像list
            List<AccessibilityNodeInfo> imageName = rootNode.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/ik");
            //获取聊天信息list
            List<AccessibilityNodeInfo> record = rootNode.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/im");

            //有聊天头像说明有聊天记录(但是不一定有文字信息,可能是图片,分享等等)
            if (imageName!=null&&imageName.size() != 0) {
                //如果record的 size 为零说明当前截取到的 list 没有文字
                if (record.size() == 0) {
                    Log.e("消息类型","当前没有文字消息");
                    //判断当前这条消息是不是和上一条一样,防止重复
                        //获取聊天对象
                        ChatName = imageName.get(0).getContentDescription().toString().replace("头像", "");
                        Log.e("AAAA", ChatName + ":" + "对方发的是图片或者表情");

                        try {
                            Thread.sleep(800);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        //当前 listview 显示的都是非文字消息,乡下滚动一次后再次遍历循环
                        listChatRecord.get(0).performAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
                        return;
                //有文本消息,开始辨析备份文本消息
                }else {
                    //获取当前页面顶部的三条文字,与上一次滚动的三条文字对比,判断是否滚动到最顶部了
                    switch (record.size()) { //判断 size 防止索引越界
                        case 1:
                             topMessage1 = record.get(record.size() - 1).getText().toString();
                             topMessage2="";
                             chatMessage2="";
                             topMessage3="";
                             chatMessage3="";
                            break;

                        case 2:
                             topMessage1 = record.get(record.size() - 1).getText().toString();
                             topMessage2 = record.get(record.size() - 2).getText().toString();

                            topMessage3="";
                            chatMessage3="";
                            break;
                        case 3:
                             topMessage1 = record.get(record.size() - 1).getText().toString();
                             topMessage2 = record.get(record.size() - 2).getText().toString();
                             topMessage3 = record.get(record.size() - 3).getText().toString();

                            break;

                            default:
                                topMessage1 = record.get(record.size() - 1).getText().toString();
                                topMessage2 = record.get(record.size() - 2).getText().toString();
                                topMessage3 = record.get(record.size() - 3).getText().toString();

                                break;
                    }
                        //如果三条消息都重复证明已经滚动到了最顶部,返回聊天列表
                        if(chatMessage1.equals(topMessage1)&&chatMessage2.equals(topMessage2)&&chatMessage3.equals(topMessage3)){
                            chatMessageIsTop=true;
                            performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK);
                        }else{
                            //不是第一条,重新赋值最后三条消息记录,遍历保存文字信息
                            chatMessage1=topMessage1;
                            chatMessage2=topMessage2;
                            chatMessage3=topMessage3;
                            chatMessageIsTop=false;
                            //遍历获取文字,并写入文件
                            for (int i = record.size()-1; i >= 0; i--) {
                                //保存消息
                                //Log.e("文字消息",record.get(i).getText().toString());
                                //Log.e("index",record.get(i).toString());
                                //获取文本消息的位置,根据位置来判断但前消息是谁发的
                                Rect outBounds = new Rect();
                                record.get(i).getBoundsInScreen(outBounds);
                                //因为对方发送的消息的左侧 left 坐标是永远不会变的,自己发送的消息会被换行折叠,因此可以用此来区分
                                String leftIndex = outBounds.left+"";
                                //不同手机这个值不一样,需要自己适配
                                if(leftIndex.equals("156")){
                                    //对方发送的消息
                                    saveMassage(chatName,record.get(i).getText().toString());
                                }else{
                                    //自己发送的消息
                                    saveMassage("我",record.get(i).getText().toString());
                                }

                            }
                            //遍历完毕后滚动下一屏聊天记录开始,并停止.8秒等待聊天记录加载
                            listChatRecord.get(0).performAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
                            try {
                                Thread.sleep(800);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }

                    //有聊天列表但是没有聊天头像和文字消息.类似于招行信用卡的公众号,不作处理直接返回聊天列表遍历下一个
                    }else{
                        chatMessageIsTop=true;
                        performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK);
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                         }
                    }
                }
            }
        }

    private void saveMassage(String chatName, String message) {


    }


    /**
     * 遍历所有控件,找到头像Imagview,里面有对联系人的描述
     */
    private void GetChatName(AccessibilityNodeInfo node) {
        for (int i = 0; i < node.getChildCount(); i++) {
            AccessibilityNodeInfo node1 = node.getChild(i);
            if ("android.widget.ImageView".equals(node1.getClassName()) && node1.isClickable()) {
                //获取聊天对象,这里两个if是为了确定找到的这个ImageView是头像的
                if (!TextUtils.isEmpty(node1.getContentDescription())) {
                    ChatName = node1.getContentDescription().toString();
                    if (ChatName.contains("头像")) {
                        ChatName = ChatName.replace("头像", "");
                    }
                }

            }
            GetChatName(node1);
        }
    }


    /**
     * 必须重写的方法:系统要中断此service返回的响应时会调用。在整个生命周期会被调用多次。
     */
    @Override
    public void onInterrupt() {
        Toast.makeText(this, "我快被终结了啊-----", Toast.LENGTH_SHORT).show();
    }

    /**
     * 服务开始连接
     */
    @Override
    protected void onServiceConnected() {
        Toast.makeText(this, "服务已开启", Toast.LENGTH_SHORT).show();
        super.onServiceConnected();
    }

    /**
     * 服务断开
     *
     * @param intent
     * @return
     */
    @Override
    public boolean onUnbind(Intent intent) {
        Toast.makeText(this, "服务已被关闭", Toast.LENGTH_SHORT).show();
        return super.onUnbind(intent);
    }

    /**
     * 遍历所有控件:这里分四种情况
     * 文字聊天: 一个TextView,并且他的父布局是android.widget.RelativeLayout
     * 语音的秒数: 一个TextView,并且他的父布局是android.widget.RelativeLayout,但是他的格式是0"的格式,所以可以通过这个来区分
     * 图片:一个ImageView,并且他的父布局是android.widget.FrameLayout,描述中包含“图片”字样(发过去的图片),发回来的图片现在还无法监听
     * 表情:也是一个ImageView,并且他的父布局是android.widget.LinearLayout
     * 小视频的秒数:一个TextView,并且他的父布局是android.widget.FrameLayout,但是他的格式是00:00"的格式,所以可以通过这个来区分
     *
     * @param node
     */
    public void GetChatRecord(AccessibilityNodeInfo node) {
        for (int i = 0; i < node.getChildCount(); i++) {
            AccessibilityNodeInfo nodeChild = node.getChild(i);

            //聊天内容是:文字聊天(包含语音秒数)
            if ("android.widget.TextView".equals(nodeChild.getClassName()) && "android.widget.RelativeLayout".equals(nodeChild.getParent().getClassName().toString())) {
                if (!TextUtils.isEmpty(nodeChild.getText())) {
                    String RecordText = nodeChild.getText().toString();
                    //这里加个if是为了防止多次触发TYPE_VIEW_SCROLLED而打印重复的信息

                    if (!RecordText.equals(ChatRecord)) {
                        ChatRecord = RecordText;
                        //判断是语音秒数还是正常的文字聊天,语音的话秒数格式为5"
                        if (ChatRecord.contains("\"")) {
                            Toast.makeText(this, ChatName + "发了一条" + ChatRecord + "的语音", Toast.LENGTH_SHORT).show();

                            Log.e("WeChatLog",ChatName + "发了一条" + ChatRecord + "的语音");
                        } else {
                            //这里在加多一层过滤条件,确保得到的是聊天信息,因为有可能是其他TextView的干扰,例如名片等
                            if (nodeChild.isLongClickable()) {
                                Toast.makeText(this, ChatName + ":" + ChatRecord, Toast.LENGTH_SHORT).show();

                                Log.e("WeChatLog",ChatName + ":" + ChatRecord);
                            }

                        }
                        return;
                    }
                }
            }

            //聊天内容是:表情
            if ("android.widget.ImageView".equals(nodeChild.getClassName()) && "android.widget.LinearLayout".equals(nodeChild.getParent().getClassName().toString())) {
                Toast.makeText(this, ChatName+"发的是表情", Toast.LENGTH_SHORT).show();

                Log.e("WeChatLog",ChatName+"发的是表情");

                return;
            }

            //聊天内容是:图片
            if ("android.widget.ImageView".equals(nodeChild.getClassName())) {
                //安装软件的这一方发的图片(另一方发的暂时没实现)
                if("android.widget.FrameLayout".equals(nodeChild.getParent().getClassName().toString())){
                    if(!TextUtils.isEmpty(nodeChild.getContentDescription())){
                        if(nodeChild.getContentDescription().toString().contains("图片")){
                            Toast.makeText(this, ChatName+"发的是图片", Toast.LENGTH_SHORT).show();

                            Log.e("WeChatLog",ChatName+"发的是图片");
                        }
                    }
                }
            }

            //聊天内容是:小视频秒数,格式为00:00
            if ("android.widget.TextView".equals(nodeChild.getClassName()) && "android.widget.FrameLayout".equals(nodeChild.getParent().getClassName().toString())) {
                if (!TextUtils.isEmpty(nodeChild.getText())) {
                    String second = nodeChild.getText().toString().replace(":", "");
                    //正则表达式,确定是不是纯数字,并且做重复判断
                    if (second.matches("[0-9]+") && !second.equals(VideoSecond)) {
                        VideoSecond = second;
                        Toast.makeText(this, ChatName + "发了一段" + nodeChild.getText().toString() + "的小视频", Toast.LENGTH_SHORT).show();

                        Log.e("WeChatLog","发了一段" + nodeChild.getText().toString() + "的小视频");
                    }
                }

            }

            GetChatRecord(nodeChild);
        }
    }


}
package com.cxk.wechatlog;

public class WeChatTextWrapper {
    public static final String WECAHT_PACKAGENAME = "com.tencent.mm";


    public static class WechatClass{
        //微信首页
        public static final String WECHAT_CLASS_LAUNCHUI = "com.tencent.mm.ui.LauncherUI";
        //微信联系人页面
        public static final String WECHAT_CLASS_CONTACTINFOUI = "com.tencent.mm.plugin.profile.ui.ContactInfoUI";
        //微信聊天页面
        public static final String WECHAT_CLASS_CHATUI = "com.tencent.mm.ui.chatting.ChattingUI";
    }


    public static class WechatId{
        /**
         * 通讯录界面
         */
        public static final String WECHATID_CONTACTUI_LISTVIEW_ID = "com.tencent.mm:id/ih";
        public static final String WECHATID_CONTACTUI_ITEM_ID = "com.tencent.mm:id/iy";
        public static final String WECHATID_CONTACTUI_NAME_ID = "com.tencent.mm:id/j1";

        /**
         * 聊天界面
         */
        public static final String WECHATID_CHATUI_EDITTEXT_ID = "com.tencent.mm:id/a_z";
        public static final String WECHATID_CHATUI_USERNAME_ID = "com.tencent.mm:id/ha";
        public static final String WECHATID_CHATUI_BACK_ID = "com.tencent.mm:id/h9";
        public static final String WECHATID_CHATUI_SWITCH_ID = "com.tencent.mm:id/a_x";
    }
}
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.cxk.wechatlog">

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <service
            android:name=".WeChatLogService"
            android:enabled="true"
            android:exported="true"
            android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
            <intent-filter>
                <action android:name="android.accessibilityservice.AccessibilityService" />
            </intent-filter>
            <meta-data
                android:name="android.accessibilityservice"
                android:resource="@xml/wechatlog_service_config"></meta-data>
        </service>
    </application>

</manifest>

源码地址 :

https://github.com/KeZengOo/AccessibilityServiceWeChatHelper

注意事项 :

1.微信每一次更新都会导致 resourceId 变化,所以尽量不要更新或者及时更改代码中的 resourceId

2.旧版本的微信每一个文本框都是用的 textview,可以直接调用 getText()获取聊天文字,新版本为了防止这个操作,变成自定义的 View 了,目前我还没有找到解决方案....希望有大神能点拨点拨...

3.我的代码在微信6.5.8上运行完美,高于该版本的都不行(参考第二条)

4.目前只是第一个 demo 版本,有很多不足的地方,遍历到公众号,等非聊天窗口时,可能会有异常

5.安装后必须在设置里打开辅助功能里的对应开关,再切换到微信,即可正常运行

6.如果觉得还不错,请给我点个赞吧

Android:AccessibilityService辅助功能基础使用(附微信抢红包教程) 好了,本文是对简单的应用,如果你觉得文章写得不错就给个赞呗?如果你有更好的想法和项目欢迎留言。一定会认真查询,修正不足。,第一时间获取最新知识点Android架构师之路很漫长,一起共勉吧!这次面试问的还是还是有难度的,要求当场写代码并且运行,也是很考察面试者写代码因为Android知识体系比较庞大和复杂的,涉及到计算机知识领域的方方面面。在这里我和身边一些朋友特意整理了一份快速进阶为Android高级工程师的系统且全面的学习资料。 阅读详情

相关推荐

OpenClaw Windows本地直连微信教程:免云、免登录、真直连

微信自动化不是模拟登录,也不是网页转发,而是对安卓客户端通信协议的本地化复现。OpenClaw作为一款基于逆向分析与无障碍服务的微信桥接工具,通过USB/ADB建立PC与手机微信App之间的低延迟直连通道,实现消息收发、会话监听、联系人管理等可编程控制。其技术核心在于Android UI层级捕获、微信关键组件Hook及轻量级本地API封装,适用于无障碍辅助、自动化测试等合规场景。本文详解Windows平台下OpenClaw 2026中文版的一键部署、依赖治理与Python实战接口调用,覆盖从环境检测、权限配

weixin_34211761的博客 500

安卓无障碍AccessibilityService滑动滚动控制距离长度

在安卓中使用无障碍服务 AccessibilityService,如何实现上下滚动和左右滚动,如何控制滚动时间和滚动距离,相信很多小伙伴并不知道如何操作,本文就来介绍一种通过系统 API 组合提供的完美解决方案。提供的这两个 API,没有设置滚动距离的参数。Android 7.0 开始官方引入了 Gesturedescription 模拟手势,使用模拟手势 API,我们可以对屏幕任意位置进行模拟点击、长按和滑动等操作。笔划路径持续的时间,从手势开始到笔划应该开始的时间......

gallonyin的博客 6020

微信消息防撤回技术解析:本地数据库实时备份方案与Python实现

消息撤回是即时通讯软件中常见的功能,其原理是服务器向接收方客户端发送撤回指令,客户端据此更新本地数据与界面显示。在需要信息留存的严肃场景下,如工作沟通与证据保全,确保已接收信息的完整性与可追溯性具有重要价值。通过技术手段在本地层面实现消息防撤回,核心在于理解客户端的数据存储机制与通信流程。其中,**本地数据库实时备份**是一种相对稳定且风险较低的方案,它通过监控并备份存储聊天记录的SQLite数据库文件,在撤回指令生效前保存原始消息。本文以PC端微信为例,详细阐述了基于Python脚本实现数据库监控与备份的

weixin_34112181的博客 370

推荐项目:WechatRecord_Xposed - 智能微信聊天记录管理器

推荐项目:WechatRecord_Xposed - 智能微信聊天记录管理器 去发现同类优质开源项目:https://gitcode.com/ 项目简介 是一个基于 Xposed 框架的 Android 插件,旨在帮助用户更方便地管理和备份他们的微信聊天记录。这款开源工具由开发者 Zdelta 制作,允许用户在不Root手机的情况下获取并导出聊天信息。 技术分析 Xposed框架:Xposed 是...

gitblog_00068的博客 1555

esp32c6 开发实战:低功耗蓝牙(BLE)开发

蓝牙栈在此程序流程中已经启动并运行,但是应用程序的功能尚未定义。功能的定义是通过响应事件来实现的,当其他设备尝试读取或写入参数以及建立连接时就会触发事件回调函数处理。BT控制器在控制器端实现主机控制器接口(HCI),链路层(LL)和物理层(PHY)。BT控制器对用户应用程序不可见,并处理BLE堆栈的较低层。蓝牙堆栈(Bluedroid stack)定义了经典蓝牙(BT Classic)和低功耗蓝牙(BLE)的通用定义和API。的判断)等对于启动低功耗蓝牙来说不重要的代码,函数初始化和启用BT控制器。

月入鱼饵的博客 2741

STM32F103移植NuttX实战:从零搭建最小系统到串口Shell(附完整配置文件)

本文详细介绍了在STM32F103C8T6开发板上移植NuttX实时操作系统的完整过程,包括环境搭建、BSP配置、系统时钟初始化、驱动裁剪、编译烧录及串口Shell交互。通过实战指南帮助开发者快速掌握NuttX移植技巧,解决典型问题,适用于嵌入式系统开发。

tree8的博客 293

Android 微信聊天记录、联系人备份并导出为表格

Android 微信聊天记录、联系人备份并导出为表格 (github代码会及时更新,更完整的代码请参考末文的 github 链接) 最近公司要求做一个项目,实现备份和导出虚拟代表和医生的微信聊天记录的功能,于是想了一下可从以下两个方面入手,并分析了一下他们的优劣势 解密微信数据库,直接用 Sql 语句查询导表上传 直接操作数据库,联系人和聊天记录完整,不会有遗漏 相比自动化更加省...

码农曾的博客 9556

Copula函数

Copula是处理统计中随机变量相关性问题的一种方法。本文是对Copula函数的各种定义,Copula函数的思想,Copula函数的性质,Copula函数的类型等理论资料总结

Explore 5万+

Android无障碍服务实战:用AccessibilityService打造微信自动回复机器人(附避坑指南)

本文详细介绍了如何利用Android的AccessibilityService构建合规高效的微信自动回复机器人,涵盖消息捕获、内容解析、自动化响应等核心技术实现。通过实战代码示例和避坑指南,帮助开发者掌握微信消息自动化处理方案,同时强调合规性设计与风险控制,确保应用安全稳定运行。

b9c0d的博客 274

Sentinel-1 Burst数据处理避坑实录:从aria2c报错到wget脚本救场

本文详细介绍了Sentinel-1 Burst数据处理的完整解决方案,从下载困境到高效处理技巧。针对aria2c报错等常见问题,提供了增强版wget脚本和智能分组方法,显著提升下载成功率和处理效率,特别适合科研机构和企业级网络环境下的哨兵数据处理需求。

weixin_42641998的博客 284

Android Framework 开发工程师职位深度解析与面试指南

摘要:博泰车联网科技(上海)股份有限公司招聘Android Framework开发工程师,负责车载系统的底层开发与优化。岗位要求5年以上经验,深入掌握Android Framework机制(WindowManager、PowerManager等模块),熟悉高通平台中间件架构及NDK/JNI开发,具备性能优化(启动时间、ANR处理)、OTA升级等能力。需熟练使用Android Studio、Git等工具,有车载开发经验者优先。工作内容包括Framework定制开发、问题排查及技术文档编写。

郑伟强dev的专栏 551

Android几行代码实现监听微信聊天

转载:http://www.cnblogs.com/cxk1995/p/6366273.html   2017.2.7更新: *现在适配微信版本更加容易了,只需要替换一个Recourse-ID即可 *可以知道对方发的是小视频还是语音,并获取秒数。 *可以区分聊天信息中的图片或者表情   实现效果: 实时监听当前聊天页面的最新一条消息,效果如图:

Imgloox的博客 1999

【ESD专题】从原理上分析TVS管PCB Layout的经验法则

我们常常会听到的一些经验法则,按这些经验法则去做,一般也不会有什么问题,但是到底是为什么呢? 没有从原理上分析和证明的时候,往往在Layout之后还要担心实际测试结果怎么样。或是在实际测试出问题后,根本无法进行分析,难道说修改一个版本就好了?那要从哪里修改呢?也无从下手呀。...

一点硬件 1446

Android开发——Accessibility机制实现模拟点击(微信自动抢红包实现)

1.何为Accessibility机制? 许多Android使用者因为各种情况导致他们要以不同的方式与手机交互。对于那些由于视力、听力或其它身体原因导致不能方便使用Android智能手机的用户,Android提供了Accessibility功能和服务帮助这些用户更加简单地操作设备,包括文字转语音、触觉反馈、手势操作、轨迹球和手柄操作。开发者可以搭建自己的Accessibility服务,这......

关于Android开发的一些技术点总结 ╮( ̄▽ ̄”)╭ 4万+

SST专题(2-5)MMC-SST 输入级的环流抑制

本文研究了模块化多电平换流器(MMC)的数学模型及其环流抑制控制方法。首先建立了MMC等效电路模型,推导了桥臂电流方程和虚拟电动势表达式,提出通过控制桥臂电压修正量来调节内部电流而不影响外部特性。针对MMC特有的二倍频负序环流问题,设计了基于负序旋转坐标变换的环流抑制控制器(CCSC),将时变环流转换为直流量进行控制。通过比例-积分调节和电压前馈补偿,生成内部不平衡压降参考值,最终形成包含功率控制和环流抑制的整体控制结构。仿真结果表明该方法能有效抑制内部环流,同时保持系统稳定运行。

嵌入式老牛的博客 536

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

Android防撤回神器Anti-recall:免root保护你的聊天记录

在即时通讯成为日常交流主要方式的今天,消息撤回功能既带来了便利,也带来了遗憾。当你错过重要信息时,Anti-recall作为一款专业的Android免root防撤回工具,能够智能捕获微信和QQ中被撤回的消息,让你不再错过任何重要内容。这款开源应用通过智能监听技术,在保护隐私的前提下,为用户提供完整的信息记录解决方案。 ## 🔍 核心功能解析:Anti-recall如何实现防撤回 Anti-r

gitblog_00134的博客 267

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

安卓语音交互开发实战:从无障碍服务到语音指令自动化

语音交互作为人机交互的重要分支,其核心在于将自然语言指令转化为设备可执行的操作。其技术原理主要涉及语音识别(ASR)将音频转为文本,以及自然语言理解(NLU)解析用户意图。在安卓平台上,实现此类功能的技术价值在于能极大提升操作效率,尤其在驾驶、多任务处理等双手受限场景。典型的应用场景包括全局语音搜索、应用快捷启动、跨应用自动化操作等。本文以构建一个类似“Sesame Preview语音模式”的功能模块为例,深入探讨如何结合安卓无障碍服务(AccessibilityService)与语音识别技术,实现从“听到

weixin_34008805的博客 544

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

Android实现屏蔽微信拉黑和删除联系人功能

实现效果: 让微信永远弹不出那个删除的对话框不就相当于屏蔽掉该功能了吗?哈哈效果如图: 实现原理: 同样是利用AccessibilityService辅助服务,关于这个服务类还不了解的同学可以先看下我前两篇的博客,原理都一样: http://www.cnblogs.com/cxk1995/p/6363574.html(抢红包) http://w...

weixin_30819085的博客 577

基于微信群控系统分析几十万几百万用户微信朋友圈和聊天记录数据

基于微信群控系统分析几十万几百万用户朋友圈和聊天记录数据打造针对用户的智能推荐系统 用户属性: 姓名、性别、年龄、所在地区、常驻地区、手机号码、微信号码、职业、岗位、身份证等等 用户行为:1、通过图文分析,定位所在区域、行业、大概的收入状况、喜好;2、如果是微商,分析常发微信圈产品;3、综合分析朋友圈人气状况;4、给用户打标签;5、产品匹配。 建立用户画像标签和大数据分析...

weixin_30824479的博客 382

Android防撤回终极指南:使用Anti-recall轻松查看微信QQ撤回消息

在当今快节奏的社交沟通中,你是否经常遇到重要消息被对方撤回的尴尬情况?无论是工作群里的重要通知,还是朋友间的有趣对话,一旦被撤回就再也无法查看。Anti-recall正是为解决这一痛点而生的Android防撤回神器,让你不再错过任何重要信息!这款强大的免root防撤回工具专门为微信和QQ用户设计,通过智能技术实时捕获并保存所有撤回消息,让你随时回顾被撤回的内容。 ## Anti-recall防撤

gitblog_00373的博客 105

Python实现微信消息转发至Windows通知中心并支持快捷回复

在跨设备工作流中,消息同步与高效处理是提升生产力的关键。通过Android Debug Bridge(ADB)与Windows原生通知API,可以实现移动端应用消息的桌面端集中展示与交互。这项技术的核心价值在于打破设备壁垒,将碎片化的通讯信息整合到统一的操作界面,尤其适合开发者、远程工作者等需要深度专注又需及时响应的场景。本文以微信消息为例,详细解析了如何利用Python结合ADB抓取安卓通知,并通过winrt库创建支持按钮交互的Windows Toast通知,最终构建一个本地化、支持快捷回复的自动化工具,

weixin_33858336的博客 428

微信抢红包插件极速上手:一次配置,从此告别“手慢无“

你有没有过这样的瞬间:会议开到一半,群消息弹出"红包雨",等你切进微信,红包列表早已一栏"手慢了"?或者家族群里长辈发了个大包,你在输入法里敲出"谢谢老板"四个字的功夫,钱已经进了别人的口袋? 这不是手速的问题,是人的反应天然有上限。而 WeChatLuckyMoney 这款微信抢红包插件,恰恰把"抢"这件事从"拼手速"变成了"拼配置"——它不需要你盯着屏幕,装好、开好、设置对,剩下的交给它。

gitblog_00856的博客 228
上一篇: Android 微信聊天记录、联系人备份并导出为表格
ParkSiHoo_
博客等级 码龄10年 20粉丝 24原创
评论 18
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值