尾声
如果你想成为一个优秀的 Android 开发人员,请集中精力,对基础和重要的事情做深度研究。
对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。 整理的这些架构技术希望对Android开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。
这里,笔者分享一份从架构哲学的层面来剖析的视频及资料给大家梳理了多年的架构经验,筹备近6个月最新录制的,相信这份视频能给你带来不一样的启发、收获。
Android进阶学习资料库
一共十个专题,包括了Android进阶所有学习资料,Android进阶视频,Flutter,java基础,kotlin,NDK模块,计算机网络,数据结构与算法,微信小程序,面试题解析,framework源码!
大厂面试真题
PS:之前因为秋招收集的二十套一二线互联网公司Android面试真题 (含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)
《2019-2021字节跳动Android面试历年真题解析》
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
========================================================================================
@property (nonatomic, strong) UIButton *testBtn;
- (UIButton *)testBtn {
if (!_testBtn) {
_testBtn = [UIButton buttonWithType:UIButtonTypeCustom];
_testBtn.frame = CGRectMake(100, 100, 100, 100);
_testBtn.backgroundColor = [UIColor redColor];
[_testBtn addTarget:self action:@selector(testAction:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:_testBtn];
}
return _testBtn;
}
- (void)testAction:(UIButton *)sender {
NSLog(@"testAction >>>> before");
[NSObject cancelPreviousPerformRequestsWithTarget:self];
[self performSelector:@selector(delayAction:) withObject:@"delay" afterDelay:1.f];
NSLog(@"testAction >>>> after");
}
- (void)delayAction:(id)sender {
NSLog(@"delayAction >>>> sender:%@",sender);
}
- (void)viewDidLoad {
[super viewDidLoad];
[self testBtn];
}
1、点击一次:
2021-02-20 11:37:38.537893+0800 TestDemo[17102:8687749] testAction >>>> before
2021-02-20 11:37:38.538475+0800 TestDemo[17102:8687749] testAction >>>> after
2021-02-20 11:37:39.539686+0800 TestDemo[17102:8687749] delayAction >>>> sender:delay
2、连续多次点击
2021-02-20 11:48:12.665228+0800 TestDemo[17102:8687749] testAction >>>> before
2021-02-20 11:48:12.665806+0800 TestDemo[17102:8687749] testAction >>>> after
2021-02-20 11:48:12.836208+0800 TestDemo[17102:8687749] testAction >>>> before
2021-02-20 11:48:12.836669+0800 TestDemo[17102:8687749] testAction >>>> after
2021-02-20 11:48:12.993410+0800 TestDemo[17102:8687749] testAction >>>> before
2021-02-20 11:48:12.993900+0800 TestDemo[17102:8687749] testAction >>>> after
2021-02-20 11:48:13.153061+0800 TestDemo[17102:8687749] testAction >>>> before
2021-02-20 11:48:13.153571+0800 TestDemo[17102:8687749] testAction >>>> after
2021-02-20 11:48:14.154847+0800 TestDemo[17102:8687749] delayAction >>>> sender:delay
总结:
-
1、performSelector: withObject: afterDelay: 这个方法是单线程的,也就是说只有当前调用此方法的函数执行完毕后,selector方法才会被调用。
-
2、cancelPreviousPerformRequestsWithTarget 没有后续参数表示取消全部。
拓展:
1、当第一次触发时,performSelector函数不作延迟;之后,每次触发延迟处理的情况时。可以设置延迟时间 delay 参数为可变参数。
3、performSelector 与 dispatch_source_t(事件联结)的区别
===================================================================================================================
原理:
1、dispatch_source不依赖于Runloop,直接和底层内核交互,准确性更高。
2、performSelector依赖于Runloop运行,会因为主线被占用而产生延迟。
执行顺序:
1、dispatch_source在selector执行之前,需要触发事件,才产生事件联结。等selector中的方法执行完之后,才会出发下一次事件。
2、performSelector:只有当前调用此方法的函数执行完毕后,selector方法才会被调用。
应用场景:
1、dispatch_source:事件联结、高精度计时器
2、performSelector:时间延迟处理(按钮防抖处理)、即时搜索数据列表处理。
dispatch_source_merge_data的机制原理见:
=================================================================================
@interface NSObject (NSDelayedPerforming)
- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray *)modes;
- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay;
+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget selector:(SEL)aSelector object:(id)anArgument;
+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget;
@end
注意:以上方法中一个重要参数:anArgument,发现如果参数不为空,那取消时的参数也要一致,否则不能取消成功。
[self performSelector:@selector(didRuninCurrModel:) withObject:[NSNumber numberWithBool:YES] afterDelay:3.0f];
[self performSelector:@selector(didRuninCurrModelNoArgument) withObject:nil afterDelay:3.0f];
假如在三秒内执行以下取消方法,则结果分别为:
//可以取消成功。
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(didRuninCurrModel:) object:[NSNumber numberWithBool:YES]];
//不能取消成功。参数不匹配
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(didRuninCurrModel:) object:[NSNumber numberWithBool:NO]];
//不能取消成功。参数不匹配
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(didRuninCurrModel:) object:nil];
//可以成功取消
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(didRuninCurrModelNoArgument) object:nil];
//可以成功取消全部。
[NSObject cancelPreviousPerformRequestsWithTarget:self];
//可以成功取消全部。
[[self class] cancelPreviousPerformRequestsWithTarget:self];
==============================================================================
#import <UIKit/UIKit.h>
#define kDefaultInterval 0.2 //默认时间间隔
NS_ASSUME_NONNULL_BEGIN
@interface UIButton (AntiShake)
/// 设置防抖时间间隔
@property (nonatomic, assign) NSTimeInterval antiShakeTimeInterval;
@end
NS_ASSUME_NONNULL_END
#import "UIButton+AntiShake.h"
#import <objc/runtime.h>
@interface UIButton()
/// 是否忽略点击事件
@property (nonatomic, assign) BOOL isIgnoreEvent;
@end
@implementation UIButton (AntiShake)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
SEL selA = @selector(sendAction:to:forEvent:);
SEL selB = @selector(mySendAction:to:forEvent:); // B是自己定义的方法
Method methodA = class_getInstanceMethod(self,selA);
Method methodB = class_getInstanceMethod(self, selB);
BOOL isAdd = class_addMethod(self, selA, method_getImplementation(methodB), method_getTypeEncoding(methodB));
if (isAdd) {
class_replaceMethod(self, selB, method_getImplementation(methodA), method_getTypeEncoding(methodA));
}else{
method_exchangeImplementations(methodA, methodB);
}
});
}
- (void)mySendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event {
if ([NSStringFromClass(self.class) isEqualToString:@"UIButton"]) {
if (self.isIgnoreEvent) {
return;
}else {
self.antiShakeTimeInterval = self.antiShakeTimeInterval <= 0 ?kDefaultInterval:self.antiShakeTimeInterval;
}
self.isIgnoreEvent = YES;
[self performSelector:@selector(setIsIgnoreEvent:) withObject:[NSNumber numberWithBool:NO] afterDelay:self.antiShakeTimeInterval];
}
[self mySendAction:action to:target forEvent:event];
}
#pragma mark - Setting & Getting
- (NSTimeInterval)antiShakeTimeInterval {
return [objc_getAssociatedObject(self, _cmd) doubleValue];
}
- (void)setAntiShakeTimeInterval:(NSTimeInterval)antiShakeTimeInterval {
objc_setAssociatedObject(self, @selector(antiShakeTimeInterval), @(antiShakeTimeInterval), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void)setIsIgnoreEvent:(BOOL)isIgnoreEvent {
objc_setAssociatedObject(self, @selector(isIgnoreEvent), @(isIgnoreEvent), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (BOOL)isIgnoreEvent {
return [objc_getAssociatedObject(self, _cmd) boolValue];
}
## 文末
> 面试:如果不准备充分的面试,完全是浪费时间,更是对自己的不负责!
不管怎么样,不论是什么样的大小面试,要想不被面试官虐的不要不要的,只有刷爆面试题题做好全面的准备,当然除了这个还需要在平时把自己的基础打扎实,这样不论面试官怎么样一个知识点里往死里凿,你也能应付如流啊
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化学习资料的朋友,可以戳这里获取](https://bbs.youkuaiyun.com/topics/618156601)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
urn [objc_getAssociatedObject(self, _cmd) boolValue];
}
## 文末
> 面试:如果不准备充分的面试,完全是浪费时间,更是对自己的不负责!
不管怎么样,不论是什么样的大小面试,要想不被面试官虐的不要不要的,只有刷爆面试题题做好全面的准备,当然除了这个还需要在平时把自己的基础打扎实,这样不论面试官怎么样一个知识点里往死里凿,你也能应付如流啊
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化学习资料的朋友,可以戳这里获取](https://bbs.youkuaiyun.com/topics/618156601)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**