么么哒 发表于 2018-5-11 16:33:42

软件崩溃日志输出接口

很多时候,我们在写APP的时候,编译的时候不提示错误,但是软件运行的过程中却会出现奔溃或是停止运行的现象。
这个时候,你就需要输出你的APP崩溃的日志,然后通过查看日志来诊断APP到底是哪里出了问题



这样我们修复APP崩溃的问题就会轻松很多,不然的话你就会一直困与找不到奔溃的地方,然后耽误很多时间。

这里我就简单说一下接下来我们要用的这个崩溃日志输出接口。

E4A程序中,左边面板:工程>接口,右键创建接口函数,函数名:CrashHandler


创建完毕后复制下面的代码粘贴到CrashHandler中

package com.e4a.runtime.api;//包名必须固定为这个,不能自己修改

import com.e4a.runtime.annotations.SimpleFunction;
import com.e4a.runtime.annotations.SimpleObject;
import com.e4a.runtime.annotations.UsesPermissions;

import com.e4a.runtime.应用操作;//可以引用E4A支持库中已经存在的类
import com.e4a.runtime.android.mainActivity;

import android.widget.Toast;
import java.io.File;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.Thread.UncaughtExceptionHandler;
import java.lang.reflect.Field;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Build;
import android.os.Environment;
import android.os.Looper;
import android.util.Log;
import android.widget.Toast;

@UsesPermissions(permissionNames = "android.permission.INTERNET")//安卓权限标记,如果接口函数中需要额外的安卓权限,可在此填写,多个权限可以用逗号隔开
@SimpleObject
public class CrashHandler implements UncaughtExceptionHandler{

   public static final String TAG = "CrashHandler";
      
    //系统默认的UncaughtException处理类   
    private Thread.UncaughtExceptionHandler mDefaultHandler;
    //CrashHandler实例
    private static CrashHandler INSTANCE = new CrashHandler();
    //程序的Context对象
    private Context mContext;
    //用来存储设备信息和异常信息
    private Map<String, String> infos = new HashMap<String, String>();

    //用于格式化日期,作为日志文件名的一部分
    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发生时会转入该函数来处理
   */
    @Override
    public void uncaughtException(Thread thread, Throwable ex) {
      if (!handleException(ex) && mDefaultHandler != null) {
            //如果用户没有处理则让系统默认的异常处理器来处理
            mDefaultHandler.uncaughtException(thread, ex);
      } else {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                Log.e(TAG, "error : ", e);
            }
            //退出程序
            android.os.Process.killProcess(android.os.Process.myPid());
            System.exit(1);
      }
    }

    /**
   * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.
   *
   * @param ex
   * @return true:如果处理了该异常信息;否则返回false.
   */
    private boolean handleException(Throwable ex) {
      if (ex == null) {
            return false;
      }
      //使用Toast来显示异常信息
      new Thread() {
            @Override
            public void run() {
                Looper.prepare();
                Toast.makeText(mContext, "很抱歉,程序出现异常,即将退出.", Toast.LENGTH_LONG).show();
                Looper.loop();
            }
      }.start();
      //收集设备参数信息   
      collectDeviceInfo(mContext);
      //保存日志文件   
      saveCrashInfo2File(ex);
      return true;
    }
      
    /**
   * 收集设备参数信息
   * @param ctx
   */
    public 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 (NameNotFoundException e) {
            Log.e(TAG, "an error occured when collect package info", e);
      }
      Field[] fields = Build.class.getDeclaredFields();
      for (Field field : fields) {
            try {
                field.setAccessible(true);
                infos.put(field.getName(), field.get(null).toString());
                Log.d(TAG, field.getName() + " : " + field.get(null));
            } catch (Exception e) {
                Log.e(TAG, "an error occured when collect crash info", e);
            }
      }
    }

    /**
   * 保存错误信息到文件中
   *
   * @param ex
   * @return返回文件名称,便于将文件传送到服务器
   */
    private String saveCrashInfo2File(Throwable ex) {
         
      StringBuffer sb = new StringBuffer();
      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.close();
      String result = writer.toString();
      sb.append(result);
      try {
            long timestamp = System.currentTimeMillis();
            String time = formatter.format(new Date());
            String fileName = "crash-" + time + "-" + timestamp + ".log";
            
                String path = Environment.getExternalStorageDirectory().getAbsolutePath()+"/eruyi/";
                File dir = new File(path);
                if (!dir.exists()) {
                  dir.mkdirs();
                }
                FileOutputStream fos = new FileOutputStream(path + fileName);
                fos.write(sb.toString().getBytes());
                fos.close();
            
            return fileName;
      } catch (Exception e) {
            Log.e(TAG, "an error occured while writing file...", e);
      }
      return null;
    }

}然后再右键 创建全局应用(APPlication)
复制粘贴以下代码
package com.e4a.runtime.android;//请勿修改此包名

import android.app.Application;
import com.e4a.runtime.api.CrashHandler;
public class 全局应用 extends E4Aapplication {

@Override
    public void onCreate() {
      super.onCreate();
      CrashHandler crashHandler = CrashHandler.getInstance();
      crashHandler.init(getApplicationContext());
    }

}

这样你的APP编译后,如果出现崩溃现象,就会将崩溃的原因写入到你的手机里:默认写在手机里的:eruyi目录中,这个目录大家也可以自行更改

如果这样你还是不懂的话就下载例子看看吧。


eyy 发表于 2018-5-12 00:51:38

新技能给力{:5_140:}

pt1314 发表于 2019-6-2 22:09:05

关键是这日志也看不懂呀

Meteor 发表于 2019-8-10 08:55:18

{:4_86:}可是我也看不懂崩溃日志啊。。

yytxl 发表于 2019-10-13 22:47:39

太好用了,这E4A做的app经常崩溃都把我搞崩溃了,使用之后确实能够很快的定位到故障点,直接定位到那个页面的哪一行代码,一些认为不会出错的地方也能够直接找到

e526883645 发表于 2019-10-14 00:41:24

什么嘛这是看不懂

lfulicn 发表于 2019-12-19 15:17:11

薅的{:4_86:}

757165015 发表于 2019-12-19 18:34:29

创建完毕后复制下面的代码粘贴到CrashHandler中

xmr11111 发表于 2019-12-29 18:25:40

经测试会让软件卡比

c3266393 发表于 2019-12-30 08:58:21

易如意中文编程学习交流论坛有你更精彩~
页: [1] 2
查看完整版本: 软件崩溃日志输出接口