- 博客(131)
- 收藏
- 关注
原创 Android广告投放过程分析
本文系统介绍了移动应用深度链接技术及其在Android平台的应用实现。首先从运营角度区分了深度链接和延迟深度链接的概念,前者直接引导用户至应用内特定页面,后者先引导未安装用户下载应用。然后详细解析了Android平台三种链接类型:深度链接(支持自定义URI)、网页链接(HTTP/HTTPS)和Android App Links(需autoVerify验证)。文章还阐述了Appsflyer的OneLink技术实现,包括直接深度链接和延迟深度链接的工作流程,以及广告投放中的归因机制。最后简要介绍了两种常见广告投
2025-11-26 18:11:50
924
原创 SYN关键字辨析,各种锁优缺点分析和面试题讲解
本文详细解析了Java中synchronized的实现原理和锁的优化机制。synchronized基于Monitor对象实现同步,通过对象头的MarkWord存储锁信息,包含偏向锁、轻量级锁和重量级锁三种状态。文章分析了自旋锁的优缺点及适用场景,比较了不同锁状态的性能特点,并指出锁只能升级不能降级。最后通过面试题形式讨论了synchronized修饰方法与可见性问题,以及悲观锁/乐观锁的区别。关键点包括:锁状态转换、自旋锁优化、偏向锁适用场景等,为理解Java同步机制提供了全面指导。
2025-11-01 17:49:42
954
原创 AQS底层原理、运用和volatile详解
摘要 本文介绍了阻塞队列和线程池的概念及其应用。阻塞队列是一种支持线程安全的队列,常用于生产者-消费者模式,平衡生产与消费速度。常见的阻塞队列包括ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue等,各有不同的实现方式和适用场景。线程池(ThreadPoolExecutor)通过复用线程降低资源消耗,提高响应速度和管理性,适用于高并发任务处理。文章详细分析了线程池的参数配置、核心类关系及不同阻塞队列的特性,如DelayQueue适用于延迟
2025-10-25 20:47:36
633
原创 并发基础知识补全和CAS原理
本文介绍了Java中线程的启动方式和死锁问题。启动线程主要有两种方式:继承Thread类或实现Runnable接口。文章重点讲解了死锁的四个必要条件:互斥条件、占用并等待、不可抢占和循环等待,并通过示例代码演示了死锁的产生过程。为避免死锁,提出了三种解决方案:按固定顺序加锁、使用超时机制(tryLock)和随机退避策略。这些方法通过破坏死锁的必要条件来预防死锁发生,确保多线程程序的安全运行。
2025-10-19 16:39:43
841
原创 多线程性能优化基础
本文介绍了Java多线程编程中的锁机制和生产者消费者模型的实现。在锁机制部分,分别讲解了类锁、对象锁和显示锁的应用场景及实现方式,包括synchronized关键字和ReentrantLock的使用规范。生产者消费者模型部分通过代码示例展示了共享资源的安全问题,指出由于线程间资源不共享导致的数据不一致问题。文章强调了多线程编程中同步机制的重要性,为处理并发问题提供了基础解决方案。
2025-10-17 21:28:03
695
原创 线程与进程理论知识入门
本文主要探讨了CPU核心数、线程数与多线程编程的关系。首先解释了CPU多核心和多线程技术的基本概念,指出核心数与线程数通常为1:1关系,但超线程技术可实现1:2。接着介绍了CPU时间片轮转机制,分析了时间片设置对系统性能的影响。在进程和线程部分,强调进程是资源分配的最小单位,线程是CPU调度的最小单位。文章还区分了并发(交替执行)和并行(同时执行)的概念,并阐述了高并发编程的优势:提高CPU利用率、加快响应速度、实现模块化编程,同时提醒注意线程安全、死锁和资源耗尽等问题。最后指出Java程序天生就是多线程的
2025-10-08 21:31:45
719
原创 Android性能优化之Apk瘦身
【摘要】随着业务迭代,APK体积逐渐增大,影响应用性能。本文介绍了APK组成结构及多种优化方法:1)使用Android Size Analyzer插件分析体积;2)通过shrinkResources移除未使用资源,配合Lint工具物理删除;3)配置resConfigs保留指定语言资源;4)动态库优化,按CPU架构分包;5)使用矢量图替代位图,通过tint属性复用资源。这些方法可有效减少APK大小,提升加载速度和运行效率。(150字)
2025-09-27 18:53:34
641
原创 Android模拟简单的网络请求框架Retrofit实现
本文介绍了静态代理和动态代理两种设计模式。静态代理通过中介类间接访问目标对象,实现业务逻辑与控制分离,但每个接口需要单独编写代理类,导致代码冗余。动态代理在运行时动态生成代理类,可同时代理多个接口,避免重复代码。静态代理适用于简单场景,而动态代理更灵活,适合接口频繁变化的系统。两种代理都能在不修改目标对象的前提下增强功能,但动态代理的扩展性更优。
2025-09-06 22:41:50
1004
原创 一篇搞懂HTTP的概念、原理、工作机制、数据格式和REST
摘要:本文介绍了HTTP协议的基本概念和工作方式。HTTP作为超文本传输协议,与HTML共同用于网络请求和传输。文章详细解析了URL格式、HTTP报文结构、请求方法(GET、POST、PUT、DELETE等)、状态码、Header类型及其作用,包括Content-Type、Content-Length等常见首部。此外还涉及REST设计风格和HTTP缓存机制,最后通过选择题测试读者对HTTP基础知识的掌握程度。全文为理解HTTP协议及其应用提供了系统性的介绍。
2025-08-30 17:10:49
796
1
原创 Java高级语言特性,注解与反射
本文介绍了Java注解的基本概念和使用方法。主要内容包括:1)注解的声明方式,使用@interface关键字;2)元注解@Target和@Retention的使用,分别限制注解作用范围和存储方式;3)注解类型元素的定义,支持默认值设置;4)注解的三种应用场景:源码级别(SOURCE)用于IDE语法检查,编译时(CLASS)处理,运行时(RUNTIME)保留。特别说明了@IntDef注解如何替代枚举实现参数限制,以及注解处理器(APT)在编译时处理注解的机制。通过示例代码展示了注解的具体应用方式。
2025-08-23 21:50:43
1042
原创 Android Crash监控
Android Crash监控机制分析 本文主要分析了Android系统中Java层和Native层的Crash监控机制。对于Java层Crash,主要通过Thread.UncaughtExceptionHandler接口捕获未处理异常,可自定义实现该接口将崩溃信息保存到本地。系统默认使用KillApplicationHandler处理未捕获异常,最终杀死应用进程。Native层Crash则通常由系统signal信号触发,机制与Java层不同。文章还展示了Java层Crash监控的具体实现代码,包括异常信息
2025-07-19 12:03:46
871
原创 Android使用拦截器结合协程实现无感知的Token预刷新方案
本文提出了一种Token预刷新方案,在Token接近过期时提前刷新以避免用户登录中断。方案采用拦截器实现,通过双重锁检查确保线程安全,并在刷新失败时引导用户重新登录。关键点包括:提前5分钟计算刷新时间、使用独立Retrofit实例防止循环调用、通过协程机制实现异步刷新。该方案能有效提升用户体验,避免传统Token过期导致的请求失败问题。实现代码以Kotlin编写,适用于Android平台。
2025-06-15 10:45:11
794
原创 Kotlin中的::操作符详解
摘要:Kotlin的::操作符支持成员引用,简化高阶函数编程。主要用法包括:引用类成员(如String::length)、构造函数(如::Person)、顶层函数、扩展函数,以及获取KClass对象。实际应用场景包括作为高阶函数参数(如users.map(User::name))和绑定实例方法。编译时会被优化为函数接口对象,如String::length转为fun(s:String):Int = s.length。这种机制使函数传递更简洁高效。
2025-05-29 19:56:24
778
原创 Kotlin委托机制使用方式和原理
本文介绍了Kotlin中的类委托和属性委托机制。类委托通过by关键字实现类似Java代理模式的效果,委托类持有真实类对象并转发方法调用。属性委托则是对属性set/get方法的委托,被委托类需要提供setValue/getValue方法。文中通过代码示例展示了两种委托的使用方式,包括接口定义、实现类、委托类以及主函数调用,并提供了反编译后的Java代码展示底层实现原理。最后通过输出结果验证了委托机制的正确性,说明Kotlin通过by关键字简化了代理模式的实现。
2025-05-29 17:14:30
1636
1
原创 一篇搞定Android方法耗时监控插件开发
摘要:本文介绍了如何通过自定义Gradle插件结合ASM框架来统计方法耗时,并在耗时超过阈值时通过Log日志定位耗时方法。ASM是一个高效的字节码操作框架,支持通过ClassVisitor、ClassReader和ClassWriter等工具读取、修改和生成类字节码。插件通过Transform Action在编译时对指定包名的类进行插桩,过滤不需要插桩的类。具体实现中,ClassVisitor用于访问类结构,MethodVisitor用于插入耗时统计代码,最终通过Log输出耗时方法和线程信息。该插件可以帮助
2025-05-10 20:37:32
1123
1
原创 一篇学会Android Gradle插件开发
自定义插件的时候经常会有这种自定义配置的需求,通过自定义的配置可以让我们的插件提供更丰富的能力。这些配置就是通过扩展插件来的。增加定义了一个SubExtension类,然后在YiRanPluginExtension实例化的时候加到ExtensionContainer中。类嵌套的话,不能写成内部类,不然编译识别不过。
2025-04-26 18:43:53
1721
1
原创 Android Gradle多渠道打包
当渠道配置越来越多的时候,app目录下的build.gradle文件就会显得有些不易阅读和维护,这时候可以将配置模块化,把渠道相关配置抽成一个channel.gradle文件,然后在app/build.gradle文件中apply依赖进来,这样可以更好的管理和维护渠道项目的渠道配置,app/build.gradle文件也会少一些。除了资源文件和代码文件之外,我们的依赖可能会根据渠道有所不同,比如在做推送功能的时候,在打华为渠道包的时候,只依赖华为的推送,而不依赖oppo的推送,也就是根据渠道来配置依赖项。
2025-04-20 14:01:24
1173
1
原创 Android性能优化之内存分析
内存抖动就是不停的在新生代创建对象,创建对象很多,会有空间分配担保,新生代空间比较小占1/3,老年代2/3,当新生代空间不够时,老年代会腾挪一部分空间给新生代,导致老年代空间下降,这个时候假设创建了大对象,从新生代经过了from到to,年龄足够到了老年代,这个时候空间不够,如果没有连续的空间,就会导致OOM。GC过程中会暂停用户线程包括UI线程,Stop the World,频繁的创建,释放对象,GC频繁出现,就会导致App整体卡顿。引用计数法:一个对象身上没有引用,不可用,这个对象就会被回收。
2025-04-06 16:47:35
877
原创 Android Compose入门和基本使用
Jetpack Compose是用于构建原生Android界面的新工具包,是一种声明式UI框架。状态是可以变化的任何值。在Compose函数中,如果数据状态没有发生改变,则UI永远不会自行改变。在Compose中,每个组件都是一个被@Composable修饰的函数,其状态就是函数的参数。因为Composeable是个函数,只能通过传参,当参数不变,则函数的输出就不会变,唯一的参数决定唯一输出。反言之,如果要让界面发生变化,则需要改变界面的状态,然后 Composable 响应这种变化。
2025-04-06 16:46:39
4886
原创 UML中类和类之间关系和各种图
百度百科截图:首先了解一下软件开发生命周期:做需求(项目经理,产品跟客户去谈,比如要求这个页面怎么样,点击按钮出现什么效果)–>形成文档(需要什么描述清楚)–>系统设计(设计一下整个功能有那些类,需要准备的数据库表)–>开发人员编写代码–>测试–>运维…这里重点在于系统设计:就好比在实际生活中要盖一栋大楼,一栋大厦,这个时候是不是需要一个设计的图纸,因为只有设计好之后,才知道设计的合不合理。如果设计得不好,就会导致楼塌,豆腐渣工程,如果设计的好,就可以给建筑工人按照图纸进行设计。
2024-09-28 17:29:25
1973
1
原创 Android中的冷启动,热启动和温启动
在App启动方式中分为三种:冷启动(cold start)、热启动(hot start)、温启动(warm start)
2024-09-16 22:04:30
4290
5
原创 Android自定义View实现不同朝向字体变色
实现效果:1.一个文字两种颜色2.实现不同朝向3.结合ViewPager思路:TextView可行?系统提供的只能够显示一种颜色,需要自定义Viewextends TextView:onMeasure()不需要实现 textColor颜色,textSize字体大小会少很多逻辑。1.一个文字两种颜色 画2.能够从左到右,从右到左3.整合到ViewPager,监听滚动事件自定义属性,不变化的颜色 originColor 变化的颜色 changeColorx要在这个位置。
2024-09-01 22:51:15
587
原创 Android invalidate方法源码分析
mLayoutRequested: 默认为 false,意味着只会执行 performDraw(), 不调用performMeasure()和performLayout()Invalidate()方法是Android中View的方法,通过用来调用onDraw方法,进行UI上的刷新。依次执行 performMeasure(),performLayout(),performDraw()最终绘制到 调用了invalidate()的View的 onDraw()方法;不断的绘制子孩子,再绘制子孩子的子孩子,
2024-08-31 17:05:50
594
原创 Android 为什么子线程不能更新UI
开了线程,更新UI 一般会调用setText(),setImageView 回调到这里面来 ViewRootImpl checkThread(),checkThread()用来检测线程。而如果在子线程中更新UI,调用checkThread的时候Thread.currentThread()就是子线程,然后和mThread 不相等,就会抛出异常。其中的mThread在构造函数中赋值mThread = Thread.currentThread();
2024-08-25 18:10:06
504
原创 Android自定义简单仿QQ运动步数进展圆环
useCenter 是否使用中心:true,连接矩形中心及弧;false不显示,(是否显示半径连线,true表示显示圆弧与圆心的半径连线,false表示不显示)(Paint.Style.FILL时)连接弧的起点终点。2.可以变化的小圆弧(红色) color borderWidth。1.固定蓝色的大圆弧 color borderWidth。顺时针,起点从135度开始扫了270度的距离到终点。3.中间的步数文字 color textSize。左边距离=控件宽度的一半减去文字宽度的一半。
2024-08-25 17:25:51
362
原创 Android自定义简单TextView
-name 属性名称format 格式:string 文字 color颜色dimension 宽高 字体大小 integer数字reference 资源引用(drawable)--background是自定义View管理的,可以不用 --> < attr name = " YiRanBackground " format = " reference|color " />
2024-08-23 23:20:47
1330
原创 Android更改包名和签名
2.手动修改app的build.gradle文件中的applicationId(改成和我们之前修改的包名相同)输入 keystore 的密码后,就可以在终端上看到 keystore 签名文件的配置信息了,如下图所示。所有可以检查本地Java的配置环境有没有问题,也可以在AndroidStudio看一下使用的Java环境。这个问题我这边的解决的办法主要是Java jdk的环境配置有问题,找不到对应的tool工具。keytool -list -v -keystore 你的keystore文件的绝对路径。
2024-08-20 18:06:56
3985
原创 一篇搞懂Android事件分发机制
但是listview的dispatchTouchEvent我们自定义的listview重写了,返回false,所以handled返回false,所以if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) 条件不成立,所以for循环会询问一下一个子view,但是我们自定义的ViewPager子View就一个listView,所以for循环就退出了。不会告诉子View分发事件,所以ListView处理不到,导致上下滑不动。
2024-08-18 11:06:29
931
1
原创 设计模式之责任链模式
在阅读OkHttp源码的过程中,我发现他在处理拦截器的过程中非常巧妙,结合了责任链模式,将Request以链路的方式传递下去,再将Response以方向链路传递上去。随着业务的发展,首页的弹窗越来越多,隐私政策弹窗,广告弹窗,好评弹窗,应用内更新弹窗等等。具体流程如上图所以,Chain作为链条,驱动着Request请求的开始,同时也是最终拿到Response的对象。能很清楚的看到每个链中对象的执行过程,按照顺序依次进入对象的执行过程,最后再反向将Response传递上去。请出我们的主角:责任链模式。
2024-04-01 10:11:34
1346
原创 Git中的fork,clone和branch
比如说现在有一个很牛逼的项目,我们进入项目地址,想将这个项目复制到自己的github仓库,然后你还想将仓库中的代码拉取到本地进行修改,修改完你还要将本地的修改后的代码推送到自己的github仓库,更甚至你还想将自己复制过来并修改后的代码再还给原作者,请求人家让你合并。接下来就可以对这个项目进行修改了,但是不建议在master分支上直接修改,最好是在master分支基础上切出一个dev分支,然后在dev分支上修改,修改完成后,再将dev分支merge到master分支。
2024-03-13 21:47:02
4000
原创 自定义View中的ListView和ScrollView嵌套的问题
这样在heightSize = mListPadding.top + mListPadding.bottom + childHeight +getVerticalFadingEdgeLength() * 2只是加了一个childHeight 所以展示的只有一个item的大小。因为Integer.MAX_VALUE右移两位等于一个30位的值,这样执行makeMeasureSpec就可以构建成一个32位的值。当我们在使用到ScrollView和ListView的时候可能会出现显示不全的问题。
2024-02-29 22:57:20
926
原创 Android组件化中的Arouter学习
因为生成文件中有很多group,有grouplogin,groupmain,要找到这些group调用loadInto方法,这样我们通过传入path路径,就能找到RouteMeta,就可以拿到class。当我们使用Arouter的注解处理器的时候,就会帮我们生成相应的类,Arouter$$Group $ $login就是login的group,而Arouter $ $Root $ $login就是去查找到group并调用group的方法。而当第二次,缓存到本地后,直接读取拿到信息,就不用再去找。
2024-01-31 22:47:30
1004
原创 Kotlin中常见的List使用
find和findLast函数返回与给定条件匹配的第一个或最后一个集合元素。在列表中,都可以使用indexOf() 或者lastIndexOf() 查询到元素在集合中的位置。node:如果集合中没有符合匹配条件的元素,返回true,否则返回false。我们注意到,截取后的集合,也是按照我们传入的下标顺序进行获取的。count函数返回集合中的元素总数或与给定条件匹配的元素数。这些函数检查是否存在与给定条件匹配的集合元素,并返回布尔值。该函数返回具有给定索引的集合元素列表。返回列表中第一个或最后一个元素的值。
2023-11-27 11:15:26
2385
原创 Java中的日期类整理
new SimpleDateFormat(“yyyy年MM月dd日 HH:mm:ss:SSS”).parse(“2022年10月31日 14:58:22:119”)s字符串中的日期格式必须和simpleDateFormat括号里的日期格式相同,才可以使用simpleDateFormat.parse(s)常见的格式:yyyy年MM月dd日 D HH:mm:ss:SSS(hh代表12小时制、SSS代表毫秒、D代表今天是这一年的第几天)包含两个构造器,Date()和Date(long);new Date();
2023-10-18 15:59:02
602
原创 Android中的RxJava入门及常用操作符
RxJava在GitHub的介绍// 翻译:RxJava 是一个在 Java VM 上使用可观测的序列来组成异步的、基于事件的程序的库也就是说:RxJava是一个基于事件流,实现异步操作的库。
2023-10-07 19:26:16
1240
1
原创 Android项目中使用Protobuf数据格式
Protobuf全称:Protocol Buffers,是Google推出的一种与平台无关,语言无关,可扩展的轻便高效的序列化数据存储格式,类似于我们常用的xml和json。问题:既然有了xml和json,Google为啥还要推出Protobuf呢?存在是有原因的,Protobuf 用两个字总结:小,快。用 Protobuf 序列化后的大小是 json 的十分之一,是 xml 格式的二十分之一,而且性能是他们的 5~100 倍。
2023-09-28 15:48:14
3033
原创 记录打开别人的Android项目进行构建时报Caused by: org.gradle.api.internal.plugins.PluginApplicationException: Failed
1.在Project视图模式下,选择gradle.properties文件。3.然后点击右上角蓝色部分的‘Sync Now’,稍等几秒进行同步即可完成。2.在最后一行(或者这个文件里的随便哪一行都可以),输入以下代码。个人感觉可能是我保存项目的路径有中文导致这个问题。这行代码的意思是“覆盖路径检查”。
2023-09-27 15:46:11
571
原创 Java中HashMap原理与分析
hash值的每个二进制位用abcde来表示,那么hash和新旧table按位与的结果,最后4位显然是相同的,唯一可能出现的区别就是第5位,也就是hash值的b所在的那一位,如果b所在的那一位是0,那么新table按位与的结果和旧table结果相同,反之如果b所在那一位是1,则新table按位与的结果就比旧table的结果多了10000(二进制),而这个二进制就是旧table的长度。JDK1.7中,由于多线程对HashMap的扩容,HashMap采用头插法,新插入的数据会从链表的头节点进行插入。
2023-08-06 21:58:49
398
原创 Android四大组件之服务
服务是Android中实现程序后台运行的方案,他非常适合去执行那些不需要和用户交互而且还要求长期运行的任务。服务的运行不依赖于任何用户界面,即使程序被切换到后台,或者用户打开了另外一个应用程序,服务仍然能够保持正常运行。每一个服务都需要在配置文件AndroidManifest.xml文件里进行生命,怎么生命呢?使用标签,其实跟前面的activity,广播接收者receiver一样生命。通过Context.startService()来开启服务,通过Context.stop()来停止服务。当然啦,还有一
2023-08-03 10:45:39
1205
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人
RSS订阅