android App异常报错捕获后使用javamail登录QQ邮箱发邮件的功能遇到的问题
网上参照了网上的帖子 两种方式 走了很多弯路 有时候没有针对性的去解决问题 这个缺点要改正其实大致的流程很简单 这里梳理一下:
一、捕获异常(单例模式注册CrashHandler 并且在handlerExcepiton中发送邮件)
二、设置邮件(1导入三个jar包1.1activation.jar 1.2mail.jar 1.3additionnal.jar )
三、发送邮件(1.密码改为授权码 2.去掉mail.smtp.auth)
四、使用了异常捕获之后 ,直接闪退 ,不弹框提示了,用户体验会好一点
1.gundumw100的博客 http://gundumw100.iteye.com/blog/1182104 来实现了异常捕获,使用的是服务器来接收错误文件的
2.然后又参照了 简书中BRYANB的文章 http://www.jianshu.com/p/ea210ef8b4b0 来实现异常捕获发送邮件的功能
之后发现报错 调试的时候发现报错 到验证密码时候报错 以为是写的有问题 然后去 Windy Wang 的博客 http://www.cnblogs.com/wangsx/archive/2013/08/30/3291036.html 修改了发送邮件的代码 发现还是报错
上网搜索后发现这位哥们儿http://www.iteye.com/problems/71677 问题和我的差不多 然后根据下面回答下载了https://code.google.com/archive/p/javamail-android/downloads 这里的三个包并且重新引入之后 还是报错
android.os.NetworkOnMainThreadException
然后看到这篇文章http://blog.csdn.net/mad1989/article/details/25964495 说3.0以后不推荐这种方法,开了线程去执行发送邮件还是报错com.sun.mail.smtp.SMTPAddressFailedException:
到了这一步之后,可以去邮箱设置里面看看开启了了pop3/smtp没有,使用qq邮箱设置了还是报错,我就改换
网易邮箱了,不论是网易邮箱还是QQ邮箱,邮箱都有个授权码,一定要用授权码来作为密码password = "";// 填写授权码猜到一定还会报错 然后参照了 http://bbs.csdn.net/topics/260074848 这里 去掉了//props.put("mail.smtp.auth", "true"); 把mail.auth=true去掉 然后就可以了
如果觉得邮件一直提醒比较烦的话可以发件和收件都为同一个人
源码:
一、发邮件工具类
package com.musicdo.puluo.utils;
import android.util.Log;
import java.util.Properties;
import javax.activation.DataHandler;
import javax.mail.Authenticator;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.util.ByteArrayDataSource;
/**
* 名 称 :发邮件工具类
* 描 述 :
* 创建者:
* 创建时间:
* 版 本 :
* 备 注 :
*/
public class EmailUtil {
/**
* 邮件发送程序
*
* @param to
* 接受人
* @param subject
* 邮件主题
* @param content
* 邮件内容
* @throws Exception
* @throws MessagingException
*/
public static void sendEmail(String to, String subject, String content) throws Exception, MessagingException {
String host = "smtp.qq.com";
String address = "[email protected]";
String from = "AA@qq.com";
String password = "aa";// 授权码
if ("".equals(to) || to == null) {
to = "[email protected]";
}
String port = "25";
SendEmail(host, address, from, password, to, port, subject, content);
}
/**
* 邮件发送程序
*
* @param host
* 邮件服务器 如:smtp.qq.com
* @param address
* 发送邮件的地址 如:[email protected]
* @param from
* 来自: [email protected]
* @param password
* 您的邮箱密码
* @param to
* 接收人
* @param port
* 端口(QQ:25)
* @param subject
* 邮件主题
* @param content
* 邮件内容
* @throws Exception
*/
public static void SendEmail(String host, final String address, String from, final String password, String to, String port, String subject, String content) throws Exception {
Multipart multiPart;
String finalString = "";
ByteArrayDataSource ds = null;
Properties props = System.getProperties();
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.host", host);
props.put("mail.smtp.user", address);
props.put("mail.smtp.password", password);
props.put("mail.smtp.port", port);
// props.put("mail.smtp.auth", "true");
Log.i("Check", "done pops");
Session session = Session.getDefaultInstance(props, new Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {//此处必须做验证,才能发送邮件
return new javax.mail.PasswordAuthentication(address,
password);
}
});
DataHandler handler = new DataHandler(new ByteArrayDataSource(finalString.getBytes(), "text/plain"));
MimeMessage message = new MimeMessage(session);
message.setFrom(new InternetAddress(from));
message.setDataHandler(handler);
Log.i("Check", "done sessions");
multiPart = new MimeMultipart();
InternetAddress toAddress;
toAddress = new InternetAddress(to);
message.addRecipient(Message.RecipientType.TO, toAddress);
Log.i("Check", "added recipient");
message.setSubject(subject);
message.setContent(multiPart);
message.setText(content);
Log.i("check", "transport");
Transport transport = session.getTransport("smtp");
Log.i("check", "connecting");
transport.connect(host, address, password);
Log.i("check", "wana send");
transport.sendMessage(message, message.getAllRecipients());
transport.close();
Log.i("check", "sent");
}
}
二、异常处理类
package com.musicdo.puluo.utils;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Looper;
import android.os.StrictMode;
import android.text.format.Time;
import android.util.Log;
import android.view.Gravity;
import android.widget.Toast;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Properties;
import java.util.TreeSet;
/**
* 名 称 :
* 描 述 :
* 创建者:
* 创建时间:
* 版 本 :
* 备 注 :
*/
public class CrashMailHandler implements Thread.UncaughtExceptionHandler {
private final static String TAG = "UncaughtExceptionHandler";
private Thread.UncaughtExceptionHandler mDefaultHandler;
private static CrashMailHandler mInstance;
private Context mContext;
/** 使用Properties来保存设备的信息和错误堆栈信息*/
private Properties mDeviceCrashInfo = new Properties();
private static final String VERSION_NAME = "versionName";
private static final String VERSION_CODE = "versionCode";
private static final String STACK_TRACE = "STACK_TRACE";
/** 在Release状态下关闭以提示程序性能
* */
public static final boolean DEBUG = false;
/** 错误报告文件的扩展名 */
private static final String CRASH_REPORTER_EXTENSION = ".cr";
private CrashMailHandler() {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads().detectDiskWrites().detectNetwork()
.penaltyLog().build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects().detectLeakedClosableObjects()
.penaltyLog().penaltyDeath().build());
}
/** 获取CrashHandler实例 ,单例模式 */
public static CrashMailHandler getInstance() {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads().detectDiskWrites().detectNetwork()
.penaltyLog().build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects().detectLeakedClosableObjects()
.penaltyLog().penaltyDeath().build());
if (mInstance == null)
mInstance = new CrashMailHandler();
return mInstance;
}
@Override
public void uncaughtException(Thread thread, Throwable throwable) {
if (!handleException(throwable) && mDefaultHandler != null) {
// 如果用户没有处理则让系统默认的异常处理器来处理
mDefaultHandler.uncaughtException(thread, throwable);
} else {
// Sleep一会后结束程序
// 来让线程停止一会是为了显示Toast信息给用户,然后Kill程序
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
Log.e(TAG, "Error : ", e);
}
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(10);
}
}
private boolean handleException(final Throwable ex) {
if (ex == null) {
return true;
}
final String msg = ex.getLocalizedMessage();
new Thread() {
@Override
public void run() {
try {
//此处发送邮件
EmailUtil.sendEmail(null,"邮件标题",“邮件内容”+msg );
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
// 使用Toast来显示异常信息
new Thread() {
@Override
public void run() {
Looper.prepare();
Toast toast = Toast.makeText(mContext, "程序出错,即将退出:\r\n" + msg,
Toast.LENGTH_LONG);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();
// MsgPrompt.showMsg(mContext, "程序出错啦", msg+"\n点确认退出");
Looper.loop();
}
}.start();
//创建消息
// collectCrashDeviceInfo(mContext);
//保存错误报告文件
// saveCrashInfoToFile(ex);
//发送锗误报告到服务器,暂时没有用到,以后可以加上 //
// sendCrashReportsToServer(mContext);
return true;
}
/**
* 把错误报告发送给服务器,包含新产生的和以前没发送的.
* @param ctx
*/
private void sendCrashReportsToServer(Context ctx) {
String[] crFiles = getCrashReportFiles(ctx);
if (crFiles != null && crFiles.length > 0) {
TreeSet<String> sortedFiles = new TreeSet<String>();
sortedFiles.addAll(Arrays.asList(crFiles));
for (String fileName : sortedFiles) {
File cr = new File(ctx.getFilesDir(), fileName);
postReport(cr);
cr.delete();// 删除已发送的报告
}
}
}
private void postReport(File file) {
// TODO 发送错误报告到服务器
// 1.简单发送没有附件和抄送
/*Intent data=new Intent(Intent.ACTION_SENDTO);
data.setData(Uri.parse("mailto:[email protected]"));
data.putExtra(Intent.EXTRA_SUBJECT, "这是标题");
data.putExtra(Intent.EXTRA_TEXT, "这是内容");
mContext.startActivity(data);*/
// 2.多个附件 可抄送 发送邮件
/* Intent intent = new Intent(Intent.ACTION_SEND_MULTIPLE);
String[] tos = { "[email protected]" };
String[] ccs = { "[email protected]" };
intent.putExtra(Intent.EXTRA_EMAIL, tos);
intent.putExtra(Intent.EXTRA_CC, ccs);
intent.putExtra(Intent.EXTRA_TEXT, "body");
intent.putExtra(Intent.EXTRA_SUBJECT, "subject");
ArrayList<Uri> imageUris = new ArrayList<Uri>();
imageUris.add(Uri.parse("file:///mnt/sdcard/a.jpg"));
imageUris.add(Uri.parse("file:///mnt/sdcard/b.jpg"));
intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, imageUris);
intent.setType("image*//*");
intent.setType("message/rfc882");
Intent.createChooser(intent, "Choose Email Client");
mContext.startActivity(intent);*/
}
/**
* 获取错误报告文件名
* @param ctx
* @return
*/
private String[] getCrashReportFiles(Context ctx) {
File filesDir = ctx.getFilesDir();
FilenameFilter filter = new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.endsWith(CRASH_REPORTER_EXTENSION);
}
};
return filesDir.list(filter);
}
public void init(Context context) {
mContext = context;
mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(this);
}
public void collectCrashDeviceInfo(Context ctx) {
try {
PackageManager pm = ctx.getPackageManager();
PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(),
PackageManager.GET_ACTIVITIES);
if (pi != null) {
mDeviceCrashInfo.put(VERSION_NAME,
pi.versionName == null ? "not set" : pi.versionName);
mDeviceCrashInfo.put(VERSION_CODE, ""+pi.versionCode);
}
}catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "Error while collect package info", e);
}
//使用反射来收集设备信息.在Build类中包含各种设备信息,
//例如: 系统版本号,设备生产商 等帮助调试程序的有用信息
//具体信息请参考后面的截图
Field[] fields = Build.class.getDeclaredFields();
for (Field field : fields) {
try {
field.setAccessible(true);
mDeviceCrashInfo.put(field.getName(), ""+field.get(null));
if (DEBUG) {
Log.d(TAG, field.getName() + " : " + field.get(null));
}
} catch (Exception e) {
Log.e(TAG, "Error while collect crash info", e);
}}}
/**
* 保存错误信息到文件中
* @param ex
* @return
*/
private String saveCrashInfoToFile(Throwable ex) {
Writer info = new StringWriter();
PrintWriter printWriter = new PrintWriter(info);
ex.printStackTrace(printWriter);
Throwable cause = ex.getCause();
while (cause != null) {
cause.printStackTrace(printWriter);
cause = cause.getCause();
}
String result = info.toString();
printWriter.close();
mDeviceCrashInfo.put("EXEPTION", ex.getLocalizedMessage());
mDeviceCrashInfo.put(STACK_TRACE, result);
try {
//long timestamp = System.currentTimeMillis();
Time t = new Time("GMT+8");
t.setToNow(); // 取得系统时间
int date = t.year * 10000 + t.month * 100 + t.monthDay;
int time = t.hour * 10000 + t.minute * 100 + t.second;
String fileName = "crash-" + date + "-" + time + CRASH_REPORTER_EXTENSION;
FileOutputStream trace = mContext.openFileOutput(fileName,
Context.MODE_PRIVATE);
mDeviceCrashInfo.store(trace, "");
trace.flush();
trace.close();
return fileName;
} catch (Exception e) {
Log.e(TAG, "an error occured while writing report file...", e);
}
return null;
}
}