自定义博客皮肤VIP专享

*博客头图:

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

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

博客底图:

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

栏目图:

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

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

-+

老猿的自留地

聚焦移动端 追寻技术本源

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

原创 类的load方法和initialize方法对比

load方法是用户代码能够最早被访问到的方法,并且类只有有load方法,就一定会在应用程序main方法调前被调用;load方法和initialize方法的这种差异, 决定了如果我们要对类的方法进行swizzle操作,比较靠谱的方式是将swizzle相关代码写在load方法中,确保相关逻辑一定会执行。

2024-09-08 10:34:45 293

原创 block对变量捕获的方式

block的捕获方式其实并不是对对象调用copy方法,而是进行值copy。对于基础类型和对象类型, 在block中进行赋值操作, 需要添加__block关键字,对于静态局部变量和全局变量, 就无需添加__block关键字。 对于非赋值操作,也无需添加__block关键字。

2024-09-07 10:33:00 318

原创 Android事件分发及响应机制

若onTouchEvent返回true,则由当前View处理该点击事件,否则, 调用父View的onTouchEvent,尝试由父View响应处理。在自上而下进行事件派发的同时,下层子View并非只能被动接受事件派发,其可以通过设定 FLAG_DISALLOW_INTERCEPT 值,来反向制约上层View的拦截事件。返回值为true,则进入事件响应流程。事件响应流程与事件分发方向相反,即从子View到父View,直到事件被响应或事件被传递到根视图。若返回值为false,则进一步派发到子view中。

2024-03-14 10:36:52 501

原创 LinearLayout和RelativeLayout对比

3. RelativeLayout的子View如果高度和RelativeLayout不同,会导致RelativeLayout在onMeasure()方法中做横向测量时,纵向的测量结果尚未完成,只好暂时使用自己的高度传入子View系统。使用RelativeLayout时需注意上层的view可能不遮挡遮挡下层View的展示,但遮挡其点击事件。2. 实现复杂的UI布局,LinearLayout通常需要更多的布局层级。RelativeLayout的在复杂嵌套布局中,可以降低布局嵌套层级。

2024-03-14 10:21:43 757

原创 常见数据编码方式对比

由64个可打印字符组成,比hex编码更难阅读,但由于每3个字节会被编码为4个字符,数据若不是3个字节的整数倍, 结尾用=填充。所以,空间占用会是原来的4/3,比hex要节省空间。另外要注意的是,虽然Base64编码后的数据难以阅读,但不能将其做为加密算法使用,因为它解码都不需要你提供密钥啊。主要解决url中的中文或不可见字符或与url冲突的字符的编码问题。英文字符原样保留,而对于非英文内容,每个字节会被编码为%xx的3个字符,空间占用是原来的3倍,所以urlEncode是一个对英文友好的编码方案。

2024-03-14 09:27:41 558

原创 Android apk 打包及签名

alias meet:指定别名为meet,你同样可以自定义别名。-validity 10000:指定证书的有效期限为10000天。3. 执行gradle的assemble命令,完成打包及签名。-keysize 2048:指定密钥的长度为2048位。-genkey:指示keytool生成一个新的密钥对。-keyalg RSA:指定密钥算法为RSA。//Demo需要给同学们使用则暂不混淆。//是否启动ZipAlign压缩。-v:显示详细输出信息。:指定生成的Keystore文件名为。,你可以自定义文件名。

2024-03-13 15:57:48 778

原创 Android编译架构选择

具体选哪一种就看自己的考量了,以性能换兼容就arm64-v8,以兼容换性能armeabi,二者稍微平衡一点的就armeabi-v7a。只适配armeabi的APP可以跑在armeabi,x86,x86_64,armeabi-v7a,arm64-v8上。: 只适配了armeabi-v7a,那如果APP装在其他架构的手机上,如arm64-v8a上,会崩吗?只适配armeabi-v7a可以运行在armeabi-v7a和arm64-v8a。只适配arm64-v8a 可以运行在arm64-v8a上。

2024-03-13 15:48:14 352

原创 Android开发环境搭建常见问题

(1)在浏览器打开此网址手动下载http://services.gradle.org/distributions/gradle-5.4.1-all.zip。(2)再次点击Installed右边的设置(齿轮)选择Install Plugin from Disk选项。(3)Android studio - Settings, 搜索gradle, 选择使用下载的本地版本。(3)选择不使用http proxy,或者Auto detect proxy settings。(2)搜索proxy,(2)解压缩zip文件。

2024-03-13 15:40:45 439

原创 Android UI卡顿检测

mainLooperPrinter,并在mainLooperPrinter中判断start和end,来获取主线程dispatch该message的开始和结束时间,并判定该时间超过阈值为主线程卡慢发生,并dump出各种信息,提供开发者分析性能瓶颈。启动一个卡顿检测线程,该线程定期的向UI线程发送一条延迟消息,执行一个标志位加1的操作,如果规定时间内,标志位没有变化,则表示产生了卡顿。缺点:轮询不优雅,有不确定性,随机漏报。微信采用的即是该方案。优点:简单,稳定,结果论,可以监控到各种类型的卡顿。

2024-03-13 11:47:59 383

原创 Android的UI渲染机制(二)

当界面有变化或者主动调用invalidate申请重绘, 会首先标记当前UI需要重新绘制,但绘制不是同步的,只有当下一次vsync信号到来时, 才会执行measure/layout/draw开始绘制。由于每次都是在收到下一个vsync信号才开始绘制, 一旦上一帧出现jank, 由于backing_buffer还在占用, 下一帧的绘制会被延迟到下下一个vsync开始。实际上,我们可以另外再开辟一个buffer,替代被占用的backing_buffer,下一帧仍然在vsync到来时就开始绘制,这就是三缓冲策略。

2024-03-13 10:34:15 622

原创 Android的UI渲染机制(一)

当客户端 addView() 或者需要更新 View 时,会通过 SharedBufferClient写入数据到 ShareClient 中,SurfaceFlinger 中的 SharedBufferServer 接收到通知会将 FrameBuffer 中的数据传输到屏幕上。调用Activity的onCreate函数。ViewRoot是View视图体系的根,每一个Window(注意是Window,比如PhoneWindow)有一个ViewRoot,它的作用是处理layout和View视图体系的绘制工作。

2024-03-13 09:58:30 1057

原创 Android ArrayList、LinkedList 和 Vector 的区别

5.LinkedList提供了List接口没有提供的方法,方便数据的头尾操作。2.ArrayList扩容时按照50%增加,Vector按照100%增加。1.ArrayList非线程安全的,Vector是线程安全的。4.LinkedList是链表实现的,因此查询慢,增删快。3.ArrayList的性能要高于Vector。

2024-03-12 16:54:13 423

原创 Android垃圾回收机制

(3)新生代使用复制算法进行垃圾回收时,实际不是1:1分成两块,而是8:1:1分成eden、from、to三块。之所以有from、to两个小块,是因为部分对象可能在进入老生代前多次gc都不释放,那2次gc、3次gc就可以在from和to块中进行复制移动。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用的内存空间一次清理掉。从GC Roots作为起点,向下搜索它们引用的对象,生成一棵引用树,树的节点视为可达对象,反之视为不可达。虚拟机栈(帧栈中的本地变量表)中引用的对象。

2024-03-12 15:21:03 506

原创 Hash冲突常见解决方案

从发生冲突的那个单元起,按照一定的次序,从哈希表中找到一个空闲的单元。然后把发生冲突的元素存入到该单元的一种方法。在开放定址法中解决冲突的方法有:线行探查法、平方探查法、双散列函数探查法等。

2024-03-12 14:51:43 391

原创 Android中ANR机制

ANR时对用户可感知,比如拥有当前前台可见的activity的进程,或者拥有前台通知的fg-service的进程,这些是用户可感知的场景。input是在每次收到新的input事件时开始检测,并在开始事件派发时设置埋雷时间点,同时检查队列中所有待处理的事件是否有正在处理并且超时的,有则ANR, 没有则重置所有事件ANR超时时间。引爆炸弹:中控系统立即封装现场,抓取快照,搜集目标执行慢的罪证(traces),便于后续的案件侦破(调试分析),最后是炸毁目标。input则采用的就是这种机制。

2024-03-12 11:47:13 512

原创 RecyclerView三种局部刷新实现

在Adapter中获取数据不直接从model中获取,而改为从AsyncListDiffer中获取。实质仍然是对DiffUtil的包装。2. 使用DiffUtil, 只更新数据有变化的item, 25.1.0版本开始支持。3. AsyncListDiffer,数据比较在子线程,27.1.0版本开始支持。1. RecyclerView自带的局部刷新。缺点:数据比较在主线程,可能ANR。缺点:要知道数据变化index。

2024-03-12 11:37:09 1377

原创 Handler实现原理

Handler是Android中提供的一种异步回调机制,也可以理解为线程间的消息机制。为了避免ANR,我们通常会把一些耗时操作(比如:网络请求、I/O操作、复杂计算等)放到子线程中去执行,而当子线程需要修改UI时则子线程需要通知主线程去完成修改UI的操作,则此时就需要我们使用Handler机制来完成子线程与主线程之间的通信。这种实现方式的好处是,无需在Handler的创建位置重写handleMessage方法,直接在发送Runnable时在Runnable的run方法中实现消息的处理逻辑即可。

2024-03-12 09:57:48 499

原创 Android自定义binder实现进程间通信

服务中创建一个binder实例, 在接收到客户端的连接时,向请求方返回回binder的引用。用即可调用其binder.transact方法与远端进行通信。transact有三个个关键参数,分别是消息的命令码、入参及返回值的引用。定义一个binder的服务(在androidManifest.xml中声明)接受远端请求。调用bindService方法,根据Binder服务名称建立与服务的连接。连接成功后, 可以获得远程服务中binder的引用。

2024-03-12 09:21:10 535

原创 Android中多线程实现

我们使用Thread类时,可以创建Thread的子类并重写其run方法, 或者自定义一个实现Runable协议的类,然后将其与Thread进行绑定。AsyncTask只能在UI线程调用,一般作为Activity的子类, 以便在重写的方法中访问 Activity中的属性和数据。Android中多线程主要有三种实现方式:通过Thread类、AsyncTask或者RxJava。使用RxJava的好处是,通过采用这种封装好的异步调度框架,可以大大简化线程间切换的过程。2. 通过AsyncTask。

2024-03-11 17:43:58 664

原创 Android App冷启动耗时优化

Android应用启动过程,主要包含app::onCreate及执行前的Application阶段及Activity::onCreate执行之后的Activity阶段,以及两个阶段之间的间隙handleMessage阶段和最终页面渲染上屏完成前数据加载阶段四个区间组成。在app:onCreate执行完成后,主线程会收到大量来自子线程的消息,当主线程执行完这些任务后,才会开始调用Activity的onCreate方法,开启应用首屏页面的解析、布局、绘制过程。这一阶段是我们最早的执行预加载的时机。

2024-03-11 12:45:19 550

原创 Android中单例模式正确实现方式

instance为null时加锁,不为null时不加锁,效率有提升,但由于android编译优化,有可能类未完成构造函数的调用, 就已经将instance指向了分配的地址空间,进而根据instance是否为空判断是否加锁不可靠,导致线程不安全。注意, 这里加的是synchronized(Singleton.class)字节码锁, 因为class对象在工程启动后是唯一的。volatile关键字的作用是, 禁止指令重排序,保证多线程时每次访问该变量时, 都从主内存中更新该变量在本线程中的本地副本。

2024-03-11 10:38:58 600 1

原创 volatile能保证线程安全吗

根据java的内存管理模型(JMM),所有的共享变量都存储于主内存(包括实例变量和类变量,不包含临时变量),每一个线程还存在自己的工作内存,线程的工作内存,保留了被线程使用的变量的工作副本。如果多线程调用increase方法,可能两个线程同时获取mCount,执行mCount+1,然后都赋值结果2, 即多线程访问时,可能打印出重复的值。volatile关键字的作用是, 禁止指令重排序,即在多线程访问该变量时, 每次都从主内存中更新该变量在本线程中的本地副本。volatile只能保证这3步不被重排序。

2024-03-11 10:21:46 870

原创 Android主界面多Tab实现方式

每个页面都各自继承Framement, 可以方便的在onCreateView实现比较复杂的页面逻辑,并且页面逻辑与主界面逻辑分离。实现方式和(3)是一致,但是使用了TabPageIndicator作为tab的指示器,可以实现更丰富的效果,同时也适用于顶部多Tab场景。支持滑动切换Tab,页面不必是Fragment, 直接使用普通的自定义View即可。优点类似RecyclerView。封装最少, 需要自己通过FragmentManager管理Fragment的显示和隐藏,缺点事。不支持滑动切换Tab。

2024-03-11 09:59:50 841

原创 Android中MultiDex优化

启动App的时候,先显示一个简单的Splash闪屏界面,然后启动Worker线程执行MultiDex#install(Context)工作,就可以避免UI线程阻塞。缺陷:需要手动分包, 确保应用正常启动,并且启动MultiDex#install(Context)所需要的类都在主dex里面,而且需要处理好进程同步问题。大致思路是,使用内置的apk更新功能把新版本的apk文件下载下来后,先在Worker线程里做好MultiDex的解压和Optimize工作。(2)异步MultiDex方案。

2024-03-11 09:47:18 614

原创 Android中进程保活方式

(3)Jobservice,在系统空闲时间或其它指定条件执行一些任务。获取系统的Jobscheduler,向其发送自定义的任务交由其进行统一调度,系统会在自定义任务指定的时机回调onStartJob方法,在Jobservice中实现该方法即可。这里的任务可以是通过Handler向主线程发送个事件,主线程可以什么都不做,或者只要打印下日志即可。(2)前台服务,主要用于特定的后台继续执行的场景,如音乐。(1)一像素acitivity前台运行方式, 不推荐, 即使系统资源不足,也强制运行,太暴力,不推荐使用。

2024-03-11 09:38:49 581

原创 Android中View对象的实例化方式

LayoutInflater的inflate方法,除了需要传入Context和Xml布局中的resId外,还有一个可选参数,用来标识生成的view是否需要挂载到父View上。View.inflate主要用于自定义View场景,该方法的第三个参数为父视图,若该参数为null,或者传入的viewGroup的rootview为null,view.getLayoutParams()无法获取视图的布局参数。不过,这种方式也存在较大限制,要求resId标识的视图必须严格是方法调用对象的子视图。

2024-03-11 09:27:58 614

原创 Flutter中动画的实现

动画三要素控制动画的三要素:Animation、Tween、和AnmaitionControllerAnimation: 产生的值的序列,有CurveAnimation等子类,, 可以将值赋值给Widget的宽高或其他属性,进而控制widget发生变化Tween:可以定义值的变化范围, 继承自,每个类都有begin和end两个属性,同时有CurveTween等子类,可以定义值在begin和end间的变化方式。

2024-03-08 09:48:01 1152

原创 Flutter性能优化

(1)图片等资源访问使用内存缓存及本地文件缓存。Flutter对GlobayKey组件有缓存,对构建复杂,并且复用率比较高的组件,合理使用GlobayKey可以提升性能。但需注意, 使用GlobayKey查找组件state是全局查找,有较大性能开销,查找不能滥用。

2024-03-06 10:58:28 1942

原创 Flutter App代码混淆

flutter build apk –obfuscate --split-debug-info 命令需要指定输出调试文件的位置,该命令会生成一个符号映射表。相关命令的其他信息,可以运行 flutter build apk -h 查看,如果不支持该命令,核实 Flutter 版本,执行 flutter upgrade 更新。Flutter 应用的混淆非常简单,只需要在构建 release 版应用时结合使用 --obfuscate 和 --split-debug-info 这两个参数即可。

2024-03-05 18:02:30 1173

原创 Flutter图片内存占用过大问题

根据Image创建实例时调用的工厂方法的不同(Image.network或者Image.assetImage,使用ImageProvider的不同子类,如NetworkImage、AssetImage分别通过网络或本地获取图⽚字节数据(load)并进行解码,创建 ImageStream⽤于监听结果。Flutter内存占用较大,还可能是因为另外一种原因:Native侧和Flutter侧都使用大量相同的图片,图片在两种环境中均被加载到内存。), 以避免缓存过大的图片原图,造成不必要的内存及磁盘消耗。

2024-03-05 17:30:52 1260

原创 Flutter整体框架

Flutter整体框架由三部分组成:Framework、Engine和Embedder。Framework是纯Dart语言实现的一个响应式框架,由许多抽象的层级组成。Framework最顶端是各种Widgets。包括我们经常用到的 Material 和 Cupertino两种风格的Widget以及下面的不带明确风格的Widgets。比如:UI/文本/图片/按钮等基础 Widgets。在 Widget 层下面,你会发现 Rendering 层。再往下是RenderObject。

2024-03-04 12:31:06 630

原创 Flutter的线程模型

GPU Task Runner运行的线程对应平台的子线程,并且和UI Task Runner运行在不同的线程上。而执行大量高CPU的运算类耗时任务,如耗时计算,编解码等,即便代码是异步执行,由于所有代码都运行在Root Isolate中,所以仍然会导致Root Isolate没法及时处理其他异步任务,从而导致UI卡顿。当运行到系统IO接口时,会将IO任务交给系统线程处理,并将异步任务加入到事件队列,然后Root Isolate回到事件循环,获取事件队列中的下一个任务进行处理。平台对线程的实现方案。

2024-03-04 11:08:39 697

原创 Platformview在iOS与Android上的实现方式对比

具体做法是,重写PlatformViewWrapper的draw方法,通过surface.lockHardwareCanvas()获得canvas画板,然后 将native组件直接绘制到该画板上,换言之,上述过程将Native组件的canvas替换为TextureWidget的对应的canvas,进而实现Native组件在Flutter的显示。该方案优点是可以完整捕获Platformview上的点击、滑动等事件,但由于每添加一个Platformview,额外会在其上新增一个全屏的。通过textureId。

2024-03-04 09:39:56 951

原创 Flutter混合栈管理方案对比

flutter_thiro是哈喽单车团队提供的一个解决方案,其与flutter_boost的主要不同是,flutter_boost的导航切换都是由native侧驱动,每次页面切换native侧都会创建一新的页面放到导航栈中,而flutter_thrio在native之间及native和flutter之间的页面切换同样由native侧驱动,但flutter页面内部的切换由flutter自带的Navigator来管理,native侧导航栈不创建对应的页面容器。,并且由于pop出的页面就会销毁,

2024-03-03 17:49:03 2087

原创 Dart中的事件队列与微任务

Future中的任务会加入下一轮的任务队列,then中的任务则是也会加入同样的任务队列。Future中的任务和then后的任务可以理解为同步任务,因此,若Future嵌套微任务,会优先执行then中的代码, 然后分别是微任务队列中的代码、事件队列中的代码。注意: 8先于7执行,因为已经调度到了当前队列中,8在6之后是立刻执行的,而7是异步执行的。注意: 9不是在10之后执行,因为9是在嵌套中下一层的队列中,因此需要等下一个消息循环才会执行。执行顺序为12,1,11,3,4,6,5,7,9,10,8,2。

2024-03-02 19:47:21 439

原创 Flutter异常上报及性能监控实现

页面PV监测核心是检测到页面栈的变化,以便统计出页面曝光次数。如果应用采用自定义导航框架,只需在导航框架正确位置添加埋点即可。若采用的是系统默认的导航框架,则可通过继承NavigatorObserver,捕获Navigator的didPush和didPop回调。在Flutter中,通常用FlutterError监测Flutter框架抛出的异常,用runZonedGuarded监测应用中用户代码异常。通常需要保留最近若干个FrameTiming的总耗时,计算其平均值,以消除不必要数据波动。

2024-03-02 09:22:42 1117

原创 Flutter中的三棵树

当Widget发生变化时,对比Element tree上对应位置上新旧Widget是否一致,只有不一致时才会重建Element并重建并重新进行RenderObject实例化。即RenderObject Tree只需进行增量的重建即可,这就大大提升了渲染效率。,创建出renderObject,并关联到element.renderobject属性上,最后完成RenderObject Tree的创建。, 页面的更新,可以根据Element变化的内容,只修改对应的RenderObject Tree,即进行。

2024-03-01 17:45:11 703

原创 Flutter中Widget的生命周期

StatelessWidget创建后不能改变,StatefulWidget可以改变,但其实也是通过重建的方式进行改变,因此代价较大,要避免滥用,防止整个UI全部重建。:包含此 State 对象的 Widget 被移除之后调用,若此 Widget 被移除之后未被添加到其他 Widget 树结构中,则会继续调用 dispose 方法;:initState 调用之后调用,或者使用了 InheritedWidget 组件会被调用,其中 InheritedWidget 可用于Flutter 状态管理;

2024-03-01 17:17:21 979

原创 Flutter中Future和Stream关系

其类似于一个异步的 Iterable,不同的是,当你向 Iterable 获取下一个事件时它会立即给你,但是 Stream 则不会立即给你而是在它准备好时告诉你。一个Stream可以通过Stream.fromFuture方法将一个Future转换为Stream, 也可以通过Stream.fromFutures将多个Future添加到Stream中。,Future中的任务会加入下一轮事件循环,而Stream中的任务则是加入微任务队列。Future和Stream类是Dart异步编程的核心。

2024-03-01 16:57:56 817

原创 Meta class的作用是什么

RootMetaclass继承于RootClass,而RootClass中的方法都是实例方法,若在RootClass中存在签名相同的方法, 方法是可以正确调用的。再加一个类方法指针。但将实例方法和类方法都存储一个类中,在消息传递时又需要对两种类型的消息进行不同的处理,可能会导致传递过程的复杂化。有一个比较有意思的点需要注意,我们调用类方法时,有可能消息会被转发到根类的实例方法。在往上根元类(root metaclass)指向自己,形成了一个闭环,一个完备的设计。形成了一个闭环,形成了一个完备的设计。

2024-03-01 10:50:06 479

iOS最新面试题及参考答案

iOS大v zhangferry写的iOS面试题总结, 内附详细答案。是iOS初、中、高级开发者在找工作准备面试的重要参考资料。面试题涵盖iOS方方面面的知识点,又不仅限于iOS,还包含算法、性能优化、网络等方方面面。

2024-02-21

LeetCode 101:和你一起你轻松刷题(C++版)

本书分为算法和数据结构两大部分,又细分了十五个章节,详细讲解了刷 LeetCode 时常用 的技巧。 本书以 C++ 作为编程语言。 本书题目精简到了 101 道,主要是不想让读者阅读和练习时间过长。掌握这 101 道题,读者就可以对算法和数据结构的掌握有比较扎实的认知,可大幅提升个人在职场上的竞争力。

2024-02-19

初识WebAssebly

第一部分:WebAssebly概念 第二部分:为什么会出现&性能 第三部分:基本用法 第四部分:WebAssebly总结

2024-02-17

iOS单元测试最佳实践

iOS单元测试最佳实践

2024-02-17

RXSwift中文版文档

RXSwift中文版文档

2024-02-17

空空如也

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

TA关注的人

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