手把手教你读取Android版微信和手Q的聊天记录(仅作技术研究学习)

本文涉及的产品
密钥管理服务KMS,1000个密钥,100个凭据,1个月
简介:

1、引言

特别说明:本文内容仅用于即时通讯技术研究和学习之用,请勿用于非法用途。如本文内容有不妥之处,请联系JackJiang进行处理!

我司有关部门为了获取黑产群的动态,有同事潜伏在大量的黑产群(QQ群、微信群)中,干起了无间道的工作。随着黑产群数量的激增,同事希望能自动获取黑产群的聊天信息,并交付风控引擎进行风险评估。于是,这个工作就交给我了,是时候表现一波了……

针对同事的需求,分析了一通,总结一下:

1)能够自动获取微信和 QQ群的聊天记录;

2)只要文字记录,图片和表情包,语音之类的不要;

3)后台自动运行,非实时获取记录。

注:本文读取聊天记录的方法只适用于监控自己拥有的微信或者QQ ,无法监控或者盗取其他人的聊天记录。本文只写了如何获取聊天记录,服务器落地程序并不复杂,不做赘述。写的仓促,有错别字还请见谅。)

学习交流:

- 即时通讯开发交流3群:185926912[推荐]

- 移动端IM开发入门文章:《新手入门一篇就够:从零开发移动端IM

(本文同步发布于:http://www.52im.net/thread-1992-1-1.html

2、相关文章

即时通讯网之前整理过微信本地数据库的读取和样本,如有兴趣可请往阅读:

微信本地数据库破解版(含iOS、Android),仅供学习研究 [附件下载]

3、准备工作

参阅很多相关的文章之后,对这个需求有了大致的想法,开始着手准备:

1)需要一个有root权限的Android手机,我用的是红米5(强调必须已被ROOT);

2)android的开发环境(就是Android Studio那一套啦);

3)android相关的开发经验(我是个PHP,第一次写Android程序,踩了不少坑)。

4、获取微信聊天记录过程分享

4.1 着手准备

微信的聊天记录保存在Android系统的:"/data/data/com.tencent.mm/MicroMsg/c5fb89d4729f72c345711cb*/EnMicroMsg.db" 目录和文件下。

该文件是加密的数据库文件,需要用到sqlcipher来打开。密码为:MD5(手机的IMEI+微信UIN)的前七位。文件所在的那个乱码文件夹的名称也是一段加密MD5值:MD5('mm'+微信UIN)。微信的UIN存放在微信文件夹“/data/data/com.tencent.mmshared_prefs/system_config_prefs.xml”中。(这个减号一定要带着!)

另外:即时通讯网之前整理过微信本地数据库的样本,如有兴趣可请往下载:《微信本地数据库破解版(含iOS、Android),仅供学习研究 [附件下载]》。

注意:如果手机是双卡双待,那么会有两个IMEI号,默认选择 IMEI1,如果不行,可以尝试一下字符串‘1234567890ABCDEF’。早期的微信会去判定你的IMEI,如果为空 默认选择这个字符串。

拿到密码,就可以打开EnMicroMsg.db了。微信聊天记录,包括个人、群组的所有记录全部存在message这张表里(如下图所示),就像下面这两张截图里展示的一样。

(为了方便截图,此图截自《微信本地数据库破解版(含iOS、Android),仅供学习研究 [附件下载]》中的样本)

(为了方便截图,此图截自《微信本地数据库破解版(含iOS、Android),仅供学习研究 [附件下载]》中的样本)

4.2 代码实现

第一步,不可能直接去访问EnMicroMsg.db。因为没有权限,还要避免和微信本身产生冲突,所以选择把这个文件拷贝到自己的项目下:

oldPath ="/data/data/com.tencent.mm/MicroMsg/c5fb89d4729f72c345711cb**\***/EnMicroMsg.db";

newPath ="/data/data/com.你的项目/EnMicroMsg.db";

copyFile(oldPath,newPath);//代码见 部分源码

第二步,拿到文件的密码:

String password = (MD5Until.md5("IMEI+微信UIN").substring(0, 7).toLowerCase());

第三步,打开文件,执行SQL:

SQLiteDatabase.loadLibs(context);

SQLiteDatabaseHook hook = newSQLiteDatabaseHook() {

    publicvoidpreKey(SQLiteDatabase database) {

    }

    publicvoidpostKey(SQLiteDatabase database) {

        database.rawExecSQL("PRAGMA cipher_migrate;");//很重要

    }

};

SQLiteDatabase db = openDatabase(newPath, password, null, NO_LOCALIZED_COLLATORS, hook);

    longnow = System.currentTimeMillis();

    Log.e("readWxDatabases", "读取微信数据库:"+ now);

    intcount = 0;

    if(msgId != "0") {

        String sql = "select * from message";

        Log.e("sql", sql);

        Cursor c = db.rawQuery(sql, null);

        while(c.moveToNext()) {

            long_id = c.getLong(c.getColumnIndex("msgId"));

            String content = c.getString(c.getColumnIndex("content"));

            inttype = c.getInt(c.getColumnIndex("type"));

            String talker = c.getString(c.getColumnIndex("talker"));

            longtime = c.getLong(c.getColumnIndex("createTime"));

            JSONObject tmpJson = handleJson(_id, content, type, talker, time);

            returnJson.put("data"+ count, tmpJson);

            count++;

        }

        c.close();

        db.close();

        Log.e("readWxDatanases", "读取结束:"+ System.currentTimeMillis() + ",count:"+ count);

    }

到此,我们就可以通过自已写的代码拿到微信的聊天记录了,之后可以直接将整理好的JSON通过POST请求发到服务器就可以了。(忍不住吐槽:写服务器落地程序用了30分钟,写上面这一坨花了三四天,还不包括搭建开发环境、下载SDK、折腾ADB什么的)。

5、获取QQ聊天记录过程分享

5.1 说明

QQ的聊天记录有点麻烦,他的文件保存在:“/data/data/com.tencent.mobileqq/databases/你的QQ号码.db”。

这个文件是不加密的,可以直接打开。QQ中群组的聊天记录是单独建表存放的,所有的QQ群信息存放在TroopInfoV2表里,需要对字段troopuin求MD5,然后找到他的聊天记录表:mr_troop_" + troopuinMD5 +"_New。

但是!(看到“但是”就没好事。。。)

问题来了,它的内容是加密的,而且加密方法还很复杂:根据手机IMEI循环逐位异或。具体的我不举例子了,太麻烦,直接看文章最后的解密方法。

5.2 代码实现

第一步,还是拷贝数据库文件:

final String QQ_old_path = "/data/data/com.tencent.mobileqq/databases/QQ号.db";

final String QQ_new_path = "/data/data/com.android.saurfang/QQ号.db";

DataHelp.copyFile(QQ_old_path,QQ_new_path);

第二步,打开并读取内容:

SQLiteDatabase.loadLibs(context);

String password = "";

SQLiteDatabaseHook hook = newSQLiteDatabaseHook() {

    publicvoidpreKey(SQLiteDatabase database) {}

    publicvoidpostKey(SQLiteDatabase database) {

        database.rawExecSQL("PRAGMA cipher_migrate;");

    }

};

 MessageDecode mDecode = newMessageDecode(imid);

HashMap<String, String> troopInfo = newHashMap<String, String>();

try{

    SQLiteDatabase db = openDatabase(newPath,password,null, NO_LOCALIZED_COLLATORS,hook);

    longnow = System.currentTimeMillis();

    Log.e("readQQDatabases","读取QQ数据库:"+now);

    //读取所有的群信息

    String sql = "select troopuin,troopname from TroopInfoV2 where _id";

    Log.e("sql",sql);

    Cursor c = db.rawQuery(sql,null);

    while(c.moveToNext()){

        String troopuin = c.getString(c.getColumnIndex("troopuin"));

        String troopname = c.getString(c.getColumnIndex("troopname"));

        String name = mDecode.nameDecode(troopname);

        String uin = mDecode.uinDecode(troopuin);

        Log.e("readQQDatanases","读取结束:"+name);

        troopInfo.put(uin, name);

    }

    c.close();


    inttroopCount = troopInfo.size();

    Iterator<String> it = troopInfo.keySet().iterator();

    JSONObject json = newJSONObject();

    //遍历所有的表

    while(troopCount > 0) {

        try{

            while(it.hasNext()) {

                String troopuin = (String)it.next();

                String troopname = troopInfo.get(troopuin);

                if(troopuin.length() < 8)

                    continue;

                String troopuinMD5 = getMD5(troopuin);

                String troopMsgSql = "select _id,msgData, senderuin, time from mr_troop_"+ troopuinMD5 +"_New";

                Log.e("sql",troopMsgSql);

                Cursor  cc = db.rawQuery(troopMsgSql,null);

                JSONObject tmp = newJSONObject();

                while(cc.moveToNext()) {

                    long_id = cc.getLong(cc.getColumnIndex("_id"));

                    byte[] msgByte = cc.getBlob(cc.getColumnIndex("msgData"));

                    String ss = mDecode.msgDecode(msgByte);

                    //图片不保留

                    if(ss.indexOf("jpg") != -1|| ss.indexOf("gif") != -1

                            || ss.indexOf("png") != -1)

                        continue;

                    String time = cc.getString(cc.getColumnIndex("time"));

                    String senderuin = cc.getString(cc.getColumnIndex("senderuin"));

                    senderuin  = mDecode.uinDecode(senderuin);

                    JSONObject tmpJson = handleQQJson(_id,ss,senderuin,time);

                    tmp.put(String.valueOf(_id),tmpJson);

                }

                troopCount--;

                cc.close();

            }

        } catch(Exception e) {

            Log.e("e","readWxDatabases"+e.toString());

        }

    }

    db.close();

}catch(Exception e){

    Log.e("e","readWxDatabases"+e.toString());

}

然后你就可以把信息发到服务器落地了(同样跟微信的记录上传一样,通过你自已写的代码发送到你的服务端就可以了)。

6、题外话:一些注意点

这里还有几个需要注意的地方。

1)最新安卓系统很难写个死循环直接跑了,所以我们需要使用Intent,来开始Service,再通过Service调用AlarmManager,就像下面的代码这样:

publicclassMainActivity extendsAppCompatActivity {

    privateIntent intent;

    @Override

    protectedvoidonCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity\_main);

        intent = newIntent(this, LongRunningService.class);

        startService(intent);

    }


    @Override

    protectedvoidonDestroy() {

        super.onDestroy();

        stopService(intent);

    }

}

然后再创建一个LongRunningService,在其中调用AlarmManager:

AlarmManager manager = (AlarmManager) getSystemService(ALARM_SERVICE);

intMinutes = 60*1000; //此处规定执行的间隔时间

longtriggerAtTime = SystemClock.elapsedRealtime() + Minutes;

Intent intent1 = newIntent(this, AlarmReceiver.class);//注入要执行的类

PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent1, 0);

manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pendingIntent);

returnsuper.onStartCommand(intent, flags, startId);

在AlarmReceiver中调用我们的方法:

//微信部分

postWXMsg.readWXDatabase();

//QQ部分

postQQMsg.readQQDatabase();

//再次开启LongRunningService这个服务,即可实现定时循环。

Intent intentNext = newIntent(context, LongRunningService.class);

context.startService(intentNext);

2)安卓不允许在主线程里进行网络连接,可以直接用 retrofit2 来发送数据(或者最简单的方法就是用AsyncTask了)。

3)项目需要授权网络连接(就是在AndroidManifast.xml里加上网络权限申请就是了);

4)项目需要引入的包:

implementation files('libs/sqlcipher.jar')

implementation files('libs/sqlcipher-javadoc.jar')

implementation 'com.squareup.retrofit2:retrofit:2.0.0'

implementation 'com.squareup.retrofit2:converter-gson:2.0.0'

5)如果复制文件时失败,校验文件路径不存在,多半是因为授权问题。需要对数据库文件授权 全用户rwx权限;

6)如果服务端使用MySql数据库的话,数据库编码请用utf8mb4编码,用来支持Emoji表情。。

7、我的部分源码

(因为种种原因,我不太好直接把源码贴上来,现把几个实用方法分享出来,可以直接使用。)

复制文件的方法:

/**

  * 复制单个文件

  *

  * @param oldPath String 原文件路径 如:c:/fqf.txt

  * @param newPath String 复制后路径 如:f:/fqf.txt

  * @return boolean

  */

 publicstaticbooleancopyFile(String oldPath, String newPath) {

     deleteFolderFile(newPath, true);

     Log.e("copyFile", "time_1:"+ System.currentTimeMillis());

     InputStream inStream = null;

     FileOutputStream fs = null;

     try{

         intbytesum = 0;

         intbyteread = 0;

         File oldfile = newFile(oldPath);

         Boolean flag = oldfile.exists();

         Log.e("copyFile", "flag:"+flag );

         if(oldfile.exists()) { //文件存在时

             inStream = newFileInputStream(oldPath); //读入原文件

             fs = newFileOutputStream(newPath);

             byte[] buffer = newbyte[2048];

             while((byteread = inStream.read(buffer)) != -1) {

                 bytesum += byteread; //字节数 文件大小

                 fs.write(buffer, 0, byteread);

             }

             Log.e("copyFile", "time_2:"+ System.currentTimeMillis());

         }

     } catch(Exception e) {

         System.out.println("复制单个文件操作出错");

         e.printStackTrace();

     } finally{

         try{

             if(inStream != null) {

                 inStream.close();

             }

             if(fs != null) {

                 fs.close();

             }

         } catch(IOException e) {

             e.printStackTrace();

         }

     }

     returntrue;

 }


 /**

  * 删除单个文件

  *

  * @param filepath

  * @param deleteThisPath

  */

 publicstaticvoiddeleteFolderFile(String filepath, booleandeleteThisPath) {

     if(!TextUtils.isEmpty(filepath)) {

         try{

             File file = newFile(filepath);

             if(file.isDirectory()) {

                 //处理目录

                 File files[] = file.listFiles();

                 for(inti = 0; i < file.length(); i++) {

                     deleteFolderFile(files[i].getAbsolutePath(), true);

                 }

             }

             if(deleteThisPath) {

                 if(!file.isDirectory()) {

                     //删除文件

                     file.delete();

                 } else{

                     //删除目录

                     if(file.listFiles().length == 0) {

                         file.delete();

                     }

                 }

             }

         } catch(Exception e) {

             e.printStackTrace();

         }

     }

 }

MD5方法:

publicclassMD5Until {

    publicstaticcharHEX_DIGITS[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',

            'A', 'B', 'C', 'D', 'E', 'F'};

    //将字符串转化为位

    publicstaticString toHexString(byte[] b){

        StringBuilder stringBuilder = newStringBuilder(b.length * 2);

        for(inti = 0; i < b.length; i++) {

            stringBuilder.append(HEX_DIGITS[(b[i] & 0xf0) >>> 4]);

            stringBuilder.append(HEX_DIGITS[b[i] & 0x0f]);

        }

        returnstringBuilder.toString();

    }

    publicstaticString md5(String string){

        try{

            MessageDigest digest = java.security.MessageDigest.getInstance("MD5");

            digest.update(string.getBytes());

            bytemessageDigest[] = digest.digest();

            returntoHexString(messageDigest);

        }catch(NoSuchAlgorithmException e){

            e.printStackTrace();

        }

        return"";

    }

}

QQ信息解密方法:

public class MessageDecode {

    public String imeiID;

    public intimeiLen;

    public MessageDecode(String imeiID)

    {

        this.imeiID = imeiID;

        this.imeiLen = imeiID.length();

    }

    public boolean isChinese(bytech) {

        intres = ch & 0x80;

        if(res != 0)

            returntrue;

        returnfalse;

    }

    public String timeDecode(String time)

    {

        String datetime = "1970-01-01 08:00:00";

        SimpleDateFormat sdFormat = newSimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        try{

            longsecond = Long.parseLong(time);

            Date dt = newDate(second * 1000);

            datetime = sdFormat.format(dt);

        } catch(NumberFormatException e) {

            e.printStackTrace();

        }

        returndatetime;

    }

    public String nameDecode(String name)

    {

        bytenbyte[] = name.getBytes();

        byteibyte[] = imeiID.getBytes();

        bytexorName[] = newbyte[nbyte.length];


        intindex = 0;

        for(inti = 0; i < nbyte.length; i++) {

            if(isChinese(nbyte[i])){

                xorName[i] = nbyte[i];

                i++;

                xorName[i] = nbyte[i];

                i++;

                xorName[i] = (byte)(nbyte[i] ^ ibyte[index % imeiLen]);

                index++;

            } else{

                xorName[i] = (byte)(nbyte[i] ^ ibyte[index % imeiLen]);

                index++;

            }

        }

        return new String(xorName);

    }

    public String uinDecode(String uin)

    {

        byteubyte[] = uin.getBytes();

        byteibyte[] = imeiID.getBytes();

        bytexorMsg[] = newbyte[ubyte.length];

        intindex = 0;

        for(inti = 0; i < ubyte.length; i++) {

            xorMsg[i] = (byte)(ubyte[i] ^ ibyte[index % imeiLen]);

            index++;

        }

        returnnewString(xorMsg);

    }


    public String msgDecode(byte[] msg)

    {

        byteibyte[] = imeiID.getBytes();

        bytexorMsg[] = newbyte[msg.length];

        intindex = 0;

        for(int i = 0; i < msg.length; i++) {

            xorMsg[i] = (byte)(msg[i] ^ ibyte[index % imeiLen]);

            index++;

        }

        return new String(xorMsg);

    }

}

附录:有关微信、QQ的技术文章汇总

微信朋友圈千亿访问量背后的技术挑战和实践总结

腾讯技术分享:腾讯是如何大幅降低带宽和网络流量的(图片压缩篇)

腾讯技术分享:腾讯是如何大幅降低带宽和网络流量的(音视频技术篇)

微信团队分享:微信移动端的全文检索多音字问题解决方案

腾讯技术分享:Android版手机QQ的缓存监控与优化实践

微信团队分享:iOS版微信的高性能通用key-value组件技术实践

微信团队分享:iOS版微信是如何防止特殊字符导致的炸群、APP崩溃的?

腾讯技术分享:Android手Q的线程死锁监控系统技术实践

微信团队原创分享:iOS版微信的内存监控系统技术实践

让互联网更快:新一代QUIC协议在腾讯的技术实践分享

iOS后台唤醒实战:微信收款到账语音提醒技术总结

腾讯技术分享:社交网络图片的带宽压缩技术演进之路

微信团队分享:视频图像的超分辨率技术原理和应用场景

微信团队分享:微信每日亿次实时音视频聊天背后的技术解密

QQ音乐团队分享:Android中的图片压缩技术详解(上篇)

QQ音乐团队分享:Android中的图片压缩技术详解(下篇)

腾讯团队分享:手机QQ中的人脸识别酷炫动画效果实现详解

腾讯团队分享 :一次手Q聊天界面中图片显示bug的追踪过程分享

微信团队分享:微信Android版小视频编码填过的那些坑》 

微信手机端的本地数据全文检索优化之路》 

企业微信客户端中组织架构数据的同步更新方案优化实战

微信团队披露:微信界面卡死超级bug“15。。。。”的来龙去脉

QQ 18年:解密8亿月活的QQ后台服务接口隔离技术

月活8.89亿的超级IM微信是如何进行Android端兼容测试的

以手机QQ为例探讨移动端IM中的“轻应用”

一篇文章get微信开源移动端数据库组件WCDB的一切!

微信客户端团队负责人技术访谈:如何着手客户端性能监控和优化

微信后台基于时间序的海量数据冷热分级架构设计实践

微信团队原创分享:Android版微信的臃肿之困与模块化实践之路

微信后台团队:微信后台异步消息队列的优化升级实践分享

微信团队原创分享:微信客户端SQLite数据库损坏修复实践》 

腾讯原创分享(一):如何大幅提升移动网络下手机QQ的图片传输速度和成功率》 

腾讯原创分享(二):如何大幅压缩移动网络下APP的流量消耗(下篇)》 

腾讯原创分享(三):如何大幅压缩移动网络下APP的流量消耗(上篇)》 

微信Mars:微信内部正在使用的网络层封装库,即将开源》 

如约而至:微信自用的移动端IM网络层跨平台组件库Mars已正式开源》 

开源libco库:单机千万连接、支撑微信8亿用户的后台框架基石 [源码下载]》 

微信新一代通信安全解决方案:基于TLS1.3的MMTLS详解》 

微信团队原创分享:Android版微信后台保活实战分享(进程保活篇)》 

微信团队原创分享:Android版微信后台保活实战分享(网络保活篇)》 

Android版微信从300KB到30MB的技术演进(PPT讲稿) [附件下载]》 

微信团队原创分享:Android版微信从300KB到30MB的技术演进》 

微信技术总监谈架构:微信之道——大道至简(演讲全文)

微信技术总监谈架构:微信之道——大道至简(PPT讲稿) [附件下载]》 

如何解读《微信技术总监谈架构:微信之道——大道至简》

微信海量用户背后的后台系统存储架构(视频+PPT) [附件下载]

微信异步化改造实践:8亿月活、单机千万连接背后的后台解决方案》 

微信朋友圈海量技术之道PPT [附件下载]》 

微信对网络影响的技术试验及分析(论文全文)》 

一份微信后台技术架构的总结性笔记》 

架构之道:3个程序员成就微信朋友圈日均10亿发布量[有视频]》 

快速裂变:见证微信强大后台架构从0到1的演进历程(一)

快速裂变:见证微信强大后台架构从0到1的演进历程(二)》 

微信团队原创分享:Android内存泄漏监控和优化技巧总结》 

全面总结iOS版微信升级iOS9遇到的各种“坑”》 

微信团队原创资源混淆工具:让你的APK立减1M》 

微信团队原创Android资源混淆工具:AndResGuard [有源码]》 

Android版微信安装包“减肥”实战记录》 

iOS版微信安装包“减肥”实战记录》 

移动端IM实践:iOS版微信界面卡顿监测方案》 

微信“红包照片”背后的技术难题》 

移动端IM实践:iOS版微信小视频功能技术方案实录》 

移动端IM实践:Android版微信如何大幅提升交互性能(一)

移动端IM实践:Android版微信如何大幅提升交互性能(二)

移动端IM实践:实现Android版微信的智能心跳机制》 

移动端IM实践:WhatsApp、Line、微信的心跳策略分析》 

移动端IM实践:谷歌消息推送服务(GCM)研究(来自微信)

移动端IM实践:iOS版微信的多设备字体适配方案探讨》 

信鸽团队原创:一起走过 iOS10 上消息推送(APNS)的坑

腾讯信鸽技术分享:百亿级实时消息推送的实战经验

IPv6技术详解:基本概念、应用现状、技术实践(上篇)

IPv6技术详解:基本概念、应用现状、技术实践(下篇)

腾讯TEG团队原创:基于MySQL的分布式数据库TDSQL十年锻造经验分享

微信多媒体团队访谈:音视频开发的学习、微信的音视频技术和挑战等

了解iOS消息推送一文就够:史上最全iOS Push技术详解

腾讯技术分享:微信小程序音视频技术背后的故事

腾讯资深架构师干货总结:一文读懂大型分布式系统设计的方方面面

微信多媒体团队梁俊斌访谈:聊一聊我所了解的音视频技术

腾讯音视频实验室:使用AI黑科技实现超低码率的高清实时视频聊天

腾讯技术分享:微信小程序音视频与WebRTC互通的技术思路和实践

手把手教你读取Android版微信和手Q的聊天记录(仅作技术研究学习)

>> 更多同类文章 ……

(本文同步发布于:http://www.52im.net/thread-1992-1-1.html

目录
相关文章
|
1月前
|
安全 Android开发 iOS开发
安卓与iOS的较量:技术深度对比
【10月更文挑战第18天】 在智能手机操作系统领域,安卓和iOS无疑是两大巨头。本文将深入探讨这两种系统的技术特点、优势以及它们之间的主要差异,帮助读者更好地理解这两个平台的独特之处。
46 0
|
22天前
|
安全 搜索推荐 Android开发
揭秘安卓与iOS系统的差异:技术深度对比
【10月更文挑战第27天】 本文深入探讨了安卓(Android)与iOS两大移动操作系统的技术特点和用户体验差异。通过对比两者的系统架构、应用生态、用户界面、安全性等方面,揭示了为何这两种系统能够在市场中各占一席之地,并为用户提供不同的选择。文章旨在为读者提供一个全面的视角,理解两种系统的优势与局限,从而更好地根据自己的需求做出选择。
58 2
|
23天前
|
存储 监控 算法
社交软件红包技术解密(三):微信摇一摇红包雨背后的技术细节
本文将由微信团队工程师张文瑞分享微信春节摇一摇红包技术背后的方方面面,希望能给同行们带来启发。
45 1
|
24天前
|
安全 搜索推荐 Android开发
揭秘iOS与安卓系统的差异:一场技术与哲学的较量
在智能手机的世界里,iOS和Android无疑是两大巨头,它们不仅定义了操作系统的标准,也深刻影响了全球数亿用户的日常生活。本文旨在探讨这两个平台在设计理念、用户体验、生态系统及安全性等方面的本质区别,揭示它们背后的技术哲学和市场策略。通过对比分析,我们将发现,选择iOS或Android,不仅仅是选择一个操作系统,更是选择了一种生活方式和技术信仰。
|
29天前
|
安全 Android开发 iOS开发
iOS与安卓:技术生态的双雄争霸
在当今数字化时代,智能手机操作系统的竞争愈发激烈。iOS和安卓作为两大主流平台,各自拥有独特的技术优势和市场地位。本文将从技术架构、用户体验、安全性以及开发者支持四个方面,深入探讨iOS与安卓之间的差异,并分析它们如何塑造了今天的移动技术生态。无论是追求极致体验的苹果用户,还是享受开放自由的安卓粉丝,了解这两大系统的内在逻辑对于把握未来趋势至关重要。
|
30天前
|
安全 搜索推荐 Android开发
揭秘iOS与Android系统的差异:一场技术与哲学的较量
在当今数字化时代,智能手机操作系统的选择成为了用户个性化表达和技术偏好的重要标志。iOS和Android,作为市场上两大主流操作系统,它们之间的竞争不仅仅是技术的比拼,更是设计理念、用户体验和生态系统构建的全面较量。本文将深入探讨iOS与Android在系统架构、应用生态、用户界面及安全性等方面的本质区别,揭示这两种系统背后的哲学思想和市场策略,帮助读者更全面地理解两者的优劣,从而做出更适合自己的选择。
|
8天前
|
人工智能 小程序 算法
微信小程序地图定位的核心技术与实际应用详解
在移动互联网时代,微信小程序凭借其轻量化和普及性,成为室内地图导航的理想平台。本文探讨了微信小程序在室内定位领域的创新应用,包括蓝牙iBeacon定位、高精度地图构建及AI路径规划等核心技术,及其在购物中心、医院、机场火车站和景区等场景的应用,展示了其为用户带来的高效、智能的导航体验。
91 0
|
1月前
|
安全 Android开发 iOS开发
安卓vs iOS:探索两种操作系统的独特魅力与技术深度###
【10月更文挑战第16天】 本文旨在深入浅出地探讨安卓(Android)与iOS这两种主流移动操作系统的特色、优势及背后的技术理念。通过对比分析,揭示它们各自如何塑造了移动互联网的生态,并为用户提供丰富多彩的智能体验。无论您是科技爱好者还是普通用户,都能从这篇文章中感受到技术创新带来的无限可能。 ###
53 2
|
1月前
|
机器学习/深度学习 人工智能 Android开发
安卓与iOS:技术演进的双城记
【10月更文挑战第16天】 在移动操作系统的世界里,安卓和iOS无疑是两个最重要的玩家。它们各自代表了不同的技术理念和市场策略,塑造了全球数亿用户的移动体验。本文将深入探讨这两个平台的发展历程、技术特点以及它们如何影响了我们的数字生活,旨在为读者提供一个全面而深入的视角,理解这两个操作系统背后的哲学和未来趋势。
33 2
|
25天前
|
搜索推荐 安全 Android开发
安卓与iOS的哲学对话:技术生态中的选择与命运
【10月更文挑战第24天】 在智能设备的世界里,安卓和iOS不仅是操作系统的简单对立,它们代表了不同的技术哲学和生态策略。本文将探讨这两种系统背后的设计理念、用户体验差异以及它们如何塑造我们的数字生活,从而引发对于“我们如何选择技术”这一命题的深入思考。
下一篇
无影云桌面