App发生崩溃保存崩溃日志在本地,并发送邮件给开发人员
2017-11-30 00:41
585 查看
App在客户手中时不时会出现闪退,崩溃等现象。但蛋疼的时有时候无法重现崩溃原因处理。于是,崩溃保存日志出来了,但保存在用户本地也看不到啊,于是,发邮件又来了。效果如图
再说个蛋疼的问题,我在公司Android stadio 2.3.3版本UncaughtExceptionHandler不会跳到这个奔溃的提示页面,但我在家里的Android stadio 3.0.1版本有没问题。默默的问问,2.3.3-3.0.1更新了什么。
好了,不哔哔,上代码。
其中主要的是实现UncaughtExceptionHandler这个接口,这个接口有什么用呢?简单点来说,会针对,某段代码做try … catch 没有catch到的代码,发生异常的时候,就会由setDefaultUncaughtExceptionHandler来处理
这里面为什么跳转页面询问用户呢,本来我是打算直接发送邮件的,但有个问题,发邮件是个耗时的操作,万一网络不好,发邮件NNNNN久,那岂不是邮件还没发出去,APP已经退出了。而且蛋疼的是在uncaughtException起不来等待框,发邮件的时间久了,岂不是奔溃的APP一直在黑屏页面嘛。
文件读写,忘了是copy网上哪位大牛的了,在次万分抱歉,这里方法较多,就不全部copy出来,就留我用到的吧
程序入口,初始化全局变量
这是发送邮件的页面
我这边发送邮件用的是java的mail.jar,activation.jar,additionnal.jar这三个类,用这三个的原因主要是因为切到java web开发也可以用。其实我不是特别明白additionnal这个jar是干嘛的,但缺了这个jar会报错,没细研究。有懂的小伙伴顺道告知下。
顺道附上我的jar包地址:
http://download.csdn.net/download/feiniyan4944/10138558
发送邮件的基础类,这里要说一下的是,QQ邮箱和腾讯企业邮必须加上这段
MailSSLSocketFactory sf = null;
try {
sf = new MailSSLSocketFactory();
sf.setTrustAllHosts(true);
} catch (GeneralSecurityException e1) {
e1.printStackTrace();
}
//开启安全协议
p.put(“mail.smtp.ssl.enable”, “true”);
p.put(“mail.smtp.ssl.socketFactory”, sf);
刚开始测试的时候,比较蛋疼的是我用163邮箱没事,转到QQ邮箱死命发不出去了,百度了下才知识QQ邮箱是使用SSL的
邮件工具类没多写,其实还有附件,抄送,多图片等等可以扩展,但懒,就写了个最简单的发送邮件,百度有很多邮件的Demo
主页面
AndroidManifest.xml
activity_main.xml
activity_crash.xml
这里面的代码还有个发送邮件的等待框,和因为Toast只能在子线程显示但很多地方很烦总报线程错,而写的工具类,但目前总是报错导致APP奔溃,目前问题还不是很清楚是什么原因,还有比较蛋疼的是我在真机跑的时候logcat不知道为什么没用日志打印,而在虚拟机没有这样的问题。
最后附上我的源码地址:
http://download.csdn.net/download/feiniyan4944/10138555
咸鱼开发,有bug处望大佬纠正
再说个蛋疼的问题,我在公司Android stadio 2.3.3版本UncaughtExceptionHandler不会跳到这个奔溃的提示页面,但我在家里的Android stadio 3.0.1版本有没问题。默默的问问,2.3.3-3.0.1更新了什么。
好了,不哔哔,上代码。
其中主要的是实现UncaughtExceptionHandler这个接口,这个接口有什么用呢?简单点来说,会针对,某段代码做try … catch 没有catch到的代码,发生异常的时候,就会由setDefaultUncaughtExceptionHandler来处理
/** * Created by supper on 2017/11/22. * UncaughtExceptionHandler做全局的catch * 通常来讲,会针对,某段代码做try … catch 没有catch到的代码,发生异常的时候,就会由setDefaultUncaughtExceptionHandler来处理。 * 当程序崩溃时,由这些代码接管 * 将报错文件报错到本地,超出日志的大小删除掉旧日志 */ public class CrashHandler implements Thread.UncaughtExceptionHandler { public static final String TAG = "APP>>CrashHandler"; //系统默认的UncaughtException处理类 private static Thread.UncaughtExceptionHandler mDefaultUncaughtException; //CarshHandler的单例实例 private static CrashHandler instance; //程序的Context对象 private Context mContext; //用来存储设备信息和异常信息 private Map<String, String> infos = new HashMap<String, String>(); private String errorLog = ""; private String errorData = ""; private String logName = "error"; private long FileSize = 1024 * 1024 * 10;//1个文件只保存10M的日志,超出重新创建日志文件 Handler handler = new Handler(Looper.getMainLooper()); /** 获取CrashHandler实例 ,单例模式 */ public static CrashHandler getInstance() { if(instance == null) instance = new CrashHandler(); return instance; } //初始化 public void init(Context context){ Log.d(TAG, "init: 初始化"); mContext = context; //获取系统默认的UncaughtException处理器 mDefaultUncaughtException = Thread.getDefaultUncaughtExceptionHandler(); //设置该CrashHandler为程序的默认处理器 Thread.setDefaultUncaughtExceptionHandler(this); cleanLog(3); } @Override public void uncaughtException(Thread thread, Throwable ex) { if (!handleException(ex) && mDefaultUncaughtException != null) { //以对代码做try … catch处理的代码,继续让系统默认的异常处理器来处理 mDefaultUncaughtException.uncaughtException(thread, ex); } else { //uncaughtException无法调出弹窗,只能跳到Activity,询问用户是否发送邮件 Intent intent = new Intent(mContext, CrashDialog.class); intent.putExtra("errorData",errorData); intent.putExtra("errorLog",errorLog); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mContext.startActivity(intent); System.exit(0); android.os.Process.killProcess(android.os.Process.myPid()); // //App重启处理 // Intent intent = new Intent(mContext, MainActivity.class); // PendingIntent restartIntent = PendingIntent.getActivity(mContext, 0, intent, Intent.FLAG_ACTIVITY_NEW_TASK); // //退出程序 // AlarmManager mgr = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE); // mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 1000, // restartIntent); // 1秒钟后重启应用 // System.exit(0); // android.os.Process.killProcess(android.os.Process.myPid()); } } private boolean handleException(Throwable ex){ if(ex == null){ return false; } //收集设备参数信息 collectDeviceInfo(mContext); try{ if(FileUtil.hasSdcard()){//SD卡可用 saveCrashInfoFile(ex); } }catch (Exception e){ Log.e(TAG,e.getMessage()); } return true; } private void collectDeviceInfo(Context ctx){ try { PackageManager pm = ctx.getPackageManager(); PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(),PackageManager.GET_ACTIVITIES); if(pi != null){ //获取版本 String versionName = pi.versionName == null ? "null" : pi.versionName; String versionCode = pi.versionCode + ""; infos.put("versionName", versionName); infos.put("versionCode", versionCode); } } catch (PackageManager.NameNotFoundException e) { Log.e(TAG, "获取版本信息不成功》》" + e); } //获取手机的配置信息 Field[] fields = Build.class.getDeclaredFields(); for (Field field : fields) { try { field.setAccessible(true);//成员变量为private,故必须进行此操 infos.put(field.getName(), field.get(null).toString()); Log.d(TAG, field.getName() + " : " + field.get(null)); } catch (Exception e) { Log.e(TAG, "获取配置信息不成功》》", e); } } } /** * 保存错误信息到文件中 * @param ex * @return 返回文件名称,便于将文件传送到服务器 * @throws Exception */ private String saveCrashInfoFile(Throwable ex) throws Exception { Log.d(TAG, "saveCrashInfoFile:错误信息" + ex.getMessage()); errorLog = ""; errorData = ""; Log.d(TAG, "saveCrashInfoFile: gg了,保存报错日志到本地"); StringBuffer sb = new StringBuffer(); try { sb.append("\n\n\n-------------------------------------------我是开始的分割线---------------------------------------------------\n"); SimpleDateFormat sDateFormat = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss"); errorData = sDateFormat.format(new java.util.Date()); sb.append(errorData + "\n"); //Map.Entry<String, String> 将map里的每一个键值对取出来封装成一个Entry对象在存到一个Set里面 for (Map.Entry<String, String> entry : infos.entrySet()) { String key = entry.getKey(); String value = entry.getValue(); sb.append(key + "=" + value + "\n"); } Writer writer = new StringWriter(); PrintWriter printWriter = new PrintWriter(writer); ex.printStackTrace(printWriter); Throwable cause = ex.getCause(); while (cause != null) { cause.printStackTrace(printWriter); cause = cause.getCause(); } printWriter.flush(); printWriter.close(); String result = writer.toString(); sb.append(result); sb.append("-------------------------------------------我是结束的分割线---------------------------------------------------\n\n\n\n"); final String fileName = getFileName(); FileUtil.writeFile(fileName,sb.toString(),true); errorLog = sb.toString(); Log.d(TAG, "saveCrashInfoFile:日志地址" + fileName); // ToastUtil.getInstance().makeText(mContext,"日志地址" + fileName); return fileName; } catch (Exception e) { Log.e(TAG, "an error occured while writing file...", e); sb.append("an error occured while writing file...\r\n"); } return null; } /** * 获取要打印日志的文本 * 未超出指定的文本大小在原日志末尾继续添加,否则重新建个日志文件 * @return */ private String getFileName(){ List<String> fileList = FileUtil.getFileNameList(getGlobalpath(),"log"); int num = 0; String path = getGlobalpath(); String fileName = ""; if(fileList != null && fileList.size() > 0){ for(String str : fileList){ String numStr = str.substring(logName.length() , logName.length() + 1); try { int m = Integer.valueOf(numStr); if(m > num){ num = m; } }catch (Exception e){} } long size = FileUtil.getFileSize(path + logName + num + ".log"); if(size < FileSize){//判断日志大小是否超过 fileName = path + logName + num + ".log"; return fileName; }else{ fileName = path + logName + (num + 1) + ".log"; FileUtil.createFile(fileName); } }else{ fileName =getGlobalpath() + logName + (num + 1) + ".log"; } return fileName; } /** * 最多可存放多少日志文件数,超出日志数删除 * @param logNum */ private void cleanLog(int logNum){ Log.d(TAG, "cleanLog: 清除日志文件 logNum:" + logNum); String path = getGlobalpath(); List<String> fileList = FileUtil.getFileNameList(path,"log"); if(fileList == null && fileList.size() < logNum){ return; } Log.d(TAG, "cleanLog: 当前日志文件数" + fileList.size()); List<Integer> intlist = new ArrayList<Integer>(); for(String str : fileList){ String numStr = str.substring(logName.length() , logName.length() + 1); try { Integer m = Integer.valueOf(numStr); if(m != null){ intlist.add(m); } }catch (Exception e){} } if(intlist != null && intlist.size() > logNum){ Collections.sort(intlist);//重新排序 for(int i = 0; i < intlist.size() - logNum;i ++){ FileUtil.delete(path + logName + intlist.get(i) + ".log",null); Log.d(TAG, "cleanLog: 清除日志文件" + logName + intlist.get(i) + ".log"); } } } /** * 获取存放日志的文件夹 * @return */ public static String getGlobalpath() { return Environment.getExternalStorageDirectory().getPath() + File.separator + "Demo" + File.separator + "Log" + File.separator; } }
这里面为什么跳转页面询问用户呢,本来我是打算直接发送邮件的,但有个问题,发邮件是个耗时的操作,万一网络不好,发邮件NNNNN久,那岂不是邮件还没发出去,APP已经退出了。而且蛋疼的是在uncaughtException起不来等待框,发邮件的时间久了,岂不是奔溃的APP一直在黑屏页面嘛。
文件读写,忘了是copy网上哪位大牛的了,在次万分抱歉,这里方法较多,就不全部copy出来,就留我用到的吧
/** * File工具类 * 主要封装了一些对文件读写的操作 * */ public final class FileUtil { private FileUtil() { throw new Error(" ̄﹏ ̄"); } /** 分隔符. */ public final static String FILE_EXTENSION_SEPARATOR = "."; /**"/"*/ public final static String SEP = File.separator; /** SD卡根目录 */ public static final String SDPATH = Environment .getExternalStorageDirectory() + File.separator; /** * 向文件中写入数据 * @param filePath 文件目录 * @param content 要写入的内容 * @param append 如果为 true,则将数据写入文件末尾处,而不是写入文件开始处 * @return 写入成功返回true, 写入失败返回false * @throws IOException */ public static boolean writeFile(String filePath, String content, boolean append) throws IOException { if (TextUtils.isEmpty(filePath)) return false; if (TextUtils.isEmpty(content)) return false; FileWriter fileWriter = null; try { createFile(filePath); fileWriter = new FileWriter(filePath, append); fileWriter.write(content); fileWriter.flush(); return true; } finally { if (fileWriter != null) { try { fileWriter.close(); } catch (IOException e) { e.printStackTrace(); } } } } /** * 获取某个目录下的指定扩展名的文件名称 * @param dirPath 目录 * @return 某个目录下的所有文件名 */ public static List<String> getFileNameList(String dirPath, final String extension) { if (TextUtils.isEmpty(dirPath)) return Collections.emptyList(); File dir = new File(dirPath); File[] files = dir.listFiles(new FilenameFilter() { @Override public boolean accept(File dir, String filename) { if (filename.indexOf("." + extension) > 0) return true; return false; } }); if (files == null) return Collections.emptyList(); List<String> conList = new ArrayList<String>(); for (File file : files) { if (file.isFile()) conList.add(file.getName()); } return conList; } /** * 删除指定目录中特定的文件 * @param dir * @param filter */ public static void delete(String dir, FilenameFilter filter) { if (TextUtils.isEmpty(dir)) return; File file = new File(dir); if (!file.exists()) return; if (file.isFile()) file.delete(); if (!file.isDirectory()) return; File[] lists = null; if (filter != null) lists = file.listFiles(filter); else lists = file.listFiles(); if (lists == null) return; for (File f : lists) { if (f.isFile()) { f.delete(); } } } /** * 获得文件或文件夹的大小 * @param path 文件或目录的绝对路径 * @return 返回当前目录的大小 ,注:当文件不存在,为空,或者为空白字符串,返回 -1 */ public static long getFileSize(String path) { if (TextUtils.isEmpty(path)) { return -1; } File file = new File(path); return (file.exists() && file.isFile() ? file.length() : -1); } }
程序入口,初始化全局变量
/** * Created by supper on 2017/11/22. */ public class BaseApplication extends Application { private static BaseApplication instance; public BaseApplication getInstance(){ return instance; } private Context mContext; @Override public void onCreate() { super.onCreate(); instance = this; mContext = this; CrashHandler.getInstance().init(mContext);//初始化carsh处理类 } }
这是发送邮件的页面
/** * Created by supper on 2017/11/29. */ public class CrashDialog extends Activity implements View.OnClickListener{ private final String TAG = "APP>>CrashDialog"; private Button btnYes, btnNo; private String errorData = "",errorLog = ""; private Context context; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_crash); context = this; initView(); setFinishOnTouchOutside(false); Intent intent = getIntent(); errorData = intent.getStringExtra("errorData"); errorLog = intent.getStringExtra("errorLog"); } private void initView() { btnYes = (Button) findViewById(R.id.btn_yes); btnNo = (Button) findViewById(R.id.btn_no); btnYes.setOnClickListener(this); btnNo.setOnClickListener(this); } @Override public void onClick(View view) { switch (view.getId()){ case R.id.btn_yes: sendMessage(); break; case R.id.btn_no: exit(); break; } } private void exit() { System.exit(0); android.os.Process.killProcess(android.os.Process.myPid()); } private void sendMessage() { /*****************************************************/ Log.d(TAG, "sendMessage: 开始发送邮件"); // 这个类主要是设置邮件 new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub MailSendVo mailInfo = new MailSendVo(); mailInfo.setMailServerHost(Config.ERROR_MAIL_SERVERHOST); mailInfo.setMailServerPost(Config.ERROR_MAIL_SERVERPORT); mailInfo.setValidate(true); mailInfo.setUserName(Config.ERROR_MAIL_USERNAME); mailInfo.setPassWord(Config.ERROR_MAIL_PASSWORD);// 您的邮箱密码 mailInfo.setFromAddress(Config.ERROR_MAIL_USERNAME); mailInfo.setToAddress(Config.ERROR_MAIL_USERNAME); mailInfo.setSubject(errorData + "错误日志"); mailInfo.setContent(errorLog); // 这个类主要来发送邮件 EmailUtil sms = new EmailUtil(); boolean isSuccess = sms.sendTextMail(mailInfo);// 发送文体格式 // sms.sendHtmlMail(mailInfo);//发送html格式 DialogThridUtils.getInstance().closeDialog(); if (isSuccess) { Log.d(TAG, "run: 发送成功"); } else { Log.d(TAG, "run: 发送失败"); } exit(); } }).start(); DialogThridUtils.getInstance().showWaitDialog(context,"正在发送邮件",false); } @Override public void onBackPressed() { super.onBackPressed(); exit(); } }
我这边发送邮件用的是java的mail.jar,activation.jar,additionnal.jar这三个类,用这三个的原因主要是因为切到java web开发也可以用。其实我不是特别明白additionnal这个jar是干嘛的,但缺了这个jar会报错,没细研究。有懂的小伙伴顺道告知下。
顺道附上我的jar包地址:
http://download.csdn.net/download/feiniyan4944/10138558
发送邮件的基础类,这里要说一下的是,QQ邮箱和腾讯企业邮必须加上这段
MailSSLSocketFactory sf = null;
try {
sf = new MailSSLSocketFactory();
sf.setTrustAllHosts(true);
} catch (GeneralSecurityException e1) {
e1.printStackTrace();
}
//开启安全协议
p.put(“mail.smtp.ssl.enable”, “true”);
p.put(“mail.smtp.ssl.socketFactory”, sf);
刚开始测试的时候,比较蛋疼的是我用163邮箱没事,转到QQ邮箱死命发不出去了,百度了下才知识QQ邮箱是使用SSL的
public class MailSendVo { /** * 发送邮件的服务器的IP和端口 */ private String mailServerHost; private String mailServerPort; /** * 邮件发送者的地址 */ private String fromAddress; /** * 邮件接受者的地址 */ private String toAddress; /** * 登陆邮件发送服务器的用户名和密码 */ private String userName; private String passWord; /** * 是否需要身份验证 */ private boolean validate = false; /** * 邮件发送的主题 */ private String subject; /** * 邮件发送的内容 */ private String content; /** * 邮件附件的文件名 */ private String[] attachFileNames; /** * 获取邮件会话属性 * @return */ public Properties getProperties(){ Properties p = new Properties(); p.put("mail.smtp.host", this.mailServerHost); p.put("mail.smtp.port", this.mailServerPort); p.put("mail.transport.protocol", "smtp"); p.put("mail.smtp.auth", validate ? "true" : "false"); //使用SSL,企业邮箱必需! MailSSLSocketFactory sf = null; try { sf = new MailSSLSocketFactory(); sf.setTrustAllHosts(true); } catch (GeneralSecurityException e1) { e1.printStackTrace(); } //开启安全协议 p.put("mail.smtp.ssl.enable", "true"); p.put("mail.smtp.ssl.socketFactory", sf); return p; } //一堆get set就不占位置了 }
邮件工具类没多写,其实还有附件,抄送,多图片等等可以扩展,但懒,就写了个最简单的发送邮件,百度有很多邮件的Demo
/** * 发送邮件工具类 * Created by supper on 2017/11/23. */ public class EmailUtil { /** * 以文本格式发送邮件 * @param mailInfo 待发送的邮件的信息 */ public boolean sendTextMail(MailSendVo mailInfo) { MyAuthenticator authenticator = null; // 判断是否需要身份认证 Properties pro = mailInfo.getProperties(); if (mailInfo.isValidate()) { // 如果需要身份认证,则创建一个密码验证器 authenticator = new MyAuthenticator(mailInfo.getUserName(),mailInfo.getPassWord()); } // 根据邮件会话属性和密码验证器构造一个发送邮件的session Session sendMailSession = Session.getDefaultInstance(pro,authenticator); try { // 根据session创建一个邮件消息 Message mailMessage = new MimeMessage(sendMailSession); // 创建邮件发送者地址 Address from = new InternetAddress(mailInfo.getFromAddress()); // 设置邮件消息的发送者 mailMessage.setFrom(from); // 创建邮件的接收者地址,并设置到邮件消息中 Address to = new InternetAddress(mailInfo.getToAddress()); mailMessage.setRecipient(Message.RecipientType.TO,to); // 设置邮件消息的主题 mailMessage.setSubject(mailInfo.getSubject()); // 设置邮件消息发送的时间 mailMessage.setSentDate(new Date()); // 设置邮件消息的主要内容 String mailContent = mailInfo.getContent(); mailMessage.setText(mailContent); // 发送邮件 Transport.send(mailMessage); return true; } catch (MessagingException ex) { Log.e("",ex.getMessage()); } return false; } }
主页面
public class MainActivity extends Activity implements View.OnClickListener{ public static final String TAG = "APP>>MainActivity"; private Button btn_error_test; private Button btn_log; private Button btn_dialog; private Integer i; private Context context; private Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what){ } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); context = this; initData(); } private void initData(){ btn_error_test = (Button)findViewById(R.id.btn_error_test); btn_log = (Button)findViewById(R.id.btn_log); btn_dialog = (Button)findViewById(R.id.btn_dialog); btn_error_test.setOnClickListener(this); btn_log.setOnClickListener(this); btn_dialog.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()){ case R.id.btn_error_test: if(true){ throw new NullPointerException(); } break; case R.id.btn_log: Log.d(TAG, "onClick: 测试" ); try { sendMail();// }catch (Exception e){ e.getMessage(); } break; case R.id.btn_dialog: handler.removeCallbacks(gotoDialog); handler.postDelayed(gotoDialog,500); break; } } Runnable gotoDialog = new Runnable() { @Override public void run() { // 跳转到崩溃提示Activity Intent intent = new Intent(context, CrashDialog.class); intent.putExtra("errorData",new Date().getTime()); intent.putExtra("errorLog","测试"); // intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intent); } }; private void sendMail(){ new AlertDialog.Builder(context) .setTitle("提示") .setMessage("APP不幸发生崩溃,是否要发送错误报告给开发人员?") .setPositiveButton("是", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { sendMessage(); dialog.dismiss(); } }) .setNegativeButton("否", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); } }).create().show(); } private void sendMessage() { /*****************************************************/ Log.d(TAG, "sendMessage: 开始发送邮件"); //为什么handler.post()发送邮件会崩溃 // 这个类主要是设置邮件 new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub MailSendVo mailInfo = new MailSendVo(); mailInfo.setMailServerHost(Config.ERROR_MAIL_SERVERHOST); mailInfo.setMailServerPost(Config.ERROR_MAIL_SERVERPORT); mailInfo.setValidate(true); mailInfo.setUserName(Config.ERROR_MAIL_USERNAME); mailInfo.setPassWord(Config.ERROR_MAIL_PASSWORD);// 您的邮箱密码 mailInfo.setFromAddress(Config.ERROR_MAIL_USERNAME); mailInfo.setToAddress(Config.ERROR_MAIL_USERNAME); mailInfo.setSubject("错误日志"); mailInfo.setContent("测试"); // 这个类主要来发送邮件 EmailUtil sms = new EmailUtil(); boolean isSuccess = sms.sendTextMail(mailInfo);// 发送文体格式 // sms.sendHtmlMail(mailInfo);//发送html格式 DialogThridUtils.getInstance().closeDialog(); if (isSuccess) { Log.d(TAG, "run: 发送成功"); } else { Log.d(TAG, "run: 发送失败"); } } }).start(); DialogThridUtils.getInstance().showWaitDialog(context,"正在发送邮件",false); } // // private void sendMessage(final String msg) { // // /*****************************************************/ // Log.d(TAG, "sendMessage: 开始发送邮件"); // // 这个类主要是设置邮件 // new Thread(new Runnable() { // // @Override // public void run() { // // TODO Auto-generated method stub // MailSendVo mailInfo = new MailSendVo(); // mailInfo.setMailServerHost("smtp.163.com"); // mailInfo.setMailServerPost("25"); // mailInfo.setValidate(true); // mailInfo.setUserName("***"); // mailInfo.setPassWord("***");// 您的邮箱密码 // mailInfo.setFromAddress("***"); // mailInfo.setToAddress("***"); // mailInfo.setSubject("这是标题"); // mailInfo.setContent(msg); // // 这个类主要来发送邮件 // EmailUtil sms = new EmailUtil(); // boolean isSuccess = sms.sendTextMail(mailInfo);// 发送文体格式 // // sms.sendHtmlMail(mailInfo);//发送html格式 // DialogThridUtils.getInstance().closeDialog(); // if (isSuccess) { // Log.d(TAG,"发送成功"); //// ToastUtil.getInstance().makeText(context,"发送成功"); // } else { // Log.d(TAG,"发送失败"); //// ToastUtil.getInstance().makeText(context,"发送失败"); // } // } // }).start(); // DialogThridUtils.getInstance().showWaitDialog(context,"正在发送邮件",false); // } }
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="100" android:versionName="1.0.0" package="com.supper.main"> <application android:name=".BaseApplication" android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <!-- 导航 unspecified --> <activity android:name=".ui.CrashDialog" android:configChanges="orientation|screenSize|smallestScreenSize|keyboard|keyboardHidden|navigation" android:launchMode="singleTask" android:screenOrientation="portrait" android:theme="@style/alert_dialog" /> </application> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> </manifest>
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <Button android:id="@+id/btn_error_test" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:text="测试闪退日志保存"/> <Button android:id="@+id/btn_log" android:layout_marginTop="20dp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:text="邮件测试"/> <Button android:id="@+id/btn_dialog" android:layout_marginTop="20dp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:text="弹窗"/> </LinearLayout>
activity_crash.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:gravity="center" android:orientation="vertical"> <LinearLayout android:layout_width="200dp" android:layout_height="wrap_content" android:layout_gravity="center" android:background="#F6F6F6" android:orientation="vertical"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="#000000" android:text="APP不幸发生崩溃,是否要发送错误报告给开发人员?"/> <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginTop="15dp" android:orientation="horizontal"> <Button android:id="@+id/btn_yes" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:layout_marginRight="10dp" android:text="是"/> <Button android:id="@+id/btn_no" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:layout_marginLeft="10dp" android:text="否"/> </LinearLayout> </LinearLayout> </LinearLayout>
这里面的代码还有个发送邮件的等待框,和因为Toast只能在子线程显示但很多地方很烦总报线程错,而写的工具类,但目前总是报错导致APP奔溃,目前问题还不是很清楚是什么原因,还有比较蛋疼的是我在真机跑的时候logcat不知道为什么没用日志打印,而在虚拟机没有这样的问题。
最后附上我的源码地址:
http://download.csdn.net/download/feiniyan4944/10138555
咸鱼开发,有bug处望大佬纠正
相关文章推荐
- android开发之应用Crash自动抓取Log_自动保存崩溃日志到本地
- android开发之应用Crash自动抓取Log_自动保存崩溃日志到本地
- android开发之应用Crash自动抓取Log_自动保存崩溃日志到本地
- .Net语言 APP开发平台——Smobiler学习日志:如何在手机中调用邮件发送接口
- .Net语言 APP开发平台——Smobiler学习日志:如何在手机中调用邮件发送接口
- android开发之应用Crash自动抓取Log_自动保存崩溃日志到本地
- android开发之应用Crash自动抓取Log_自动保存崩溃日志到本地
- 【Android应用开发】 Android 崩溃日志 本地存储 与 远程保存
- 【Android应用开发】 Android 崩溃日志 本地存储 与 远程保存
- android开发之应用Crash自动抓取Log_自动保存崩溃日志到本地
- android开发之应用Crash自动抓取Log_自动保存崩溃日志到本地
- android开发之应用Crash自动抓取Log_自动保存崩溃日志到本地
- android开发之应用Crash自动抓取Log_自动保存崩溃日志到本地
- android开发之应用Crash自动抓取Log_自动保存崩溃日志到本地
- android开发之应用Crash自动抓取Log_自动保存崩溃日志到本地
- android开发之应用Crash自动抓取Log_自动保存崩溃日志到本地
- android开发之应用Crash自动抓取Log_自动保存崩溃日志到本地
- 羊皮书APP(Android版)开发系列(十三)Android 邮件发送的实现
- log4net使用(保存日志分别 到文本文件,smtp发送邮件,mssql数据库,mysql数据库)
- .Net语言 APP开发平台——Smobiler学习日志:如何调用API进行短信发送