I am an android developer, I develop apps for clients.
However, in client devices, some app crash issue usually happened. But it didn't happen in my device.
But I cannot reach my client, so, here is question:~~~~
Is there any plugin or tools that can implement into my android apps, so when the apps is crashed in my client's device, the crash logs will be sent to my email or somewhere I can check it, so I can debug it.
Please advise me, thanks
Go to https://play.google.com/apps/publish/ and publish your app. When users install your app and it crashes it notifies Google, and when you'll login to this control-panel you'll be able to find such reports under the "Reports" tab and then under "CRASHES & ANRS".
Try taking a look at Crashlytics: http://try.crashlytics.com/sdk-android/
I think you should extend Application class, and redefine Exception Handle with stable code (independed of android version).
Code example:
YourApplication.java
public class YourApplication extends Application {
#Override
public void onCreate() {
Thread.setDefaultUncaughtExceptionHandler(ExceptionHandler.inContext(getApplicationContext()));
super.onCreate();
}}
ExceptionHandler.java
final public class ExceptionHandler implements Thread.UncaughtExceptionHandler{
private final DateFormat formatter = new SimpleDateFormat("dd.MM.yy HH:mm");
private final DateFormat fileFormatter = new SimpleDateFormat("dd-MM-yy");
private String versionName = "0";
private int versionCode = 0;
private final String stacktraceDir;
private final Thread.UncaughtExceptionHandler previousHandler;
private ExceptionHandler(Context context, boolean chained) {
PackageManager mPackManager = context.getPackageManager();
PackageInfo mPackInfo;
try {
mPackInfo = mPackManager.getPackageInfo(context.getPackageName(), 0);
versionName = mPackInfo.versionName;
versionCode = mPackInfo.versionCode;
} catch (PackageManager.NameNotFoundException e) {
// ignore
}
if(chained)
previousHandler = Thread.getDefaultUncaughtExceptionHandler();
else
previousHandler = null;
stacktraceDir = String.format("/Android/data/%s/files/", context.getPackageName());
}
static ExceptionHandler inContext(Context context) {
return new ExceptionHandler(context, true);
}
static ExceptionHandler reportOnlyHandler(Context context) {
return new ExceptionHandler(context, false);
}
#Override
public void uncaughtException(Thread thread, Throwable exception) {
final String state = Environment.getExternalStorageState();
final Date dumpDate = new Date(System.currentTimeMillis());
if (Environment.MEDIA_MOUNTED.equals(state)) {
StringBuilder reportBuilder = new StringBuilder();
reportBuilder
.append("\n\n\n")
.append(formatter.format(dumpDate)).append("\n")
.append(String.format("Version: %s (%d)\n", versionName, versionCode))
.append(thread.toString()).append("\n");
processThrowable(exception, reportBuilder);
File sd = Environment.getExternalStorageDirectory();
File stacktrace = new File(
sd.getPath() + stacktraceDir,
String.format(
"stacktrace-%s.txt",
fileFormatter.format(dumpDate)));
File dumpdir = stacktrace.getParentFile();
boolean dirReady = dumpdir.isDirectory() || dumpdir.mkdirs();
if (dirReady) {
FileWriter writer = null;
try {
writer = new FileWriter(stacktrace, true);
writer.write(reportBuilder.toString());
} catch (IOException e) {
// ignore
} finally {
try {
if (writer != null)
writer.close();
} catch (IOException e) {
// ignore
}
}
}
}
if(previousHandler != null)
previousHandler.uncaughtException(thread, exception);
}
private void processThrowable(Throwable exception, StringBuilder builder) {
if(exception == null)
return;
StackTraceElement[] stackTraceElements = exception.getStackTrace();
builder
.append("Exception: ").append(exception.getClass().getName()).append("\n")
.append("Message: ").append(exception.getMessage()).append("\nStacktrace:\n");
for(StackTraceElement element : stackTraceElements) {
builder.append("\t").append(element.toString()).append("\n");
}
processThrowable(exception.getCause(), builder);
}}
If your app will crased, you can find a log with StackTrace at /sdcard/android/data/you.package.name/files/ all log files.
Also you can check new log files at this folder at every app start. If you find new files, you can send this to your email.
Related
I have an arduino nano with a HC-05 bluetooth module.I need to receive some data from that module into Unity so i made an android librarie(in Android Studio) that can handle the bluetooth connection.
The problem is that.For the first 2 seconds i receive data then the process seems to slow down.I wait up to 10 seconds between the data sets.
I read something about the standard bound rate of the module and i think that could be the source of the problem but when i change it from AT commands mode.Nothing seems to change.
*When i read the data from an Bluetooth terminal(from Google Play).It feels mutch faster than my app.
Some ideas please?
The library code
public class BluetoothConnector {
private static final BluetoothConnector ourInstance = new BluetoothConnector();
public static BluetoothConnector getInstance() {
return ourInstance;
}
ArrayList<BluetoothDevice> devices_true = new ArrayList<>();
ArrayList<String>device_names = new ArrayList<>();
final UUID mUUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
BluetoothAdapter adapter;
BluetoothSocket skt;
BluetoothDevice cur_device;
BluetoothDevice con_device;
Set<BluetoothDevice> devices;
InputStream inputStream;
OutputStream outputStream;
private BluetoothConnector() {}
public String Loooper_Connect(){
try{
adapter = BluetoothAdapter.getDefaultAdapter();
devices = adapter.getBondedDevices();
for(BluetoothDevice device:devices)
{
devices_true.add(device);
device_names.add(device.getName());
}
cur_device = devices_true.get(device_names.indexOf("LooperController"));
if(cur_device == null)return "No controller found";
con_device = adapter.getRemoteDevice(cur_device.getAddress());
skt = con_device.createInsecureRfcommSocketToServiceRecord(mUUID);
adapter.cancelDiscovery();
skt.connect();
inputStream = skt.getInputStream();
outputStream = skt.getOutputStream();
outputStream.write("Device Connected".getBytes());
outputStream.flush();
return "Controller Connected!";
}
catch (Exception ex)
{
return "Error : " + ex.toString();
}
}
public String ReadData(){
byte[] primary_data = new byte[256];
try {
if(inputStream.available() > 0)
inputStream.read(primary_data);
else return "";
return new String(primary_data);
} catch (IOException e) {
return e.toString();
}
}
}
And my unity code:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Main : MonoBehaviour {
// Use this for initialization
AndroidJavaClass bluetoothComunication;
AndroidJavaObject bluetoothCom;
public GameObject cub;
public string sol;
string d;
void Start () {
try
{
bluetoothComunication = new AndroidJavaClass("com.example.bluetoothlooper.BluetoothConnector");
bluetoothCom = bluetoothComunication.CallStatic<AndroidJavaObject>("getInstance");
sol = bluetoothCom.Call<string>("Loooper_Connect");
}
catch (Exception ex)
{
sol = ex.ToString();
}
}
string data;
void Update () {
try
{
data = bluetoothCom.Call<string>("ReadData");
if (data.Length > 1)
{
cub.SetActive(true);
}
else
{
cub.SetActive(false);
}
}
catch(Exception ex)
{
data = ex.ToString();
}
}
}
I figured out what happend and i will explain this in case that somebody has the same problem.The inputStream from java doesn't recognize line endings, so when you read the current buffer, you take everything from there.
For example if you write every second "Hello",when you will read this the result after 2 seconds will be "HelloHello" and that creates something verry strange in the main program(unity).
SO...to resolve this problem you need to implement an BufferedReader that will read line by line the received data.
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
String d = reader.readLine();
return d;
I hope i figured out correctly what happens there.If i am wrong please correct me!
I used the following Drive API code in Android for download files from Google Drive.
GoogleSignInAccount signInAccount = GoogleSignIn.getLastSignedInAccount(GoogleDriveActivity.this);
DriveClient mDriveClient = Drive.getDriveClient(getApplicationContext(), signInAccount);
DriveResourceClient mDriveResourceClient = Drive.getDriveResourceClient(getApplicationContext(), signInAccount);
By using this code I am able to download all files i.e Docx, Doc, Image, xls, xlsx, txt, pdf etc.
but it has given the issue for the following files.
Google Doc (application/vnd.google-apps.document),
SpreadSheet (application/vnd.google-apps.spreadsheet),
Presentation file (application/vnd.google-apps.presentation)
even I tried to change metadata for the selected file by using this code but still, its shown file size is 0 (Zero) and
the extension is null.
MetadataChangeSet changeSet = new MetadataChangeSet.Builder()
.setMimeType(Constants.MIME_TYPE_DOCX)
.build();
Task<Metadata> updateMetadataTask =
mDriveResourceClient.updateMetadata(file, changeSet);
So please suggest the solution if anybody implemented it.
I tried to download Google Doc, Spreadsheet and Presentation file by using Google Drive Android API but didn’t get any proper solution for it by using Drive API.
But I have read in many places that you can download this documents using REST. Finally, I got the right solution for it when I combined both these codes i.e. Drive API Code and REST code
Here is the code for it.
First, you need to add these two lines in your build.gradle file in App module.
compile('com.google.api-client:google-api-client-android:1.23.0') {
exclude group: 'org.apache.httpcomponents'
}
compile('com.google.apis:google-api-services-drive:v3-rev107-1.23.0') {
exclude group: 'org.apache.httpcomponents'
}
Second, Initialize GoogleAccountCredential and Drive by your selected account.
private com.google.api.services.drive.Drive driveService = null;
private GoogleAccountCredential signInCredential;
private long timeStamp;
private String fileName;
// Initialize credentials and service object.
signInCredential = GoogleAccountCredential.usingOAuth2(
getApplicationContext(), Arrays.asList(SCOPES))
.setBackOff(new ExponentialBackOff());
if (!TextUtils.isEmpty(signInAccount.getAccount().name)) {
signInCredential.setSelectedAccountName(signInAccount.getAccount().name);
signInCredential.setSelectedAccount(new Account(signInAccount.getAccount().name, getPackageName()));
}
if (!TextUtils.isEmpty(signInCredential.getSelectedAccountName())) {
HttpTransport transport = AndroidHttp.newCompatibleTransport();
JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
driveService = new com.google.api.services.drive.Drive.Builder(transport, jsonFactory, signInCredential)
.setApplicationName(appName)
.build();
}
//Pass two parameters i.e fileId and mimeType which one you get when you select the file name.
public void retrieveGoogleDocContents(String fileId, String mimeType) throws IOException {
try {
File storageDir =createStorageDir();
timeStamp = System.currentTimeMillis();
//selectedFileName which one you get when you select any file from the drive, or you can use any name.
fileName = selectedFileName + "." +getFileExtension(mimeType);
File localFile = new File(storageDir, timeStamp + "_" + fileName);
if (!localFile.exists()) {
if (localFile.createNewFile())
Log.d(TAG, fileCreated);
}
AsyncTask<Void, Void, Boolean> task = new AsyncTask<Void, Void, Boolean>() {
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected Boolean doInBackground(Void... params) {
boolean isSuccess = false;
OutputStream outputStream = null;
try {
outputStream = new FileOutputStream(localFile);
com.google.api.services.drive.Drive.Files.Export request = driveService.files().export(fileId,getFileMimeType(mimeType));
request.getMediaHttpDownloader().setProgressListener(new GoogleDriveActivity.CustomProgressListener());
request.getMediaHttpDownloader().setDirectDownloadEnabled(false);
request.executeMediaAndDownloadTo(outputStream);
isSuccess = true;
} catch (UserRecoverableAuthIOException e) {
Log.d(TAG, "REQUEST_AUTHORIZATION Called");
startActivityForResult(e.getIntent(), REQUEST_AUTHORIZATION);
} catch (IOException transientEx) {
// Network or server error, try later
Log.e(TAG, transientEx.toString());
} finally {
close(outputStream);
}
return isSuccess;
}
#Override
protected void onPostExecute(Boolean isSuccess) {
Log.i(TAG, "Download Successfully :" + isSuccess);
}
};
task.execute();
} catch (IOException e){
Log.e(TAG, e.toString());
}
}
public static void close(Closeable c) {
if (c == null) return;
try {
c.close();
} catch (IOException e) {
log.log(Level.SEVERE, e.getMessage(), e);
}
}
public static File createStorageDir() {
String path = Environment.getExternalStorageDirectory() + "/" + Constants.IMAGE_DIRECTORY;
File storageDir = new File(path);
if (!storageDir.exists()) {
if (storageDir.mkdir())
Log.d(TAG, "Directory created.");
else
Log.d(TAG, "Directory is not created.");
} else
Log.d(TAG, "Directory exist.");
return storageDir;
}
Here are file mime type and extension.
public final static String ICON_DOCX = "docx";
public final static String ICON_PPTX = "pptx";
public final static String ICON_XLSX = "xlsx";
public final static String MIME_TYPE_GOOGLE_DOC = "application/vnd.google-apps.document";
public final static String MIME_TYPE_GOOGLE_SPREADSHEET = "application/vnd.google-apps.spreadsheet";
public final static String MIME_TYPE_GOOGLE_PRESENTATION = "application/vnd.google-apps.presentation";
public final static String MIME_TYPE_DOCX = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
public final static String MIME_TYPE_XLSX = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
public final static String MIME_TYPE_PPTX = "application/vnd.openxmlformats-officedocument.presentationml.presentation";
public static String getFileExtension(String fileMimeType) {
String fileExtension = Constants.ICON_PDF;
if (fileMimeType.equals(Constants.MIME_TYPE_GOOGLE_DOC))
fileExtension = Constants.ICON_DOCX;
else if (fileMimeType.equals(Constants.MIME_TYPE_GOOGLE_SPREADSHEET))
fileExtension = Constants.ICON_XLSX;
else if (fileMimeType.equals(Constants.MIME_TYPE_GOOGLE_PRESENTATION))
fileExtension = Constants.ICON_PPTX;
return fileExtension;
}
public static String getFileMimeType(String fileMimeType) {
String newMimeType = Constants.MIME_TYPE_PDF;
if (fileMimeType.equals(Constants.MIME_TYPE_GOOGLE_DOC))
newMimeType = Constants.MIME_TYPE_DOCX;
else if (fileMimeType.equals(Constants.MIME_TYPE_GOOGLE_SPREADSHEET))
newMimeType = Constants.MIME_TYPE_XLSX;
else if (fileMimeType.equals(Constants.MIME_TYPE_GOOGLE_PRESENTATION))
newMimeType = Constants.MIME_TYPE_PPTX;
return newMimeType;
}
I want to save a string from a TextArea to the device and then reload it after reopening the app. I have tried following the examples (link) but cant get it to work. Main problem arises when i try to read the file and use a StringInputConverter.
private void saveAndLoad(TextArea textArea){
File textFile = new File(ROOT_DIR,"text_file");
String text2 = textArea.getText();
String loadedFile = "none";
if (textFile.exists()){
FileClient fileClient = FileClient.create(textFile);
loadedFile = DataProvider.retrieveObject(fileClient.createObjectDataReader(
new StringInputConverter()));
}
try(FileWriter writer = new FileWriter(textFile)){
writer.write(textArea.getText());
} catch (IOException e) {
e.printStackTrace();
}
textArea.setText(text2);
}
Edit: inserted code which i tried to start reading file with and image of the error i am getting
If you check the DataProvider::retrieveObject documentation:
Retrieves an object using the specified ObjectDataReader. A GluonObservableObject is returned, that will contain the object when the read operation completed successfully.
It returns GluonObservableObject<String>, which is an observable wrapper of the string, not the string itself.
You need to get first the observable, and when the operation ends successfully you can retrieve the string:
if (textFile.exists()) {
FileClient fileClient = FileClient.create(textFile);
GluonObservableObject<String> retrieveObject = DataProvider
.retrieveObject(fileClient.createObjectDataReader(new StringInputConverter()));
retrieveObject.stateProperty().addListener((obs, ov, nv) -> {
if (ConnectState.SUCCEEDED.equals(nv)) {
loadedFile = retrieveObject.get();
}
});
}
This is a quick implementation of this functionality:
public class BasicView extends View {
private static final File ROOT_DIR;
static {
ROOT_DIR = Services.get(StorageService.class)
.flatMap(StorageService::getPrivateStorage)
.orElseThrow(() -> new RuntimeException("Error"));
}
private final File textFile;
private final TextField textField;
private String loadedFile = "none";
public BasicView(String name) {
super(name);
textFile = new File(ROOT_DIR, "text_file");
textField = new TextField();
VBox controls = new VBox(15.0, textField);
controls.setAlignment(Pos.CENTER);
controls.setPadding(new Insets(30));
setCenter(controls);
}
#Override
protected void updateAppBar(AppBar appBar) {
appBar.setNavIcon(MaterialDesignIcon.MENU.button(e -> System.out.println("Menu")));
appBar.setTitleText("Basic View");
appBar.getActionItems().add(MaterialDesignIcon.SAVE.button(e -> save()));
appBar.getActionItems().add(MaterialDesignIcon.RESTORE_PAGE.button(e -> restore()));
}
private void save() {
try (FileWriter writer = new FileWriter(textFile)) {
writer.write(textField.getText());
} catch (IOException ex) {
ex.printStackTrace();
}
}
private void restore() {
if (textFile.exists()) {
FileClient fileClient = FileClient.create(textFile);
GluonObservableObject<String> retrieveObject = DataProvider
.retrieveObject(fileClient.createObjectDataReader(new StringInputConverter()));
retrieveObject.stateProperty().addListener((obs, ov, nv) -> {
if (ConnectState.SUCCEEDED.equals(nv)) {
loadedFile = retrieveObject.get();
textField.setText(loadedFile);
}
});
}
}
}
My application is running fine but when it went in testing,it have some issue somewhere it got crashed and some issue also happens my question is how can i get all logs of my application in testing phase from device when device is not connection to computer because tester not use eclipse.
please help me to resolve this. Thanks in advance for any help.
You should use a crash report library like BugSense or Accra.
This kind of libs mimics the Android market feature of released apps, but during testing phase : you can get complete crash reports, with stack traces and other data (first time/last time of bug occurence, device model, etc.)
If you really want log. You can use the code below:
logcatProc = Runtime.getRuntime().exec(cmds);
mReader = new BufferedReader(new InputStreamReader(logcatProc.getInputStream()), 1024);
String line = null;
while ((line = mReader.readLine()) != null) {
if (line.length() == 0) {
continue;
}
if (out != null && line.contains(mPID)) {
out.write((simpleDateFormat2.format(new Date()) + " " + line + "\n").getBytes());
}
}
cmds = "logcat *:e *:i | grep \"(" + mPID + ")\"";
you can use your own grep format.
Tips: The logcat will be flushed off in a while
so you should put above code in a thread
You should try android-logging-log4j.
By the help of this Api you can record all the crash log in form of Text, Xml format and will be stored in the path you will provide like Phone memory or SD card.
So even if device is in Offline mode all the crash entry will be recorded over there. So you can get from that device and go through it for debugging.
Hope this will help you.
You have to use this code to get your own project logs.
public class LogcatFileManager {
private static LogcatFileManager INSTANCE = null ;
private static String PATH_LOGCAT;
private LogDumper mLogDumper = null ;
private int MPID;
private SimpleDateFormat simpleDateFormat1 = new SimpleDateFormat ( "yyyyMMdd" );
private SimpleDateFormat simpleDateFormat2 = new SimpleDateFormat ( "yyyy-MM-dd HH: mm: SS" );
public static LogcatFileManager getInstance () {
if (INSTANCE == null ) {
INSTANCE = new LogcatFileManager ();
}
return INSTANCE;
}
private LogcatFileManager () {
MPID = android.os.Process.myPid ();
}
private void setFolderPath (String folderPath) {
File folder = new File (folderPath);
if (! folder.exists ()) {
folder.mkdirs ();
}
if (! folder.isDirectory ())
throw new IllegalArgumentException ( "The folder path is Not a logcat Directory:" + folderPath);
PATH_LOGCAT = folderPath.endsWith("/") ? folderPath : folderPath + "/";
}
public void start (String saveDirectoy) {
setFolderPath (saveDirectoy);
if (mLogDumper == null )
mLogDumper = new LogDumper (String.valueOf (MPID), PATH_LOGCAT);
mLogDumper.start ();
}
public void stop () {
if (mLogDumper!=null ) {
mLogDumper.stopLogs();
mLogDumper = null ;
}
}
public class LogDumper extends Thread {
private Process logcatProc;
private BufferedReader MReader = null ;
private boolean mRunning = true ;
String cmds = null ;
private String MPID;
private FileOutputStream out = null ;
public LogDumper (String pid, String dir) {
MPID = pid;
try {
out = new FileOutputStream (new File(dir, "logcat-" + simpleDateFormat1.format (new Date ()) +".log"),true);
} catch (FileNotFoundException e) {
e.printStackTrace ();
}
cmds = "logcat *:e *:i | grep \"(" + MPID + ")\"";
}
public void stopLogs () {
mRunning = false ;
}
#Override
public void run() {
// TODO Auto-generated method stub
try {
logcatProc = Runtime.getRuntime().exec(cmds);
MReader = new BufferedReader ( new InputStreamReader (logcatProc.getInputStream ()), 1024 );
String line = null ;
while (mRunning && (line=MReader.readLine())!=null ) {
if (! mRunning) {
break ;
}
if (line.length () == 0 ) {
continue ;
}
if (out!=null && line.contains (MPID)) {
out.write ((simpleDateFormat2.format(new Date())+""+line+"\n").getBytes());
}
}
} catch (IOException e) {
e.printStackTrace ();
} finally {
if (logcatProc!=null ) {
logcatProc.destroy();
logcatProc = null ;
}
if (MReader!=null) {
try {
MReader.close ();
MReader = null ;
} catch (IOException e) {
e.printStackTrace ();
}
}
if (out!=null) {
try {
out.close ();
} catch (IOException e) {
e.printStackTrace ();
}
out = null ;
}
}
}
}
}
crashlytics is something you need: http://try.crashlytics.com/
You will be informed about errors, version of app that crashed etc...
Goal
Collect periodic updates of the LogCat and save (append) those chunks of text to a file on the SDcard
Problem
The Log class doesn't provide updates since a specific time-stamp
Possible solution
My plan is to periodically run code that is similar to: http://www.helloandroid.com/tutorials/reading-logs-programatically
or https://stackoverflow.com/a/9039352/550471
However, with one notable difference: use the -v time parameter to ensure that each line is time-stamped.
After each time the LogCat data is collected, the app will store the time-stamp of the last Log entry. The next time the LogCat data is collected the app will search through the text to find the time-stamp and then save the chunk of data to sdcard that was added to the Log since the specified time-stamp.
Possible problem
If the LogCat data is collected at too short periods then the CPU is busy processing a lot of 'old' data.
If the Logcat data is collected at too long periods then some data could be missed.
Is there a better way ?
This is what I came up with - it works very well when it doesn't freeze up.
As you might know, Runtime.getRuntime().exec("") has a pretty good chance of causing an ANR in Android earlier than Jelly Bean. If someone has a solution to overcome the ANR, then please share.
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import android.os.Environment;
import android.util.Log;
/*
* For (compressed) buffer sizes, see: http://elinux.org/Android_Logging_System
* buffer:main = 64KB
* buffer:radio = 64KB
* buffer:system = 64KB
* buffer:event = 256KB
*
* NOTE: the 'command' must include "-d -v time" !!
* to switch buffers, use "-b <buffer>"
*/
public class LogCatReader {
// constants
private static final String CR = "\r\n";
private static final String END_OF_DATE_TIME = "): ";
private static final int DEFAULT_SEARCH_START_INDEX = 0;
// member variables
private StringBuilder mLog;
private LogThread mLogThread = null;
private String mLastLogReadToken = "";
private String mLogCommand = "";
private int mStringCapacity;
private File mFileTarget = null;
// constructor
public LogCatReader(String command, int capacity) {
mLogCommand = command;
mStringCapacity = capacity;
}
// returns complete logcat buffer
// note: takes about 1.5sec to finish
synchronized public StringBuilder getLogComplete() {
try {
// capacity should be about 25% bigger than buffer size since the
// buffer is compressed
mLog = new StringBuilder(mStringCapacity);
// command to capture log
Process process = Runtime.getRuntime().exec(mLogCommand);
BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(process.getInputStream()));
String line;
while ((line = bufferedReader.readLine()) != null) {
// append() is costly if capacity needs to be increased, be sure
// to reserve enough in the first place
mLog.append(line + CR);
}
} catch (IOException e) {
}
return mLog;
}
public String getLogUpdatesOnly() {
String strReturn = "";
StringBuilder sbLog = getLogComplete();
try {
int iStartindex = DEFAULT_SEARCH_START_INDEX;
// if there exists a token from a previous search then use that
if (mLastLogReadToken.length() > 0) {
iStartindex = sbLog.indexOf(mLastLogReadToken);
// if string not found then start at beginning
if (iStartindex == -1) {
// start search at beginning of log
iStartindex = DEFAULT_SEARCH_START_INDEX;
}
}
int iEndindex = sbLog.length();
// if token is found then move index to the next line
if (iStartindex > DEFAULT_SEARCH_START_INDEX) {
iStartindex = sbLog.indexOf(CR, iStartindex);
if (iStartindex != -1) {
iStartindex += CR.length();
} else {
// return an empty string
iStartindex = iEndindex;
}
}
// grab the data between the start and end indices
strReturn = sbLog.substring(iStartindex, iEndindex);
// grab date/time token for next search
iStartindex = sbLog.lastIndexOf(END_OF_DATE_TIME);
if (iStartindex != -1) {
iEndindex = iStartindex;
iStartindex = sbLog.lastIndexOf(CR, iEndindex);
iStartindex += CR.length();
if (iStartindex == -1) {
// read from beginning
iStartindex = 0;
}
mLastLogReadToken = sbLog.substring(iStartindex, iEndindex);
}
} catch (Exception e) {
strReturn = "";
}
return strReturn;
}
public void startPeriodicLogCatReader(int timePeriod, String logfilename) {
if (mLogThread == null) {
mLogThread = new LogThread(timePeriod, logfilename);
mLogThread.start();
}
}
public void stopPeriodicLogCatReader() {
if (mLogThread != null) {
mLogThread.interrupt();
mLogThread = null;
}
}
private class LogThread extends Thread {
private boolean mInterrupted;
private int mTimePeriod;// in seconds
private String mLogref;
private BufferedWriter mBuffWriter = null;
public boolean mPauseLogCollection = false;
// constructor: logfilename is optional - pass null to not use
public LogThread(int timePeriod, String logfilename) {
mTimePeriod = timePeriod;
if (logfilename != null) {
File fLogFolder = new File(
Environment.getExternalStorageDirectory() + "/logfiles");
if (fLogFolder.exists() == false) {
if (fLogFolder.mkdirs() == false) {
Log.e("LogCatReader",
"Could not create "
+ fLogFolder.getAbsolutePath());
}
}
mFileTarget = new File(
Environment.getExternalStorageDirectory() + "/logfiles",
logfilename);
if (mFileTarget.exists() == false) {
try {
// file doesn't yet exist - create a fresh one !
mFileTarget.createNewFile();
} catch (IOException e) {
e.printStackTrace();
mFileTarget = null;
}
}
}
}
#Override
public void interrupt() {
mInterrupted = true;
super.interrupt();
}
#Override
public void run() {
super.run();
// initialization
mInterrupted = false;
// set up storage
if (mFileTarget != null) {
try {
mBuffWriter = new BufferedWriter(new FileWriter(
mFileTarget, true), 10240);
} catch (IOException e) {
e.printStackTrace();
}
}
while ((mInterrupted == false) && (mBuffWriter != null)) {
if (mPauseLogCollection == false) {
// read log updates
mLogref = getLogUpdatesOnly();
// save log updates to file
try {
mBuffWriter.append(mLogref);
mBuffWriter.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
if (!mInterrupted) {
try {
sleep(mTimePeriod * 1000);
} catch (InterruptedException e) {
}
}
}
if (mBuffWriter != null) {
try {
mBuffWriter.close();
mBuffWriter = null;
} catch (IOException e) {
e.printStackTrace();
}
}
}
}// end of inner class
}// end of outer class
The procedure I used to find only the updates is to capture the date and time of the very last line of a logcat and use that as the search token next time around.
To use this class, the following is an example:
LogCatReader logcatPeriodicReader = new LogCatReader("logcat -b main -d -v time", 80 * 1024);//collect "main" buffer, exit after reading logcat
logcatPeriodicReader.startPeriodicLogReader(90, "log.txt");//read logcat every 90 secs