轉(zhuǎn)載請注明出處:https://juejin.im/post/5de4bcf7f265da06041eeffd
寫在前面
上一篇博客中已經(jīng)介紹了怎么通過UncaughtExceptionHandler接口實現(xiàn)全局異常的抓取申屹,以及崩潰日志的保存描睦。
不太清楚這些操作的可以看我上一篇博客:優(yōu)雅的處理Android崩潰(一)贼邓。
本篇博客主要解決以下3個問題:
- 1 . 實現(xiàn)崩潰項目重啟。
- 2 . 實現(xiàn)崩潰日志讀取。
- 3 . 實現(xiàn)日志上傳對話框彈出,并通過email上傳崩潰日志。
問題分析
初步分析:
初步計劃在項目崩潰時暮芭,循環(huán)讀取異常日志,然后啟動上傳日志對話框欲低,實現(xiàn)日志上傳。
發(fā)現(xiàn)問題:
在uncaughtException()方法中沒有辦法啟動對話框畜晰,如果在該方法直接寫startActivity(new Intent(...砾莱,...));程序會卡在該代碼,并報ANR凄鼻,最終報異常腊瑟。
解決問題:
在我們設(shè)置全局異常以后,在進入全局異常時系統(tǒng)就提示盡快收集信息块蚌,進程將被結(jié)束闰非,因此不可在此處啟動activity對話框。
所以異常發(fā)生時峭范,通過SharedPreferences將錯誤日志的路徑寫入配置文件中财松,然后重啟,在啟動的時候先檢測該配置文件是否有錯誤日志信息,如果有則讀取文件辆毡,并彈出對話框?qū)崿F(xiàn)日志上傳.
代碼編寫
- 1 .我們實現(xiàn)異常日志路徑記錄:
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(App.instance);
Editor editor = preferences.edit();
editor.putLong(key, value);
return editor.commit();
- 2 . 實現(xiàn)項目重啟:
// 重啟
Intent intent = new Intent(mContext.getApplicationContext(), ActivateActivity.class);
PendingIntent restartIntent = PendingIntent.getActivity(mContext.getApplicationContext(), 0, intent,
Intent.FLAG_ACTIVITY_NEW_TASK);
AlarmManager mgr = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
// 設(shè)置1毫秒后重啟應(yīng)
mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 1, restartIntent);用
android.os.Process.killProcess(android.os.Process.myPid());
//結(jié)束進程
System.exit(1);
- 3 . 實現(xiàn)自定義對話框
public class ExDialog extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ex_dialog);
}
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_up: {
//通過郵件上傳日志
new Thread(new Runnable() {
@Override
public void run() {
EmaliUtil emaliUtil = new EmaliUtil();
if (emaliUtil.sendMessage(SharedPF.getSharder().getString(CrashHandler.TAG), ExDialog.this)) {
App.instance.toast(true, "發(fā)送成功");
}else{
App.instance.toast(true, "發(fā)送失敗菜秦,請檢測網(wǎng)絡(luò)環(huán)境是否通暢");
}
ExDialog.this.finish();
}
}).start();
}
break;
case R.id.iv_close: {
ExDialog.this.finish();
}
break;
default:
break;
}
}
@Override
public void finish() {
//設(shè)置異常日志路徑為空
SharedPF.getSharder().setString(CrashHandler.TAG, "");
super.finish();
}
- 4 . 修改清單文件設(shè)置為對話框主題
<activity
android:name=".activity.ExDialog"
android:theme="@android:style/Theme.DeviceDefault.Dialog.NoActionBar" >
</activity>
- 5 . 集成郵件發(fā)送包并編寫發(fā)送代碼。
郵件發(fā)送的jar下載鏈接:鏈接:http://pan.baidu.com/s/1i5DZR8p 密碼:7tvt - 注:前三個類直接復(fù)制粘貼就好舶掖,最后一個類改成你自己的郵箱地址
MailSenderInfo.java
public class MailSenderInfo {
// 發(fā)送郵件的服務(wù)器的IP和端口
private String mailServerHost;
private String mailServerPort = "25";
// 郵件發(fā)送者的地址
private String fromAddress;
// 郵件接收者的地址
private String toAddress;
// 登陸郵件發(fā)送服務(wù)器的用戶名和密碼
private String userName;
private String password;
// 是否需要身份驗證
private boolean validate = false;
// 郵件主題
private String subject;
// 郵件的文本內(nèi)容
private String content;
// 郵件附件的文件名
private String[] attachFileNames;
/**
* 獲得郵件會話屬性
*/
public Properties getProperties() {
Properties p = new Properties();
p.put("mail.smtp.host", this.mailServerHost);
p.put("mail.smtp.port", this.mailServerPort);
p.put("mail.smtp.auth", validate ? "true" : "false");
return p;
}
public String getMailServerHost() {
return mailServerHost;
}
public void setMailServerHost(String mailServerHost) {
this.mailServerHost = mailServerHost;
}
public String getMailServerPort() {
return mailServerPort;
}
public void setMailServerPort(String mailServerPort) {
this.mailServerPort = mailServerPort;
}
public boolean isValidate() {
return validate;
}
public void setValidate(boolean validate) {
this.validate = validate;
}
public String[] getAttachFileNames() {
return attachFileNames;
}
public void setAttachFileNames(String[] fileNames) {
this.attachFileNames = fileNames;
}
public String getFromAddress() {
return fromAddress;
}
public void setFromAddress(String fromAddress) {
this.fromAddress = fromAddress;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getToAddress() {
return toAddress;
}
public void setToAddress(String toAddress) {
this.toAddress = toAddress;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
public String getContent() {
return content;
}
public void setContent(String textContent) {
this.content = textContent;
}
}
MyAuthenticator.java
public class MyAuthenticator extends Authenticator {
String userName = null;
String password = null;
public MyAuthenticator() {
}
public MyAuthenticator(String username, String password) {
this.userName = username;
this.password = password;
}
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(userName, password);
}
}
SimpleMailSender.java
public class SimpleMailSender {
/**
* 以文本格式發(fā)送郵件
*
* @param mailInfo
* 待發(fā)送的郵件的信息
*/
public boolean sendTextMail(MailSenderInfo mailInfo) {
// 判斷是否需要身份認證
MyAuthenticator authenticator = null;
Properties pro = mailInfo.getProperties();
if (mailInfo.isValidate()) {
// 如果需要身份認證球昨,則創(chuàng)建一個密碼驗證器
authenticator = new MyAuthenticator(mailInfo.getUserName(), mailInfo.getPassword());
}
// 根據(jù)郵件會話屬性和密碼驗證器構(gòu)造一個發(fā)送郵件的session
Session sendMailSession = Session.getDefaultInstance(pro, authenticator);
try {
// 根據(jù)session創(chuàng)建一個郵件消息
Message mailMessage = new MimeMessage(sendMailSession);
// 創(chuàng)建郵件發(fā)送者地址
Address from = new InternetAddress(mailInfo.getFromAddress());
// 設(shè)置郵件消息的發(fā)送者
mailMessage.setFrom(from);
// 創(chuàng)建郵件的接收者地址,并設(shè)置到郵件消息中
Address to = new InternetAddress(mailInfo.getToAddress());
mailMessage.setRecipient(Message.RecipientType.TO, to);
// 設(shè)置郵件消息的主題
mailMessage.setSubject(mailInfo.getSubject());
// 設(shè)置郵件消息發(fā)送的時間
mailMessage.setSentDate(new Date());
// 設(shè)置郵件消息的主要內(nèi)容
String mailContent = mailInfo.getContent();
mailMessage.setText(mailContent);
// 發(fā)送郵件
Transport.send(mailMessage);
return true;
} catch (MessagingException ex) {
ex.printStackTrace();
}
return false;
}
/**
* 以HTML格式發(fā)送郵件
*
* @param mailInfo
* 待發(fā)送的郵件信息
*/
public static boolean sendHtmlMail(MailSenderInfo mailInfo) {
// 判斷是否需要身份認證
MyAuthenticator authenticator = null;
Properties pro = mailInfo.getProperties();
// 如果需要身份認證眨攘,則創(chuàng)建一個密碼驗證器
if (mailInfo.isValidate()) {
authenticator = new MyAuthenticator(mailInfo.getUserName(), mailInfo.getPassword());
}
// 根據(jù)郵件會話屬性和密碼驗證器構(gòu)造一個發(fā)送郵件的session
Session sendMailSession = Session.getDefaultInstance(pro, authenticator);
try {
// 根據(jù)session創(chuàng)建一個郵件消息
Message mailMessage = new MimeMessage(sendMailSession);
// 創(chuàng)建郵件發(fā)送者地址
Address from = new InternetAddress(mailInfo.getFromAddress());
// 設(shè)置郵件消息的發(fā)送者
mailMessage.setFrom(from);
// 創(chuàng)建郵件的接收者地址主慰,并設(shè)置到郵件消息中
Address to = new InternetAddress(mailInfo.getToAddress());
// Message.RecipientType.TO屬性表示接收者的類型為TO
mailMessage.setRecipient(Message.RecipientType.TO, to);
// 設(shè)置郵件消息的主題
mailMessage.setSubject(mailInfo.getSubject());
// 設(shè)置郵件消息發(fā)送的時間
mailMessage.setSentDate(new Date());
// MiniMultipart類是一個容器類,包含MimeBodyPart類型的對象
Multipart mainPart = new MimeMultipart();
// 創(chuàng)建一個包含HTML內(nèi)容的MimeBodyPart
BodyPart html = new MimeBodyPart();
// 設(shè)置HTML內(nèi)容
html.setContent(mailInfo.getContent(), "text/html; charset=utf-8");
mainPart.addBodyPart(html);
// 將MiniMultipart對象設(shè)置為郵件內(nèi)容
mailMessage.setContent(mainPart);
// 發(fā)送郵件
Transport.send(mailMessage);
return true;
} catch (MessagingException ex) {
ex.printStackTrace();
}
return false;
}
}
EmaliUtil.java
public class EmaliUtil {
private Context mcontext;
public boolean sendMessage(String fileName, Context context) {
mcontext = context;
// 這個類主要是設(shè)置郵件
MailSenderInfo mailInfo = new MailSenderInfo();
mailInfo.setMailServerHost("smtp.163.com");
mailInfo.setMailServerPort("25");
mailInfo.setValidate(true);
mailInfo.setUserName("******@163.com");
mailInfo.setPassword("*******");// 您的郵箱密碼
mailInfo.setFromAddress("*******@163.com");
mailInfo.setToAddress("*******@163.com");
mailInfo.setSubject("郵件標(biāo)題");
//讀取日志
String contentTemp = readTxtByStringBuffer(fileName);
mailInfo.setContent(contentTemp);
boolean isSuccess = false;
try {
// 這個類主要來發(fā)送郵件
SimpleMailSender sms = new SimpleMailSender();
isSuccess = sms.sendTextMail(mailInfo);// 發(fā)送文體格式
} catch (Exception e) {
Log.e("shuxinshuxin", e.toString());
}
// sms.sendHtmlMail(mailInfo);//發(fā)送html格式
return isSuccess;
}
// 使用cache進行讀取
private String readTxtByStringBuffer(String fileName) {
File file = new File(fileName);
if (file.exists()) {
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(file), 10 * 1024 * 1024);
String stringMsg = null;
StringBuffer buffer = new StringBuffer();
while ((stringMsg = reader.readLine()) != null) {
buffer.append(stringMsg);
buffer.append("\n");
}
reader.close();
return buffer.toString();
} catch (Exception e) {
e.printStackTrace();
}
return "";
} else {
return "系統(tǒng)沒有找到日志文件";
}
}
private String getVersionName() {
try {
// 獲取packagemanager的實例
PackageManager packageManager = mcontext.getPackageManager();
// getPackageName()是你當(dāng)前類的包名鲫售,0代表是獲取版本信息
PackageInfo packInfo;
packInfo = packageManager.getPackageInfo(mcontext.getPackageName(), 0);
String version = packInfo.versionCode + "";
return version;
} catch (NameNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return "";
}
}
- 6 . 程序啟動時共螺,首先判斷上次是否有崩潰日志,如果有則啟動上傳日志對話框龟虎,調(diào)用EmaliUtil.sendMessage("","")璃谨;實現(xiàn)日志上傳,該方法已經(jīng)封裝到了自定義的對話框中鲤妥。
String strTemp = SharedPF.getSharder().getString(CrashHandler.TAG);
if (!strTemp.equals("")) {
//如果有異常則啟動異常上傳對話框佳吞;
startActivity(new Intent(this, ExDialog.class));
}