Android核心服务解析篇(三)——Android系统的启动

从大的方面来说。Android系统的启动能够分为两个部分:第一部分是Linux核心的启动,第二部分是Android系统的启动。

第一部分主要包含系统引导,核心和驱动程序等,因为它们不属于本篇要讲的内容,这里就不再讨论。

在本篇博客中,我们重点解说Android系统的启动,这一过程主要经过两个阶段。各自是应用的初始化流程与system_service进程及核心服务的创建流程。

1.初始化流程

初始化流程。顾名思义,它完毕Android的一些初始化工作,包含设置必要的环境变量,启动必要的服务进程,挂载必要的设备等,而这些工作将会为整个Android打下坚实的基础。

①应用的初始化流程

在核心启动完毕以后。将进入Android文件系统及应用的初始化流程。此时将会转向运行init.c中的main()函数(路径:/system/core/init/init.c),该函数的运行流程例如以下图所看到的:

以下我们了解一下上图中的注解

注解1:/dev表示设备文件系统或者udev挂载点。/proc用来挂载存放系统过程性信息的虚拟文件系统,/sys用于挂载“sysfs文件系统”。因为前面调用了umask(0)。因此mkdir(“/dev”,0755)得到的权限应该是0755.

注解2:init.rc的解析结果是形成action_list(onkeyword相关的部分),service_list(service_keyword相关的部分)以及action_queue(须要运行的命令或服务),以便兴许流程使用。

注解3:解析/proc/cmdline文件,将当中的属性导入Android系统全局变量。

注解4

Ⅰget_hardware_name()方法用于解析/proc/cpuinfo文件获取硬件信息,并用于拼接成一个init.<hardware_name>.rc文件。继续解析。

Ⅱ在解析init.rc文件的过程中,系统会依据该文件的内容形成一些须要命令,动作或者触发器的列表并将这些存入在内在中,以便在必要的时候使用。

不同的厂商可能依据不同的硬件需求定制不同的.rc文件,这些.rc文件的名称一般为“init.<hardware_name>.rc”,而解析这些.rc文件的结果相同也会形成一些命令,动作或者触发器的列表,而这些列表将会合并解析init.rc所得的命令和动作的列表中,而且形成终于须要运行的命令和动作。

注解5:加入顺序为:early-init下的全部动作,wait_for_coldboot_done_action,property_init_action。keychord_init_action,console_init_action,set_init_properties_action。init下的动作,property_service_init_action,signal_init_action。check_startup_action,early-boot下的全部动作,boot下的全部动作。queue_property_triggers_action。这些动作组成了开机过程中看到的设备的状态,比方开机动画等。

注解6:这里会启动运行设置属性,创建或挂载动作以及启动服务等操作。

须要注意是的这里启动的服务包含最重要的servicemanager和zygote服务进程。

至此。init进程进入死循环中处理一些消息以等待命令的到来。

在这个过程中。我们将要了解下面知识。

Ⅰ在init执行的过程中产生了很多服务,它们是整个Android的基础。各自是ueventd,console,adbd,servicemanager。vold,netd,debuggerd,ril-daemon,surfaceflinger,zygote,drm。media。bootanim。dbus。bluetoothd,installd,flash_recovery,racoon,mtpd,keystore和dumstate。

Ⅱ整个init的行为甚至整个Android核心的属性都受到启动脚本init.rc的影响。

以下我们就重点介绍zygote的启动行为。具体了解init.rc的语法。

②init.rc的使用方法

Android初始化语言由声明的4个类型组成,它们各自是动作(action),命令(command),服务(service),和选项(option),以#开头的行表示凝视。

动作和服务声明新的一节而且有唯一的名字,全部的命令或者选项属于近期声明的节。

假设下一个动作或者服务的名字已存在(也就是重名),则它将作为错误被忽略。

Ⅰ动作

动作是命令序列。它有一个触发器,用于确定行动应在何时发生。

当发生某一个事件时,它能够匹配到一个动作触发器,而且该动作会被加入到要运行队列的尾部(除非它已经在队列中了)。

队列中的每一个动作是按顺序出列的,详细例如以下所看到的:

on early-init

write /proc/1/oom-adj -16

setcon u:r:init:s0

start ueventd

动作表现为下面的形式:

on <trigger>

<command>

<command>

<command>

.........

触发器是一些字符串。这些字符串可用于匹配一定类型的事件,而且用于触发动作。

下表罗列了一些触发器的定义。

触发器 说明
boot 当初始化流程触发的时候。boot是首先被触发的动作(在完毕/init.conf文件载入之后)
<name>=<value> 当以<name>命名的属性被设为特定的值<value>时,该触发器发生
device-added-<path> 当加入设备节点时,device-added-<path>定义的触发器执行
device-removed-<path> 当移除设备节点时,device-removed-<path>定义的触发器执行
service-exited-<name> 当指定的服务退出时,service-exited-<name>类型的触发器执行
<string> 自己定义的触发器。可由init代码负责管理

Ⅱ命令

命令是组成动作的成员,也就是说。动作由一个个命令组成。下表罗列了动作支持的命令。

命令 说明
exec  <path> [<argument>]* fork并运行程序(<path>)。

这在程序完毕运行之前将堵塞一切进程,因此最好避免使用exec命令。该命令中两个參数的含义例如以下所看到的。

?<path>:可运行文件的路径

?[<argument>]*:可运行文件所需的參数,參数个数能够是0或者多个

export <name> <value> 设置名字为<name>的环境变量为<value>
ifup <interface> 打开网络接口<interface>
import <filename> 解析一个初始化配置文件。导入系统中
hostname <name> 设置主机名
chdir <directory> 改动工作文件夹。它的功能和cd命令一样
chmod <octal-mode> <path> 改动文件的訪问权限
chown <owner> <group> <path> 改动<path>指定的问题的全部者和组
chroot <directory> 改动进程根文件夹为<directory>
class_start <serviceclass> 启动<serviceclass>类别的服务。假设它们没有执行的话
class_stio <serviceclass> 停止<serviceclass>类别的服务,假设它们已经处于执行状态的话
domainname <name> 设置域名
insmod <path> 在<path>上安装模块
mkdir <path> [mode] [owner] [group] 创建一个文件夹,当中文件夹路径以及名称由<path>指明。

这里能够通过參数给定文件夹的模式,全部者和组。假设没有提供[mode] [owner] [group]。则用权限755来创建文件夹,而且它属于root用户root组

mount <type> <device> <dir> [<mountoption>]* 尝试在文件夹<dir>上挂载被命名的设备,<device>可能是[email protected]的形式。以便指定名为mtd块的设备。<mountoption>包含ro,rw,remount和noatime等
setkey TBD
setprop <name> <value>  设置系统属性<name> 为<value>
setrlimit <resource> <cur> <max> 设置指定资源的使用限制
start <service> 启动指定的服务。假设服务还没有执行的话
stop <service> 停止指定的服务,假设服务眼下正在执行的话
symlink <target> <path> 用值<target>来在<path>上创建一个符号链接
sysclktz <mins_west_of_gmt> 设置系统闹钟基准(假设系统闹钟为GMT。则为0)
trigger <event> 触发一个事件。用于运行该触发器中的操作
write <path> <string> [<string>]* 在<path>上打开文件而且用write(2)来将一个或多个字符串写到文件上。

在init.rc中,Android 定义了若干动作,而且这些动作用于完毕Android的初始化工作。以下以当中一个动作的配置来说明一下:

on fs

mount yaffs2 [email protected] /system

mount yaffs2 [email protected] /system ro remount

mount yaffs2 [email protected] /data nosuid nodev

mount yaffs2 [email protected] /cache nosuid nodev

这个样例配置了一个触发器为fs的动作,它由4条命令组成,这4条命令都使用mount命令挂载设备。

Ⅲ服务

服务是一些程序,当它们退出的时候。init启动而且(选择性地)又一次启动。

服务表现为下面形式:

service <name> <pathname> [<argument>]*

<option>

<option>

...........

当中各个參数的含义例如以下所看到的:

?<name>:为服务指定一个名字。

?<pathname>:指定服务须要运行的文件路径。

?[<argument>]*:启动服务所须要的參数,參数个数能够是0个或者多个。

Ⅳ选项

选项是服务的改动器。能够影响怎样以及何时初始化执行服务。下表罗列了选项列表。

选项 说明
critical 这是一个对于设备来说比較关键的服务,假设它在4分钟内退出超过4次,那么设备将又一次启动并进入recovery模式。
disabled 这个服务不能通过类别自己主动启动,它必须通过服务名字来显示启动
setenv <name> <value> 设置启动进程中环境变量(由<name>指定)的值为<value>
socket <name> <type> <perm> [<user> [<group>]] 创建名为/dev/socket/<name>的一个Unix域port而且将它的fd传递到被启动的进程上

<type>必须是dgram,stream,seqpacket.设置用户和组的默认值为0

user <username> 在运行该服务之前变换username,假设进程须要Linux的能力,就不能使用该命令
group <groupname> [<groupname>]* 在运行该服务之前变换组名
oneshot 在服务退出时不要又一次启动它
class <name> 为服务指定一个类名。一个被命名的类中的全部服务都能够一起被启动或停止。

假设服务没有通过类选项来指定的话,它是在类default中的

onrestart 当服务又一次启动时。运行一条命令

以下以init.rc文件里的配置为例简要说明一个服务的配置:

service zygote /system/bin/app_process -Xzygote /system/bin -zygote

--start-system-server

class main

socket zygote stream 666

onrestart write /sys/android_power/request_state wake

onrestart write /sys/power/state on

onrestart restart media

onrestart restart netd

在上面代码中,第一行配置了一个名为zygote的服务,这个服务将会执行/system/bin/app_process,剩余部分为參数(以空格切割)。

剩下的几行代码声明了此服务的选项。

这说明zygote是一个类型为main的服务(classmain)而且它会创建一个socket。这个socket的类型为stream,权限为666(socket zygote stream 666)。当重新启动此服务的时候,须要完毕下面事情。

?写/sys.android_power/request_state为wake

?写/sys/power/state为on

?又一次启动media服务

?又一次启动netd服务

init.rc文件须要在init启动期被解析成系统能够识别的数据结构。

前面我们读懂了init.rc的含义,以下我们就来看看init是怎样保存和组织这些信息的,首先,我们来看看在init中怎样表示动作,服务和命令,例如以下表所看到的:

组件 数据结构 说明
列表节点(listnode)
<<struct>>

listnode

+next:listnode

+prev:listnode

 

listnode是一个表示位置的数据结构,能够用来定义不同类型节点(比方动作或者服务)的运行顺序

从左側的数据结构中能够看出。这里包括了两个listnode的指针。它们用于指向前一个和后一个将要运行的节点

这些信息将帮助各种节点(动作,服务,以及命令等)组成一个双向循环列表

动作(action)
<<struct>>

action

+alist::listnode

+qlist:listnode

+tlist:listnode

+hash:signed int

+*name:char

+commands:listnode

+*current:command

 
action中包括4个表示节点位置信息的节点,它们分别表示它本身在全部动作中的位置(alist),在加入动作的队列中的位置(qlist),以及在某个触发器中的全部动作列表的位置(tlist)

action 数据结构中包括了其它的重要信息,比方动作的名字(name),包括的全部命令列表(commands)以及当前命令

服务(service)
<<struct>>

service

+slist:listnode

+*name:char

+*classname:char

+flags:unsigned

+pid:pid_t

+time_started:time_t

+time_crashed:time_t

+nr_crashed:int

+uid:uid_t

+gid:gid_t

+supp_gids[NR_SVC_SUPP_GIDS]:gid_t

+*sockets:socketinfo

+*envvars:svcenvinfo

+onrestart:action

+*keycodes:int

+nkeycodes:int

+keychord_id:int

+ioprio_class:int

+ioprio_pri:int

+nargs:int

+*arg[1]:char

 
这个数据结构中包括了服务的信息。主要包括例如以下内容:

?该服务在全部服务列表中逻辑位置的数据结构“listnode”(slist)

?服务的基本信息。比方服务的名称,进程的相关信息,所须要參数信息等

命令(command)
<<struct>>

command

+clist:listnode

+(*func)(int nargs,char **args):iint

+nargs:int

+*args[1]:char

 
这个数据结构中包含下面内容:

?节点的位置信息(clist)

?命令须要运行的函数的函数指针(func)

?參数信息:nargs和args[1]

最后。我们通过解析init.rc中的一个片段来说明解析过程。

開始解析之前,须要了解整个解析过程至关重要的一个数据结构,那就是parse_state,它保存了整个解析过程中所处的状态,下图显示了它的“成分”

<<struct>>

parse_state

+*ptr:char

+*text:char

+line:int

+nexttoken:int

+*context:void

+(*parse_line)(struct parse_state *state,int nargs,char **args):void

+*filename:char

 

③用init解析整个init.rc文件

如今我们 回到init启动的初期,这里它调用了init_parse_config_file()方法,而这种方法就是解析init.rc文件的入口。

用init解析整个init.rc文件的流程例如以下图所看到的。

以下我们了解一下上图中的注解、

注解1:state是一个被命名为parser_satte的结构体。用于保存当前文件的解析状态信息。包含解析的文件(filename),当前解析的行号(line),当前解析的文字指针(ptr),指示下一个动作的变量(nexttoken)以及解析这一行须要的函数指针(parse_line)等。

注解2:next_token()函数位于/system/core/init/parse.c中。用于分析init.rc文件的内容。它仅仅返回3个状态,各自是:T_EOF(文件结束),T_NEWLINE(一行结束)和T_TEXT(表示遇到第一个空格)。

注解3:init.rc中每一行的信息通过空格被切割为若干段,而这些信息共同组成args[INIT_PARSER_MAXARGS]的内容,并由nargs计数。

比如on fs经过解析后。这一行分为两段(各自是on和fs)。分别存放在args中,计数器的值为2.。

注解4:init.rc的每一行经过切割后,须要分析其类型(由lookup_keyword返回)。/system/core/init/keywords.h中定义了全部关键字的类型。在片段KEYWORD(on,SECTION,0,0)中。on关键字是一个SECTION,有0个(也就是不须要)參数,没有相应的触发函数(也就是最后一个0)。

注解5:state.parse_line是一个函数的指针,能够依据keyword指向两种不同的解析方法——parse_line_service(处理服务的选项)和parse_line_action(处理行为的命令)。依照这个流程,init完毕整个init.rc文件的解析,并生成service_list和action_list。兴许流程所须要的信息将从这两个列表中获取,将须要运行的命令或启动的服务增加action_queue中。这样就完毕了Android系统基础部分的启动。

在启动的过程中,须要特别注意的是,我们通过action_for_each_trigger()方法声明须要运行的命令队列,该方法的代码例如以下所看到的:

void action_for_each_trigger(const char * trigger,void (*func)(struct action *act)){

struct listnode * node。

struct action *act;

list_for_each(node,&action_list){

act=node_to_item(node,struct action,alist);

if(!strcmp(act->name,trigger)){

func(act);

}

}

}

在上述代码中,list_for_each()用于遍历action_list中的每个节点,返回节点在列表中的位置信息,然后通过node_to_item()方法生成一个action的信息,最后运行func()函数。

action_for_each_trigger()方法在init.rc中是这样调用的:

action_for_each_trigger(early-init,action_add_queue_tail);

它的含义是。在action_list中查找名字为early-init的节点。并将其信息通过action_add_queue_tail()方法增加action_queue队列的尾部。

然后在init的无限循环中遍历action_queue中的每个节点。运行它们所包括的命令。

说到这里,我们了解了init怎样对待init.rc文件的内容。以下扩展一下知识,概要介绍一下Android系统中*.rc文件的keyword及其使用需求,例如以下表。

假设你想改动或编写自己的.rc文件,那么请关注下表。

keyword 类型 參数个数
capability OPTION 0
chdir COMMAND 1
chroot COMMAND 1
class OPTION 0
class_start COMMAND 1
class_stop COMMAND 1
class_reset COMMAND 1
console OPTION 0
critical OPTION 0
disabled OPTION 0
domainname COMMAND 1
exec COMMAND 1
export COMMAND 2
group OPTION 0
hostname COMMAND 1
ifup COMMAND 1
insmod COMMAND 1
import SECTION 1
keycodes OPTION 0
mkdir COMMAND 1
mount COMMAND 3
on SECTION 0
oneshot OPTION 0
onrestart OPTION 0
restart COMMAND 1
rm COMMAND 1
rmdir COMMAND 1
service SECTION 0
setenv OPTION 2
setkey COMMAND 0
setprop COMMAND 2
setrlimit COMMAND 3
socket OPTION 0
start COMMAND 1
stop COMMAND 1
trigger COMMAND 1
Symlink COMMAND 1
sysclktz COMMAND 1
user OPTION 0
wait COMMAND 1
write COMMAND 2
copy COMMAND 2
chown COMMAND 2
chmod COMMAND 2
loglevel COMMAND 1
load_persist_props COMMAND 0
ioprio OPTION 0

2.创建system_service进程

在init进程的启动过程中。比較重要的部分由孵化进程启动system_service进程,以下具体介绍一下这个部分。system_service进程将会为我们创建一些重要的Android核心服务,包含ActivityManagerService,PackageManagerService和PowerManagerService等。这些将成为应用程序的基础。并为应用程序提供必要的接口。

①创建流程

完毕应用程序的初始化之后。init进程将创建一个名叫system_service的重要进程,而我们将在此进程中创建Android核心服务。下图显示了system_process进程以及核心服务的创建过程。

注解1:init进程会按顺序启动各种类型的服务(包含core和main)。首先启动core类型的服务。然后启动main类型的服务。因为孵化服务为main类型,所以它会在core类型的服务之后启动。因此,这里启动用于管理服务的服务——servicemanager。

启动和入口例如以下所看到的。

?启动:service zygote /system/bin/app_process -Xzygote /system/bin --zygote--start-system-server

?入口:/frameworks/base/cmds/app_process/app_main.cpp的main()函数。

注解2:此时转向/frameworks/base/core/jni/AndroidRuntime.cpp的start()函数。

注解3:启动代码例如以下:

jmethodId startMeth=env->GetStaticMethodID(startClass,"main",....);

env->CallStaticVoidMethod(startClass,startMeth,strArray);

此时转向com.android.internal.os.ZygoteInit的main()方法运行。

注解4

?载入frameworks下的preloaded-classes类。

?载入framework-res.apk下的资源。

注解5:孵化进程的主要目的就是孵化出system_process进程,这个时候流程将转向/frameworks/base/services/java/com/android/server/SystemServer.java的main()方法运行,而自身进入死循环成为守护进程。

注解6:init1()调用本地android_server_SystemServer_init1(/frameworks/base/services/jni/com_android_server_SystemServer.cpp)后,通过libAndroid_servers.So的system_init()函数启动两个服务并启动init2()、

注解7:这里启动并注冊剩余的必需服务(比方包服务和Activity服务等)。终于会启动Launcher来到桌面,至此整个启动过程完毕。

②system_service简单介绍

system_service进程很重要,它创建了很多重要的服务,那么怎样增加system_service中并接受管理呢?详细如以下的代码所看到的:

try{

Slog.i(TAG,"Backup Service");

ServiceManager.addService(Context.BACKUP_SERVICE,new BackupManagerService(context));

}catch(Throwable e){

Slog.e(TAG,"Failure starting Backup Service",e);

}

以上代码展示了system_process怎样将备份服务增加服务管理器中的。当中粗体部分的代码完毕了两件事情:第一,创建备份服务。第二,使用ServiceManager的addService()方法将创建出来的备份服务实例增加服务管理器中加以管理。

下表列出了system_service的服务keyword等知识。

服务keyword 备注
entropy EntropyService 熵服务
power PowerManagerService 电源管理服务(Context.POWER_SERVICE)
activity ActivityManagerService Activity管理服务
telephony.registry TelephonyRegistry 电话服务
package PackageManagerService 包管理服务
account AccountManagerService 账户管理服务(Context.ACCOUT_SERVICE)
battery BatteryService 电池服务
vibrator VibratorService 振动服务
alarm AlarmManagerService 报警服务(Context.ALARM_SERVICE)
window WindowManagerService 窗体服务(Context.WINDOW_SERVICE)
bluetooth BluetoothService 蓝牙服务(BluetoothAdapter.BLUETOOTH_SERVICE)
statusbar StatusBarManagerService 状态栏服务(Context.STATUS_BAR_SERVICE)
input_method InputMethodManagerService 输入法管理服务(Context.INPUT_METHOD_SERVICE)
location LocationManagerService 位置管理服务(Context.LOCATION_SERVICE)
wallpaper WallpaperManagerService 壁纸管理服务(Context.WALLPAPER_SERVICE)
audio AudioService 声音服务(Context.AUDIO_SERVICE)
user UserManagerService 用户管理服务(Context.USER_SERVICE)

以下以获取声音服务为例介绍获取服务的方法:

AudioService as=(AudioService)context.getSystemService(Context.AUDIO_SERVICE);

此时整个系统也就完毕了启动工作,这也意味着我们能够開始使用Android设备了。

时间: 2024-08-25 11:36:41

Android核心服务解析篇(三)——Android系统的启动的相关文章

Android核心服务解析篇(一)——Android系统的启动

从大的方面来说,Android系统的启动可以分为两个部分:第一部分是Linux核心的启动,第二部分是Android系统的启动.第一部分主要包括系统引导,核心和驱动程序等,由于它们不属于本篇要讲的内容,这里就不再讨论.在本篇博客中,我们重点讲解Android系统的启动,这一过程主要经过两个阶段,分别是应用的初始化流程与system_service进程及核心服务的创建流程. 1.初始化流程 初始化流程,顾名思义,它完成Android的一些初始化工作,包括设置必要的环境变量,启动必要的服务进程,挂载必

Android核心服务解析篇(二)——Android源码结构分析

获得Android源码后,我们来分析源码结构.源码的全部工程分为如下三个部分. ①Core Project:核心工程部分,这是建立Android系统的基础,保存在根目录的各个文件夹中. ②External Project:扩展工程部分,可以使其他开源项目具有扩展功能,保存在external文件夹中. ③Package:包部分,提供了Android的应用程序,内容提供者,输入法和服务,保存在package文件夹中. 在获取的Android4.3源码目录中,包含了原始Android的目标机代码,主机

Android核心服务解析篇(一)——下载Android源代码

在文字,语音,图像来说,人记住的最好或者是最多的往往是图像.对于想了解Android核心服务的程序员来说,下载Android源码来了解核心服务是最快,也是最好的方法.下面我们就从安装Android源码开头来了解Android核心服务. 1.在Linux系统中获取Android源码 ⑴下载repo 在用户目录下,创建bin文件夹,用于存放repo,并把该路径设置到环境变量中去,命令如下: $ mkdir ~/bin $ PATH=~/bin:$PATH 下载repo的脚本,用于执行repo,命令如

Android volley 解析(三)之文件上传篇

前面我们讲了如何通过 volley 实现表单的提交,而这篇文章跟上一篇衔接很大,如果没有看上一篇 blog 的朋友,建议先去看看 Android Volley解析(二)之表单提交篇 因为文件上传实质就是表单的提交,只不过它提交的数据包含文件类型,接下来还是按照表单提交的套路来分析. 数据格式 这里我们通过图片上传的案例来分析,其他文件也是同样的实现方式:以下是我在传图网传图时,上传的数据格式,先来分析一下 POST http://chuantu.biz/upload.php HTTP/1.1 H

Android之实战篇(三)

先给出我们用到的工具类 1.发送请求的工具类 本实例采用HttpClient与服务器通信,用到了一个工具类对Httpclient进行封装:定义了两个方法来发送请求 getRequest:发送GET请求 postRequest :发送POST请求 HttpUtil.java(注意IP地址换成自己的IP地址,这个BASE_URL = "http://losthost:8080/auction/,要不然,你就) [java] view plaincopy package com.infy.auctio

Android内核开发:学会分析系统的启动log

本文是<Android内核开发>系列的第八篇文章,本文主要关注如何分析Android系统的启动log,学会通过搜索重要的TAG标签,找到Android启动过程中的重要节点. 要学会分析系统的启动log信息,首先得了解Android系统的启动过程,建议先阅读<Android内核开发:图解Android系统的启动过程>这篇文章,它详细介绍了Android系统的启动过程.其次,你需要知道如何抓取系统启动log信息,建议阅读<Android内核开发:如何统计系统启动时间>这篇文

Android AOP之路三 Android上的注解

一.简介 啥是注解,不懂的可以先看我上一篇文章. 在android 里面 注解主要用来干这么几件事: 和编译器一起给你一些提示警告信息. 配合一些ide 可以更加方便快捷 安全有效的编写java代码.谷歌出的support-annotations这个库 就是主要干这个的. 和反射一起 提供一些类似于spring 可配置的功能,方便简洁. 二.Support Annotations栗子 这里使用官方的一个库,说明在开发中的简单一个应用. 2.1 导包 在新建项目的时候会自动导的,可以看build.

Android volley 解析(四)之缓存篇

这是 volley 的第四篇 blog 了,写完这篇,volley 的大部分用法也都算写了一遍,所以暂时不会写 volley 的文章了,如果想看我前面写的文章,可以点这里 Android volley 解析(三)之文件上传篇 为什么要用缓存 我们知道,当客户端在请求网络数据的时候,是需要消耗流量的,特别是对于移动端用户来说,对于流量的控制要求很高.所以在做网络请求的时候,如果对数据更新要求不是特别高,往往都会用到缓存机制,一方面能减少对服务端的请求,控制流量:另一方面,当客户端在没有网络的情况下

Android源代码解析之(十三)--&amp;gt;apk安装流程

转载请标明出处:一片枫叶的专栏 上一篇文章中给大家分析了一下android系统启动之后调用PackageManagerService服务并解析系统特定文件夹.解析apk文件并安装的过程,这个安装过程实际上是没有图形界面的,底层调用的是我们平时比較熟悉的adb命令,那么我们平时安装apk文件的时候大部分是都过图形界面安装的,那么这样的方式安装apk详细的流程是如何的呢? 本文我们就来详细看一下apk的详细安装过程,通过本文的学习希望帮助大家大概的了解到Android系统安装Apk文件的基本流程.好