然后我们根据需求来划分Log等级,一般都为 debug 并提供对外的打印方法。 注:这里的等级划分、和打印方法所要的信息都是跟着需求来。但是都是大同小异。我这里多写什么信息,打印方法也只留最基本的 /** */ public static final String CRASH_LEVEL = “CRASH”; public static final String ERROR_LEVEL = “ERROR”; public static final String WARNING_LEVEL = “WARNING”; public static final String INFO_LEVEL = “INFO”; public static final String DEBUG_LEVEL = “DEBUG”; public void c(String logData) { writeToFile(CRASH_LEVEL, logData); } public void e(String logData) { writeToFile(ERROR_LEVEL, logData); } public void w(String logData) { writeToFile(WARNING_LEVEL, logData); } public void i(String logData) { writeToFile(INFO_LEVEL, type, logData); } public void d(String logData) { writeToFile(DEBUG_LEVEL, logData); } witeTofile就是写入到文件的方法: private static StackTraceElement getCallerStackTraceElement() { return Thread.currentThread().getStackTrace()[5]; } /** 将Log信息写入文件 isDouble为是否连续两次写入,防止连续两次上传服务器。 @param level @param type @param logData */ public static void writeToFile(String level, String logData) { if (null == logPath) { LogUtil.e(TAG, “logPath == null ,未初始化LogToFile”); return; } String fileName = logPath + “/AppLogs_Android.log”; StackTraceElement caller = getCallerStackTraceElement(); // 获取到类名 String callerClazzName = caller.getClassName(); callerClazzName = callerClazzName.substring(callerClazzName .lastIndexOf(“.”) + 1); //要写入的LOG内容 String log = type + " - " + callerClazzName + " - " + caller.getMethodName() + " - line " + caller.getLineNumber() + " - " + logData + “\n”; LogUtil.d(TAG, log); //如果父路径不存在 File file = new File(logPath); if (!file.exists()) { file.mkdirs();//创建父路径 } FileOutputStream fos = null; BufferedWriter bw = null; try { fos = new FileOutputStream(fileName, true); bw = new BufferedWriter(new OutputStreamWriter(fos)); bw.write(log); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { if (bw != null) { bw.close();//关闭缓冲流 } } catch (IOException e) { e.printStackTrace(); } } if (getLogsFileSize(fileName) >= 1f) { sendToServer(fileName); } else if (level == CRASH_LEVEL || level == ERROR_LEVEL || level == WARNING_LEVEL) { sendToServer(fileName); } } 上面有一个巨顶的方法: Thread.currentThread().getStackTrace()[5] 这行函数能够得离当前执行代码的指令。(注:不一定是5) 因为在执行命令时,指令栈保存当前线程最近执行的代码。 它的原理是这样的: 1、MainActivity : LogTool.d(xxx,xxx) 跳转-》 2、LogTool: d(xxx,xx){wirteToFile(xxx,xxx)} 跳转-》 3、LogTool:writeToFile(xxx){} 假如上面是一个指令栈,那么我就去获取这个栈的栈底元素 [1] MainActivity… 这一行,得到的是个 得到该对象之后,我们可以将该指令反射,通过 所以上面代码中的第5行只是我这边的情况,别的代码就要自己去推理具体在哪一行咯。 接下来就是上传到服务器,这里使用Okhttp,上传的格式是包括文件+一些附带String信息,所以使用mutipart来提交表单,并且开启一个线程来提交。 /** 上传Log文件至服务器 @return */ public static void sendToServer(final String pathName) { new Thread(new Runnable() { @Override public void run() { final File file = new File(pathName); MediaType MEDIA_TYPE_TXT = MediaType.parse(“text/plain”); RequestBody fileBody = MultipartBody.create(MEDIA_TYPE_TXT, file); MultipartBody multiBuilder = new MultipartBody.Builder() .setType(MultipartBody.FORM) .addFormDataPart(“log_file”, file.getName(), fileBody) .addFormDataPart(“system”, “Android”) .addFormDataPart(“phone_number”, “110”).build(); Request request = new Request.Builder().url(apiUrl).post(multiBuilder).build(); OkHttpClient okHttpClient = new OkHttpClient(); Call call = okHttpClient.newCall(request); call.enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { //请求失败的处理 LogUtil.e(TAG, "上传Log文件失败 : " + e.toString()); if (getLogsFileSize(pathName) > 1f) { file.delete(); } } @Override public void onResponse(Call call, Response response) throws IOException { //请求成功的处理 LogUtil.d(TAG, "上传Log文件成功 : " + response.body().string()); //清空文件 file.delete(); } }); } }).start(); } 到这里就上传完啦。 但是我们还没有关注Crash的捕捉,因为我们自己无法判断Crash是出现在什么地方的,所以我们要写一个全局的Crash捕捉器,而Android提供了这个类,叫:Thread.UncaughtExceptionHandler,我们通过实现该接口,重写uncaughtException()方法,来处理遇到异常的情况。 该类如下(也需要在App初始化时init): public class CrashHandlerManager implements Thread.UncaughtExceptionHandler { private static final String TAG = “CrashHandlerManager”; private static CrashHandlerManager instance; private Context context; private Thread.UncaughtExceptionHandler uncaughtExceptionHandler; //收集信息集合 private Map public synchronized static CrashHandlerManager getInstance() { if (instance == null) { instance = new CrashHandlerManager(); } return instance; } private CrashHandlerManager() { } /** 初始化程序异常处理器 @param context */ public void initCrash(Context context) { this.context = context; //获取系统默认的UncaughtException处理器 uncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler(); //设置该CrashHandler为程序得默认处理器 Thread.setDefaultUncaughtExceptionHandler(this); } /** 当UncaughtException发生会进入此方法 @param t @param e */ @Override public void uncaughtException(Thread t, Throwable e) { if (!handleException(e) && uncaughtExceptionHandler != null) { //如果自定义没有处理就交给系统去处理 uncaughtExceptionHandler.uncaughtException(t, e); } 自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。 深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前! 因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。 既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化! 由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新! 如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android) 《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取! 密,与我们生活最相关的技术一直都在寻找最终的技术落地平台,以前是windows系统,而现在则是移动系统了,移动系统中又是以Android占比绝大部分为前提,所以AndroidNDK技术已经是我们必备技能了。 [外链图片转存中…(img-xhllqCgS-1712681454732)] [外链图片转存中…(img-hPdbzlfB-1712681454732)] 《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!StackTraceElement
对象。getClassName()
获取类名,通过getMethodName()
获取方法名、通过getLineNumber()
获取当前行数,就省的我们一步一步去找具体哪行了。结语