有時在解決問題時,經常需要借助logcat才能分析定位問題,這里寫了一個小工具,能夠記錄app運行期間的log, 這樣測試人員在反饋bug時,只需要把logcat發給我們就可以了。具體代碼如下:
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Environment;
import android.text.TextUtils;
import android.util.Log;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
/**
* A tool class is used to capture the logcat generated by the app running.
* <p>The logcat save path: sdcard/Android/data/packageName/files/Documents/logcatSaveDir</p>
*
* @author xp.chen
*/
public class LogWatcher {
public static final String TAG = LogWatcher.class.getSimpleName();
private static volatile LogWatcher instance = null;
private static final String LOG_FILE_PREFIX = "logcat_";
private static final String LOG_FILE_SUFFIX = ".txt";
private static String sLogDirPath;
private Context mContext;
private Process mLogcatProcess;
private File mLogcatFile;
private LogWatcher() {}
public static LogWatcher getInstance() {
if (instance == null) {
synchronized (LogWatcher.class) {
if (instance == null) {
instance = new LogWatcher();
}
}
}
return instance;
}
/**
* Init the logcat watcher.
*
* @param context Application context.
* @param logDirName Logcat save dir
* @return LogcatWatcher instance.
*/
public LogWatcher init(Context context, String logDirName) {
if (context == null)
throw new IllegalArgumentException("LogWatcher: init failed, context can not be null");
if (TextUtils.isEmpty(logDirName))
throw new IllegalArgumentException("LogWatcher: init failed, logDirName can not be null");
this.mContext = context.getApplicationContext();
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
File documentFileDir = mContext.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS);
if (documentFileDir != null) {
sLogDirPath = documentFileDir.getAbsolutePath() + File.separator + logDirName;
} else {
Log.e(TAG, "LogWatcher: init LogWatcher failed!");
}
} else {
sLogDirPath = mContext.getFilesDir().getAbsolutePath() + File.separator + logDirName;
}
return this;
}
/**
* Start capture the logcat generated by the app.
*/
public void startWatch() {
stopWatch();
if (TextUtils.isEmpty(sLogDirPath)) {
Log.e(TAG, "LogWatcher: can not watch log, the log dir can not be created");
return;
}
mLogcatFile = createNewLogFile();
if (mLogcatFile == null) {
Log.e(TAG, "LogWatcher: can not create new log file");
return;
} else {
Log.i(TAG, "LogWatcher: log file save path >>> " + mLogcatFile.getAbsolutePath());
}
// Clear cache log
try {
Process process = Runtime.getRuntime().exec("logcat -c");
process.destroy();
} catch (IOException e) {
e.printStackTrace();
}
//final String LOGCAT_FILTER_CMD = "logcat -v time *:v | grep \"(" + android.os.Process.myPid() + ")\" > " + newLogFile.getAbsolutePath();
final String LOGCAT_FILTER_CMD = "logcat -v time *:V -f " + mLogcatFile.getAbsolutePath();
try {
mLogcatProcess = Runtime.getRuntime().exec(LOGCAT_FILTER_CMD);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Stop capture the logcat generated by the app.
*/
public void stopWatch() {
if (mLogcatProcess != null) {
mLogcatProcess.destroy();
mLogcatProcess = null;
}
if (mLogcatFile != null) {
notifySystemToScan(mContext, mLogcatFile);
}
}
private File createNewLogFile() {
File logSaveDir = new File(sLogDirPath, getCurrentDateStr());
if (!logSaveDir.exists()) {
boolean mkdirs = logSaveDir.mkdirs();
if (!mkdirs) {
Log.e(TAG, "LogWatcher: create new save dir failed");
return null;
}
}
String logFileName = LOG_FILE_PREFIX + getCurrentTimeStr() + LOG_FILE_SUFFIX;
File logFile = new File(logSaveDir, logFileName);
try {
boolean createRet = logFile.createNewFile();
if (!createRet) {
Log.e(TAG, "LogWatcher: create new log file failed");
return null;
}
} catch (IOException e) {
e.printStackTrace();
return null;
}
return logFile;
}
private static String getCurrentTimeStr() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss", Locale.getDefault());
return sdf.format(new Date(System.currentTimeMillis()));
}
private static String getCurrentDateStr() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
return sdf.format(new Date(System.currentTimeMillis()));
}
private static void notifySystemToScan(Context context, File file) {
if (context == null || file == null) return;
Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
Uri uri = Uri.fromFile(file);
intent.setData(uri);
context.sendBroadcast(intent);
}
}
為了簡化使用,我把所有相關的操作都放在一個類里去完成了,這樣只需要包含這一個文件即可。notifySystemToScan()
的作用是為了在寫入完成后能夠刷新logcat文件,否則寫入完畢后連上電腦在Windows上看不到文件。
默認保存路徑為:Android/data/packageName/files/Documents/Logcat目錄下
。為了方便查看,在保存時我按照日期創建了不同的文件夾對logcat進行保存。
主要實現思想利用了adb logcat -f
這個指令,它可以持續不斷的向某個文件中寫入數據。
開始記錄:
private void startLogWatcher() {
LogWatcher.getInstance().init(getApplicationContext(), PathDefine.LOG_SAVE_DIR).startWatch();
}
停止記錄:
LogWatcher.getInstance().stopWatch();
運行截圖:
(完)