android全局处理非捕捉异常
16lz
2021-12-04
一、概述
android应用捕捉所有线程没有catch的exception,并将相关日志文件压缩并发送到服务器。环境如下:
android: slf4j+logback,retrofit
server: play framework 2
二、自定义Application
public class MyApp extends Application { private static final Logger log = LoggerFactory.getLogger(MyApp.class); public boolean isUIThread(){ return Looper.getMainLooper().getThread() == Thread.currentThread(); } @Override public void onCreate() { log.debug("onCreate"); super.onCreate(); //处理非捕捉异常 Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { @Override public void uncaughtException(Thread thread, Throwable e) { handleUncaughtException(thread, e); } }); } private void handleUncaughtException(Thread thread, Throwable e) { log.error("UncaughtException thread=" + thread.getName(), e); if(isUIThread()) {//UI thread throw exception invokeLogActivity(); }else{//non UI thread throw exception new Handler(Looper.getMainLooper()).post(new Runnable() { @Override public void run() { invokeLogActivity(); } }); } } //启动另外一个acitivy发送日志 private void invokeLogActivity(){ Intent intent = new Intent(); intent.setAction("cn.mydomain.SEND_LOG"); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); System.exit(1); }}
三、发送错误日志文件的Activity
<activity android:label="@string/base_warning" android:name=".ui.SendErrorLogActivity" android:theme="@android:style/Theme.Holo.Dialog" > <intent-filter> <action android:name="cn.mydomain.SEND_LOG" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter></activity>
public class SendErrorLogActivity extends Activity{ private static final Logger log = LoggerFactory.getLogger(SendErrorLogActivity.class); private Button button; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setFinishOnTouchOutside(false); LinearLayout layout = new LinearLayout(this); layout.setOrientation(LinearLayout.VERTICAL); layout.setPadding(8, 8, 8, 8); layout.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); TextView textView = new TextView(this); textView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); textView.setText(R.string.error_exit); textView.setTextAppearance(this, R.style.TextAppearance_AppCompat_Large); textView.setPadding(8, 8, 8, 8); layout.addView(textView); button = new Button(this); button.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); button.setText(R.string.base_confirm); button.setGravity(Gravity.CENTER); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { log.info("send error file to server"); try { SimpleDateFormat sdf = new SimpleDateFormat("yyyy_MM_dd_hh_mm_ss"); String targetFile = getCacheDir() + "/errorMsg" + sdf.format(new Date()) + ".gz"; CompressUtil.createGzFile(MyApp.instance.getAppDir() + "/files/log.txt", targetFile);//压缩文件 button.setEnabled(false); //发送文件到服务器 MainManage.uploadErrorFile(new File(targetFile), new Function() { @Override public void success(Object o) { log.error("send error file success"); log.error("app exit by uncaught exception"); System.exit(1); } @Override public void fail(Throwable t) { log.error("send error file fail", t); log.error("app exit by uncaught exception"); System.exit(1); } }); } catch (Exception e1) { log.error("send error file fail", e1); log.error("app exit by uncaught exception"); System.exit(1); } } }); layout.addView(button); setContentView(layout); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { log.error("app exit by uncaught exception without send error file"); System.exit(1); return true; } return super.onKeyDown(keyCode, event); }}
MainManage调用核心方法
public static void uploadErrorFile(File file, final Function callback){ log.info("uploadErrorFile"); service.uploadErrorFile(new TypedFile("application/x-gzip", file), new MyCallBack("uploadErrorFile fileName=" + file.getName(), callback)); } public interface MyService{ @Multipart @POST("/app/errorFile") void uploadErrorFile(@Part("errorFile") TypedFile errorFile, Callback<Response> cb); }
四、服务器处理
routes
POST /app/errorFile cn.mydomain.web.controllers.Controller.uploadErrorFile()
public Result uploadErrorFile(){ Http.MultipartFormData body = request().body().asMultipartFormData(); if(body == null){ return badRequest(); } Http.MultipartFormData.FilePart errorFile = body.getFile("errorFile"); if (errorFile != null) { String fileName = errorFile.getFilename(); logger.info("uploadErrorFile fileName=" + fileName); File file = errorFile.getFile(); File targetDir = new File(GlobalData.ERROR_FILE_PATH); if(!targetDir.exists()){ targetDir.mkdirs(); } try { IOUtils.copy(new FileInputStream(file), new FileOutputStream(GlobalData.ERROR_FILE_PATH + fileName)); } catch (IOException e) { logger.error("upload error file fail filename=" + fileName, e); } return ok("File uploaded"); } else { return badRequest(); } }
版权声明:本文为博主原创文章,未经博主允许不得转载。
更多相关文章
- android之HttpURLConnection
- android 中调用接口发送短信
- Android(安卓)SDK 在线更新镜像服务器资源
- Android(安卓)系统应用调用,intent 的使用方法总结
- Android发送短信
- Android(安卓)发邮件
- Android(安卓)短信发送监控
- Android(安卓)客户端与服务器交互方式
- android客户端与服务器数据交换原则