要想理清一个Service,最面好从它提供的服务开始进行分析。根据前面对DBMS的介绍可知,它提供了记录系统运行时日志信息的功能,所以这里先从dropbox日志文件的生成时说起。
当某个应用程序因为发生异常而崩溃(crash)时,ActivityManagerService(简称AMS,下同)的handleApplicationCrash函数被调用,其代码如下:
**ActivityManagerService.java**
~~~
public void handleApplicationCrash(IBinder app,
ApplicationErrorReport.CrashInfocrashInfo) {
ProcessRecordr = findAppProcess(app, "Crash");
......
//调用addErrorToDropBox函数,第一个参数是一个字符串,为“crash”
addErrorToDropBox("crash",r, null, null, null, null, null, crashInfo);
......
}
~~~
下面来看addErrorToDropBox函数:
**ActivityManagerService.java**
~~~
public void addErrorToDropBox(String eventType,
ProcessRecord process, ActivityRecord activity,
ActivityRecordparent, String subject,
final String report, final File logFile,
final ApplicationErrorReport.CrashInfo crashInfo) {
/*
dropbox日志文件的命名有一定的规则,其前缀都是一个特定的tag(标签),
tag由两部分组成,合起来是”进程类型”_”事件类型”。
下边代码中的processClass函数返回该进程的类型,包括“system_server”、“system_app”
和“data_app”三种。eventType用于指定事件类型,目前也有三种类型:“crash“、”wtf“
(what aterrible failure)和“anr”
*/
finalString dropboxTag = processClass(process) + "_" + eventType;
//获取DBMS Bn端的对象DropBoxManager
final DropBoxManager dbox = (DropBoxManager)
mContext.getSystemService(Context.DROPBOX_SERVICE);
/*
对于DBMS,不仅通过tag于标示文件名,还可以根据配置的情况,允许或禁止特定tag日志
文件的记录。isTagEnable将判断DBMS是否禁止该标签,如果该tag已被禁止,则不允许记
录日志文件
*/
if(dbox == null || !dbox.isTagEnabled(dropboxTag)) return;
//创建一个StringBuilder,用于保存日志信息
final StringBuilder sb = new StringBuilder(1024);
appendDropBoxProcessHeaders(process, sb);
......//将信息保存到字符串sb中
//单独启动一个线程用于向DBMS添加信息
Thread worker = new Thread("Error dump: " + dropboxTag) {
@Override
public void run() {
if (report != null) {
sb.append(report);
}
if (logFile != null) {
try {//如果有log文件,那么就把log文件内容读到sb中
sb.append(FileUtils.readTextFile(logFile,
128 * 1024,"\n\n[[TRUNCATED]]"));
} ......
}
//读取crashInfo信息,一般记录的是调用堆栈信息
if (crashInfo != null && crashInfo.stackTrace != null) {
sb.append(crashInfo.stackTrace);
}
String setting = Settings.Secure.ERROR_LOGCAT_PREFIX + dropboxTag;
//查询Settings数据库,判断该tag类型的日志是否对所记录的信息有行数限制,
//例如某些tag的日志文件只准记录1000行的信息
int lines =Settings.Secure.getInt(mContext.getContentResolver(),
setting, 0);
if (lines > 0) {
sb.append("\n");
InputStreamReader input =null;
try {
//创建一个新进程以运行logcat,后面的参数都是logcat常用的参数
java.lang.Processlogcat = new
ProcessBuilder("/system/bin/logcat",
"-v","time", "-b", "events", "-b","system", "-b",
"main", "-t", String.valueOf(lines))
.redirectErrorStream(true).start();
//由于新进程的输出已经重定向,因此这里可以获取最后lines行的信息,
//不熟悉ProcessBuidler的读者可以查看SDK中关于它的用法说明
......
}
}
//调用DBMS的addText
dbox.addText(dropboxTag, sb.toString());
}
};
if(process == null || process.pid == MY_PID) {
worker.run(); //如果是SystemServer进程crash了,则不能在别的线程执行
}else {
worker.start();
}
}
~~~
由上面代码可知,addErrorToDropBox在生成日志的内容上花了不少工夫,因为这些是最重要的,最后仅调用addText函数便将内容传给DBMS的功能。
addText函数定义在DropBoxManager类中,代码如下:
**DropBoxManager.java**
~~~
public void addText(String tag, String data) {
/*
mService和DBMS交互。DBMS对外只提供一个add函数用于日志添加,而DBM提供了3个函数,
分别是addText、addData、addFile,以方便我们的使用
*/
try {mService.add(new Entry(tag, 0, data)); } ......
}
~~~
DBM向DBMS传递的数据被封装在一个Entry中。下面来看DBMS的add函数,其代码如下:
**DropBoxManagerService.java**
~~~
public void add(DropBoxManager.Entry entry) {
Filetemp = null;
OutputStream output = null;
finalString tag = entry.getTag();//先取出这个Entry的tag
try{
int flags = entry.getFlags();
......
//做一些初始化工作,包括生成dropbox目录、统计当前已有的dropbox文件信息等
init();
if (!isTagEnabled(tag)) return;//如果该tag被禁止,则不能生成日志文件
long max = trimToFit();
long lastTrim = System.currentTimeMillis();
//BlockSize一般是4KB
byte[] buffer = new byte[mBlockSize];
//从Entry中得到一个输入流。与Java I/O相关的类比较多,且用法非常灵活
//建议读者阅读《Java编程思想》中“Java I/O系统”一章
InputStreaminput = entry.getInputStream();
......
int read = 0;
while (read < buffer.length) {
int n = input.read(buffer, read, buffer.length - read);
if (n <= 0) break;
read += n;
}
//先生成一个临时文件,命名方式为”drop线程id.tmp”
temp = new File(mDropBoxDir, "drop" +
Thread.currentThread().getId()+ ".tmp");
int bufferSize = mBlockSize;
if (bufferSize > 4096) bufferSize = 4096;
if (bufferSize < 512) bufferSize = 512;
FileOutputStream foutput = new FileOutputStream(temp);
output = new BufferedOutputStream(foutput, bufferSize);
//生成GZIP压缩文件
if (read == buffer.length &&
((flags &DropBoxManager.IS_GZIPPED) == 0)) {
output = new GZIPOutputStream(output);
flags = flags | DropBoxManager.IS_GZIPPED;
}
/*
DBMS很珍惜/data分区,若所生成文件的size大于一个BlockSize,
则一定要先压缩。
*/
......//写文件,这段代码非常繁琐,其主要目的是尽量节省存储空间
/*
生成一个EntryFile对象,并保存到DBMS内部的一个数据容器中。
DBMS除了负责生成文件外,还提供查询的功能,这个功能由getNextEntry完成。
另外,刚才生成的临时文件在createEntry函数中会重命为带标签的名字,
读者可自行分析createEntry函数
*/
long time = createEntry(temp, tag, flags);
temp = null;
Intent dropboxIntent = new
Intent(DropBoxManager.ACTION_DROPBOX_ENTRY_ADDED);
dropboxIntent.putExtra(DropBoxManager.EXTRA_TAG, tag);
dropboxIntent.putExtra(DropBoxManager.EXTRA_TIME, time);
if (!mBooted) {
dropboxIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
}
//发送DROPBOX_ENTRY_ADDED广播。系统中目前还没有程序接收该广播
mContext.sendBroadcast(dropboxIntent,
android.Manifest.permission.READ_LOGS);
}......
}
~~~
上面代码中略去了DBMS写文件的部分,我们从代码注释中可获悉,DBMS非常珍惜/data分区的空间,每一个日志文件都需要考虑压缩以节省存储空间。如果说细节体现功力,那么这正是一个极好的例证。
一个真实设备上/data/system/dropbox目录的内容如图3-2所示。
:-: ![](http://img.blog.csdn.net/20150803104313385?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
图3-2 真实设备中dropbox目录的内容
图3-2中最后一项data_app_anr@1324836096560.txt.gz的大小是6.1KB,该文件解压后得到的文件大小是42kB。看来,压缩确实节省了不少存储空间。
另外,我们从图3-2中还发现了其他不同的tag,如SYSTEM_BOOT、SYSTEM_TOMBSTONE等,这些都是由BootReceiver在收到BOOT_COMPLETE广播后收集相关信息并传递给DBMS而生成的日志文件。
- 前言
- 第1章 搭建Android源码工作环境
- 1.1 Android系统架构
- 1.2 搭建开发环境
- 1.2.1 下载源码
- 1.2.2 编译源码
- 1.2.3 利用Eclipse调试system_process
- 1.3 本章小结
- 第2章 深入理解Java Binder和MessageQueue
- 2.1 概述
- 2.2 Java层中的Binder架构分析
- 2.2.1 Binder架构总览
- 2.2.2 初始化Java层Binder框架
- 2.2.3 addService实例分析
- 2.2.4 Java层Binder架构总结
- 2.3 心系两界的MessageQueue
- 2.3.1 MessageQueue的创建
- 2.3.2 提取消息
- 2.3.3 nativePollOnce函数分析
- 2.3.4 MessageQueue总结
- 2.4 本章小结
- 第3章 深入理解SystemServer
- 3.1 概述
- 3.2 SystemServer分析
- 3.2.1 main函数分析
- 3.2.2 Service群英会
- 3.3 EntropyService分析
- 3.4 DropBoxManagerService分析
- 3.4.1 DBMS构造函数分析
- 3.4.2 dropbox日志文件的添加
- 3.4.3 DBMS和settings数据库
- 3.5 DiskStatsService和DeviceStorageMonitorService分析
- 3.5.1 DiskStatsService分析
- 3.5.2 DeviceStorageManagerService分析
- 3.6 SamplingProfilerService分析
- 3.6.1 SamplingProfilerService构造函数分析
- 3.6.2 SamplingProfilerIntegration分析
- 3.7 ClipboardService分析
- 3.7.1 复制数据到剪贴板
- 3.7.2 从剪切板粘贴数据
- 3.7.3 CBS中的权限管理
- 3.8 本章小结
- 第4章 深入理解PackageManagerService
- 4.1 概述
- 4.2 初识PackageManagerService
- 4.3 PKMS的main函数分析
- 4.3.1 构造函数分析之前期准备工作
- 4.3.2 构造函数分析之扫描Package
- 4.3.3 构造函数分析之扫尾工作
- 4.3.4 PKMS构造函数总结
- 4.4 APK Installation分析
- 4.4.1 adb install分析
- 4.4.2 pm分析
- 4.4.3 installPackageWithVerification函数分析
- 4.4.4 APK 安装流程总结
- 4.4.5 Verification介绍
- 4.5 queryIntentActivities分析
- 4.5.1 Intent及IntentFilter介绍
- 4.5.2 Activity信息的管理
- 4.5.3 Intent 匹配查询分析
- 4.5.4 queryIntentActivities总结
- 4.6 installd及UserManager介绍
- 4.6.1 installd介绍
- 4.6.2 UserManager介绍
- 4.7 本章学习指导
- 4.8 本章小结
- 第5章 深入理解PowerManagerService
- 5.1 概述
- 5.2 初识PowerManagerService
- 5.2.1 PMS构造函数分析
- 5.2.2 init分析
- 5.2.3 systemReady分析
- 5.2.4 BootComplete处理
- 5.2.5 初识PowerManagerService总结
- 5.3 PMS WakeLock分析
- 5.3.1 WakeLock客户端分析
- 5.3.2 PMS acquireWakeLock分析
- 5.3.3 Power类及LightService类介绍
- 5.3.4 WakeLock总结
- 5.4 userActivity及Power按键处理分析
- 5.4.1 userActivity分析
- 5.4.2 Power按键处理分析
- 5.5 BatteryService及BatteryStatsService分析
- 5.5.1 BatteryService分析
- 5.5.2 BatteryStatsService分析
- 5.5.3 BatteryService及BatteryStatsService总结
- 5.6 本章学习指导
- 5.7 本章小结
- 第6章 深入理解ActivityManagerService
- 6.1 概述
- 6.2 初识ActivityManagerService
- 6.2.1 ActivityManagerService的main函数分析
- 6.2.2 AMS的 setSystemProcess分析
- 6.2.3 AMS的 installSystemProviders函数分析
- 6.2.4 AMS的 systemReady分析
- 6.2.5 初识ActivityManagerService总结
- 6.3 startActivity分析
- 6.3.1 从am说起
- 6.3.2 AMS的startActivityAndWait函数分析
- 6.3.3 startActivityLocked分析
- 6.4 Broadcast和BroadcastReceiver分析
- 6.4.1 registerReceiver流程分析
- 6.4.2 sendBroadcast流程分析
- 6.4.3 BROADCAST_INTENT_MSG消息处理函数
- 6.4.4 应用进程处理广播分析
- 6.4.5 广播处理总结
- 6.5 startService之按图索骥
- 6.5.1 Service知识介绍
- 6.5.2 startService流程图
- 6.6 AMS中的进程管理
- 6.6.1 Linux进程管理介绍
- 6.6.2 关于Android中的进程管理的介绍
- 6.6.3 AMS进程管理函数分析
- 6.6.4 AMS进程管理总结
- 6.7 App的 Crash处理
- 6.7.1 应用进程的Crash处理
- 6.7.2 AMS的handleApplicationCrash分析
- 6.7.3 AppDeathRecipient binderDied分析
- 6.7.4 App的Crash处理总结
- 6.8 本章学习指导
- 6.9 本章小结
- 第7章 深入理解ContentProvider
- 7.1 概述
- 7.2 MediaProvider的启动及创建
- 7.2.1 Context的getContentResolver函数分析
- 7.2.2 MediaStore.Image.Media的query函数分析
- 7.2.3 MediaProvider的启动及创建总结
- 7.3 SQLite创建数据库分析
- 7.3.1 SQLite及SQLiteDatabase家族
- 7.3.2 MediaProvider创建数据库分析
- 7.3.3 SQLiteDatabase创建数据库的分析总结
- 7.4 Cursor 的query函数的实现分析
- 7.4.1 提取query关键点
- 7.4.2 MediaProvider 的query分析
- 7.4.3 query关键点分析
- 7.4.4 Cursor query实现分析总结
- 7.5 Cursor close函数实现分析
- 7.5.1 客户端close的分析
- 7.5.2 服务端close的分析
- 7.5.3 finalize函数分析
- 7.5.4 Cursor close函数总结
- 7.6 ContentResolver openAssetFileDescriptor函数分析
- 7.6.1 openAssetFileDescriptor之客户端调用分析
- 7.6.2 ContentProvider的 openTypedAssetFile函数分析
- 7.6.3 跨进程传递文件描述符的探讨
- 7.6.4 openAssetFileDescriptor函数分析总结
- 7.7 本章学习指导
- 7.8 本章小结
- 第8章 深入理解ContentService和AccountManagerService
- 8.1 概述
- 8.2 数据更新通知机制分析
- 8.2.1 初识ContentService
- 8.2.2 ContentResovler 的registerContentObserver分析
- 8.2.3 ContentResolver的 notifyChange分析
- 8.2.4 数据更新通知机制总结和深入探讨
- 8.3 AccountManagerService分析
- 8.3.1 初识AccountManagerService
- 8.3.2 AccountManager addAccount分析
- 8.3.3 AccountManagerService的分析总结
- 8.4 数据同步管理SyncManager分析
- 8.4.1 初识SyncManager
- 8.4.2 ContentResolver 的requestSync分析
- 8.4.3 数据同步管理SyncManager分析总结
- 8.5 本章学习指导
- 8.6 本章小结