自定义博客皮肤VIP专享

*博客头图:

格式为PNG、JPG,宽度*高度大于1920*100像素,不超过2MB,主视觉建议放在右侧,请参照线上博客头图

请上传大于1920*100像素的图片!

博客底图:

图片格式为PNG、JPG,不超过1MB,可上下左右平铺至整个背景

栏目图:

图片格式为PNG、JPG,图片宽度*高度为300*38像素,不超过0.5MB

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

-+

  • 博客(38)
  • 收藏
  • 关注

转载 Flutter 基础 | 动画框架分析及其中的设计模式

作者:唐子玄在阅读 Flutter 动画源码时收获颇多,深深地被它的设计框架及代码实现方式所折服~~若不关心源码,想直接上手动画实战代码可以跳到动画实例解析。动画源码解析动画值序列 Tween动画是为了缓解值的“跳变”,跳变体验不好。比如弹窗通常是由小变大(scale),由浅变深(alpha),为了让弹窗不那么突兀,就得生成 scale 和 alpha 的值序列,让弹窗一点一点变大,一点一点显现出来。在 Flutter 中生成动画值序列的工作是由Tween完成的。Tween 继承自A.

2022-03-07 15:22:08 509 1

原创 Handler消息机制和ThreadLocal原理

一、简介Handler作为Android线程间通信的常用方式,主要由Handler、Looper、MessageQueue、message四个部分组成 ,Handler负责线程间消息通讯的发送消息和接收处理消息Looper用于从MessageQueue中轮询获得MessageMessageQueue则是一个单线链表的用于存储Message的数据结构Message则是线程间通信的载体总体流程图如下简单使用,如下子线程发送message,主线程更新UIprivate Handler han

2022-03-06 21:55:56 2667

原创 Android 开发如何从应用深入到Framework?

前言作为一个基本上可以说是从0开始起步读源码,到现在已经完成了一系列源码剖析技术文章的作者来讲,我觉得我的经验还是有一定的可借鉴性的如何深入学习Framework源码?首先,我也是一个应用层开发者,我想大部分有“如何深入framework源码”这个疑问的,应该大都是应用层开发那对于我们来讲,读源码最大的问题,其实是没有应用场景,或者说短期来看成本高,收益底,容易半途而废一、针对这个问题,首先是要要有一定的定力和研究精神,打算拿下哪部分的源码分析,即使遇到再多的问题,也要想办法解决,自己定的目标

2022-03-04 21:01:49 341

转载 AMS中关于内存回收的一些操作

作者:tracyliu1.内存回收当 Android 应用程序退出时,并不清理其所占用的内存,Linux 内核进程也相应的继续存在,所谓“退出但不关闭”。从而使得用户调用程序时能够在第一时间得到响应。当系统内存不足时,系统将激活内存回收过程。为了不因内存回收影响用户体验(如杀死当前的活动进程),Android 基于进程中运行的组件及其状态规定了默认的五个回收优先级:IMPORTANCE_FOREGROUND:IMPORTANCE_VISIBLE:IMPORTANCE_SERVICE:I.

2022-03-04 14:37:16 461

转载 Android 自定义View流程解析

作者:QiShare1.简介在开发中,View视图具有非常重要的作用,它是直接呈现给使用者的,因此向用户展示精美高效的View视图很有意义。Android系统提供了丰富的视图组件,如TextView、ImageView、Button等,还提供了RelativeLayout、LinearLayout、FrameLayout等组合组件,使用这些组件搭配能实现良好的视图效果。但是,有时候我们需要实现更加个性化和有特点的视觉效果,使用系统提供的组件就比较难满足这种需求了,此时自定义View视图便派上用场了,.

2022-03-03 17:04:37 293

转载 Jetpack LiveData 观察它 响应它

作者:Jere_Chen前言作为Android开发者,如果你接触过Jetpack,那么相信你对LiveData肯定不陌生,它以观察者模式为设计理念,常常与ViewModel一起配合使用,响应式编程,从而实现数据驱动UI。今天让我们秉持知其然又知其所以然态度来深入学习一下LiveData。使用方法我们先来回顾一下其使用方法,举个简单的例子。class MyViewModel : ViewModel() { //3\. 更改被观察数据 private val _text = M.

2022-03-02 15:13:03 158

转载 “终于懂了” 系列:Android组件化,全面掌握

作者:胡飞洋一、背景随着项目逐渐扩展,业务功能越来越多,代码量越来越多,开发人员数量也越来越多。此过程中,你是否有过以下烦恼?项目模块多且复杂,编译一次要5分钟甚至10分钟?太慢不能忍?改了一行代码 或只调了一点UI,就要run整个项目,再忍受一次10分钟?合代码经常发生冲突?很烦?被人偷偷改了自己模块的代码?很不爽?做一个需求,发现还要去改动很多别人模块的代码?别的模块已实现的类似功能,自己要用只能去复制一份代码再改改?“这个不是我负责的,我不管”,代码责任范围不明确?只做了一个.

2022-03-01 14:12:08 2913 2

转载 精准化测试看ASM在Android中的强势插入-ASM

作者:徐宜生ASM是一个操纵字节码的开源工具,可以在编译期间对原始字节码插入一些新的逻辑,它通常会和Gradle Transform配合使用。ASM包含两种API使用方式——Core API和Tree API,大部分场景下都是使用Core API。Core API是基于事件访问的形式来表示类,把类抽象为一系列事件,每个事件表示类的一种元素,例如它的一个标头、一个字段、一个方法声明、一条指令等。Core API定义了一组可能事件,以及这些事件必须遵循的访问顺序,还提供了一个ClassVisitor.

2022-02-28 14:52:04 443

转载 Flutter布局指南之深入理解BoxConstraints

作者:徐宜生不管你是Android开发,还是Flutter开发,当你开始使用Flutter茫茫多的Widget时,可能会猜测Widget在屏幕上的尺寸和位置,但事实上,你会经历多次错误和失败,Flutter的Widget并不会总是像你想象的那样进行布局。如果不了解Widget的约束条件是如何应用的,就很难预测Widget的尺寸。很多时候,你根本不知道为什么一个Widget的尺寸比你预期的要大,或者比你想象的要小。因此,在这篇文章中,让我们试着了解约束条件是如何工作的,以及对Widget尺寸的影响。.

2022-02-27 20:34:32 573

转载 不再害怕面试问ArrayMap一文完全看懂Android ArrayMap源码解析

作者:VIjolie前言ArrayMap是谷歌推出的在安卓等设备上用于替代HashMap的数据结构,和HashMap相比,具有更高的内存使用率,因此适合在Android等内存较为紧张的移动设备,下面结合源码分析ArrayMap实现原理,主要分为添加数据、查找数据、删除数据以及缓存四部分,注意本文基于api29。构造函数首先先来来康康ArrayMap的构造函数如下:/** {@hide} */public ArrayMap(int capacity, boolean identityHashC.

2022-02-25 16:06:00 2063

转载 Kotlin coroutine单元测试的更好方法

作者:尼耳多如果你用Kotlin编程,你很有可能在异步工作中使用coroutines。然而,使用coroutines的代码也应该被单元测试。多亏了kotlinx-coroutines-test 库,乍一看,这似乎是一个简单的任务。然而,有一件事被许多开发者忽视了,它有可能使你的测试不可靠:coroutines如何处理异常。在这篇文章中,我们将介绍一个典型的项目情况,即一些生产代码调用coroutines。这段代码是经过单元测试的,但测试配置不完整,在coroutine内调用的代码可能会抛出一个异.

2022-02-24 14:49:54 848

转载 抽丝剥茧 Jetpack | Lifecycle 到底解决了什么问题?

作者:路遥TMLifecycle(生命周期) 在任何 GUI 编程中都是基石般的存在,Android 也不例外。作为用户,在页面跳转,旋转屏幕,查看通知,切换应用等日常操作中,都期望获得流畅连贯的使用体验。在这背后,就需要我们开发者在生命周期组件的不同阶段中进行相应的逻辑处理。这里的生命周期组件,可能是整个应用,也可能是单个页面。对应到 Android 中,Activity、Fragment,甚至 Service 都可以成为生命周期组件。现在假设这样一个需求,你需要封装一个播放器的基础组件,供各个.

2022-02-23 15:19:42 123

转载 听说你还不懂依赖任务启动框架?带你撸一个

作者:王晨彦前言我们在开发应用的时候,一般都会引入 SDK,而大部分 SDK 都要求我们在 Application 中初始化,当我们引入的 SDK 越来越多,就会出现 Application 越来越长,如果 SDK 的初始化任务相互依赖,还要处理很多条件判断,这时,如果再来个异步初始化,相信大家都会崩溃。有人可能会说,我都在主线程按顺序初始化不就行了,当然行,只要老板不来找你麻烦「小王啊,咱们的 APP 启动时间怎么这么久?」开个玩笑,可见,一个优秀的启动框架对于 APP 启动性能而言,是多么.

2022-02-22 14:24:04 237

转载 这是一份全面 & 详细的热修复学习指南

作者:Carson带你学Android热修复技术介绍重新发布版本代价大,成本高,不及时,用户体验差,对此有几种解决方案:Hybird:原生+H5混合开发,缺点是人工成本搞,用户体验不如纯原生方案好;插件化:移植成本高,对老代码的改造费时费力,而且无法动态修改;热修复技术,将补丁上传到云端,app可以直接从云端下来补丁直接应用;热修复技术对于国内开发者来说是一个比较实用的功能,可以解决如下问题:发布新版本代价较大,用户下载安装成本高;版本更新的效率问题,需要.

2022-02-21 14:51:43 160

转载 MVVM框架中Kotlin Flow的实践

作者:少冰半糖柠檬茶前言在 Google Android 团队宣布了 Jetpack 的视图模型之后,MVVM 架构已经成为了 Android 开发最流行的架构之一。如下图所示:不过在 Google 的前期官方文档中,其 Repository 层是直接使用 LiveData 的,而且连 Jetpack Room 也对 LiveData 进行了支持,接口可以直接返回 LiveData 的数据。所以在很长一段时间内,各种开源的 MVVM 框架或者博客中,也是在 Repository 层中直接使用 L.

2022-02-18 16:45:59 539

转载 Android为View添加拖放效果

作者:QiShare1.引言在开发中,拖放是一种比较常见的手势操作,使用它能够让应用的交互更加地便捷和友好,本文将简要介绍如何为Android中的View添加拖放效果。2.主要方法和类介绍2.1 startDragAndDrop()和startDrag()要实现View的拖放,需要调用View的startDragAndDrop()或startDrag()方法,其中startDragAndDrop()方法要求API版本为24或以上,调用方法后,View便可以拖动了,此方法需要传递的参数如下:/.

2022-02-17 16:18:45 350 1

转载 Shine——更简单的Android网络请求库封装

作者:FreddyChen写在前面距离上一篇文章跟我一起开发商业级IM(3)—— 长连接稳定性之连接及重连发布的时间,大概已有一年多,先跟大家说声抱歉。主要是因为工作太忙,业务需求过多,没办法专心写博客。先立个Flag:IM系列文章一定会坚持写完,同时Github项目也会逐步完善,敬请期待。这次就暂不更新IM系列相关的文章及项目了,先给大家带来一个稍微轻量级同时也比较实用的网络请求封装库:Shine,同时也希望自己借此机会重新拾起写博客和开源项目的激情,废话少说,我们直接开始吧。Shine是什么.

2022-02-16 13:38:24 513

转载 Gradle基础 | 自定义插件并上传到JitPack

作者:Petterp引言每一个使用 Gradle 的同学,肯定都听过或者写过插件,因为其本身并不难,但碍于现在网上的文章千篇一律,大部分都比较老,新同学一上手反而是和我一样,花了大把时间在最基础的第一步如何写一个简单demo上。再者如果大家使用 AndroidStudio BumBlebee 去创建项目,那对照网上教程差别更大,甚是花费时间,而本篇就是帮你省掉这些时间。本篇主要概括创建插件的三种方式,并如何上传到 JitPack 中。开发环境基于最新的 Gradle7.0.4 , Andro.

2022-02-15 17:13:47 163

转载 Compose中的国际化与本地化、暗黑模式与黑夜模式

作者:乐翁龙开篇闲谈这两年负责的都是面向海外(欧美、中东等)的项目,之前在View的时代下总结了一套国际化与本地化的经验,见《Android 国际化与本地化探索》,文中事无巨细的从 语言翻译 、 UI设计 、 代码规范 三个方面阐述了我的解决方案。切换到到Compose后,又完全处理了一遍国际化的流程。同时发现在适配暗黑模式中Compose提供了开箱即用的支持,大大简化了我们的开发难度,这篇文章就将经验分享给大家。文中若有纰漏之处,还望大家不吝赐教。国际化与本地化关于国际化中的翻译规范以及UI.

2022-02-14 15:21:40 414

转载 减少RxJava中多余的线程调度

作者:路人宇为什么要抑制线程调度对于一次可观察序列中的多次 subscribeOn 或者 observeOn 操作,哪怕指定在相同的 Schedulers.io 调度器上,观察者操作也会在不同的线程上执行,即发生从io线程到io线程的切换。这种线程调度是否可避免的呢?假如我们有以下代码:fun fetchItem(): Single<Item> { return download(itemId.getAndIncrement()) .flatMap(::un.

2022-02-13 21:54:57 211

原创 这些关于 Handler 的知识点你都知道吗?

在安卓面试中,关于 Handler 的问题是必备的,但是这些关于 Handler 的知识点你都知道吗?一、题目层次Handler 的基本原理子线程中怎么使用 HandlerMessageQueue 获取消息是怎么等待为什么不用 wait 而用 epoll 呢?线程和 Handler Looper MessageQueue 的关系多个线程给 MessageQueue 发消息,如何保证线程安全Handler 消息延迟是怎么处理的View.post 和 Handler.post 的区别Han

2022-02-12 21:09:50 226

原创 这几个点是 Android开发进阶提升的关键~

Android开发已经凉了吗?这两年有很多人有这方面的疑惑,在大方向讲,任何一个行业都有有一个成长周期,Android经历了2011-13年的野蛮生长后,已经进入了成熟期,这个阶段绝大部分企业对Android岗位的需求回归“理性”,不是懂一点Android基础就能找到工作的年代,这时候再从0起步可能比较晚,但Android市场却普遍缺乏高级人才。目前Android市场上初中级的工资6K到18K不等,高级大概在20K-35K,很多朋友初中级开发者卡在20K这个门槛始终迈不过去,想要学习又不知道该怎么着手

2022-02-11 21:30:00 258 1

转载 哪怕不学Gradle,这些常见操作,你也值得掌握

作者:Petterp引言Gradle 是每个 Android 同学都逃不开的一个话题。你是否看到别人的 Gradle 文件干净又卫生?而自己的又是一团乱麻????不用怕,本篇将结合我的开发日常,将一些常用的操作分享出来,希望可以帮到像我一样不怎么会[玩]Gradle 的同学,相信会对大家有所帮助。模板代码提取这是最基础的操作了,对于一个普通 model.gradle ,默认的配置如下:如果我们每个 model 都这样写,那岂不是很麻烦,那么让我们提取通用代码:优化步骤新建一个 grad

2022-02-11 14:13:01 345

原创 Android性能优化问题方案的总结~

虽然总说“英雄不问出处”,但大厂卡学历是默认的“潜规则”。不过最近一个老弟,让我挺振奋的!人家完全靠实力上岸。他就属于死磕型的,是我近2年见过的少有的Android性能优化高手。 要说他也挺聪明,贼会选领域。你出去随便问,10个大牛9个都会说精通性能优化对一名Android开发来说性价比最高!这不仅是大厂永恒的敲门砖,更是工作中最能突显价值的金字招牌。近几年面试进一步向深、向广。做了哪些性能优化呢(启动速度、电量、页面、内存…);如何减少APP启动时间;内存优化,对于内存泄露就必须得了解;还比如

2022-02-10 21:33:43 153 2

转载 浅谈Android开发中的MVVM模式

作者:miaowmiaow前言看到有人说 MVVM 核心是 双向绑定,没有使用 Databinding 的项目都是假 MVVM 。即:MVVM == 双向绑定,双向绑定 == Databinding,MVVM == Databinding。今天我们便来聊一聊 Android 中的 MVVM。MVVMMVVM(Model-View-ViewModel)最早由微软提出。ViewModel指 “Model of View”——视图的模型。Model:它仅仅关注数据本身,不关心任何行为。Vi.

2022-02-10 13:51:31 1538

转载 如何写出又臭、又长、难以维护的代码?

作者:唐子玄1多态多态是编程语言支持的一种特性,这种特性使得静态的代码运行时可能产生动态的行为,这样一来编程时不需要为类型所烦恼,可以编写统一的处理逻辑而不是依赖特定的类型。在星巴克消费的时候就会发生多态,多态方便了顾客。不管是现金,支付宝,亦或是微信,顾客不需要关心每种支付背后的细节,只需递交支付工具即可。收银员是产生多态行为的关键,他把同样的递交行为在结账机上动态地和不同的支付方式做了对接。深入结账机内部,假设它使用如下方式实现支付的多态:class PayManager { fu.

2022-02-09 14:18:56 572

转载 Flutter 必知必会系列 —— 随心所欲的自定义绘制

作者:Time_sun在 Flutter 中,framework 为我们提供了丰富的组件,一些常见的功能和样式都有组件直接提供,比如圆角、颜色、透明度、间距等等。然而当组件中有许多设计的元素时,就需要我们拿着画笔自定义绘制了。比如下面这样的:这个时候我们无法使用既有的组件组装成上面的效果,那我们就需要自己绘制成这样的效果。本篇文章就告诉大家 Flutter 中怎么绘制自定义的显示内容。绘制前的准备绘制组件 CustomPaint绘制一般考虑三个要素:画布(Canvers)、画笔(Pain.

2022-02-08 13:55:05 1029 2

转载 Android Studio 新特性详解

作者:郭霖前言在 2021 年早些时候举办的 Google I/O 大会上,我们详细介绍了 Android Studio Arctic Fox 的主要功能,该版本目前已经位于稳定版的发布渠道供大家下载使用。Android Studio Arctic Fox 主要聚焦于以下三个方面的改进:**1. 设计 : **Arctic Fox 是首个包含 Jetpack Compose 的支持工具及大量设计工具和检查器的稳定版本,从而让您可以更轻松地创建和预览界面。**2. Android 设备支持 : .

2022-02-07 14:31:00 418 1

转载 深入理解DiskLruCache源码

作者:岩浆李的游鱼leo2前言我们在用第三方框架的时候,比如glide,okhttp等,使用起来已经轻而易举,因为我们只管用。忽视了其用到的缓存技术。让我们一起来理解下缓存的本质。内存缓存一般现在用的是LruCache缓存,磁盘缓存是DiskLruCache。它们用的都是LRU算法(最近最少使用)。网络缓存当然就是网络请求,数据放在后端数据库了。LRU算法:Least Recently Used 即为近期最少使用。在缓存数据的时候,如果数据不存在缓存中,则放入缓存中,如存在缓存中,会将缓冲重新放入.

2022-01-26 14:52:26 171

转载 Android LiveData原理分析

作者:PG_KING前言官方介绍:LiveData 是一种可观察的数据存储器类。与常规的可观察类不同,LiveData 具有生命周期感知能力,意指它遵循其他应用组件(如 Activity、Fragment 或 Service)的生命周期。这种感知能力可确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者。它有以下的优势:确保界面符合数据状态不会发生内存泄露不会因Activity停止而导致崩溃不再需要手动处理生命周期数据始终保持最新状态适当的配置修改共享资源接下来我.

2022-01-25 15:01:12 763

转载 LiveData为啥连续postValue两次,第一次的值会丢失?

作者:付十一有一天,我正听着歌,开心的敲着代码,这时候小王同志急冲冲的跑过来拍了拍我的肩膀,“付老板,我这遇到一个问题,连续两次请求同一个接口,参数传的不同,但是livedata的onChange回调只走了一次,UI界面上只有一个地方更新成功了,这是咋回事啊?”一听有bug,这我可就来劲了,立马摘下耳机,“来,上代码”,“诶~ 你这是在UI层注册了一个监听,然后在请求接口的地方,利用livedata连续postValue两次。看情况你这应该是postValue搞的鬼,不急,我来模拟下场景,看.

2022-01-24 14:57:02 499 2

转载 【译】一篇掌握LiveData transformations

作者:xuyisheng这个系列我做了协程和Flow开发者的一系列文章的翻译,旨在了解当前协程、Flow、LiveData这样设计的原因,从设计者的角度,发现他们的问题,以及如何解决这些问题,pls enjoy it。在使用Android架构组件时,LiveData是一个很好的工具。在我知道如何使用Transformations类之前,我一直在滥用LiveData,并产生了大量的烂代码。在使用LiveData和架构组件的几年中,我想我已经找到了一些好的做法和模式,我想与你分享。The basics.

2022-01-21 16:09:07 259 1

转载 Android触控事件处理机制(基于Android 11)

作者:安卓M豆先生1 概述用户手指点击按压屏幕后,屏幕触控驱动产生中断,Linux内核会将硬件产生的触控事件包装成Event放在设备的dev/input/目录下。此后Android系统需要解决以下几个问题以实现整个触控事件的分发处理:如何从设备上读取触控事件?读取到触控事件后该如何派发事件?派发事件时如何找到接收事件的目标应用窗口?找到目标应用窗口后如何将事件传递到目标窗口?目标应用窗口内部中的事件如何处理?下面将结合最新Android 11系统源码,通过分析回答这些问题来了解And.

2022-01-20 17:05:05 2864

转载 拯救OOM 字节自研 Android 虚拟机内存管理优化黑科技 mSponge

作者:Android 平台架构本文描述的虚拟机内存管理优化方案,是从应用侧视角对 Android 虚拟机内存管理进行改造,优化了虚拟机对 LargeObjectSpace 的内存管理策略,间接增加其它内存空间使用上限。改造后的方案,32 位运行环境 LargeObjectSpace 的内存使用上限可达到 2G 甚至更多(64 位环境使用上限理论上会趋于无限大)。通过本方案可以最大程度上从系统侧解决诸多应用都会遇到的内存瓶颈和 OOM 问题,一键接入,安全可靠。1.背景Java OOM对于 An.

2022-01-17 14:17:07 298 1

原创 Android Handler机制

Looper是整个跨线程通信的管理者 // 内部持有的变量如下: ThreadLocal<Looper> MainLooper Observer MessageQueue Thread1.首先先回忆一下Handler怎么用Android线程通信分为以下两种情况1.子线程发消息给UI线程2.UI线程发消息给子线程3.子线程发消息给另个子线程1.子线程发消息给UI线程class FragmentContentActivity : A

2021-11-03 14:46:00 1191 1

转载 再见吧!内存泄漏!!!

作者:codelang检测内存是否泄漏非常简单,只要在任意位置调用 Debug.dumpHprofData(file) 即可,通过拿到 hprof 文件进行分析就可以知道哪里产生了泄漏,但 dump 的过程会 suspend 所有的 java 线程,导致用户界面无响应,所以又不能随意 dump。为了能找到合理的 dump 时机,leakCanary 就采用预判的方式,在 onDestroy 中先检测一下当前 Activity 是否存在泄漏的风险,如果有这种情况,就开始 dump。需要注意的是,在 on.

2021-10-28 15:18:47 210 1

转载 JetPack Compose 主题配色太少怎么办? 来设计自己的颜色系统吧

好文推荐:作者:Petterp引言JetPack Compose 正式版已经发布好几个月了,在这段时间里,除了业务相关需求之外,我也开始了 Compose 在实际项目中的落地实验,因为一旦要接入当前项目,那么遇到的问题其实远远大于新创建一个项目所需要的问题。本篇要解决的就是 Compose 默认 Material 主题颜色太少,如何配置自己的业务颜色板,或者说,如何自定义自己的颜色系统,并由点入深,系统的分析相关实现方法与原理。问题在开始之前,我们先看看目前创建一个 Compose 项目,默.

2021-10-27 21:26:37 305 1

原创 AndroidBAT高级面试合集——Binder 通信原理与机制

先上一张 Binder 的工作流程图。(如果不清晰,可以 复制图片链接到浏览器 或 保存到本地 查看,我经常都是这样看图的哈)一开始上手,陌生的东西比较多,But,其实并不复杂。喔,流程图是用 ProcessOn 画的。很棒的在线画图工具。出发前预备子弹 我们知道进程之间,虚拟地址不同,是不能直接通信的,这是 一种保护机制。打开任务管理器,查看一下 N 多的进程,试想一下如果这些进 程直接通信会带来什么后果?而用户空间可以通过 System calls(系统回调)与内核空间通信的,如果在内核 空间

2020-12-03 21:55:22 145

空空如也

空空如也

TA创建的收藏夹 TA关注的收藏夹

TA关注的人

提示
确定要删除当前文章?
取消 删除