前言
ACRA 是 Application Crash Report for Android 的缩写,看其单词知其意安卓应用程序崩溃报告。 作为安卓应用程序开发者,相信你一定很关注自己的软件在用户实际应用过程中的用户体验、系统稳定性等信息,特别是当系统出现崩溃或者运行不正常的情况下,第一手的崩溃异常信息对提升软件的整体竞争力是相当重要的。而ACRA可以很好的帮助你得到这些宝贵的信息。
项目实践
准备:
0. Android开发环境(Eclipse + ADT + AndroidSDK)
1. 下载最新版本的ACRA包 - v4.3.0
2. 从官网阅读相关文档,设置好相关环境
说明:
ACRA 允许你的安卓应用程序通过谷歌电子表格文档(Google Docs spreadsheet),邮件(email),服务端 HTTP POST 脚本(server-side HTTP POST script)等方式来处理崩溃报告。常见的可以使用邮件和服务端 HTTP POST 脚本,以下例子我将首先使用邮件的方式发送崩溃报告,之后大概介绍下如何使用服务端 HTTP POST 脚本的方式发送崩溃报告。
使用 Email 的方式发送崩溃报告
项目中使用到的相关文件
ACRATest
|- src
|- com.acra.mobile
|- IOMobile.java
|- IOReportSender.java
|- AndroidManifest.xml
首先看下IOMobile.java
1 @ReportsCrashes(
2 formKey = "",
3 mailTo = "reports@yourdomain.com",
4 customReportContent = {
5 ReportField.APP_VERSION_NAME, ReportField.APP_VERSION_CODE,
6 ReportField.ANDROID_VERSION, ReportField.PHONE_MODEL,
7 ReportField.CUSTOM_DATA, ReportField.STACK_TRACE, ReportField.LOGCAT },
8 mode = ReportingInteractionMode.TOAST,
9 forceCloseDialogAfterToast =
false,
10 resToastText = R.string.crash_error_report_toast_text)
11
public
class IOMobile
extends Application {
12
13 @Override
14
public
void onCreate() {
15 ACRA.init(
this);
16 ErrorReporter.getInstance().setReportSender(
new IOReportSender(
this));
17
super.onCreate();
18 }
19
20 }
注意:
1. IOMobile 继承 Application
2. IOMobile 在 AndroidManifest.xml 文件中的配置要正确
3. 第15行代码 ACRA.init(this),将ACRA注入到项目中
4. 第16行代码 new IOReportSender(this),用于定制自己的ReportSender
再来看看 IOReportSender.java
IOReportSender.java
public
class IOReportSender
implements ReportSender {
private
static
final String TAG = "IOReportSender";
private Context context =
null;
public IOReportSender(Context context) {
this.context = context;
}
@Override
public
void send(CrashReportData errorContent)
throws ReportSenderException {
sendMailReport(errorContent);
}
private
void sendMailReport(CrashReportData errorContent)
throws ReportSenderException {
new EmailIntentSender(context).send(errorContent);
}
}
注意:
1. 你可以在该类中定制异常信息,选择性发送信息。
最后来看下AndroidManifest.xml 文件
View Code
<
manifest
xmlns:android
="http://schemas.android.com/apk/res/android"
...
>
<
uses-sdk
android:minSdkVersion
="8"
android:targetSdkVersion
="15"
/>
<
supports-screens
android:anyDensity
="true"
android:largeScreens
="true"
android:normalScreens
="true"
android:resizeable
="true"
android:smallScreens
="true"
/>
<
uses-permission
android:name
="android.permission.VIBRATE"
/>
<
uses-permission
android:name
="android.permission.ACCESS_COARSE_LOCATION"
/>
<
uses-permission
android:name
="android.permission.ACCESS_FINE_LOCATION"
/>
<
uses-permission
android:name
="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"
/>
<
uses-permission
android:name
="android.permission.READ_PHONE_STATE"
/>
<
uses-permission
android:name
="android.permission.INTERNET"
/>
<
uses-permission
android:name
="android.permission.RECEIVE_SMS"
/>
<
uses-permission
android:name
="android.permission.RECORD_AUDIO"
/>
<
uses-permission
android:name
="android.permission.MODIFY_AUDIO_SETTINGS"
/>
<
uses-permission
android:name
="android.permission.READ_CONTACTS"
/>
<
uses-permission
android:name
="android.permission.WRITE_CONTACTS"
/>
<
uses-permission
android:name
="android.permission.WRITE_EXTERNAL_STORAGE"
/>
<
uses-permission
android:name
="android.permission.ACCESS_NETWORK_STATE"
/>
<
uses-permission
android:name
="android.permission.GET_ACCOUNTS"
/>
<
uses-permission
android:name
="android.permission.BROADCAST_STICKY"
/>
<
application
android:name
=".IOMobile"
android:icon
="@drawable/ic_launcher"
android:label
="@string/app_name"
android:theme
="@style/AppTheme"
>
...
</
application
>
</
manifest
>
使用 server-side HTTP POST script 的方式发送崩溃报告
这种方式发送崩溃报告,需要服务端有相应的请求响应,重要代码如下
IOReportSender
1
public
class IOReportSender
implements ReportSender {
2
3
private Context mContext =
null;
4
private Map<ReportField, String> mMapping =
new HashMap<ReportField, String>();
5
private String formUri =
null;
6
7
public IOReportSender(Context ctx) {
8 mContext = ctx;
9 formUri = ctx.getString(R.string.CrashErrorReport_URL);
10 }
11
12 @Override
13
public
void send(CrashReportData errorContent)
throws ReportSenderException {
14 ErrorReporter.getInstance().putCustomData("Cookie", getCookies());
15
16
if (isNetAvailable(mContext)) {
17 doHttpReport(errorContent);
18 }
else {
19 getMailSender().send(errorContent);
20 }
21 }
22
23
private ReportSender getMailSender() {
24
return
new EmailIntentSender(mContext);
25 }
26
27
private
void doHttpReport(CrashReportData errorContent)
throws ReportSenderException {
28
try {
29 URL reportUrl;
30 Map<String, String> finalReport = remap(errorContent);
31 URL webViewUrl =
new URL(App.getWebViewLoadUrl());
32 String realFormUri = webViewUrl.getProtocol() + "://" + webViewUrl.getHost() + ((webViewUrl.getPort() == -1) ? "" : ":" + webViewUrl.getPort()) + formUri;
33 reportUrl =
new URL(realFormUri);
34 doPost(finalReport, reportUrl, ACRA.getConfig().formUriBasicAuthLogin(), ACRA.getConfig().formUriBasicAuthPassword());
35 }
catch (Exception e) {
36
throw
new ReportSenderException("Error while sending report to Http Post Form.", e);
37 }
38 }
39
40
private Map<String, String> remap(Map<ReportField, String> report) {
41 Map<String, String> finalReport =
new HashMap<String, String>(report.size());
42 ReportField[] fields = ACRA.getConfig().customReportContent();
43
if (fields.length == 0) {
44 fields = ACRA.DEFAULT_REPORT_FIELDS;
45 }
46
for (ReportField field : fields) {
47
if (mMapping ==
null || mMapping.get(field) ==
null) {
48 finalReport.put(field.toString(), report.get(field));
49 }
else {
50 finalReport.put(mMapping.get(field), report.get(field));
51 }
52 }
53
return finalReport;
54 }
55
56
public
static
boolean isNetAvailable(Context mContext) {
57
try {
58 ConnectivityManager nInfo = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
59 nInfo.getActiveNetworkInfo().isConnectedOrConnecting();
60
61 ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
62 State mobile = cm.getNetworkInfo(0) !=
null ? cm.getNetworkInfo(0).getState() : NetworkInfo.State.DISCONNECTED;
63 State wifi = cm.getNetworkInfo(1) !=
null ? cm.getNetworkInfo(1).getState() : NetworkInfo.State.DISCONNECTED;
64
if (wifi == NetworkInfo.State.CONNECTED || wifi == NetworkInfo.State.CONNECTING) {
65
//
wifi
66
return
true;
67 }
else
if (mobile == NetworkInfo.State.CONNECTED || mobile == NetworkInfo.State.CONNECTING) {
68
//
mobile
69
return
true;
70 }
71
72
else {
73
return
false;
74 }
75 }
catch (Exception e) {
76
return
false;
77 }
78 }
79
80
/**
81
* Send an HTTP(s) request with POST parameters.
82
*
83
*
@param
parameters
84
* parameters
85
*
@param
url
86
* url
87
*
@param
login
88
* login
89
*
@param
password
90
* password
91
*
@throws
IOException
92
* IOException
93
*/
94
private
void doPost(Map<?, ?> parameters, URL url, String login, String password)
throws IOException {
95
96
//
Construct data
97
StringBuilder dataBfr =
new StringBuilder();
98
for (Object key : parameters.keySet()) {
99
if (dataBfr.length() != 0) {
100 dataBfr.append('&');
101 }
102 Object value = parameters.get(key);
103
if (value ==
null) {
104 value = "";
105 }
106 dataBfr.append(URLEncoder.encode(key.toString(), "UTF-8")).append('=').append(URLEncoder.encode(value.toString(), "UTF-8"));
107 }
108
109 LogHttpRequest req =
new LogHttpRequest(isNull(login) ?
null : login, isNull(password) ?
null : password);
110 String cookie = getCookies();
111 req.sendPost(url.toString(), dataBfr.toString(), cookie);
112 }
113
114
private
boolean isNull(String aString) {
115
return aString ==
null || aString == ACRA.NULL_VALUE;
116 }
117
118
private String getCookies() {
119 String cookie =
null;
120
try {
121 CookieSyncManager.getInstance().sync();
122 cookie = CookieManager.getInstance().getCookie(App.getWebViewLoadUrl());
123 }
catch (Exception e) {
124 }
125
return cookie;
126 }
127 }
如果需要帮助,可以联系我进行进一步的探讨。
文章参考链接
ACRA官方网站: http://code.google.com/p/acra/
作者信息:
QQ: 1321518080
Email: hucaijun520.ok@163.com