现在的位置: 首页 > 综合 > 正文

Android 崩溃异常时弹出Dialog让用户选择是否上传错误信息

2018年05月06日 ⁄ 综合 ⁄ 共 6791字 ⁄ 字号 评论关闭

想必凡是使用安卓手机的童鞋们都碰到过这样的情况,不知道是内存还是什么的原因,弹出个提示框,某某应用已终止,对这类的应用,我们会没来由的觉得烦躁,严重点就会删除,所以作为程序员,我们在开发应用的时候,可以选择通过让用户选择是否上传错误信息,来增加交互不显得那么生硬,另外,就是因为不可能购买所有设备逐个调试,所以在程序发布出去之后,如果出现了崩溃现象,能及时获取在该设备上导致崩溃的信息,这对于下一个版本的bug修复帮助极大,废话不多说,先上图吧

欢迎转载,请加地址http://blog.csdn.net/jing110fei/article/details/38797599


如果用户点击取消则直接关闭程序,如果用户点击上传,则将错误信息上传至服务器,有关上传至服务器的代码我就不在这里过多描述了,重点说下怎么实现获取异常信息,和弹出Dialog。

首先,我们先人为的创建个错误异常,用来测试。

public class DemoMainActivity extends Activity {
	 private String string;  
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_demo_main);
       
        System.out.println(string.equals("any string")); 
       
    } 
}

创建CrashHandler类是专门用来处理异常,我自己根据需要,上传的信息包括,时间-软件版本-手机型号-还有错误信息。

public class CrashHandler implements UncaughtExceptionHandler{
	public static final String TAG = "CrashHandler";  
	 StringBuffer sbs = new StringBuffer(); 
    //系统默认的UncaughtException处理类   
    private Thread.UncaughtExceptionHandler mDefaultHandler;  
    //CrashHandler实例  
    private static CrashHandler INSTANCE = new CrashHandler();  
    //程序的Context对象  
    private Context mContext;  
 
    //用于格式化日期
    private DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");  
  
    /** 保证只有一个CrashHandler实例 */  
    private CrashHandler() {  
    }  
  
    /** 获取CrashHandler实例 ,单例模式 */  
    public static CrashHandler getInstance() {  
        return INSTANCE;  
    }  
  
    /** 
     * 初始化 
     *  
     * @param context 
     */  
    public void init(Context context) {  
        mContext = context;  
        //获取系统默认的UncaughtException处理器  
        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();  
        //设置该CrashHandler为程序的默认处理器  
        Thread.setDefaultUncaughtExceptionHandler(this); 
    }  
    /** 
     * 当UncaughtException发生时会转入该函数来处理 
     */  
	public void uncaughtException(Thread thread, Throwable ex) {
		// TODO Auto-generated method stub
		if (!handleException(ex) && mDefaultHandler != null) {  
            //如果用户没有处理则让系统默认的异常处理器来处理  
            mDefaultHandler.uncaughtException(thread, ex);  
        }
		
		
	}
	/** 
     * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.
     *  
     * @param ex 
     * @return true:如果处理了该异常信息;否则返回false. 
     */  
    private boolean handleException(Throwable ex) {  
        if (ex == null) {  
            return false;  
        }  
        final Throwable exsThrowable=ex;
        new Thread() {  
            @Override  
            public void run() {  
                Looper.prepare(); 
                showDialog(mContext);
                Looper.loop(); 
            }  
        }.start(); 
        // 1.获取软件版本信息    
        String versioninfo = getVersionInfo();  
        
        // 2.获取手机的硬件信息.   
        String mobileInfo  = getMobileInfo();  
          
        // 3.把错误的堆栈信息 获取出来    
        String errorinfo = getErrorInfo(ex);  
        String time = formatter.format(new Date());  
        sbs.append(time);
        sbs.append("\n");  
        sbs.append("软件版本:"+versioninfo);
        sbs.append("\n");  
        sbs.append("手机型号"+mobileInfo);
        sbs.append("\n");  
        sbs.append(errorinfo);
        
        return true;  
    }  
    private void showDialog(final Context context)
    {
    	final Dialog dialog;
    	AlertDialog.Builder buder=new AlertDialog.Builder(context);
    	buder.setTitle("温馨提示");
    	buder.setMessage("由于发生了一个未知错误,应用已关闭,我们对此引起的不便表示抱歉!" +
    			"您可以将错误信息上传到我们的服务器,帮助我们尽快解决该问题,谢谢!");
    	buder.setPositiveButton("上传", new OnClickListener() {
					
					public void onClick(DialogInterface dialog, int which) {
						// TODO Auto-generated method stub
						//上传处理---我这里没写,大家根据实际情况自己补上,我这里是一个Toast提示,提示内容就是我们要上传的信息
						Log.e(TAG, "---"+sbs.toString());
						Toast.makeText(mContext, sbs.toString(), Toast.LENGTH_LONG).show();
						new Thread(new Runnable() {
							
							public void run() {
								// TODO Auto-generated method stub
								try {  
					                Thread.sleep(3000); 
					              //  mDefaultHandler.uncaughtException(thread, ex);  
					            } catch (InterruptedException e) {  
					                Log.e(TAG, "error : ", e);  
					            }  
					            //退出程序  
							   android.os.Process.killProcess(android.os.Process.myPid());  
					           System.exit(1);
							}
						}).start();
					 
					}
				});
    	buder.setNegativeButton("取消", new OnClickListener() {
					
					public void onClick(DialogInterface dialog, int which) {
						// TODO Auto-generated method stub
						android.os.Process.killProcess(android.os.Process.myPid());  
			            System.exit(1);  
					}
				});  
    	
    	dialog=buder.create(); 
    	dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
    	dialog.setCanceledOnTouchOutside(false);//设置点击屏幕其他地方,dialog不消失
    	dialog.setCancelable(false);//设置点击返回键和HOme键,dialog不消失
    	dialog.show();
    	
    	Log.i("PLog", "2");
    }
    
   
    //打印错误信息
    private String getErrorInfo(Throwable arg1) {   
    	 Writer writer = new StringWriter();  
         PrintWriter printWriter = new PrintWriter(writer);  
         arg1.printStackTrace(printWriter);  
         Throwable cause = arg1.getCause();  
         while (cause != null) {  
             cause.printStackTrace(printWriter);  
             cause = cause.getCause();  
         }  
         printWriter.close();  
         String error = writer.toString(); 
    	 return error;  
     } 
	/** 
     * 获取手机的硬件信息 
     * @return 
     */  
    private String getMobileInfo() {  
        StringBuffer sb = new StringBuffer();  
        sb.append(""+android.os.Build.BRAND + android.os.Build.MODEL);
        sb.append("\n"); 
        return sb.toString();
    }  
  
    /** 
     * 获取软件的版本信息 
     * @return 
     */  
    private String getVersionInfo(){  
        try {  
            PackageManager pm = mContext.getPackageManager();  
              PackageInfo info =pm.getPackageInfo(mContext.getPackageName(), 0);  
              return  info.versionName;  
         } catch (Exception e) {  
             e.printStackTrace();  
             return "版本号未知";  
         }  
     }  

    
}

完成这个CrashHandler后,我们需要在一个Application环境中让其运行,为此,我们继承android.app.Application,添加自己的代码,CrashApplication.java代码如下:

public class CrashApplication extends Application{

	@Override
	public void onCreate() {
		// TODO Auto-generated method stub
		super.onCreate();
		 CrashHandler crashHandler = CrashHandler.getInstance();  
	        crashHandler.init(getApplicationContext());  
	}
	
}

最后,为了让我们的CrashApplication取代android.app.Application的地位,在我们的代码中生效,我们需要修改AndroidManifest.xml:

<application
        android:name=".CrashApplication"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".DemoMainActivity"
            android:label="@string/title_activity_demo_main" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

还有,为了能使AlertDialog能够正常弹出,我们还需要加上权限

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> 


有些童鞋可能觉得弹出的AlertDialog的样子不太满意,想自己实现,在这里我也是尝试了好久,一开始以为不能自定义,事实上是可以实现的,R.layout.sports_dialog就是我自定义的布局,用下列代码替换上面CrashHandler中showDialog()方法

	final Dialog dialog;
	dialog = new Dialog(mContext,R.style.sports_dialog);
	LayoutInflater inflater=(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    	View v = inflater.inflate(R.layout.sports_dialog, null);
    	((TextView) v.findViewById(R.id.message)).setText("由于发生了一个未知错误,应用已关闭,我们对此引起的不便表示抱歉!" +
    			"您可以将错误信息上传到我们的服务器,帮助我们尽快解决该问题,谢谢!");
    	TextView tv = (TextView)v.findViewById(R.id.title); 
    	tv.setText("温馨提示");
    	v.findViewById(R.id.bt_ok).setOnClickListener(
				new View.OnClickListener() {
					public void onClick(View arg0) {
						dialog.dismiss();
						Toast.makeText(mContext, sbs.toString(), Toast.LENGTH_LONG).show();
						new Thread(new Runnable() {
							
							public void run() {
								// TODO Auto-generated method stub
								try {  
					                Thread.sleep(3000); 
					               
					              //  mDefaultHandler.uncaughtException(thread, ex);  
					            } catch (InterruptedException e) {  
					                Log.e(TAG, "error : ", e);  
					            }  
					            //退出程序  
							   android.os.Process.killProcess(android.os.Process.myPid());  
					           System.exit(1);
							}
						}).start();
					}
				});
		v.findViewById(R.id.bt_cancel).setOnClickListener(
				new View.OnClickListener() {
					public void onClick(View arg0) {
						android.os.Process.killProcess(android.os.Process.myPid());  
			            System.exit(1);
					}
				});
			dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
    	dialog.setCanceledOnTouchOutside(false);//设置点击屏幕其他地方,dialog不消失
    	dialog.setCancelable(false);//设置点击返回键和HOme键,dialog不消失
    	dialog.setContentView(v);
    	dialog.show();


OK,就是这样,需要的可以直接用的,嘿嘿。

PS:如何在这弹出Dialog是自己处理的,

崩溃异常捕获部分参考自:http://blog.csdn.net/liuhe688/article/details/6584143

抱歉!评论已关闭.