Simplify operations watcher

pull/152/head
M66B 6 years ago
parent 4b7caeb219
commit 80c28636e6

@ -80,7 +80,6 @@ import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationCompat;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import androidx.lifecycle.LifecycleService; import androidx.lifecycle.LifecycleService;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.Observer; import androidx.lifecycle.Observer;
import static android.os.Process.THREAD_PRIORITY_BACKGROUND; import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
@ -493,7 +492,7 @@ public class ServiceSynchronize extends LifecycleService {
final Map<EntityFolder, Folder> folders = new HashMap<>(); final Map<EntityFolder, Folder> folders = new HashMap<>();
List<Thread> idlers = new ArrayList<>(); List<Thread> idlers = new ArrayList<>();
List<Handler> handlers = new ArrayList<>(); List<TwoStateOwner> owners = new ArrayList<>();
try { try {
// Listen for store events // Listen for store events
istore.addStoreListener(new StoreListener() { istore.addStoreListener(new StoreListener() {
@ -815,124 +814,109 @@ public class ServiceSynchronize extends LifecycleService {
} else } else
folders.put(folder, null); folders.put(folder, null);
// Observe operations final TwoStateOwner owner = new TwoStateOwner();
Handler handler = new Handler(getMainLooper()) {
private List<Long> waiting = new ArrayList<>();
private List<Long> handling = new ArrayList<>();
private LiveData<List<EntityOperation>> liveOperations;
new Handler(getMainLooper()).post(new Runnable() {
@Override @Override
public void handleMessage(android.os.Message msg) { public void run() {
Log.i(account.name + "/" + folder.name + " observe=" + msg.what); db.operation().liveOperations(folder.id).observe(owner, new Observer<List<EntityOperation>>() {
try { private List<Long> waiting = new ArrayList<>();
if (msg.what == 0) { private List<Long> handling = new ArrayList<>();
liveOperations.removeObserver(observer); private final ExecutorService folderExecutor = Executors.newSingleThreadExecutor(Helper.backgroundThreadFactory);
handling.clear(); private final PowerManager.WakeLock wlFolder = pm.newWakeLock(
} else { PowerManager.PARTIAL_WAKE_LOCK, BuildConfig.APPLICATION_ID + ":folder." + folder.id);
liveOperations = db.operation().liveOperations(folder.id);
liveOperations.observe(ServiceSynchronize.this, observer); @Override
} public void onChanged(final List<EntityOperation> operations) {
} catch (Throwable ex) { boolean process = false;
Log.e(ex); List<Long> ops = new ArrayList<>();
} List<Long> waits = new ArrayList<>();
} for (EntityOperation op : operations) {
if (EntityOperation.WAIT.equals(op.name))
private Observer<List<EntityOperation>> observer = new Observer<List<EntityOperation>>() { waits.add(op.id);
private final ExecutorService folderExecutor = Executors.newSingleThreadExecutor(Helper.backgroundThreadFactory); if (!handling.contains(op.id))
private final PowerManager.WakeLock wlFolder = pm.newWakeLock( process = true;
PowerManager.PARTIAL_WAKE_LOCK, BuildConfig.APPLICATION_ID + ":folder." + folder.id); ops.add(op.id);
@Override
public void onChanged(final List<EntityOperation> operations) {
boolean process = false;
List<Long> ops = new ArrayList<>();
List<Long> waits = new ArrayList<>();
for (EntityOperation op : operations) {
if (EntityOperation.WAIT.equals(op.name))
waits.add(op.id);
if (!handling.contains(op.id))
process = true;
ops.add(op.id);
}
for (long wait : waits)
if (!waiting.contains(wait)) {
Log.i(folder.name + " not waiting anymore");
process = true;
break;
} }
waiting = waits; for (long wait : waits)
handling = ops; if (!waiting.contains(wait)) {
Log.i(folder.name + " not waiting anymore");
if (handling.size() > 0 && process) { process = true;
Log.i(folder.name + " operations=" + operations.size()); break;
(folder.poll ? pollExecutor : folderExecutor).submit(new Runnable() { }
@Override waiting = waits;
public void run() { handling = ops;
try {
wlFolder.acquire(); if (handling.size() > 0 && process) {
Log.i(folder.name + " process"); Log.i(folder.name + " operations=" + operations.size());
(folder.poll ? pollExecutor : folderExecutor).submit(new Runnable() {
// Get folder @Override
Folder ifolder = folders.get(folder); // null when polling public void run() {
final boolean shouldClose = (ifolder == null);
try { try {
Log.i(folder.name + " run " + (shouldClose ? "offline" : "online")); wlFolder.acquire();
Log.i(folder.name + " process");
if (ifolder == null) { // Get folder
// Prevent unnecessary folder connections Folder ifolder = folders.get(folder); // null when polling
if (db.operation().getOperationCount(folder.id, null) == 0) final boolean shouldClose = (ifolder == null);
return;
db.folder().setFolderState(folder.id, "connecting"); try {
Log.i(folder.name + " run " + (shouldClose ? "offline" : "online"));
ifolder = istore.getFolder(folder.name); if (ifolder == null) {
ifolder.open(Folder.READ_WRITE); // Prevent unnecessary folder connections
if (db.operation().getOperationCount(folder.id, null) == 0)
return;
db.folder().setFolderState(folder.id, "connected"); db.folder().setFolderState(folder.id, "connecting");
db.folder().setFolderError(folder.id, null); ifolder = istore.getFolder(folder.name);
} ifolder.open(Folder.READ_WRITE);
Core.processOperations(ServiceSynchronize.this, db.folder().setFolderState(folder.id, "connected");
account, folder,
isession, istore, ifolder,
state);
} catch (Throwable ex) { db.folder().setFolderError(folder.id, null);
Log.e(folder.name, ex); }
Core.reportError(ServiceSynchronize.this, account, folder, ex);
db.folder().setFolderError(folder.id, Helper.formatThrowable(ex, true)); Core.processOperations(ServiceSynchronize.this,
state.error(); account, folder,
} finally { isession, istore, ifolder,
if (shouldClose) { state);
if (ifolder != null && ifolder.isOpen()) {
db.folder().setFolderState(folder.id, "closing"); } catch (Throwable ex) {
try { Log.e(folder.name, ex);
ifolder.close(false); Core.reportError(ServiceSynchronize.this, account, folder, ex);
} catch (MessagingException ex) { db.folder().setFolderError(folder.id, Helper.formatThrowable(ex, true));
Log.w(folder.name, ex); state.error();
} finally {
if (shouldClose) {
if (ifolder != null && ifolder.isOpen()) {
db.folder().setFolderState(folder.id, "closing");
try {
ifolder.close(false);
} catch (MessagingException ex) {
Log.w(folder.name, ex);
}
} }
if (folder.synchronize && (folder.poll || !capIdle))
db.folder().setFolderState(folder.id, "waiting");
else
db.folder().setFolderState(folder.id, null);
} }
if (folder.synchronize && (folder.poll || !capIdle))
db.folder().setFolderState(folder.id, "waiting");
else
db.folder().setFolderState(folder.id, null);
} }
} finally {
wlFolder.release();
} }
} finally {
wlFolder.release();
} }
} });
}); }
} }
} });
}; }
}; });
// Start watching for operations owner.start();
handler.sendEmptyMessage(1); owners.add(owner);
handlers.add(handler);
} }
// Keep alive alarm receiver // Keep alive alarm receiver
@ -1014,9 +998,9 @@ public class ServiceSynchronize extends LifecycleService {
db.account().setAccountError(account.id, Helper.formatThrowable(ex)); db.account().setAccountError(account.id, Helper.formatThrowable(ex));
} finally { } finally {
// Stop watching for operations // Stop watching for operations
for (Handler handler : handlers) for (TwoStateOwner owner : owners)
handler.sendEmptyMessage(0); owner.stop();
handlers.clear(); owners.clear();
EntityLog.log(this, account.name + " closing"); EntityLog.log(this, account.name + " closing");
db.account().setAccountState(account.id, "closing"); db.account().setAccountState(account.id, "closing");

@ -0,0 +1,28 @@
package eu.faircode.email;
import androidx.annotation.NonNull;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LifecycleRegistry;
public class TwoStateOwner implements LifecycleOwner {
private LifecycleRegistry registry;
TwoStateOwner() {
registry = new LifecycleRegistry(this);
}
void start() {
registry.handleLifecycleEvent(Lifecycle.Event.ON_START);
}
void stop() {
registry.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
}
@NonNull
@Override
public Lifecycle getLifecycle() {
return registry;
}
}
Loading…
Cancel
Save