[Android] 一种优化view inflate耗时的方法

注:此方法会有内存泄漏(context)泄漏。

做项目的过程中,发现一种一种优化view inflate耗时的方法,思路是预先将view inflate出来备用,用的时候直接取出来,同时再生成下一个(要考虑屏幕方向)。代码如下所示:

用法大概如下所述:
1. 在适当的地方调用push方法。
2. 在使用view的时候,使用类似如下的代码:

[Android] 解决因键盘和表情panel显示/隐藏引起的闪屏

在实现一些IM页面,例如对话框,评论框时,常常会遇到键盘和表情panel显示/隐藏引起的闪屏问题。问题的根本原因是当键盘收起或弹出时,layout会发生变化,此时panel的高度的计算如果时机不对,那么会导致闪屏。

解决的办法有很多,这里提供一种重写onMeasure方法的实现:

使用方法:

layout是这样的:

源码如下所示:

[Java] 异步case单元测试工具

有时候,需要对一些异步方法进行单元测试(尽管不推荐这样做),传统的assert只能在调用线程抛出AssertException,而测试框架往往只能监测主线程上的AssertException(JUnit),所以需要在主线程上等待异步调用结束,再去assert结果。

利用Java8的CompletableFuture,我们可以很方便地做到这一点,代码如下所示:

使用例子如下所示:

欢迎提出任何建议~

[Gradle] 如何在android项目中对纯Java module使用release/debug build并启用proguard

如何在android项目中对纯Java module使用release/debug build并启用proguard?这里提供一种方法,尽管不是很“好看”,但是可用。

假设app模块依赖lib模块,lib模块是一个纯Java模块(apply plugin: 'java')。整个过程可以分成两步。

引入Proguard

在lib模块的build.gradle里添加以下代码

这里做了几件事情:

  1. 定义proguard task,令jar这个java task依赖于它。即proguard task运行完毕后才运行jar。
  2. 使用文件替换的方式,在jar运行前替换掉class文件。
  3. 通过观察gradle console的输出,得知app在编译时会运行jar task。经过1.2.两步我们可以在正常编译
    流程中插入proguard处理后的class文件。

接下来看怎么样根据debug和release compile关闭或开启proguard

引入debug/release

在lib模块的build.gradle中加入如下代码:

然后在app的build.gradl中加入如下代码:

这段代码做了两个事情:

  1. 监听了task的添加流程,在’preReleaseBuild’和’preDebugBuild’的execute阶段添加了代码逻辑。
  2. 在’preReleaseBuild’和’preDebugBuild’执行完后,执行lib模块中的onRelease或者onDebug方法。

综上,实现了在android项目中对纯Java module使用release/debug build并启用proguard。

注:本文默认读者具有gradle基础知识。
注:附Java Plugin依赖图:
https://i1.wp.com/docs.gradle.org/3.3/userguide/img/javaPluginTasks.png?w=840&ssl=1

[Java]Java Socket选项解析

Java里,Socket的Option由java.net.SocketOptions这个接口声明,如下所示:

它们是什么意思呢?下面对每项进行解析。

1. TCP_NODELAY

Disable Nagle’s algorithm for this connection. Written data
to the network is not buffered pending acknowledgement of
previously written data.

纳格算法(Nagle’s algorithm)的工作方式是合并(coalescing)一定数量的输出资料后一次送出。
当某application不断地发送小单位的数据时,tcp会缓存一定量的数据再进行发送。具体算法如下所示:

注:按照算法,是尚未收到前一次ack或未达到指定累积大小前缓存接下来的数据。TCP专用

综上,TCP_NODELAY如果为true,那么数据就不进行缓存(等待上一个ack或达到指定累积大小)。

2. SO_BINDADDR

Fetch the local address binding of a socket (this option cannot
be “set” only “gotten”, since sockets are bound at creation time,
and so the locally bound address cannot be changed). The default local
address of a socket is INADDR_ANY, meaning any local address on a
multi-homed host. A multi-homed host can use this option to accept
connections to only one of its addresses (in the case of a
ServerSocket or DatagramSocket), or to specify its return address
to the peer (for a Socket or DatagramSocket). The parameter of
this option is an InetAddress.

如注释所述,这个Option有如下特点:

  1. 只能通过getOption读取,不能通过setOption设置。因为地址绑定发生在Socket初始化的时候,
    后面就不能更改了。
  2. 默认值为INADDR_ANY。如果用于ServerSocket,即在“multi-homed host(同一主机上有多个网
    络地址)”的情况下,socket接收所有网络接口发过来的数据。设置了指定的Address,那么就接收指定
    接口的数据;如果用于Socket,设置了指定的Address表示向该地址发送数据。
  3. 该选项值的类型为InetAddress

3. SO_REUSEADDR

Sets SO_REUSEADDR for a socket. This is used only for MulticastSockets
in java, and it is set by default for MulticastSockets.

仅用于DatagramSocket(UDP)。一般来说,一个端口释放后会等待两分钟之后才能再被使用,
SO_REUSEADDR是让端口释放后立即就可以被再次使用。

SO_REUSEADDR可以用在以下四种情况下。
(摘自《Unix网络编程》卷一,即UNPv1)
1、当有一个有相同本地地址和端口的socket1处于TIME_WAIT状态时,而你启
动程序的socket2要占用该地址和端口,你的程序就要用到该选项。
2、SO_REUSEADDR允许同一port上启动同一服务器的多个实例(多个进程)。但
每实例绑定的IP地址是不能相同的。在有多块网卡或用IP Alias技术的机器可
以试这种情况。
3、SO_REUSEADDR允许单个进程绑定相同的端口到多个socket上,但每个soc
kt绑定的ip地址不同。这和2很相似,区别请看UNPv1。
4、SO_REUSEADDR允许完全相同的地址和端口的重复绑定。但这只用于UDP的
多播,不用于TCP。

4. SO_BROADCAST

Sets SO_BROADCAST for a socket. This option enables and disables
the ability of the process to send broadcast messages. It is supported
for only datagram sockets and only on networks that support
the concept of a broadcast message (e.g. Ethernet, token ring, etc.),
and it is set by default for DatagramSockets.

SO_BROADCAST决定了socket是否可以发广播。仅用于支持广播的网络。

5. IP_MULTICAST_IF

Set which outgoing interface on which to send multicast packets.
Useful on hosts with multiple network interfaces, where applications
want to use other than the system default. Takes/returns an InetAddress.

用于确定组播(multicast)的网络接口。选项值类型为InetAddress

注:关于单播、广播和组播的概念,可参考http://blog.csdn.net/dabing69221/article/details/17286441

6. IP_MULTICAST_IF

同#IP_MULTICAST_IF,但是可以设IPV4或IPV6地址。

7. IP_MULTICAST_LOOP

This option enables or disables local loopback of multicast datagrams.
This option is enabled by default for Multicast Sockets.

这个选项用于组播。其中“local loopback”的意思是组播包会同时发送给自己(即发送方可以收到自己发出去的数据)。

8. IP_TOS

This option sets the type-of-service or traffic class field
in the IP header for a TCP or UDP socket.

用于设置IP包TOS部分的值。UDP可以在bind之后任意更改设置,TCP在bind前设置。

一般来说,TOS值有以下三种:

9. SO_LINGER

Specify a linger-on-close timeout. This option disables/enables
immediate return from a close() of a TCP Socket. Enabling
this option with a non-zero Integer timeout means that a
close() will block pending the transmission and acknowledgement
of all data written to the peer, at which point the socket is closed
gracefully. Upon reaching the linger timeout, the socket is
closed forcefully, with a TCP RST. Enabling the option with a
timeout of zero does a forceful close immediately. If the specified
timeout value exceeds 65,535 it will be reduced to 65,535.

设置了一个大于0的值给SO_LINGER,表示当关闭这个TCP socket时,如果发送队列里还有数据,
那么堵塞等待一段时间( linger interval)。如果超时了,那么强制关闭连接。设置等于0
的值表示直接强制关闭连接。超时上限为65535。

10. SO_TIMEOUT

Set a timeout on blocking Socket operations:

ServerSocket.accept();
SocketInputStream.read();
DatagramSocket.receive();

The option must be set prior to entering a blocking
operation to take effect. If the timeout expires and the
operation would continue to block,
java.io.InterruptedIOException is raised. The Socket is
not closed in this case.

SO_TIMEOUT选项用于对read()操作设置timeout,单位为毫秒。当timeout发生时,会抛出
InterruptedIOException。设置不大于0的值会令read()操作永远堵塞。

11. SO_SNDBUF

Set a hint the size of the underlying buffers used by the
platform for outgoing network I/O. When used in set, this is a
suggestion to the kernel from the application about the size of
buffers to use for the data to be sent over the socket. When
used in get, this must return the size of the buffer actually
used by the platform when sending out data on this socket.

设置SO_SNDBUF表示向内核建议socket发送缓冲区的大小。获取SO_SNDBUF一定返回实际发送缓冲区
的大小。单位字节。

12. SO_RCVBUF

Set a hint the size of the underlying buffers used by the
platform for incoming network I/O. When used in set, this is a
suggestion to the kernel from the application about the size of
buffers to use for the data to be received over the
socket. When used in get, this must return the size of the
buffer actually used by the platform when receiving in data on
this socket.

设置SO_RCVBUF表示向内核建议socket接收缓冲区的大小。获取SO_RCVBUF一定返回实际接收缓冲区
的大小。单位字节。

13. SO_KEEPALIVE

When the keepalive option is set for a TCP socket and no data
has been exchanged across the socket in either direction for
2 hours (NOTE: the actual value is implementation dependent),
TCP automatically sends a keepalive probe to the peer. This probe is a
TCP segment to which the peer must respond.
One of three responses is expected:
1. The peer responds with the expected ACK. The application is not
notified (since everything is OK). TCP will send another probe
following another 2 hours of inactivity.
2. The peer responds with an RST, which tells the local TCP that
the peer host has crashed and rebooted. The socket is closed.
3. There is no response from the peer. The socket is closed.
The purpose of this option is to detect if the peer host crashes.

SO_KEEPALIVE启用时,当tcp连接中没有数据流动超过2小时时,系统主动发送一个keepalive探寻包(probe)给
对方,对方必须响应这个包。收到ack的时候表示连接正常。满足以下条件时视为连接关闭:

  1. 对方返回了RST
  2. 对方没有任何返回

14. SO_OOBINLINE

When the OOBINLINE option is set, any TCP urgent data received on
the socket will be received through the socket input stream.
When the option is disabled (which is the default) urgent data
is silently discarded.

该选项默认disable,当enable时,表示urgent data直接在接收方read()操作中返回(和普通数据混在一起),
否则接收方直接忽略该数据。

[Java] 不依赖Android环境的Java Handler

最近项目中遇到将android project转成java project的需求,要把项目中android相关的部分去掉。其中最纠结的就是如何替换handler。
handler多用于指定线程(Looper)上执行回调,还可以当成一个消息队列使用。通过sendMessageDelayed可以做到事件的延时执行、条件执行和去抖(debouncing)。
于是,我利用Java里面的ScheduledExecutorService写了一个简单的Handler,接口与原Handler基本一致,代码如下所示:

Message类:

Looper类:

Handler类:

使用方法如下所示:

[vscode] markdown_index 可以为你的markdown标题添加序号的插件

markdown_index是一个可以为你的markdown标题添加序号的插件。

假设原文如下所示:

安装markdown_index,运行 > markdown add index,即可自动添加序号,如下所示:

注:重复运行命令可以自动更新序号。
注:仅对“#”标记的标题等级有效。
注:在vscode market搜索“markdown-index”即可安装

github: https://github.com/legendmohe/markdown_index
email: legendmohe@foxmail.com