../img/avatar.png

This Tinggeng

记录一个进程重启的问题

why 在项目中,比如推送了一个具体的产品,肯定是打算显示具体的产品详情页,这时候点击返回,也自然打算让用户返回首页,还是让用户留在 APP 内。 最近项目在接入华为推送的时候,遇到一个问题,我们的默认启动 Activity 是一个广告页,在从详情页返回到主页的时候,在主页再返回,出现了广告页. 类似于如图. How 看到这个现象猜测,是不是当栈中有 Activity 实例的时候,进程是会自动重启.为此做了一个实验,A->B,在 B 中关闭虚拟机,这时候,虚拟机自动重启。打开了 A 页面。 A->B->C,在 C 中关闭虚拟机。 这时候,栈中的信息如下. 此时在 C 中执行关闭虚拟机的操作,然后进程重启.下图是虚拟机重启之后的栈信息。发现,除了之前栈顶的 C 销往了,栈下的 B,C 都还在.并且 B 的状态信息还是重启之前的,除了 pid 不一样了. 这时候按下返回,栈信息如下. Conclusion 为何会出现欢迎页 目前发现,华为推送在点击通知的时候,会自动启动 APP 的默认 Activity. 关于为何进程会重启 目前发现的现象是,当栈底还有 Activity 的时候,就会重启. Google 了一些博客,还是未发现有合理的解释. 为何重启后 task 中原有栈顶的 Activity 的信息和重启之前一模一样? 原因是什么?目前不知道怎么下手分析,仅仅是猜测,应该是和 ActivityRecord 有关,系统在重启的时候直接复用了.

BottomNavigationView 的使用

why BottomNavigationView 这个概念很早之前就被提出,之后出一个第三方库.但是一直未有官方的支持,今天正好看到有官方支持,记录一下. what BottomNavigationView 是 material design 中的设计的实现,这种设计很早就出现了。 how 添加依赖 1 compile 'com.android.support:design:25.1.1' 在menu下新建 menu配置文件,顺序就是 bottom bar 显示的顺序 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/recents" android:title="Recents" android:icon="@drawable/ic_recents"/> <item android:id="@+id/favorites" android:title="Favorites" android:icon="@drawable/ic_favorites"/> <item android:id="@+id/nearby" android:title="Nearby" android:icon="@drawable/ic_nearby"/> </menu> 在 drawable 和 drawable-v21下新建item的背景文件。 1 2 3 4 5 6 7 8 9 10 11 12 <?xml version="1.0" encoding="utf-8"?

加快 AndroidStudio 编译速度之 build cache

Why AndroidStudio 编译速度慢,已经是人神共愤的事情了.本文是一篇译文,讲述如果利用 build cache 技术加快编译速度。分成两部分,一部分是第三方博文,另外一部分是官方文档.援引文章在结尾给出. Using build cache in Android Studio makes Gradle build faster 为何关心 build cache? 因为 build cache 可以加快 clean 和 build 的速度。当你执行 ‘gradle clean build’ 或者类似的命令的时候。 How does it make the build faster? 通过缓存已经分包的 libraries,这个过程是不在 Gradle 的缓存管理范围内的。无论是通过 Android studio 或者 命令行的方式执行 clean 操作,build-cache 内的包都会被保留,等到下次 build apk 的时候,被复用。可以在 build-cache 目录下查看缓存的结构。 这是文件夹下列出的是一系列命名比较奇怪的文件和文件夹。文件大小是 0 字节的文件是用来锁定文件使用的。这个是非常必要的,因为同一个缓存文件可以被不同的项目使用。锁文件,可以防止两个项目同时对一个缓存文件进行读写操作。 Exploded aar caches aar 缓存以文件夹的形式展现。有两种类型的缓存,一种是 dex 缓存,一种是解压完的 aar 形式的缓存。解压完的 aar 将直接保存在对应的 output 文件夹下。比如 220674f5fc7186b424e032744f0eeb413d469b54 文件夹的 input 文件 包含以下内容:

Java 内存模型_2

Why 理解 JMM 就需要理解 JVM 中的运行时内存区域分为哪几部分,以及各个部分的区别. 内存区域是什么? Java 虚拟机在执行 Java 程序的过程中会把它所管理的内存划分为若干个不同的数据区域.这些区域都有各自的用于,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而存在,有些区域则依赖用户县城的启动和结束而建立和销毁. 上图展示了 JVM 虚拟机所管理的几个运行时数据区域. 分为两类: 所有线程都共享的,即 JVM 虚拟机就一份 线程隔离的数据区,即每个线程所特有的一份,每份线程都会创建一份.它的生命周期与线程相同. 1. 程序计数器 程序计数器是一块儿较小的内存空间,它可以看做是当前线程所执行的字节码的行号指示器.在虚拟机的概念模型里,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支,循环,跳转,异常处理,线程恢复等基础功能都需要依赖这个计数器来完成. Java 虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器(对于多核处理器来说是一个内核)都会执行一条线程中的指令.因此,为了线程切换之后,能恢复到正确的执行位置,每条线程都需要一个独立的程序计数器,各条线程之间的计数器互不影响,独立存储,我们称这类内存区域为"线程私有"的内存. 如果线程正在执行的是一个 Java 方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果执行的是 native 方法,这个计数器的值则为空. 程序计数器是唯一一个在 Java 虚拟机规范中没有规定任何 OOM 情况的区域. 2. Java 虚拟机栈 虚拟机栈描述的是 Java 方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧,栈帧是用于存储 局部变量表,操作数栈,动态链接,方法出口等 信息.每个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中的入栈到出栈的过程. 局部变量表存放编译器可知的各种基本数据类型(boolean、byte、char、short、int、float、long、double),对象引用(reference类型,它不等同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向代表对象的句柄或者其他与次对象相关的位置) 和 returnAddress 类型(指向了一条字节码指令的地址). 局部变量表所需要的内存空间在编译期完成分配,当进入一个方法时,这个方法需要在帧中分配多大的局部变量空间是完全确定的,在方法运行期间不会改变局部变量的大小. 这个区域存在两种异常状况:如果线程的栈深度大于虚拟机允许的深度,讲抛出 StackOverflowError 异常;如果虚拟机栈是可以扩展的(当前大部分的 Java 虚拟机都可以动态扩展,只不过 Java 规范也允许固定长度的虚拟机栈),如果扩展时无法申请到足够的内存,就会抛出 OOM 异常. 3. 本地方法栈 本地方法栈的作用与虚拟机栈所发挥的作用是非常相似的,它们之间的区别不过是虚拟机栈为虚拟机执行 Java 方法(字节码) 服务,而本地方法栈则为虚拟机使用到 Native 方法服务.虚拟机规范中并未对本地方法栈中的使用的语言,使用方式与数据结果有强制规定,因此具体的虚拟机可以自由实现,甚至有的虚拟机直接将本地方法栈和虚拟机栈合二为一. 本地方法栈也会抛出和虚拟机栈一样的异常: StackOverflowError 和 OutOfMemoryError 异常. 4. Java 堆 Java 堆是 JVM 虚拟机管理的内存中最大的一块儿.

Java 内存模型_1

概述 本文记录 Java 中的内存模型的基础部分1。 本篇作为学习 Java 内存模型基础部分的笔记,加上些许自己的理解和解释. 为什么需要理解 Java 内存模型 结论:并发产生的内存可见性问题. 并发编程中的两个关键问题: 线程之间如何通行(以何种机制来交换信息) 线程之间如何同步 已有的通信机制: 共享内存: 线程之间共享程序的公共状态,线程之间通过读写内存中的公共状态来隐式进行通信. 消息传递: 在这种模型中,线程之间没有公共状态,线程之间必须通过发送消息显式地通信. 同步,指的是控制不同线程之间操作发生的相对顺序的机制: 在共享内存模型中,程序员必须显式的指定某个方法或者某段代码需要在线程之间互斥执行. 在消息传递的模型里,由于消息的发送必须在消息的接收之前,因此,同步是隐式进行的. java 采用的是共享内存模型,而 java 线程之间的通信总是隐式进行,整个通信过程对程序员完全是透明的. 对于 java 程序员而言,如果不了解 java 内存模型,在编写多线程程序的时候,就会遇到各种各样的内存可见性的问题.所以,对 java 的内存模型需要有一定的了解. Java 采用的共享内存模型是什么样的 Java 采用的是共享内存模型作为线程间的通信机制. 共享内存: 堆内存,在 java 中,所有的 实例域,静态域和数组元素 存储在堆内存中,堆内存在线程之间共享. 局部变量,方法定义的参数和异常处理参数,不会在线程之间共享,它们不会有内存可见性问题,也不受内存模型的影响. Java 线程之间的通信由 Java内存模型(JMM)控制,JMM 决定了一个线程对共享变量的写入何时对另个线程可见.从抽象的角度来看,JMM 定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存中,每个线程都有一个私有的本地内存,本地内存中存储了该线程以读写共享变量的副本.本地内存是 JMM 的一个抽象概念,并不真实存在.它涵盖了缓存,写缓冲区,寄存器以及其他的硬件和编译器优化. 分析: 线程 A 和线程 B 通信过程 线程 A 将本地内存 A 中更新过的共享变量刷新到主内存中去. 线程 B 去主内存中读取线程 A 更新过的共享变量. 本地内存 A 和本地内存 B 都有主内存中共享的变量 x 的副本.

IPC_Binder_java_1

背景 由于 Binder 很复杂,这个分多篇展开,目前先将零碎的知识整合,在后面几篇进行总结. 概述: Binder 用于进程间通信,而 Handler 消息机制用于同进程的线程间通信。 Binder 的英文涵义是别针,回形针的意思。 在 Android 中 Binder 的存在是为了完成进程间的通信,将进程"别" 在一起。比如说:普通应用可以调用播放器提供的服务:播放、暂停、停止等功能。 Binder 是工作在 Linux 层面,属于一个驱动,只是这个驱动是不需要硬件的,或者说是基于操作系统的一小块内存。从线程的角度来讲,Binder 驱动的代码是运行在内核态的,客户端程序调用 Binder 是通过系统调用完完成。 Binder 框架:一种架构 Binder 框架提供 服务端接口、Binder 驱动、客户端接口 三个模块。 从服务端的角度来说,一个 Binder 服务端实际上就是一个 Binder 类的对象,该类一旦创建,内部就会启动一个隐藏线程。该线程接下来就用于接收 Binder 驱动发送来的消息,收到消息之后,会执行到 Binder 对象中的 onTransact 方法,在这个方法中,根据不同的参数,执行不同的服务代码。因此,要实现一个 Binder 服务,就必须重载 onTransact 方法。 在 onTransact 方法中,会获取传递进来的参数,将其转换成服务函数的参数。onTransact 参数的来源于 客户端的调用 transact 方法。所以,如果 transact 方法的参数有固定的格式输入,那么 onTransact 就会有相应的固定格式输出。 从 Binder 驱动的角度来说。任何一个服务端的 Binder 对象被创建的时候,都同时会在 Binder 驱动中创建一个 mRemote 对象,这个对象也是 Binder 类。客户端想要访问远程服务的时候,都是通过这个 mRemote 对象。