如何在Android的日志最开心的一天400字吗

一、JNI(Java Native Interface)
& & & &1、什么是JNI:
&&&&&&&&&&&&&&JNI(Java Native Interface):java本地开发接口
& & & & & & & JNI是一个协议,这个协议用来沟通java代码和外部的本地代码(c/c++)
& & & & & & & 外部的c/c++代码也可以调用java代码
& & & &2、为什么使用JNI:
& & & & & & & 效率上 C/C++是本地语言,比java更高效
& & & & & & & 代码移植,如果之前用C语言开发过模块,可以复用已经存在的c代码
& & & & & & & java反编译比C语言容易,一般加密算法都是用C语言编写,不容易被反编译
& & & &3、Java基本数据类型与C语言基本数据类型的对应
& & & & & & &&
& & & &3、引用类型对应
& & & & & & &&
& & & &4、堆内存和栈内存的概念
& & & & & & & 栈内存:系统自动分配和释放,
& & & & & & & & & & & 保存全局、静态、局部变量,
& & & & & & & & & & & 在站上分配内存叫静态分配,
& & & & & & & & & & & 大小一般是固定的
& & & & & & & 堆内存:程序员手动分配(malloc/new)和释放(free/java不用手动释放,由GC回收),
& & & & & & & & & & & 在堆上分配内存叫动态分配,
& & & & & & & & & & & 一般硬件内存有多大堆内存就有多大
二、交叉编译
& & & &1、交叉编译的概念
& & & & & 交叉编译即在一个平台,编译出另一个平台能够执行的二进制代码
& & & & & 主流平台有: Windows、 Mac os、 Linux
& & & & & 主流处理器: x86、 arm、 mips
& & & &2、交叉编译的原理
& & & & & 即在一个平台上,模拟其他平台的特性
& & & & & 编译的流程: 源代码--&编译--&链接--&可执行程序
& & & &3、交叉编译的工具链
& & & & & 多个工具的集合,一个工具使用完后接着调用下一个工具
& & & &4、常见的交叉编译工具
& & & & & NDK(Native Development Kit): 开发JNI必备工具,就是模拟其他平台特性类编译代码的工具
& & & & & CDT(C/C++ Development Tools): 是Eclipse开发C语言的一个插件,高亮显示C语言的语法
& & & & & Cygwin: 一个Windows平台的Unix模拟器(可以参考之前博客)
& & & &5、NDK的目录结构(可以在Google官网下载NDK开发工具,需要FQ)
& & & & &&docs: 帮助文档
& & & & & build/tools:linux的批处理文件
& & & & & platforms:编译c代码需要使用的头文件和类库
& & & & & prebuilt:预编译使用的二进制可执行文件
& & & & & sample:jni的使用例子
& & & & & source:ndk的源码
& & & & & toolchains:工具链
& & & & & ndk-build.cmd:编译打包c代码的一个指令,需要配置系统环境变量
三、JNI的第一个例子
& & & & & 好了,准备知识已经完毕,下面开始我们的一个JNI例子。
& & & & 1、新建一个Android项目,在根目录下创建 jni文件夹,用于存放 C源码。
& & & & 2、在java代码中,创建一个本地方法 getStringFromC 本地方法没有方法体。
private native String getStringFromC();
&&&&&&& 3、在jni中创建一个C文件,定义一个函数实现本地方法,函数名必须用使用 本地方法的全类名,点改为下划线。
1 #include &stdio.h&
2 #include &stdlib.h&
3 #include &jni.h&
4 //方法名必须为本地方法的全类名点改为下划线,穿入的两个参数必须这样写,
5 //第一个参数为Java虚拟机的内存地址的二级指针,用于本地方法与java虚拟机在内存中交互
6 //第二个参数为一个java对象,即是哪个对象调用了这个 c方法
7 jstring Java_com_mwp_jnihelloworld_MainActivity_getStringFromC(JNIEnv* env,
jobject obj){
//定义一个C语言字符串
char* cstr = "hello form c";
//返回值是java字符串,所以要将C语言的字符串转换成java的字符串
//在jni.h 中定义了字符串转换函数的函数指针
(*NewStringUTF)(JNIEnv*, const char*);
//第一种方法:很少用
jstring jstr1 = (*(*env)).NewStringUTF(env, cstr);
//第二种方法,推荐
jstring jstr2 = (*env) -& NewStringUTF(env, cstr);
return jstr2;
&&&&&&&&4、在jni中创建 Android.mk文件,用于配置 本地方法
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
#编译生成的文件的类库叫什么名字
LOCAL_MODULE
#要编译的c文件
LOCAL_SRC_FILES := Hello.c
include $(BUILD_SHARED_LIBRARY)
&&&&&&&&&5、在jni目录下执行 ndk-build.cmd指令,编译c文件
& & & & &6、在java代码中加载编译后生成的so类库,调用本地方法,将项目部署到虚拟机上之后就会发现toast弹出的C代码定义的字符串,第一个例子执行成功了。
//加载打包完毕的 so类库
System.loadLibrary("hello");
&&&&&&&&&7、jni打包的C语言类库默认仅支持 arm架构,需要在jni目录下创建 Android.mk 文件添加如下代码可以支持x86架构
APP_ABI := armeabi armeabi-v7a x86
四、JNI常见错误
& & & & &1、findLibrary returned null:
& & & & & & & & CPU平台不匹配 或者 在加载类库时,类库名字写错了
& & & & &2、本地方法找不到:
& & & & & & & & 忘记加载类库了 或者 C代码中方法名写错了
五、javah工具与javap工具
& & & & &1、javah: &生成本地方法头文件
& & & & & & 需要在C/C++模块下才能生效
& & & & & & 在JDK1.7中,在src目录下执行javah 全类名
& & & & & & 在JDK1.6中,在bin/classes目录下执行
& & & & &2、javap: &打印方法签名
& & & & & & 在C语言中调用java的方法需要用到反射,C语言的反射需要一个方法签名,使用javap能够生成方法签名,很熟练的话也可以自己写方法签名
& & & & & & 在bin/classes目录下执行 javap -s 全类名
& & & & & &&
六、使用本地方法加密字符串的一个小例子
& & & C语言字符串与Java中的字符串类型不同,所以需要进行字符串类型转换。
& & & 一个重要的思想:C语言计算字符串的长度不方便,但是java很方便,只需要调用一个length()方法就可以,所以像这种需求,那个语言有优势就用哪个语言算,算完当做参数传递给另一种语言就ok。
& & & & & & & & & & & 混合语言编程这应该是一种非常有用的思想。
& & &Java非常容易被反编译,所以加密都是用 c语言写的&
#include &jni.h&
#include &string.h&
//将java字符串转换为c语言字符串(工具方法)
Jstring2CStr(JNIEnv*
(*env)-&FindClass(env,"java/lang/String");
(*env)-&NewStringUTF(env,"GB2312");
(*env)-&GetMethodID(env,clsstring,
"getBytes",
"(Ljava/lang/S)[B");
jbyteArray
(jbyteArray)(*env)-&CallObjectMethod(env,jstr,mid,strencode); // String .getByte("GB2312");
(*env)-&GetArrayLength(env,barr);
(*env)-&GetByteArrayElements(env,barr,JNI_FALSE);
(char*)malloc(alen+1);
memcpy(rtn,ba,alen);
rtn[alen]=0;
(*env)-&ReleaseByteArrayElements(env,barr,ba,0);
JNIEXPORT jstring JNICALL Java_com_mwp_encodeanddecode_MainActivity_encode
(JNIEnv * env, jobject obj, jstring text, jint length){
char* cstr = Jstring2CStr(env, text);
for(i = 0;i&i++){
*(cstr+i) += 1; //加密算法,将字符串每个字符加1
return (*env)-&NewStringUTF(env,cstr);
JNIEXPORT jstring JNICALL Java_com_mwp_encodeanddecode_MainActivity_decode
(JNIEnv * env, jobject obj, jstring text, jint length){
char* cstr = Jstring2CStr(env, text);
for(i = 0;i&i++){
*(cstr+i) -= 1;
return (*env)-&NewStringUTF(env, cstr);
七、JNI操作一个数组(引用传递)
& & & & &&传递数组其实是传递一个堆内存的数组首地址的引用过去,所以实际操作的是同一块内存,
& & & & & 当调用完方法,不需要返回值,实际上参数内容已经改变,
& & & & & Android中很多操作硬件的方法都是这种C语言的传引用的思路
1 public class MainActivity extends Activity {
System.loadLibrary("encode");
int[] array = {1,2,3,4,5};
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
public void click(View v){
encodeArray(array);
//不需要返回值,实际操作的是同一块内存,内容已经发生了改变
for (int i : array) {
System.out.println(i);
//传递数组其实是传递一个堆内存的数组首地址的引用过去,所以实际操作的是同一块内存,
//当调用完方法,不需要返回值,实际上参数内容已经改变,
//Android中很多操作硬件的方法都是这种C语言的传引用的思路,要非常熟练
private native void encodeArray(int[] arr);
1 #include &jni.h&
com_mwp_jniarray_MainActivity
encodeArray
* Signature: ([I)V
7 JNIEXPORT void JNICALL Java_com_mwp_jniarray_MainActivity_encodeArray
(JNIEnv * env, jobject obj, jintArray arr){
//拿到整型数组的长度以及第0个元素的地址
(*GetArrayLength)(JNIEnv*, jarray);
int length = (*env)-&GetArrayLength(env, arr);
(*GetIntArrayElements)(JNIEnv*, jintArray, jboolean*);
int* arrp = (*env)-&GetIntArrayElements(env, arr, 0);
for(i = 0;i&i++){
*(arrp + i) += 10; //将数组中的每个元素加10
八、偷用美图秀秀的C语言本地类库加深JNI的理解
& & 项目中不需要有c代码,只需要有一个编译过后的类库供Java调用就可以了。
& & 将美图秀秀的apk文件解压缩,将lib目录下C类库导入自己的项目,
& & 反编译美图秀秀的apk文件,将其本地方法类 JNI.java复制到自己的项目
& & 根据本地方法名和参数猜函数的作用及如何使用,
&&& 下例调用了美图的一个LOMO美化效果
1 public class MainActivity extends Activity {
//加载美图秀秀的类库
System.loadLibrary("mtimage-jni");
private ImageV
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
iv = (ImageView) findViewById(R.id.iv);
bitmap = BitmapFactory.decodeFile("sdcard/aneiyi.jpg");
iv.setImageBitmap(bitmap);
public void click(View v){
int width = bitmap.getWidth();
int height = bitmap.getHeight();
//用于保存所有像素信息的数组
int[] pixels = new int[width*height];
//获取图片的像素颜色信息,保存至pixels
bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
JNI jni = new JNI();
//调用美图秀秀本地库中的美图方法,靠猜
//arg0:保存了所有像素颜色信息的数组
//arg1:图片的宽
//arg2:图片的高
//此方法是通过改变pixels的像素颜色值来实现美化效果,传递一个数组参数是不需要返回值的
jni.StyleLomoB(pixels, width, height);
Bitmap bmNew = Bitmap.createBitmap(pixels, width, height, bitmap.getConfig());
iv.setImageBitmap(bmNew);
九、在C语言中调用java方法(反射)
& & & & 1、有时需要在C语言中调用java的方法,如刷新UI显示加载资源进度
&&&&&&&&&& 在本地方法C语言代码中打印 Android的Logcat日志输出,Google已经帮我们封装好了方法,只需要调用一下就可以
& & & & & &如果要输出中文的话,必须将C语言的文件编码改成 utf-8,否则乱码
& & & & & &在C语言中调用java的方法需要用到反射,C语言的反射需要一个方法签名,使用javap能够生成方法签名,很熟练的话也可以自己写方法签名
& & & & & &在bin/classes目录下执行 javap -s 全类名
1 public class MainActivity extends Activity {
System.loadLibrary("hello");
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
public void click(View v){
public native void cLog();
public void show(String message){
Builder builder = new Builder(this);
builder.setTitle("标题");
builder.setMessage(message);
builder.show();
#include &jni.h&
#include &android/log.h&
#define LOG_TAG "System.out"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
JNIEXPORT void JNICALL Java_com_mwp_ccalljava2_MainActivity_cLog
(JNIEnv * env, jobject obj){
//打印log输出
LOGD("我是C语言打印的debug日志");
LOGI("我是C语言打印的info日志");
//通过反射来调用java的方法,需要知道方法签名,使用javap得到方法签名
//在bin/classes目录下执行 javap -s 全类名
//1、得到类的字节码对象
(*FindClass)(JNIEnv*, const char*);
jclass clazz = (*env)-&FindClass(env, "com/mwp/ccalljava2/MainActivity");
//jmethodID
(*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
jmethodID methodID = (*env)-&GetMethodID(env, clazz, "show", "(Ljava/lang/S)V");
(*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
(*env)-&CallVoidMethod(env,obj,methodID, (*env)-&NewStringUTF(env, "这是弹窗的内容"));
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_LDLIBS += -llog
LOCAL_MODULE
LOCAL_SRC_FILES := log.c
include $(BUILD_SHARED_LIBRARY)
十、模拟监测压力传感器
& & & & 传感器的原理是使用敏感电阻如(光敏电阻,热敏电阻)等监测电流电压的变化
& & & & Android程序只需要处理传感器传递的数据,并将其显示在界面上就可以。
& & & & 下面模拟一个压力传感器来练习JNI编程
1 public class MainActivity extends Activity {
System.loadLibrary("monitor");
private MyProgressB
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mpb = (MyProgressBar) findViewById(R.id.mpb);
mpb.setMax(100);
public void start(View v){
new Thread(){
public void run() {
startMonitor();
}.start();
public void stop(View v){
stopMonitor();
public native void startMonitor();
public native void stopMonitor();
//供本地方法调用刷新UI
public void show(int pressure){
mpb.setPressure(pressure);
#include &jni.h&
#include &stdio.h&
#include &stdlib.h&
//模拟压力传感其传递数据
int getPressure(){
return rand()%101;
//用于控制循环的开关
JNIEXPORT void JNICALL Java_com_mwp_monitor_MainActivity_startMonitor
(JNIEnv * env, jobject obj){
monitor = 1;
while(monitor){
//本地方法获取传感器数据
pressure= getPressure();
//使用反射调用java方法刷新界面显示
(*FindClass)(JNIEnv*, const char*);
clazz= (*env)-&FindClass(env, "com/mwp/monitor/MainActivity");
//jmethodID
(*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
methodid= (*env)-&GetMethodID(env, clazz, "show","(I)V");
(*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
(*env)-&CallVoidMethod(env, obj, methodid, pressure);
JNIEXPORT void JNICALL Java_com_mwp_monitor_MainActivity_stopMonitor
(JNIEnv * env, jobject obj){
//结束循环
monitor = 0;
十一、使用C++代码实现本地方法
& & & & &1、把c文件后缀名换成cpp
& & & & &2、Android.mk文件中的hello.c也要换成hello.cpp
& & & & &3、c++的使用的环境变量结构体中,访问了c使用的结构体的函数指针,函数名全部都是一样的,只是参数去掉了结构体指针
& & & & &4、访问函数指针时,把env前面的*号去掉,因为此时env已经是一级指针
& & & & &5、clean,清除之前编译的残留文件
& & & & &6、把声明函数的h文件放入jni文件夹中,include该h文
#include &jni.h&
#include "com_mwp_cplusplus_MainActivity.h"
JNIEXPORT jstring JNICALL Java_com_mwp_cplusplus_MainActivity_helloC
(JNIEnv * env, jobject obj){
char* cstr = "hello from c";
//return (*env)-&NewStringUTF(env, cstr);
return env-&NewStringUTF(cstr);
阅读(...) 评论()&>&android-logging-log4j-1.0.3.jar
android-logging-log4j-1.0.3.jar
上传大小:7KB
安卓日志log4j的两个jar包 工具之一
综合评分:5
{%username%}回复{%com_username%}{%time%}\
/*点击出现回复框*/
$(".respond_btn").on("click", function (e) {
$(this).parents(".rightLi").children(".respond_box").show();
e.stopPropagation();
$(".cancel_res").on("click", function (e) {
$(this).parents(".res_b").siblings(".res_area").val("");
$(this).parents(".respond_box").hide();
e.stopPropagation();
/*删除评论*/
$(".del_comment_c").on("click", function (e) {
var id = $(e.target).attr("id");
$.getJSON('/index.php/comment/do_invalid/' + id,
function (data) {
if (data.succ == 1) {
$(e.target).parents(".conLi").remove();
alert(data.msg);
$(".res_btn").click(function (e) {
var parentWrap = $(this).parents(".respond_box"),
q = parentWrap.find(".form1").serializeArray(),
resStr = $.trim(parentWrap.find(".res_area_r").val());
console.log(q);
//var res_area_r = $.trim($(".res_area_r").val());
if (resStr == '') {
$(".res_text").css({color: "red"});
$.post("/index.php/comment/do_comment_reply/", q,
function (data) {
if (data.succ == 1) {
var $target,
evt = e || window.
$target = $(evt.target || evt.srcElement);
var $dd = $target.parents('dd');
var $wrapReply = $dd.find('.respond_box');
console.log($wrapReply);
//var mess = $(".res_area_r").val();
var mess = resS
var str = str.replace(/{%header%}/g, data.header)
.replace(/{%href%}/g, 'http://' + window.location.host + '/user/' + data.username)
.replace(/{%username%}/g, data.username)
.replace(/{%com_username%}/g, data.com_username)
.replace(/{%time%}/g, data.time)
.replace(/{%id%}/g, data.id)
.replace(/{%mess%}/g, mess);
$dd.after(str);
$(".respond_box").hide();
$(".res_area_r").val("");
$(".res_area").val("");
$wrapReply.hide();
alert(data.msg);
}, "json");
/*删除回复*/
$(".rightLi").on("click", '.del_comment_r', function (e) {
var id = $(e.target).attr("id");
$.getJSON('/index.php/comment/do_comment_del/' + id,
function (data) {
if (data.succ == 1) {
$(e.target).parent().parent().parent().parent().parent().remove();
$(e.target).parents('.res_list').remove()
alert(data.msg);
//填充回复
function KeyP(v) {
var parentWrap = $(v).parents(".respond_box");
parentWrap.find(".res_area_r").val($.trim(parentWrap.find(".res_area").val()));
评论共有30条
不错很好用
很好用,刚好用上
不错,朋友拿来直接用起来了
VIP会员动态
热门资源标签
CSDN下载频道资源及相关规则调整公告V11.10
下载频道用户反馈专区
下载频道积分规则调整V1710.18
spring mvc+mybatis+mysql+maven+bootstrap 整合实现增删查改简单实例.zip
资源所需积分/C币
当前拥有积分
当前拥有C币
输入下载码
为了良好体验,不建议使用迅雷下载
android-logging-log4j-1.0.3.jar
会员到期时间:
剩余下载个数:
剩余积分:0
为了良好体验,不建议使用迅雷下载
积分不足!
资源所需积分/C币
当前拥有积分
您可以选择
程序员的必选
绿色安全资源
资源所需积分/C币
当前拥有积分
当前拥有C币
为了良好体验,不建议使用迅雷下载
资源所需积分/C币
当前拥有积分
当前拥有C币
为了良好体验,不建议使用迅雷下载
资源所需积分/C币
当前拥有积分
当前拥有C币
您的积分不足,将扣除 10 C币
为了良好体验,不建议使用迅雷下载
无法举报自己的资源
你当前的下载分为234。
你还不是VIP会员
开通VIP会员权限,免积分下载
你下载资源过于频繁,请输入验证码
您因违反CSDN下载频道规则而被锁定帐户,如有疑问,请联络:!
若举报审核通过,可返还被扣除的积分
被举报人:
请选择类型
资源无法下载 ( 404页面、下载失败、资源本身问题)
资源无法使用 (文件损坏、内容缺失、题文不符)
侵犯版权资源 (侵犯公司或个人版权)
虚假资源 (恶意欺诈、刷分资源)
含色情、危害国家安全内容
含广告、木马病毒资源
*投诉人姓名:
*投诉人联系方式:
*版权证明:
*详细原因:
android-logging-log4j-1.0.3.jarandroid app记录运行日志 捕获奔溃异常 ,存储日志到文件
app在运行过程中,为了后期的维护升级,记录日志是一个非常好的方法。
为了读取到app运行时的日志,一般的作法是单独开一个线程,在app运行的启动线程,然后app退出时停掉线程。
然而我们更好的方法是开启一个service,然后在里面做日志记录,代码如下:
package com.hai.
import java.io.BufferedR
import java.io.DataInputS
import java.io.F
import java.io.FileNotFoundE
import java.io.FileOutputS
import java.io.IOE
import java.io.InputS
import java.io.InputStreamR
import java.util.L
import android.app.ActivityM
import android.app.ActivityManager.RunningAppProcessI
import android.app.S
import android.content.I
import android.os.E
import android.os.IB
import android.os.L
import android.util.L
import android.widget.T
public class MyLogcat extends Service {
boolean readlog =
public IBinder onBind(Intent intent) {
public void onCreate() {
super.onCreate();
Log.d("hhp", "onCreate");
thread = new Thread(new Runnable() {
public void run() {
log2();//个人觉得这个方法更实用
public void onStart(Intent intent, int startId) {
thread.start();
Log.d("hhp", "onStart");
super.onStart(intent, startId);
private void log2() {
Log.d("hhp", "log2 start");
String[] cmds = { "logcat", "-c" };
String shellCmd = "logcat -v time -s *:W "; // adb logcat -v time *:W
Process process =
Runtime runtime = Runtime.getRuntime();
BufferedReader reader =
runtime.exec(cmds).waitFor();
process = runtime.exec(shellCmd);
reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line =
while ((line = reader.readLine()) != null) {
if (line.contains(String.valueOf(android.os.Process.myPid()))) {
// line = new String(line.getBytes("iso-8859-1"), "utf-8");
writeTofile(line);
} catch (Exception e) {
e.printStackTrace();
Log.d("hhp", "log2 finished");
private void log() {
Log.d("hhp", "log start");
String[] cmds = { "logcat", "-c" };
String shellCmd = "logcat -v time -s *:W ";// //adb logcat -v time *:W
Process process =
InputStream is =
DataInputStream dis =
String line = "";
Runtime runtime = Runtime.getRuntime();
runtime.exec(cmds);
process = runtime.exec(shellCmd);
is = process.getInputStream();
dis = new DataInputStream(is);
// String filter = GetPid();
String filter = android.os.Process.myPid() + "";
while ((line = dis.readLine()) != null) { //这里如果输入流没断,会一直循环下去。
line = new String(line.getBytes("iso-8859-1"), "utf-8");
if (line.contains(filter)) {
int pos = line.indexOf(":");
Log.d("hhp2", line + "");
writeTofile(line);
} catch (Exception e) {
Log.d("hhp", "log finished");
private void writeTofile(String line) {
String content = line + "\r\n";
File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath()
+ "/logcat/myLog.txt");
if (!file.exists()) {
file.createNewFile();
} catch (Exception e) {
e.printStackTrace();
FileOutputS
fos = new FileOutputStream(file, true);
fos.write(content.getBytes());
fos.flush();
fos.close();
} catch (Exception e) {
e.printStackTrace();
public void onDestroy() {
super.onDestroy();
stopSelf();
代码比较简单,所以没怎么注视了。说下大概思路:在service开启的时候,就开启线程不停地从logcat中读取输入流,
把读到的信息存入文件中,service停止的时候线程stop,就这么简单。
当然要读入系统日志还需要添加权限:
&uses-permission android:name="android.permission.READ_LOGS" /&
下面是我记录的测试日志,信息记录的有点多,实际中可以运用正则过滤掉一些信息。
上面的代码基本可以记录本app运行中的日志,但如果中途有未捕获的异常导致app奔溃,那么这个未捕获的异常导致的奔溃上面代码就记录不到了。
因为这个异常导致app奔溃,虚拟机挂掉,那当然记录日志的线程也停了。那怎么捕获这类我们未捕获的异常(运行时异常)呢,幸好android这样
一个接口UncaughtExceptionHandler,当app奔溃前,它会先通知这个接口,这样我们就可以在app奔溃前做点自己想做的事了。
关于怎么捕获奔溃异常,我觉得这位哥们的一片博客写的不错, 我借鉴着改了下:
package com.hai.
import java.io.F
import java.io.FileOutputS
import java.io.PrintW
import java.io.StringW
import java.io.W
import java.lang.Thread.UncaughtExceptionH
import java.lang.reflect.F
import java.text.DateF
import java.text.SimpleDateF
import java.util.D
import java.util.HashM
import java.util.M
import android.content.C
import android.content.pm.PackageI
import android.content.pm.PackageM
import android.content.pm.PackageManager.NameNotFoundE
import android.os.B
import android.os.E
import android.os.L
import android.util.L
import android.widget.T
public class CrashHandler implements UncaughtExceptionHandler {
public static final String TAG = "CrashHandler";
// 系统默认的UncaughtException处理类
private Thread.UncaughtExceptionHandler mDefaultH
// CrashHandler实例
private static CrashHandler INSTANCE = new CrashHandler();
// 程序的Context对象
private Context mC
// 用来存储设备信息和异常信息
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 =
// 获取系统默认的UncaughtException处理器
mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
// 设置该CrashHandler为程序的默认处理器
Thread.setDefaultUncaughtExceptionHandler(this);
* 当UncaughtException发生时会转入该函数来处理
public void uncaughtException(Thread thread, Throwable ex) {
if (!handleException(ex) && mDefaultHandler != null) {
// 如果用户没有处理则让系统默认的异常处理器来处理
mDefaultHandler.uncaughtException(thread, ex);
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(final Throwable ex) {
if (ex == null) {
// 使用Toast来显示异常信息
new Thread() {
public void run() {
Looper.prepare();
ex.printStackTrace();
Toast.makeText(mContext, "很抱歉,程序出现异常,即将退出.", Toast.LENGTH_LONG).show();
Looper.loop();
}.start();
// 收集设备参数信息
collectDeviceInfo(mContext);
// 保存日志文件
saveCrashInfo2File(ex);
* 收集设备参数信息
* @param ctx
public void collectDeviceInfo(Context ctx) {
PackageManager pm = ctx.getPackageManager();
PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(), PackageManager.GET_ACTIVITIES);
if (pi != null) {
String versionName = pi.versionName == null ? "null" : pi.versionN
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) {
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();
String time = formatter.format(new Date());
sb.append(time + result);
long timestamp = System.currentTimeMillis();
String fileName = "crash.log";
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
String path =Environment.getExternalStorageDirectory().getAbsolutePath()+ "/logcat/";
File dir = new File(path);
if (!dir.exists()) {
dir.mkdirs();
FileOutputStream fos = new FileOutputStream(path + fileName, true);
fos.write((sb.toString()).getBytes());
fos.close();
return fileN
} catch (Exception e) {
Log.e(TAG, "an error occured while writing file...", e);
上面我们实现了这个接口,然后在奔溃前做了一些友好处理,如存储奔溃日志,主动杀死进程,不让弹出系统的强制关闭对话框。
然后我们在Application中这样引用即可
package com.
import android.app.A
import com.hai.logcat.CrashH
public class MyApplication extends Application {
CrashHandler handler =
public void onCreate() {
super.onCreate();
handler = CrashHandler.getInstance();
handler.init(getApplicationContext());
没有更多推荐了,}

我要回帖

更多关于 我最开心的一天400字 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信