mobi.cangol.mobile.service.crash.CrashServiceImpl Maven / Gradle / Ivy
/**
* Copyright (c) 2013 Cangol
*
* Licensed under the Apache License, Version 2.0 (the "License")
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package mobi.cangol.mobile.service.crash;
import android.annotation.TargetApi;
import android.app.Application;
import android.os.AsyncTask;
import android.os.Build;
import android.os.StrictMode;
import android.text.TextUtils;
import java.io.File;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.Thread.UncaughtExceptionHandler;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import mobi.cangol.mobile.CoreApplication;
import mobi.cangol.mobile.http.AsyncHttpClient;
import mobi.cangol.mobile.http.AsyncHttpResponseHandler;
import mobi.cangol.mobile.http.RequestParams;
import mobi.cangol.mobile.logging.Log;
import mobi.cangol.mobile.service.AppService;
import mobi.cangol.mobile.service.PoolManager;
import mobi.cangol.mobile.service.Service;
import mobi.cangol.mobile.service.ServiceProperty;
import mobi.cangol.mobile.service.conf.ConfigService;
import mobi.cangol.mobile.service.session.SessionService;
import mobi.cangol.mobile.utils.DeviceInfo;
import mobi.cangol.mobile.utils.FileUtils;
import mobi.cangol.mobile.utils.Object2FileUtils;
import mobi.cangol.mobile.utils.TimeUtils;
/**
* @author Cangol
*/
@Service("CrashService")
class CrashServiceImpl implements CrashService, UncaughtExceptionHandler {
private final static String TAG = "CrashService";
private final static String _CRASH = ".crash";
private boolean debug = true;
private Thread.UncaughtExceptionHandler mDefaultExceptionHandler;
private Application mContext;
private SessionService mSessionService;
private ConfigService mConfigService;
private ServiceProperty mServiceProperty = null;
private AsyncHttpClient asyncHttpClient;
private String mUrl;
private Map mParams;
@TargetApi(Build.VERSION_CODES.GINGERBREAD)
@Override
public void onCreate(Application context) {
mContext = context;
mDefaultExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(this);
CoreApplication app = (CoreApplication) mContext;
mSessionService = (SessionService) app.getAppService(AppService.SESSION_SERVICE);
mConfigService = (ConfigService) app.getAppService(AppService.CONFIG_SERVICE);
StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
FileUtils.newFolder(mConfigService.getTempDir().getAbsolutePath());
StrictMode.setThreadPolicy(oldPolicy);
}
@Override
public void init(ServiceProperty serviceProperty) {
this.mServiceProperty = serviceProperty;
PoolManager.buildPool(mServiceProperty.getString(CRASHSERVICE_THREADPOOL_NAME), mServiceProperty.getInt(CRASHSERVICE_THREAD_MAX));
asyncHttpClient = AsyncHttpClient.build(mServiceProperty.getString(CRASHSERVICE_THREADPOOL_NAME));
}
@Override
public String getName() {
return TAG;
}
@Override
public void onDestroy() {
asyncHttpClient.cancelRequests(mContext, true);
}
@Override
public void setDebug(boolean debug) {
this.debug = debug;
}
@Override
public ServiceProperty getServiceProperty() {
return mServiceProperty;
}
@Override
public ServiceProperty defaultServiceProperty() {
ServiceProperty sp = new ServiceProperty(TAG);
sp.putString(CRASHSERVICE_THREADPOOL_NAME, TAG);
sp.putInt(CRASHSERVICE_THREAD_MAX, 1);
sp.putString(CRASHSERVICE_REPORT_URL, "");
sp.putString(CRASHSERVICE_REPORT_ERROR, "error");
sp.putString(CRASHSERVICE_REPORT_POSITION, "position");
sp.putString(CRASHSERVICE_REPORT_TIMESTAMP, "timestamp");
sp.putString(CRASHSERVICE_REPORT_CONTEXT, "content");
sp.putString(CRASHSERVICE_REPORT_FATAL, "fatal");
return sp;
}
@Override
public void setReport(String url, Map params) {
this.mUrl = url;
this.mParams = params;
}
private void report(final ReportError report) {
if (debug) {
Log.d(TAG, "report .crash " + report.path);
}
RequestParams params = this.mParams == null ? new RequestParams() : new RequestParams(this.mParams);
params.put(mServiceProperty.getString(CRASHSERVICE_REPORT_ERROR), report.error);
params.put(mServiceProperty.getString(CRASHSERVICE_REPORT_POSITION), report.position);
params.put(mServiceProperty.getString(CRASHSERVICE_REPORT_CONTEXT), report.context);
params.put(mServiceProperty.getString(CRASHSERVICE_REPORT_TIMESTAMP), report.timestamp);
params.put(mServiceProperty.getString(CRASHSERVICE_REPORT_FATAL), report.fatal);
asyncHttpClient.post(mContext, mUrl, params, new AsyncHttpResponseHandler() {
@Override
public void onStart() {
super.onStart();
}
@Override
public void onSuccess(String content) {
super.onSuccess(content);
FileUtils.delFileAsync(report.path);
}
@Override
public void onFailure(Throwable error, String content) {
super.onFailure(error, content);
}
});
}
protected String throwableToString(Throwable ex) {
Writer writer = new StringWriter();
PrintWriter pw = new PrintWriter(writer);
ex.printStackTrace(pw);
pw.close();
String error = writer.toString();
return error;
}
protected ReportError makeReportError(Throwable ex) {
String timestamp = TimeUtils.getCurrentTime();
String filename = timestamp.replaceAll(" ", "").replaceAll("-", "").replaceAll(":", "");
ReportError error = new ReportError();
error.error = ex.toString();
error.position = ex.getStackTrace()[0].toString();
error.context = throwableToString(ex);
error.timestamp = timestamp;
error.fatal = "0";
error.path = mConfigService.getTempDir() + File.separator + filename + _CRASH;
return error;
}
@Override
public void uncaughtException(Thread thread, Throwable ex) {
Thread.setDefaultUncaughtExceptionHandler(mDefaultExceptionHandler);
ReportError error = makeReportError(ex);
Log.e("AndroidRuntime", error.context);
if (debug) {
Log.d(TAG, "save .crash " + error.path);
}
Object2FileUtils.writeObject(error, error.path);
//System.gc();
mSessionService.saveString("exitCode", "1");
mSessionService.saveString("exitVersion", DeviceInfo.getAppVersion(mContext));
//0 正常推退出 1异常退出
System.exit(0);
}
@Override
public void report(final CrashReportListener crashReportListener) {
new AsyncErrorScan() {
@Override
protected void onPostExecute(List result) {
super.onPostExecute(result);
for (final ReportError errorReport : result) {
if (!TextUtils.isEmpty(mUrl)) {
report(errorReport);
}
if (crashReportListener != null) {
crashReportListener.report(errorReport.path,
errorReport.error,
errorReport.position,
errorReport.context,
errorReport.timestamp,
errorReport.fatal);
}
}
}
}.execute(mConfigService.getTempDir().getAbsolutePath());
}
static class ReportError implements java.io.Serializable {
private static final long serialVersionUID = 0L;
String error;
String position;
String context;
String timestamp;
String fatal;
String path;
ReportError() {
}
}
static class AsyncErrorScan extends AsyncTask> {
@Override
protected List doInBackground(String... params) {
List files = FileUtils.searchBySuffix(new File(params[0]), null, _CRASH);
//System.gc();
List reports = new ArrayList();
Object obj = null;
for (final File file : files) {
obj = FileUtils.readObject(file);
if (obj != null) {
reports.add((ReportError) obj);
} else {
FileUtils.delFileAsync(file.getAbsolutePath());
}
}
return reports;
}
}
}