本文参考《Android系统源代码情景分析》,作者罗升阳。

一、~/Android/frameworks/base/cmd/servicemanager

-----binder.h

-----binder.c

-----service_manager.c

~/Android//kernel/goldfish/drivers/staging/android

-----binder.c

-----binder.h


二、源码分析

1、从service_manager.c的main开始执行。

----~/Android/frameworks/base/cmd/servicemanager/service_manager.c

int main(int argc, char **argv){    struct binder_state *bs;    void *svcmgr = BINDER_SERVICE_MANAGER;    bs = binder_open(128*1024);    if (binder_become_context_manager(bs)) {        LOGE("cannot become context manager (%s)\n", strerror(errno));        return -1;     }       svcmgr_handle = svcmgr;//注意svcmgr_handle与下面的svcmgr_handler不一样,svcmgr_handle为全局变量,目前为空的函数指针,((void*)0)    binder_loop(bs, svcmgr_handler);//svcmgr_handler是一个函数指针,指向具体的函数    return 0;}
---- ~/Android/frameworks/base/cmd/servicemanager/binder.c

struct binder_state{           int fd;    void *mapped;    unsigned mapsize;};
---- ~/Android/frameworks/base/cmd/servicemanager/binder.h

#define BINDER_SERVICE_MANAGER ((void*) 0)

Service Manager是一个特殊的Service组件,它的特殊之处就在于与Service Manager进程的Binder本地对象是一个虚拟的对象。这个虚拟的Binder本地对象的地址值等于0,并且在Binder驱动程序中引用了它的Binder引用对象的句柄值也等于0。

ServiceManager的启动过程由三个步骤组成:第一是调用函数binder_open打开设备文件/dev/binder,以及将它映射到本进程地址空间;第二是调用binder_become_context_manager将自己注册为Binder进程间通信机制的上下文管理者;第三步是调用函数binder_loop来循环等待和处理Client进程的通信要求。


2、打开和映射Binder设备文件

----~/Android/frameworks/base/cmd/servicemanager/binder.c

struct binder_state *binder_open(unsigned mapsize){           struct binder_state *bs;    bs = malloc(sizeof(*bs));    if (!bs) {        errno = ENOMEM;        return 0;    }    bs->fd = open("/dev/binder", O_RDWR);//文件描述符保存在fd中    if (bs->fd < 0) {        fprintf(stderr,"binder: cannot open device (%s)\n",                strerror(errno));        goto fail_open;    }            bs->mapsize = mapsize;//128*1024    bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);//映射到本进程地址空间    if (bs->mapped == MAP_FAILED) {        fprintf(stderr,"binder: cannot map device (%s)\n",                strerror(errno));        goto fail_map;    }        /* TODO: check version */    return bs;fail_map:    close(bs->fd);fail_open:    free(bs);    return 0;} 
其中open("/dev/binder", O_RDWR)映射到binder驱动程序binder_open方法。

binder_open方法位于~/Android/kernel/goldfish/drivers/staging/android/binder.c

static int binder_open(struct inode *nodp, struct file *filp){        struct binder_proc *proc;              if (binder_debug_mask & BINDER_DEBUG_OPEN_CLOSE)                printk(KERN_INFO "binder_open: %d:%d\n", current->group_leader->pid, current->pid);        proc = kzalloc(sizeof(*proc), GFP_KERNEL);//创建binder_proc结构体        if (proc == NULL)                     return -ENOMEM;                        get_task_struct(current);              proc->tsk = current;  //初始化各个参数        INIT_LIST_HEAD(&proc->todo);           init_waitqueue_head(&proc->wait);        proc->default_priority = task_nice(current);        mutex_lock(&binder_lock);              binder_stats.obj_created[BINDER_STAT_PROC]++;        hlist_add_head(&proc->proc_node, &binder_procs);//将binder_proc结构体proc加入到一个全局hash队列binder_procs中        proc->pid = current->group_leader->pid;//进程的pid        INIT_LIST_HEAD(&proc->delivered_death);        filp->private_data = proc;//将binder_proc结构体proc保存在参数filp的成员变量private_data中          mutex_unlock(&binder_lock);            if (binder_proc_dir_entry_proc) {//如果存在/proc/binder/proc目录                char strbuf[11];                snprintf(strbuf, sizeof(strbuf), "%u", proc->pid);                remove_proc_entry(strbuf, binder_proc_dir_entry_proc);                create_proc_read_entry(strbuf, S_IRUGO, binder_proc_dir_entry_proc, binder_read_proc_proc, proc);//在/proc/binder/proc目录下创建一个以进程ID为名称的只读文件        }        return 0;}
创建了binder_proc结构体,分别初始化各个参数,将binder_proc结构体proc加入到一个全局hash队列binder_procs中。Binder驱动程序将所有打开了设备文件/dev/binder的进程都加入全局hash队列binder_procs中,因此,通过遍历这个hash队列就知道系统当前有多少进程在使用Binder进程间通信机制。然后将binder_proc结构体proc保存在参数filp的成员变量private_data中。最后在/proc/binder/proc目录下创建一个以进程ID为名称的只读文件,并且以函数binder_read_proc_proc作为它的文件内容读取函数。通过读取文件/proc/binder/proc/<PID>的内容,我们就可以获得进程<PID>的Binder线程池,Binder实体对象,Binder引用对象,以及内核缓冲区等信息。

其中mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0)映射到binder驱动程序binder_mmap方法,暂时先不分析。


3、注册为Binder上下文管理者

---~/Android/frameworks/base/cmd/servicemanager/binder.c

int binder_become_context_manager(struct binder_state *bs){       return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);}
ioctl方法映射到binder驱动程序binder_ioctl方法。

binder_ioctl位于~/Android/kernel/goldfish/drivers/staging/android/binder.c

static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg){int ret;struct binder_proc *proc = filp->private_data;//获取刚刚在open中创建的binder_proc结构体struct binder_thread *thread;unsigned int size = _IOC_SIZE(cmd);//命令的大小void __user *ubuf = (void __user *)arg;//参数地址.........mutex_lock(&binder_lock);thread = binder_get_thread(proc);//获取或者创建一个binder_thread结构体if (thread == NULL) {ret = -ENOMEM;goto err;}switch (cmd) {............case BINDER_SET_CONTEXT_MGR:if (binder_context_mgr_node != NULL) {.........}if (binder_context_mgr_uid != -1) {.........} elsebinder_context_mgr_uid = current->cred->euid;//初始化进程有效用户IDbinder_context_mgr_node = binder_new_node(proc, NULL, NULL);//初始化一个Binder实体对象if (binder_context_mgr_node == NULL) {ret = -ENOMEM;goto err;}binder_context_mgr_node->local_weak_refs++;//强弱指针,待以后分析binder_context_mgr_node->local_strong_refs++;binder_context_mgr_node->has_strong_ref = 1;binder_context_mgr_node->has_weak_ref = 1;break;.............}ret = 0;err:if (thread)thread->looper &= ~BINDER_LOOPER_STATE_NEED_RETURN;mutex_unlock(&binder_lock);.............return ret;}
binder_get_thread实现如下:

~/Android/kernel/goldfish/drivers/staging/android/binder.c

static struct binder_thread *binder_get_thread(struct binder_proc *proc){struct binder_thread *thread = NULL;struct rb_node *parent = NULL;struct rb_node **p = &proc->threads.rb_node;while (*p) {//根据当前主线程pid,来查找是否已经分配了binder_thread结构体parent = *p;thread = rb_entry(parent, struct binder_thread, rb_node);if (current->pid < thread->pid)p = &(*p)->rb_left;else if (current->pid > thread->pid)p = &(*p)->rb_right;elsebreak;}if (*p == NULL) {//如果没有找到thread = kzalloc(sizeof(*thread), GFP_KERNEL);//分配binder_thread结构体if (thread == NULL)return NULL;binder_stats.obj_created[BINDER_STAT_THREAD]++;thread->proc = proc;//初始化各个变量thread->pid = current->pid;//线程的pidinit_waitqueue_head(&thread->wait);INIT_LIST_HEAD(&thread->todo);rb_link_node(&thread->rb_node, parent, p);//根据pid将thread->rb_node插入到proc->threads维护的红黑树中rb_insert_color(&thread->rb_node, &proc->threads);thread->looper |= BINDER_LOOPER_STATE_NEED_RETURN;thread->return_error = BR_OK;thread->return_error2 = BR_OK;}return thread;}
一个进程会有很多线程,所以thread->rb_node为proc->threads维护的红黑数中的一个节点。thread按照pid大小将rb_node插入到proc->threads维护的红黑树的对应节点处。所以首先根据当前主线程pid,来查找是否已经分配了binder_thread结构体。如果没有那么分配binder_thread结构体,初始化各个变量,根据pid将thread->rb_node插入到proc->threads维护的红黑树中。将looper状态设备为BINDER_LOOPER_STATE_NEED_RETURN,表示该线程在完成当前操作之后,需要马上返回到用户空间,而不可以去处理进程间的通信请求。

全局变量binder_context_mgr_node用来描述一个Binder实体对象,如果它的值不为NULL,说明已经注册了Binder进程间通信上下文管理者了。全局变量binder_context_mgr_uid用来描述进程有效用户ID,如果它的值不等于-1,说明已经注册了Binder进程间通信上下文管理者了。目前没有Binder进程间通信上下文管理者,所以binder_context_mgr_uid和binder_context_mgr_node都要初始化。


binder_context_mgr_node实现如下:

-----~/Android/kernel/goldfish/drivers/staging/android/binder.c

static struct binder_node *binder_new_node(struct binder_proc *proc, void __user *ptr, void __user *cookie){struct rb_node **p = &proc->nodes.rb_node;struct rb_node *parent = NULL;struct binder_node *node;while (*p) {//根据node的ptr,来查找是否已经分配了binder_node结构体parent = *p;node = rb_entry(parent, struct binder_node, rb_node);if (ptr < node->ptr)p = &(*p)->rb_left;else if (ptr > node->ptr)p = &(*p)->rb_right;elsereturn NULL;}node = kzalloc(sizeof(*node), GFP_KERNEL);//如果没有找到,分配binder_node结构体if (node == NULL)return NULL;binder_stats.obj_created[BINDER_STAT_NODE]++;rb_link_node(&node->rb_node, parent, p);//根据ptr将node->rb_node插入proc->nodes中rb_insert_color(&node->rb_node, &proc->nodes);node->debug_id = ++binder_last_id;//初始化各个变量node->proc = proc;node->ptr = ptr;//NULLnode->cookie = cookie;//NULLnode->work.type = BINDER_WORK_NODE;INIT_LIST_HEAD(&node->work.entry);INIT_LIST_HEAD(&node->async_todo);if (binder_debug_mask & BINDER_DEBUG_INTERNAL_REFS)printk(KERN_INFO "binder: %d:%d node %d u%p c%p created\n",       proc->pid, current->pid, node->debug_id,       node->ptr, node->cookie);return node;}

一个进程会有很多实体对象,所以node->rb_node为proc->nodes维护的红黑数中的一个节点。node按照ptr地址大小将rb_node插入到proc->threads维护的红黑树的对应节点处。所以首先根据当前node的ptr,来查找是否已经分配了binder_node结构体。如果没有那么分配binder_node结构体,初始化各个变量,根据ptr将thread->rb_node插入到proc->threads维护的红黑树中。

然后返回binder_ioctl中, 刚才Service Manager进程的Binder实体对象binder_context_mgr_node内部弱引用计数local_weak_refs以及内部强引用计数local_strong_refs进行初始化,即将它们的值设置为1,避免Binder驱动程序将其释放。接着又将成员变量has_strong_ref和has_weak_ref的值设置为1,表示Binder驱动程序已经请求Service Manager进程本地对象强引用计数和弱引用计数。由于Service Manager进程本地对象在运行过程中一直存在,所以实际上不需要请求增加它的引用计数,而是直接将它对应的Binder实体对象binder_context_mgr_node的成员变量has_strong_ref和has_weak_ref的值设置为1。
最后会将thread->looper状态位BINDER_LOOPER_STATE_NEED_RETURN清零。


4、循环等待Client进程请求

返回用户空间,main函数开始执行binder_loop,实现如下:

---~/Android/frameworks/base/cmd/servicemanager/binder.c

void binder_loop(struct binder_state *bs, binder_handler func){    int res;    struct binder_write_read bwr;    unsigned readbuf[32];    bwr.write_size = 0;    bwr.write_consumed = 0;    bwr.write_buffer = 0;        readbuf[0] = BC_ENTER_LOOPER;//首先将BC_ENTER_LOOPER协议写入缓冲区readbuf中    binder_write(bs, readbuf, sizeof(unsigned));//调用binder_write将它发送到Binder驱动程序中    for (;;) {        bwr.read_size = sizeof(readbuf);        bwr.read_consumed = 0;        bwr.read_buffer = (unsigned) readbuf;        res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);//bwr.write_size为0,bwr.read_size不为0        if (res < 0) {            LOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));            break;        }        res = binder_parse(bs, 0, readbuf, bwr.read_consumed, func);        if (res == 0) {            LOGE("binder_loop: unexpected reply?!\n");            break;        }        if (res < 0) {            LOGE("binder_loop: io error %d %s\n", res, strerror(errno));            break;        }    }}
首先将BC_ENTER_LOOPER协议写入缓冲区readbuf中,接着调用binder_write将它发送到Binder驱动程序中。函数binder_write的实现如下:

---~/Android/frameworks/base/cmd/servicemanager/binder.c

int binder_write(struct binder_state *bs, void *data, unsigned len){    struct binder_write_read bwr;    int res;    bwr.write_size = len;    bwr.write_consumed = 0;    bwr.write_buffer = (unsigned) data;    bwr.read_size = 0;    bwr.read_consumed = 0;    bwr.read_buffer = 0;    res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);//bwr的write_size不为0,read_size为0    if (res < 0) {        fprintf(stderr,"binder_write: ioctl failed (%s)\n",                strerror(errno));    }    return res;}
ioctl方法同样映射到binder驱动程序 binder_ioctl方法。
binder_ioctl位于 ~/Android/ kernel/goldfish/drivers/staging/android/binder.c

static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg){int ret;struct binder_proc *proc = filp->private_data;struct binder_thread *thread;unsigned int size = _IOC_SIZE(cmd);void __user *ubuf = (void __user *)arg;.........mutex_lock(&binder_lock);thread = binder_get_thread(proc);//上次获取的thread,looper为0if (thread == NULL) {ret = -ENOMEM;goto err;}switch (cmd) {//cmd为上面传递过来的BINDER_WRITE_READcase BINDER_WRITE_READ: {struct binder_write_read bwr;if (size != sizeof(struct binder_write_read)) {ret = -EINVAL;goto err;}if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {//从用户空间传进来的一个binder_write_read结构体拷贝出来,并且保存在变量bwr中ret = -EFAULT;goto err;}                .........if (bwr.write_size > 0) {//bwr.write_size大于0,执行这里ret = binder_thread_write(proc, thread, (void __user *)bwr.write_buffer, bwr.write_size, &bwr.write_consumed);if (ret < 0) {bwr.read_consumed = 0;if (copy_to_user(ubuf, &bwr, sizeof(bwr)))ret = -EFAULT;goto err;}}if (bwr.read_size > 0) {//bwr.read_size等于0,不执行这里ret = binder_thread_read(proc, thread, (void __user *)bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags & O_NONBLOCK);if (!list_empty(&proc->todo))wake_up_interruptible(&proc->wait);if (ret < 0) {if (copy_to_user(ubuf, &bwr, sizeof(bwr)))ret = -EFAULT;goto err;}}                ...........if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {//将结果返回用户空间bwrret = -EFAULT;goto err;}break;}..........ret = 0;err:if (thread)thread->looper &= ~BINDER_LOOPER_STATE_NEED_RETURN;//looper为BINDER_LOOPER_STATE_ENTERED,由于对应位已经为0,此时执行此句无效果mutex_unlock(&binder_lock);        ...........return ret;}
由于bwr.write_size大于0,开始执行binder_thread_write,实现如下:

binder_ioctl位于~/Android/kernel/goldfish/drivers/staging/android/binder.c

intbinder_thread_write(struct binder_proc *proc, struct binder_thread *thread,    void __user *buffer, int size, signed long *consumed){uint32_t cmd;void __user *ptr = buffer + *consumed;//起始位置void __user *end = buffer + size;//结束位置while (ptr < end && thread->return_error == BR_OK) {if (get_user(cmd, (uint32_t __user *)ptr))//cmd为BC_ENTER_LOOPERreturn -EFAULT;ptr += sizeof(uint32_t);//由于只有一个cmd,此时ptr已经等于end............switch (cmd) {...........case BC_ENTER_LOOPER:        ..............if (thread->looper & BINDER_LOOPER_STATE_REGISTERED) {//此时looper为0,不会执行这里thread->looper |= BINDER_LOOPER_STATE_INVALID;binder_user_error("binder: %d:%d ERROR:"" BC_ENTER_LOOPER called after ""BC_REGISTER_LOOPER\n",proc->pid, thread->pid);}thread->looper |= BINDER_LOOPER_STATE_ENTERED;//执行本次写操作,最终的目的居然是looper设置成BINDER_LOOPER_STATE_ENTEREDbreak;.........*consumed = ptr - buffer;//由于只有一个cmd,consumed为size}return 0;}
返回binder_ioctl,bwr.read_size等于0,不会执行,最后将结果返回用户空间bwr。

返回用户空间,接着执行binder_loop,从上面我们看出来,驱动程序是否执行读、写操作,取决于用户空间write_size,read_size的大小。此时write_size为0,read_size不为0,ioctl方法同样映射到binder驱动程序binder_ioctl方法。
binder_ioctl位于~/Android/kernel/goldfish/drivers/staging/android/binder.c

static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg){int ret;struct binder_proc *proc = filp->private_data;struct binder_thread *thread;unsigned int size = _IOC_SIZE(cmd);void __user *ubuf = (void __user *)arg;.........mutex_lock(&binder_lock);thread = binder_get_thread(proc);//上次获取的thread,looper为BINDER_LOOPER_STATE_ENTEREDif (thread == NULL) {ret = -ENOMEM;goto err;}switch (cmd) {//cmd为上面传递过来的BINDER_WRITE_READcase BINDER_WRITE_READ: {struct binder_write_read bwr;if (size != sizeof(struct binder_write_read)) {ret = -EINVAL;goto err;}if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {//从用户空间传进来的一个binder_write_read结构体拷贝出来,并且保存在变量bwr中ret = -EFAULT;goto err;}                .........if (bwr.write_size > 0) {//bwr.write_size等于0,不执行这里ret = binder_thread_write(proc, thread, (void __user *)bwr.write_buffer, bwr.write_size, &bwr.write_consumed);if (ret < 0) {bwr.read_consumed = 0;if (copy_to_user(ubuf, &bwr, sizeof(bwr)))ret = -EFAULT;goto err;}}if (bwr.read_size > 0) {//bwr.read_size大于0,执行这里ret = binder_thread_read(proc, thread, (void __user *)bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags & O_NONBLOCK);if (!list_empty(&proc->todo))wake_up_interruptible(&proc->wait);if (ret < 0) {if (copy_to_user(ubuf, &bwr, sizeof(bwr)))ret = -EFAULT;goto err;}}                ...........if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {//将结果返回用户空间bwrret = -EFAULT;goto err;}break;}..........ret = 0;err:if (thread)thread->looper &= ~BINDER_LOOPER_STATE_NEED_RETURN;mutex_unlock(&binder_lock);        ...........return ret;}

由于bwr.read_size大于0,开始执行binder_thread_read,实现如下:

binder_ioctl位于~/Android/kernel/goldfish/drivers/staging/android/binder.c

static intbinder_thread_read(struct binder_proc *proc, struct binder_thread *thread,void  __user *buffer, int size, signed long *consumed, int non_block){void __user *ptr = buffer + *consumed;//起始位置void __user *end = buffer + size;//结束位置int ret = 0;int wait_for_proc_work;if (*consumed == 0) {if (put_user(BR_NOOP, (uint32_t __user *)ptr))//BR_NOOP存入刚才的局部变量中return -EFAULT;ptr += sizeof(uint32_t);}retry:wait_for_proc_work = thread->transaction_stack == NULL && list_empty(&thread->todo);//wait_for_proc_work目前为1,表示线程没有要处理的任务if (thread->return_error != BR_OK && ptr < end) {..........}thread->looper |= BINDER_LOOPER_STATE_WAITING;//looper为BINDER_LOOPER_STATE_ENTERED,BINDER_LOOPER_STATE_WAITINGif (wait_for_proc_work)//为1proc->ready_threads++;//ready_threads为1,进程多了一个空闲线程mutex_unlock(&binder_lock);if (wait_for_proc_work) {//为1if (!(thread->looper & (BINDER_LOOPER_STATE_REGISTERED |BINDER_LOOPER_STATE_ENTERED))) {     // 此时为false,必须是两个状态之一      ...............}binder_set_nice(proc->default_priority);//把当前线程的优先级设置为它所属进程的优先级if (non_block) {//非阻塞要立刻返回处理结果if (!binder_has_proc_work(proc, thread))//有任务就接着往下执行,没有任务就返回ret = -EAGAIN;} elseret = wait_event_interruptible_exclusive(proc->wait, binder_has_proc_work(proc, thread));//睡眠等待直到其所属的进程有新的未处理项为止} else {if (non_block) {//非阻塞要立刻返回处理结果if (!binder_has_thread_work(thread))有任务就接下往下执行,没有任务就返回ret = -EAGAIN;} elseret = wait_event_interruptible(thread->wait, binder_has_thread_work(thread));//睡眠等待直到线程有新的未处理项为止}mutex_lock(&binder_lock);if (wait_for_proc_work)//为1proc->ready_threads--;//ready_thread为0thread->looper &= ~BINDER_LOOPER_STATE_WAITING;//looper为BINDER_LOOPER_STATE_ENTEREDif (ret)return ret;while (1) {........}done:*consumed = ptr - buffer;..........return 0;}

static intbinder_has_proc_work(struct binder_proc *proc, struct binder_thread *thread){return !list_empty(&proc->todo) || (thread->looper & BINDER_LOOPER_STATE_NEED_RETURN);}static intbinder_has_thread_work(struct binder_thread *thread){return !list_empty(&thread->todo) || thread->return_error != BR_OK ||(thread->looper & BINDER_LOOPER_STATE_NEED_RETURN);}
如果线程thread中没有要处理的数据,那么就处理进程proc上等待的数据。如果进程上没有数据要处理,那么睡眠等待直到其所属的进程有新的未处理项为止。

如果线程thread中有要处理的数据,那么就处理线程thread上的数据。如果线程上没有数据要处理,那么睡眠等待直到线程有新的未处理项为止。

如果是非阻塞访问,如果没有数据,就立刻返回,不会睡眠等待。如果有数据,就继续往下执行。

目前由于线程thread没有要处理的数据,进程上也没有要处理的数据,那么睡眠等待直到其所属的进程有新的未处理项为止。


三、目前为止各对象的引用计数

Service Manager本地对象,不需要引用计数,因为永远不会销毁。

Service Manager实体对象,内部强引用计数为1,内部弱引用计数为1,has_strong_refs为1,has_week_refs为1。

更多相关文章

  1. Android JNI 开启子线程后调用 Activity 方法更新UI
  2. Android多线程
  3. Android启动各种系统服务线程
  4. Android使用AIDL实现进程间通信
  5. 【摘录】 Android屏幕元素层次结构
  6. Android主线程与子线程的关系

随机推荐

  1. Android画图学习总结(五)——Paint
  2. 一个关于Android视频流的Github项目
  3. Android(安卓)6.0 运行时权限处理完全解
  4. (转)CrossWalk - android 动态加载so库文件
  5. [Android] AIDL的使用情况和实例介绍
  6. 为什么Android官方废弃SoftRefrerence软
  7. Android数据持久化之SQLite数据库用法分
  8. Android中使用mvvm
  9. mono for android 的ADB
  10. Android学习第一天--Activity生命周期