mirror of
https://github.com/oxen-io/session-android.git
synced 2024-12-24 00:37:47 +00:00
Merge pull request #666 from hjubb/lazy_db_instantiation
Startup Time Improvements Part 1
This commit is contained in:
commit
517291c021
@ -56,7 +56,6 @@ import org.thoughtcrime.securesms.jobmanager.impl.JsonDataSerializer;
|
||||
import org.thoughtcrime.securesms.jobs.FastJobStorage;
|
||||
import org.thoughtcrime.securesms.jobs.JobManagerFactories;
|
||||
import org.thoughtcrime.securesms.logging.AndroidLogger;
|
||||
import org.thoughtcrime.securesms.logging.PersistentLogger;
|
||||
import org.thoughtcrime.securesms.logging.UncaughtExceptionLogger;
|
||||
import org.thoughtcrime.securesms.home.HomeActivity;
|
||||
import org.thoughtcrime.securesms.notifications.BackgroundPollWorker;
|
||||
@ -118,7 +117,6 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
||||
private ReadReceiptManager readReceiptManager;
|
||||
private ProfileManager profileManager;
|
||||
private ObjectGraph objectGraph;
|
||||
private PersistentLogger persistentLogger;
|
||||
public MessageNotifier messageNotifier = null;
|
||||
public Poller poller = null;
|
||||
public Broadcaster broadcaster = null;
|
||||
@ -248,10 +246,6 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
||||
return isAppVisible;
|
||||
}
|
||||
|
||||
public PersistentLogger getPersistentLogger() {
|
||||
return persistentLogger;
|
||||
}
|
||||
|
||||
// Loki
|
||||
|
||||
private void initializeSecurityProvider() {
|
||||
@ -279,8 +273,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
||||
}
|
||||
|
||||
private void initializeLogging() {
|
||||
persistentLogger = new PersistentLogger(this);
|
||||
Log.initialize(new AndroidLogger(), persistentLogger);
|
||||
Log.initialize(new AndroidLogger());
|
||||
}
|
||||
|
||||
private void initializeCrashHandling() {
|
||||
|
@ -17,16 +17,17 @@
|
||||
package org.thoughtcrime.securesms.database;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import net.sqlcipher.database.SQLiteDatabase;
|
||||
|
||||
import org.thoughtcrime.securesms.attachments.DatabaseAttachmentProvider;
|
||||
import org.thoughtcrime.securesms.crypto.AttachmentSecret;
|
||||
import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider;
|
||||
import org.thoughtcrime.securesms.crypto.DatabaseSecret;
|
||||
import org.thoughtcrime.securesms.crypto.DatabaseSecretProvider;
|
||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
||||
import org.thoughtcrime.securesms.database.LokiAPIDatabase;
|
||||
import org.thoughtcrime.securesms.database.SessionJobDatabase;
|
||||
|
||||
public class DatabaseFactory {
|
||||
|
||||
@ -35,28 +36,28 @@ public class DatabaseFactory {
|
||||
private static DatabaseFactory instance;
|
||||
|
||||
private final SQLCipherOpenHelper databaseHelper;
|
||||
private final SmsDatabase sms;
|
||||
private final MmsDatabase mms;
|
||||
private final AttachmentDatabase attachments;
|
||||
private final MediaDatabase media;
|
||||
private final ThreadDatabase thread;
|
||||
private final MmsSmsDatabase mmsSmsDatabase;
|
||||
private final DraftDatabase draftDatabase;
|
||||
private final PushDatabase pushDatabase;
|
||||
private final GroupDatabase groupDatabase;
|
||||
private final RecipientDatabase recipientDatabase;
|
||||
private final GroupReceiptDatabase groupReceiptDatabase;
|
||||
private final SearchDatabase searchDatabase;
|
||||
private final JobDatabase jobDatabase;
|
||||
private final LokiAPIDatabase lokiAPIDatabase;
|
||||
private final LokiMessageDatabase lokiMessageDatabase;
|
||||
private final LokiThreadDatabase lokiThreadDatabase;
|
||||
private final LokiUserDatabase lokiUserDatabase;
|
||||
private final LokiBackupFilesDatabase lokiBackupFilesDatabase;
|
||||
private final SessionJobDatabase sessionJobDatabase;
|
||||
private final SessionContactDatabase sessionContactDatabase;
|
||||
private final Storage storage;
|
||||
private final DatabaseAttachmentProvider attachmentProvider;
|
||||
private SmsDatabase sms;
|
||||
private MmsDatabase mms;
|
||||
private AttachmentDatabase attachments;
|
||||
private MediaDatabase media;
|
||||
private ThreadDatabase thread;
|
||||
private MmsSmsDatabase mmsSmsDatabase;
|
||||
private DraftDatabase draftDatabase;
|
||||
private PushDatabase pushDatabase;
|
||||
private GroupDatabase groupDatabase;
|
||||
private RecipientDatabase recipientDatabase;
|
||||
private GroupReceiptDatabase groupReceiptDatabase;
|
||||
private SearchDatabase searchDatabase;
|
||||
private JobDatabase jobDatabase;
|
||||
private LokiAPIDatabase lokiAPIDatabase;
|
||||
private LokiMessageDatabase lokiMessageDatabase;
|
||||
private LokiThreadDatabase lokiThreadDatabase;
|
||||
private LokiUserDatabase lokiUserDatabase;
|
||||
private LokiBackupFilesDatabase lokiBackupFilesDatabase;
|
||||
private SessionJobDatabase sessionJobDatabase;
|
||||
private SessionContactDatabase sessionContactDatabase;
|
||||
private Storage storage;
|
||||
private DatabaseAttachmentProvider attachmentProvider;
|
||||
|
||||
public static DatabaseFactory getInstance(Context context) {
|
||||
synchronized (lock) {
|
||||
@ -68,55 +69,134 @@ public class DatabaseFactory {
|
||||
}
|
||||
|
||||
public static MmsSmsDatabase getMmsSmsDatabase(Context context) {
|
||||
return getInstance(context).mmsSmsDatabase;
|
||||
DatabaseFactory factory = getInstance(context);
|
||||
synchronized (lock) {
|
||||
if (factory.mmsSmsDatabase == null) {
|
||||
factory.mmsSmsDatabase = new MmsSmsDatabase(context, factory.databaseHelper);
|
||||
}
|
||||
return factory.mmsSmsDatabase;
|
||||
}
|
||||
}
|
||||
|
||||
public static ThreadDatabase getThreadDatabase(Context context) {
|
||||
return getInstance(context).thread;
|
||||
DatabaseFactory factory = getInstance(context);
|
||||
synchronized (lock) {
|
||||
if (factory.thread == null) {
|
||||
factory.thread = new ThreadDatabase(context, factory.databaseHelper);
|
||||
}
|
||||
return factory.thread;
|
||||
}
|
||||
}
|
||||
|
||||
public static SmsDatabase getSmsDatabase(Context context) {
|
||||
return getInstance(context).sms;
|
||||
DatabaseFactory factory = getInstance(context);
|
||||
synchronized (lock) {
|
||||
if (factory.sms == null) {
|
||||
factory.sms = new SmsDatabase(context, factory.databaseHelper);
|
||||
}
|
||||
return factory.sms;
|
||||
}
|
||||
}
|
||||
|
||||
public static MmsDatabase getMmsDatabase(Context context) {
|
||||
return getInstance(context).mms;
|
||||
DatabaseFactory factory = getInstance(context);
|
||||
synchronized (lock) {
|
||||
if (factory.mms == null) {
|
||||
factory.mms = new MmsDatabase(context, factory.databaseHelper);
|
||||
}
|
||||
return factory.mms;
|
||||
}
|
||||
}
|
||||
|
||||
public static AttachmentDatabase getAttachmentDatabase(Context context) {
|
||||
return getInstance(context).attachments;
|
||||
DatabaseFactory factory = getInstance(context);
|
||||
synchronized (lock) {
|
||||
if (factory.attachments == null) {
|
||||
AttachmentSecret attachmentSecret = AttachmentSecretProvider.getInstance(context).getOrCreateAttachmentSecret();
|
||||
factory.attachments = new AttachmentDatabase(context, factory.databaseHelper, attachmentSecret);
|
||||
}
|
||||
return factory.attachments;
|
||||
}
|
||||
}
|
||||
|
||||
public static MediaDatabase getMediaDatabase(Context context) {
|
||||
return getInstance(context).media;
|
||||
DatabaseFactory factory = getInstance(context);
|
||||
synchronized (lock) {
|
||||
if (factory.media == null) {
|
||||
factory.media = new MediaDatabase(context, factory.databaseHelper);
|
||||
}
|
||||
return factory.media;
|
||||
}
|
||||
}
|
||||
|
||||
public static DraftDatabase getDraftDatabase(Context context) {
|
||||
return getInstance(context).draftDatabase;
|
||||
DatabaseFactory factory = getInstance(context);
|
||||
synchronized (lock) {
|
||||
if (factory.draftDatabase == null) {
|
||||
factory.draftDatabase = new DraftDatabase(context, factory.databaseHelper);
|
||||
}
|
||||
return factory.draftDatabase;
|
||||
}
|
||||
}
|
||||
|
||||
public static PushDatabase getPushDatabase(Context context) {
|
||||
return getInstance(context).pushDatabase;
|
||||
DatabaseFactory factory = getInstance(context);
|
||||
synchronized (lock) {
|
||||
if (factory.pushDatabase == null) {
|
||||
factory.pushDatabase = new PushDatabase(context, factory.databaseHelper);
|
||||
}
|
||||
return factory.pushDatabase;
|
||||
}
|
||||
}
|
||||
|
||||
public static GroupDatabase getGroupDatabase(Context context) {
|
||||
return getInstance(context).groupDatabase;
|
||||
DatabaseFactory factory = getInstance(context);
|
||||
synchronized (lock) {
|
||||
if (factory.groupDatabase == null) {
|
||||
factory.groupDatabase = new GroupDatabase(context, factory.databaseHelper);
|
||||
}
|
||||
return factory.groupDatabase;
|
||||
}
|
||||
}
|
||||
|
||||
public static RecipientDatabase getRecipientDatabase(Context context) {
|
||||
return getInstance(context).recipientDatabase;
|
||||
DatabaseFactory factory = getInstance(context);
|
||||
synchronized (lock) {
|
||||
if (factory.recipientDatabase == null) {
|
||||
factory.recipientDatabase = new RecipientDatabase(context, factory.databaseHelper);
|
||||
}
|
||||
return factory.recipientDatabase;
|
||||
}
|
||||
}
|
||||
|
||||
public static GroupReceiptDatabase getGroupReceiptDatabase(Context context) {
|
||||
return getInstance(context).groupReceiptDatabase;
|
||||
DatabaseFactory factory = getInstance(context);
|
||||
synchronized (lock) {
|
||||
if (factory.groupReceiptDatabase == null) {
|
||||
factory.groupReceiptDatabase = new GroupReceiptDatabase(context, factory.databaseHelper);
|
||||
}
|
||||
return factory.groupReceiptDatabase;
|
||||
}
|
||||
}
|
||||
|
||||
public static SearchDatabase getSearchDatabase(Context context) {
|
||||
return getInstance(context).searchDatabase;
|
||||
DatabaseFactory factory = getInstance(context);
|
||||
synchronized (lock) {
|
||||
if (factory.searchDatabase == null) {
|
||||
factory.searchDatabase = new SearchDatabase(context, factory.databaseHelper);
|
||||
}
|
||||
return factory.searchDatabase;
|
||||
}
|
||||
}
|
||||
|
||||
public static JobDatabase getJobDatabase(Context context) {
|
||||
return getInstance(context).jobDatabase;
|
||||
DatabaseFactory factory = getInstance(context);
|
||||
synchronized (lock) {
|
||||
if (factory.jobDatabase == null) {
|
||||
factory.jobDatabase = new JobDatabase(context, factory.databaseHelper);
|
||||
}
|
||||
return factory.jobDatabase;
|
||||
}
|
||||
}
|
||||
|
||||
public static SQLiteDatabase getBackupDatabase(Context context) {
|
||||
@ -125,41 +205,95 @@ public class DatabaseFactory {
|
||||
|
||||
// region Loki
|
||||
public static LokiAPIDatabase getLokiAPIDatabase(Context context) {
|
||||
return getInstance(context).lokiAPIDatabase;
|
||||
DatabaseFactory factory = getInstance(context);
|
||||
synchronized (lock) {
|
||||
if (factory.lokiAPIDatabase == null) {
|
||||
factory.lokiAPIDatabase = new LokiAPIDatabase(context, factory.databaseHelper);
|
||||
}
|
||||
return factory.lokiAPIDatabase;
|
||||
}
|
||||
}
|
||||
|
||||
public static LokiMessageDatabase getLokiMessageDatabase(Context context) {
|
||||
return getInstance(context).lokiMessageDatabase;
|
||||
DatabaseFactory factory = getInstance(context);
|
||||
synchronized (lock) {
|
||||
if (factory.lokiMessageDatabase == null) {
|
||||
factory.lokiMessageDatabase = new LokiMessageDatabase(context, factory.databaseHelper);
|
||||
}
|
||||
return factory.lokiMessageDatabase;
|
||||
}
|
||||
}
|
||||
|
||||
public static LokiThreadDatabase getLokiThreadDatabase(Context context) {
|
||||
return getInstance(context).lokiThreadDatabase;
|
||||
DatabaseFactory factory = getInstance(context);
|
||||
synchronized (lock) {
|
||||
if (factory.lokiThreadDatabase == null) {
|
||||
factory.lokiThreadDatabase = new LokiThreadDatabase(context, factory.databaseHelper);
|
||||
}
|
||||
return factory.lokiThreadDatabase;
|
||||
}
|
||||
}
|
||||
|
||||
public static LokiUserDatabase getLokiUserDatabase(Context context) {
|
||||
return getInstance(context).lokiUserDatabase;
|
||||
DatabaseFactory factory = getInstance(context);
|
||||
synchronized (lock) {
|
||||
if (factory.lokiUserDatabase == null) {
|
||||
factory.lokiUserDatabase = new LokiUserDatabase(context, factory.databaseHelper);
|
||||
}
|
||||
return factory.lokiUserDatabase;
|
||||
}
|
||||
}
|
||||
|
||||
public static LokiBackupFilesDatabase getLokiBackupFilesDatabase(Context context) {
|
||||
return getInstance(context).lokiBackupFilesDatabase;
|
||||
DatabaseFactory factory = getInstance(context);
|
||||
synchronized (lock) {
|
||||
if (factory.lokiBackupFilesDatabase == null) {
|
||||
factory.lokiBackupFilesDatabase = new LokiBackupFilesDatabase(context, factory.databaseHelper);
|
||||
}
|
||||
return factory.lokiBackupFilesDatabase;
|
||||
}
|
||||
}
|
||||
|
||||
public static SessionJobDatabase getSessionJobDatabase(Context context) {
|
||||
return getInstance(context).sessionJobDatabase;
|
||||
DatabaseFactory factory = getInstance(context);
|
||||
synchronized (lock) {
|
||||
if (factory.sessionJobDatabase == null) {
|
||||
factory.sessionJobDatabase = new SessionJobDatabase(context, factory.databaseHelper);
|
||||
}
|
||||
return factory.sessionJobDatabase;
|
||||
}
|
||||
}
|
||||
|
||||
public static SessionContactDatabase getSessionContactDatabase(Context context) {
|
||||
return getInstance(context).sessionContactDatabase;
|
||||
DatabaseFactory factory = getInstance(context);
|
||||
synchronized (lock) {
|
||||
if (factory.sessionContactDatabase == null) {
|
||||
factory.sessionContactDatabase = new SessionContactDatabase(context, factory.databaseHelper);
|
||||
}
|
||||
return factory.sessionContactDatabase;
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region Refactor
|
||||
public static Storage getStorage(Context context) {
|
||||
return getInstance(context).storage;
|
||||
DatabaseFactory factory = getInstance(context);
|
||||
synchronized (lock) {
|
||||
if (factory.storage == null) {
|
||||
factory.storage = new Storage(context, factory.databaseHelper);
|
||||
}
|
||||
return factory.storage;
|
||||
}
|
||||
}
|
||||
|
||||
public static DatabaseAttachmentProvider getAttachmentProvider(Context context) {
|
||||
return getInstance(context).attachmentProvider;
|
||||
DatabaseFactory factory = getInstance(context);
|
||||
synchronized (lock) {
|
||||
if (factory.attachmentProvider == null) {
|
||||
factory.attachmentProvider = new DatabaseAttachmentProvider(context, factory.databaseHelper);
|
||||
}
|
||||
return factory.attachmentProvider;
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
|
||||
@ -172,31 +306,8 @@ public class DatabaseFactory {
|
||||
SQLiteDatabase.loadLibs(context);
|
||||
|
||||
DatabaseSecret databaseSecret = new DatabaseSecretProvider(context).getOrCreateDatabaseSecret();
|
||||
AttachmentSecret attachmentSecret = AttachmentSecretProvider.getInstance(context).getOrCreateAttachmentSecret();
|
||||
|
||||
this.databaseHelper = new SQLCipherOpenHelper(context, databaseSecret);
|
||||
this.sms = new SmsDatabase(context, databaseHelper);
|
||||
this.mms = new MmsDatabase(context, databaseHelper);
|
||||
this.attachments = new AttachmentDatabase(context, databaseHelper, attachmentSecret);
|
||||
this.media = new MediaDatabase(context, databaseHelper);
|
||||
this.thread = new ThreadDatabase(context, databaseHelper);
|
||||
this.mmsSmsDatabase = new MmsSmsDatabase(context, databaseHelper);
|
||||
this.draftDatabase = new DraftDatabase(context, databaseHelper);
|
||||
this.pushDatabase = new PushDatabase(context, databaseHelper);
|
||||
this.groupDatabase = new GroupDatabase(context, databaseHelper);
|
||||
this.recipientDatabase = new RecipientDatabase(context, databaseHelper);
|
||||
this.groupReceiptDatabase = new GroupReceiptDatabase(context, databaseHelper);
|
||||
this.searchDatabase = new SearchDatabase(context, databaseHelper);
|
||||
this.jobDatabase = new JobDatabase(context, databaseHelper);
|
||||
this.lokiAPIDatabase = new LokiAPIDatabase(context, databaseHelper);
|
||||
this.lokiMessageDatabase = new LokiMessageDatabase(context, databaseHelper);
|
||||
this.lokiThreadDatabase = new LokiThreadDatabase(context, databaseHelper);
|
||||
this.lokiUserDatabase = new LokiUserDatabase(context, databaseHelper);
|
||||
this.lokiBackupFilesDatabase = new LokiBackupFilesDatabase(context, databaseHelper);
|
||||
this.storage = new Storage(context, databaseHelper);
|
||||
this.attachmentProvider = new DatabaseAttachmentProvider(context, databaseHelper);
|
||||
this.sessionJobDatabase = new SessionJobDatabase(context, databaseHelper);
|
||||
this.sessionContactDatabase = new SessionContactDatabase(context, databaseHelper);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,244 +0,0 @@
|
||||
package org.thoughtcrime.securesms.logging;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.annotation.AnyThread;
|
||||
import androidx.annotation.WorkerThread;
|
||||
|
||||
import org.session.libsignal.utilities.NoExternalStorageException;
|
||||
|
||||
import org.session.libsignal.utilities.ListenableFuture;
|
||||
import org.session.libsignal.utilities.SettableFuture;
|
||||
import org.session.libsignal.utilities.Log;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
public class PersistentLogger extends Log.Logger {
|
||||
|
||||
private static final String TAG = PersistentLogger.class.getSimpleName();
|
||||
|
||||
private static final String LOG_V = "V";
|
||||
private static final String LOG_D = "D";
|
||||
private static final String LOG_I = "I";
|
||||
private static final String LOG_W = "W";
|
||||
private static final String LOG_E = "E";
|
||||
private static final String LOG_WTF = "A";
|
||||
|
||||
private static final String LOG_DIRECTORY = "log";
|
||||
private static final String FILENAME_PREFIX = "log-";
|
||||
private static final int MAX_LOG_FILES = 5;
|
||||
private static final int MAX_LOG_SIZE = 300 * 1024;
|
||||
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS zzz");
|
||||
|
||||
private final Context context;
|
||||
private final Executor executor;
|
||||
private final byte[] secret;
|
||||
|
||||
private LogFile.Writer writer;
|
||||
|
||||
public PersistentLogger(Context context) {
|
||||
this.context = context.getApplicationContext();
|
||||
this.secret = LogSecretProvider.getOrCreateAttachmentSecret(context);
|
||||
this.executor = Executors.newSingleThreadExecutor(r -> {
|
||||
Thread thread = new Thread(r, "PersistentLogger");
|
||||
thread.setPriority(Thread.MIN_PRIORITY);
|
||||
return thread;
|
||||
});
|
||||
|
||||
executor.execute(this::initializeWriter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void v(String tag, String message, Throwable t) {
|
||||
write(LOG_V, tag, message, t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void d(String tag, String message, Throwable t) {
|
||||
write(LOG_D, tag, message, t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void i(String tag, String message, Throwable t) {
|
||||
write(LOG_I, tag, message, t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void w(String tag, String message, Throwable t) {
|
||||
write(LOG_W, tag, message, t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void e(String tag, String message, Throwable t) {
|
||||
write(LOG_E, tag, message, t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void wtf(String tag, String message, Throwable t) {
|
||||
write(LOG_WTF, tag, message, t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void blockUntilAllWritesFinished() {
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
|
||||
executor.execute(latch::countDown);
|
||||
|
||||
try {
|
||||
latch.await();
|
||||
} catch (InterruptedException e) {
|
||||
android.util.Log.w(TAG, "Failed to wait for all writes.");
|
||||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
public ListenableFuture<String> getLogs() {
|
||||
final SettableFuture<String> future = new SettableFuture<>();
|
||||
|
||||
executor.execute(() -> {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
try {
|
||||
File[] logs = getSortedLogFiles();
|
||||
for (int i = logs.length - 1; i >= 0; i--) {
|
||||
try {
|
||||
LogFile.Reader reader = new LogFile.Reader(secret, logs[i]);
|
||||
builder.append(reader.readAll());
|
||||
} catch (IOException e) {
|
||||
android.util.Log.w(TAG, "Failed to read log at index " + i + ". Removing reference.");
|
||||
logs[i].delete();
|
||||
}
|
||||
}
|
||||
|
||||
future.set(builder.toString());
|
||||
} catch (NoExternalStorageException e) {
|
||||
future.setException(e);
|
||||
}
|
||||
});
|
||||
|
||||
return future;
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private void initializeWriter() {
|
||||
try {
|
||||
writer = new LogFile.Writer(secret, getOrCreateActiveLogFile());
|
||||
} catch (NoExternalStorageException | IOException e) {
|
||||
android.util.Log.e(TAG, "Failed to initialize writer.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
private void write(String level, String tag, String message, Throwable t) {
|
||||
executor.execute(() -> {
|
||||
try {
|
||||
if (writer == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (writer.getLogSize() >= MAX_LOG_SIZE) {
|
||||
writer.close();
|
||||
writer = new LogFile.Writer(secret, createNewLogFile());
|
||||
trimLogFilesOverMax();
|
||||
}
|
||||
|
||||
for (String entry : buildLogEntries(level, tag, message, t)) {
|
||||
writer.writeEntry(entry);
|
||||
}
|
||||
|
||||
} catch (NoExternalStorageException e) {
|
||||
android.util.Log.w(TAG, "Cannot persist logs.", e);
|
||||
} catch (IOException e) {
|
||||
android.util.Log.w(TAG, "Failed to write line. Deleting all logs and starting over.");
|
||||
deleteAllLogs();
|
||||
initializeWriter();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void trimLogFilesOverMax() throws NoExternalStorageException {
|
||||
File[] logs = getSortedLogFiles();
|
||||
if (logs.length > MAX_LOG_FILES) {
|
||||
for (int i = MAX_LOG_FILES; i < logs.length; i++) {
|
||||
logs[i].delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteAllLogs() {
|
||||
try {
|
||||
File[] logs = getSortedLogFiles();
|
||||
for (File log : logs) {
|
||||
log.delete();
|
||||
}
|
||||
} catch (NoExternalStorageException e) {
|
||||
android.util.Log.w(TAG, "Was unable to delete logs.", e);
|
||||
}
|
||||
}
|
||||
|
||||
private File getOrCreateActiveLogFile() throws NoExternalStorageException {
|
||||
File[] logs = getSortedLogFiles();
|
||||
if (logs.length > 0) {
|
||||
return logs[0];
|
||||
}
|
||||
|
||||
return createNewLogFile();
|
||||
}
|
||||
|
||||
private File createNewLogFile() throws NoExternalStorageException {
|
||||
return new File(getOrCreateLogDirectory(), FILENAME_PREFIX + System.currentTimeMillis());
|
||||
}
|
||||
|
||||
private File[] getSortedLogFiles() throws NoExternalStorageException {
|
||||
File[] logs = getOrCreateLogDirectory().listFiles();
|
||||
if (logs != null) {
|
||||
Arrays.sort(logs, (o1, o2) -> o2.getName().compareTo(o1.getName()));
|
||||
return logs;
|
||||
}
|
||||
return new File[0];
|
||||
}
|
||||
|
||||
private File getOrCreateLogDirectory() throws NoExternalStorageException {
|
||||
File logDir = new File(context.getCacheDir(), LOG_DIRECTORY);
|
||||
if (!logDir.exists() && !logDir.mkdir()) {
|
||||
throw new NoExternalStorageException("Unable to create log directory.");
|
||||
}
|
||||
|
||||
return logDir;
|
||||
}
|
||||
|
||||
private List<String> buildLogEntries(String level, String tag, String message, Throwable t) {
|
||||
List<String> entries = new LinkedList<>();
|
||||
Date date = new Date();
|
||||
|
||||
entries.add(buildEntry(level, tag, message, date));
|
||||
|
||||
if (t != null) {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
t.printStackTrace(new PrintStream(outputStream));
|
||||
|
||||
String trace = new String(outputStream.toByteArray());
|
||||
String[] lines = trace.split("\\n");
|
||||
|
||||
for (String line : lines) {
|
||||
entries.add(buildEntry(level, tag, line, date));
|
||||
}
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
private String buildEntry(String level, String tag, String message, Date date) {
|
||||
return DATE_FORMAT.format(date) + ' ' + level + ' ' + tag + ": " + message;
|
||||
}
|
||||
}
|
@ -4,6 +4,10 @@ apply plugin: 'kotlin-android'
|
||||
android {
|
||||
compileSdkVersion androidCompileSdkVersion
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion androidMinimumSdkVersion
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
|
Loading…
x
Reference in New Issue
Block a user