Android 设计模式概述
如何提高软件系统的可维护性和可复用性是面向对象程序设计思想需要解决的核心问题。在面向对象程序设计中,可维护性的复用是以设计原则为基础的。每一个设计原则都蕴含着面向对象程序设计的思想,可以从不同的角度提升一个软件系统的架构水平。
面向对象程序设计原则是为支撑可维护性的复用而诞生的,它们是从很多的设计方案中总结出来的指导性原则,通常体现在设计模式中。
OutOfMemoryError:pthread_create (1040KB stack) failed 异常分析
线上出现一些 OutOfMemoryError
,经过分析崩溃数据,发现出现 OOM
时进程的可用空间还是非常大的。
从崩溃的堆栈信息中可以分析出引发该 OOM
的主要与 OkHttp
有关系,而业务场景中都是普通的数据请求(JSON
),并没有使用 BitMap
这一类的较大内存占用的资源。更何况,进程的可用内存空间还是很充足的。
虽然堆栈信息不尽相同,但是最终从大量堆栈 LOG
中分析出有价值的信息:
1 | java.lang.OutOfMemoryError: pthread_create (1040KB stack) failed: Try again |
经过与源码文件进行比较,OkHttpStack
第73行的方法实际上是通过 OkHttpClient.Builder
来实例化一个 OkHttpClient
对象。
通常情况下我们所理解的 OOM
是由于进程的可用堆内存不够(即 Runtime.getRuntime().maxMemory()
小于需要申请的内存大小)的情况下系统会抛出 OutOfMemoryError
。其报错信息中详细的记录了我们的内存分配需求和可用堆内存的大小信息:
1 | java.lang.OutOfMemoryError: Failed to allocate a XXX byte allocation with XXX free bytes and XXXKB until OOM |
通过对 pthread_create
关键字的搜索,最终在 thread.cc 中找到了抛出该异常的位置,并且通过对 Thread::CreateNativeThread
函数的分析发现,在创建线程时,系统会先判断当前线程数是否超过了系统对线程数的限制,如果超过该限制则抛出 java.lang.OutOfMemoryError:pthread_create (XXXXKB stack) failed
异常。
那么,问题的根源在哪里?通过对代码的分析,发现每次发起一个网络请求的时候都会创建一个 OkHttpClient
,而每个 OkHttpClient
对象都会初始化一个线程池(线程的生命周期又较长),如果在短时间内发起多次请求,那么线程池会被创建多个,而线程数也会随之增加。解决的方案则是将 OkHttpClient
通过单例的方式对外提供。
针对上述线程创建的问题,可以参考 不可思议的OOM,作者针对出现类似问题的场景做了深入分析。
关于 HTTPClient
的问题,可以参考 OkHttp竟然玩出OOM?,作者针对为什么创建多个线程池的场景进行了深入的分析。
Android Studio 使用 Setting Repository 同步配置信息
Setting Repository
是 IntelliJ IDEA(Android Studio)
的一项配置托管服务,我们可以通过在 Github
或者其他 Git
服务上创建一个 Git Repository
的方式统一托管、同步配置信息:
- 在
Github
创建一个代码仓库(例如:android-studio-setting-repository
) - 在
Github
上创建一个Personal access tokens
(保留read
和write
相关权限即可) - 将版本库地址填入
Android Studio
的File/Setting Repository/Upstream URL
(尽量使用https
而不是ssh
的方式) - 点击
Overwrite Remote
完成配置信息的提交
Overwrite Remote
是将本地配置信息写入到远程仓库,Overwrite Load
是将远程仓库的信息写入到本地,Merge
则是合并两者之间的差异。
我的 Android Studio
配置仓库为 fiissh/android-studio-setting-repository。
Git Commit Message 模板
Commit Message 规范
每一个 Commit Message
应该包含一个 header
、body
和 footer
。其中,header
、body
和 footer
之间以空行作为间隔。header
是必须要填写的:
header
通常包含此次提交的类型:feat
新特性fix
bug
修复docs
文档改动style
格式化refactor
重构代码test
添加缺失的测试, 重构测试, 不包括生产代码变动chore
更新grunt任务等; 不包括生产代码变动
body
通常包含此次改动的影响范围、改动的详细信息footer
通常包含一些需要关闭的issues
信息等
模板文件
建议将如下内容保存到本机的某个目录,例如 MAC
的 ~/.git-commit-template.txt
:
1 | # <类型>: (类型的值见下面描述) <主题> (最多50个字) |
使用模板
使用如下命令将模板文件指定为 Git
的 commit.template
:
1 | git config --global commit.template <.git-commit-template.txt file path> |
例如:
1 | git config --global commit.template ~/.git-commit-template.txt |
使用插件
对于 Android Studio
或者 IDEA
来说,可以使用 Git Commit Template 插件,在图形化 Commit
界面使用该插件生成标准 Commit Message
。
Android 线程和线程池简介
Android
中使用线程有多种不同的方式,其中最根本的是通过 Thread
或 Runnable
派生线程对象。
但是,使用 Thread
或 Runnable
实现多线程的方式,无法有效的在任务结束之后将结果返回。为了解决这一问题,Java
额外提供了 Callable
和 FutureTask
两种创建线程的方式,允许我们在线程执行完成之后得到返回的结果。
另外,基于 Thread
、Runnable
、Callable
和 FutureTask
的封装,Android
系统中还提供了如下多线程的实现方式:
AsyncTask
:基于线程池和Handler
的封装,通常用于执行需要更新UI
的任务HandlerThread
:基于Thread
和Handler
的封装,接收来自Handler
的消息,其内部只有一个线程在执行任务IntentService
:基于Service
和HandlerThread
的封装,线程的优先级更高
本文将围绕线程和线程池的使用展开讨论。
Flutter 开发(14):BasicMessageChannel 的使用
与 MethodChannel
和 EventChannel
接收一个 MethodCodec
对象所不同的是,BasicMessageChannel
接收一个 MessageCodec<T>
作为其编解码器的实现。BasicMessageChannel
允许我们使用自定义的消息编解码器进行异步消息传递。
本文中所使用的代码示例出自 Flutter
官方示例,感兴趣的小伙伴请移步 flutter/examples/flutter_view
Flutter 开发(13):EventChannel 的使用
EventChannel
适用于 Flutter
端和设备平台端数据事件的通信。比较常见的使用 EventChannel
的场景是通过广播监听(Android
平台上的 BroadcastReceiver
)动态的监听设备事件。
本文中的示例代码是 Flutter
官方示例,感兴趣的小伙伴可以移步 flutter/examples/platform_channel/。
Flutter 开发(12):MethodChannel 的使用
在 Flutter
中,MethodChannel
允许我们发送与方法调用相关的消息。MethodChannel
适用于 Flutter
端和设备平台端进行直接的方法调用,这种方法调用是双向的,即既可以通过 MethodChannel
从 Flutter
调用设备平台上的方法,也可以从设备平台上调用 Flutter
层的代码。
Flutter 开发(11):Flutter 层异常捕获
Flutter
中的异常可以分为同步异常和异步异常。通常情况下我们可以使用 try-catch
捕获同步异常,而异步异常则需要使用 Flutter
提供的其他 API
。