写点什么

从零开始仿写一个抖音 App——日志和埋点以及后端初步架构

用户头像
Android架构
关注
发布于: 刚刚

讨论 1:项目会不会使用 kotlin?


  • 1.目前我的计划是在基础模块上面使用 java ,在业务模块中看情况选择几个模块使用 kotlin。


讨论 2:本系列文章是标题党,蹭抖音的热度


  • 1.首先明确一点为什么我要以抖音为例子,原因就是我的公司就是开发短视频的,技术上有类似的地方,而本公司的产品是不可能作为例子开发的,所以我就以抖音为例希望能过一遍大公司的项目开发流程和架构,不仅仅是给读者带来好处,对我来说也是一个很好的提升。

  • 2.当然不可否认的是抖音这个 title 给我带来了一定的流量,也吸引了一部分人的眼球,但是我问心无愧。因为每一篇文章的内容都是我花费两周以上的业余时间撰写的,内容的质量上我敢说比一般的文章要好上不少。

  • 3.有句话说得好:人红是非多,放在文章上也是一样。我不希望打无谓的口水仗所以:以后如果文章中有与技术和文章无关的攻击或者诋毁的评论我会直接删除,并且不做回复。

二、日志和埋点

日志在一个项目中起着非常重要的辅助作用,它可以让开发人员方便的定位 bug。它可以在系统上线之后让后台监控 app 的性能以及稳定性。他还可以收集用户的行为数据以方便对用户的需求进行分析。在这一节中我会分析 5 种不同的日志,并讲解其中几种日志的实现方式。


首先我先列举一下五种不同的 log 吧。


  • 1.debug 日志:用于开发人员本地 debug

  • 2.aop debug 日志:用于开发人员本地 debug, 使用了 aop 可以通过简单的注释,对方法和类进行切片打日志。用于打一些需要统一执行的日志。

  • 3.网络请求 日志:用于开发人员在本地对网络请求 debug

  • 4.本地文件 日志:用于记录在 app 上线之后出现的 bug,将日志打到文件中,可以通过一个入口让用户手动点击上传日志。

  • 5.埋点 日志:用于记录用户使用 app 的数据、app 性能等等的埋点日志,数据结构由前后端协商定义,最后会存入后端的数据库以便进行一些数据分析。埋点的方式可以是手动的,可以是自动的。

1、debug 日志


  • 1.debug 日志比较简单,如图一就是将 android 自身提供的 Log 类进行一些封装,添加一些自己需要的特性和扩展,这里就不多赘述了具体实现可以看项目中的代码。

2、aop 日志


  • 1.很多人在写一些重复性的日志的时候就会想到 aop,这种技术可以在注解的方法前后注入需要的模板代码。我在上一篇文章中讲到了这个技术,有兴趣的同学可以去看看,这里我就简单说一下。

  • 2.首先我们得先定义一个注解类,其可以用于注解类或者方法。注解类中可以被填入一些信息,比如是否需要打印方法的初入参等等。

  • 3.在注解类使用了之后,我们需要用到 gradle transform。这种技术可以让我们在编译期间扫描所有的类,从而找被注解类所注解的方法和类。

  • 4.最后我们可以用上 javassist 来给找到的方法前后注入我们需要的代码。注意这里的日志可以是本地的 debug 日志,也可以是本地文件日志,还可以是埋点日志。可以说 aop 日志只是一种对另外几种日志的自动化封装。

3、网络请求日志


  • 1.我们在调试网络请求的时候,除了抓包还会打印出网络请求。这个时候就如果有一种统一的形式来打印日志的话就会方便许多。

  • 2.现在绝大部分的厂商使用的网络请求库都是 okhttp ,所以我就直接在其上面进行日志的定制就行了。因为项目的 http 模块还没有进行开发,所以还没有实现代码,这里就讲一讲大致方案。之后在开发 http 模块的时候会顺便讲解具体实现。

  • 3.在讲解方案之前我们需要知道,okhttp 的工作方式。如图 3 中所示,在一个 okhttp 请求的过程中会经过一个个拦截器,从本地向网络请求的时候会经过一次,网络请求回来的时候又会经过一次。

  • 4.所以我们就可以添加一个日志拦截器在两次经过拦截器的时候打印请求的 head 和按需打印 请求的 body。注意,这里打印可以是向 debug 日志、本地文件日志、埋点日志这三个地方打印。分别用于本地 debug、线上 debug 和网络性能监控。

4、本地文件日志


  • 1.当我们在线上遇见 bug 的时候咋办呢?有些 crash 的日志可以通过 bugly 这种平台来进行回捞。但是有些奇葩的 bug 只在某些机型甚至某些用户的手机上发生。这个时候本地文件日志就派上用场了。

  • 2.我们可以在开发的时候在一些关键的功能上手动添加上本地文件日志。当某个用户报了 bug 之后我们就可以让其通过一个入口将文件日志发送到后台,最后由开发人员进行日志分析找到问题。

  • 3.接下来我就来通过代码结合上面的图 4 来讲解本地文件日志的实现方式。

  • 4.我们先来看看图 4:

  • 1.LocalFileLogger 负责提供本模块对外的 api,主要功能有两个:

  • 1.初始化和绑定 LocalFileLoggerService(这是一个 service,可以通过 binder 来与外部交互)

  • 2.通过 binder 将外部的添加日志的请求交给 LocalFileLoggerService

  • 2.LocalFileLoggerService 中会初始化一个 HandlerThread,本 Service 会通过 Handler 向其不断的抛入经过高性能拼接的日志的添加请求。

  • 3.FileLogger 是负责将日志写入本地的类,其也初始化了一个 HandlerThread,并且自定义了一个 LoggerHandler。这个 Handler 会将 LocalFileLoggerService 抛过来的一条条日志进行累积,当积累到了一定量的时候。发出写入日志的请求交给 HandlerThread 执行。






  • 5.再来看看代码,我们跟着代码走一遍流程:

  • 1.首先在图 5 中我们可以看见在 addLog 中经过一系列的调用,最终交给了 sLogInterface.log 这个对象是一个 Binder 对象,用于操作 LocalFileLoggerService 。

  • 2.进入到图 6,可以看见 Service 初始化了一个 HandlerThread 然后定义了一个 Handler 用于向其中抛送请求。然后在看 mBinder 的实现就是通过 Handler 向 HandlerThread 中抛送 FileLogger.addLog 的执行请求。

  • 3.进入到图 7,可以看见在 FileLogger 初始化的时候也初始化了一个 HandlerThread ,然后定义了一个 LoggerHandler 来向其中抛日志写入请求。FileLog


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


ger.addLog 方法中是直接发送一个请求。


  • 4.再看图 8,LoggerHandler.add 中并不会立即向本地写入日志,而是会有一 LOG_CACHE_COUNT 阈值,只有超过了这个阈值才会向文件系统中写入日志。

5、埋点日志


  • 1.埋点日志其实和文件日志类似,我这里就结合图 9 简单说一下,具体的代码大家可以去翻看项目

  • 2.首先还是有一个 UploadLogManager 用于给外部提供 api 以及初始化 LocalFileLoggerService。这里比文件系统复杂的地方就在于多了一个 UploadLogConfiguration 用于装配一些设置。

  • 3.有了 LocalFileLoggerService 之后这里分两个不同的埋点日志添加方式。

  • 1.实时埋点日志添加:外部需要立即将当前的埋点日志上报,此时就直接将请求发送给 UploadLogHandler 然后交给 HandlerThread 执行,最终 通过 LogSender 执行网络上报。

  • 2.非实时埋点日志添加:这种方式是每隔一定的时间,LocalFileLoggerService 会从 UploadLogStorage 中取出一定量的日志,合并之后再像 1 中一样上报埋点。

  • 4.目前因为 Http 模块和 数据库模块都没有开始写,所以 UploadLogStorage 和 LogSender 都还只是接口,但是并不影响代码逻辑。

三、后端架构的初步设想

虽然本项目的着重点是仿抖音 android 端 app 的开发,但是后台方面也会有所涉及。接下来笔者会介绍一下本项目在后端方面的目标和预期达到的效果。

1、RPC

可能会有客户端的同学对 RPC(远程过程调用) 这个词不怎么了解,我这里就先简单介绍一下。


拿 Java 来说:比如我们有两个服务 A、B 在两个服务器上,此时我们要在 A 上调用 B 的服务获取其上的数据 Foo。那么在 A 中可以写成 Foo f = b.XXXService();。在这里 Foo 是 A、B 两个服务所定义的数据传输结构,b 是 B 服务所抽象出来的对象,其内部实现可以是各种网络数据交互协议,比如说 http 协议。简单来说:RPC 就是要像调用本地的函数一样去调远程函数。


现存的 RPC 框架有很多,各个大厂也都开源了自己框架,我这里就介绍和比较一下几个框架,最后结合本项目的需求选择适合的框架。


  • 1.Dubbo:这个是阿里开源的一个框架,后来阿里因为种种原因把他废弃了,最后被当当网维护扩展出了一个 Dubbox。这里就讲一讲他的优劣势吧:

  • 1.优势:

  • 1.Dubbo 是用 java 写的,对于 android 客户端的开发者来说比较友好。

  • 2.Dubbo 的生态目前来说还是比较好的,笔者去年在有赞实习 java 开发的时候,用过半年的 Dubbo,感觉各种坑都有人踩过,各种库也都比较完善。

  • 3.对于服务治理支持的比较到位。

  • 2.劣势:

  • 1.跨平台能力差,原生的 Dubbo 基本上没有跨平台能力,后面的话集成了 thrift 作为扩展的话就有了,不过我总感觉集成之后用起来不方便。

  • 2.以 java 作为主开发语言的话,不能快速迭代。我们项目的时间主要是要向 android 客户端倾斜,所以需要一个能快速迭代的语言。

  • 3.序列化和反序列化的速度与其他 RPC 框架相比都不是很拔尖。

  • 4.性能较其他几个框架差。

  • 2.Thrift:这个是 FaceBook 开源的一个框架,2007 年由 facebook 贡献到 apache 基金,是 apache 下的顶级项目。

  • 1.优势:

  • 1.跨平台能力强,支持几乎所有的主流语言。

  • 2.性能比较好

  • 2.劣势:

  • 1.跨平台的语言协议写起来比较麻烦。

  • 2.不支持服务治理

  • 3.Grpc:由 Google 开源的框架,我司目前后端也在使用这个框架

  • 1.优势:

  • 1.跨平台能力强、支持大部分主流开发语言

  • 2.跨平台语言协议用的是 ProtoBuf,与我们客户端的技术栈一致。

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
从零开始仿写一个抖音App——日志和埋点以及后端初步架构