Close boundary on destroy

pull/160/head
M66B 6 years ago
parent e09bd95cc6
commit 47da32aa97

@ -71,15 +71,7 @@ public class BoundaryCallbackMessages extends PagedList.BoundaryCallback<TupleMe
private Handler handler; private Handler handler;
private ExecutorService executor = Executors.newSingleThreadExecutor(Helper.backgroundThreadFactory); private ExecutorService executor = Executors.newSingleThreadExecutor(Helper.backgroundThreadFactory);
private boolean destroyed = false; private State state;
private boolean error = false;
private int index = 0;
private List<Long> messages = null;
private MailService iservice = null;
private IMAPFolder ifolder = null;
private Message[] imessages = null;
interface IBoundaryCallbackMessages { interface IBoundaryCallbackMessages {
void onLoading(); void onLoading();
@ -100,11 +92,7 @@ public class BoundaryCallbackMessages extends PagedList.BoundaryCallback<TupleMe
void setCallback(IBoundaryCallbackMessages intf) { void setCallback(IBoundaryCallbackMessages intf) {
this.handler = new Handler(); this.handler = new Handler();
this.intf = intf; this.intf = intf;
this.state = new State();
if (imessages == null)
index = 0;
else
index = imessages.length - 1;
} }
@Override @Override
@ -114,19 +102,21 @@ public class BoundaryCallbackMessages extends PagedList.BoundaryCallback<TupleMe
} }
@Override @Override
public void onItemAtEndLoaded(final TupleMessageEx itemAtEnd) { public void onItemAtEndLoaded(@NonNull final TupleMessageEx itemAtEnd) {
Log.i("Boundary at end"); Log.i("Boundary at end");
queue_load(false); queue_load(false);
} }
private void queue_load(final boolean zero) { private void queue_load(final boolean zero) {
final State state = this.state;
executor.submit(new Runnable() { executor.submit(new Runnable() {
private int fetched; private int fetched;
@Override @Override
public void run() { public void run() {
try { try {
if (destroyed || error) if (state.destroyed || state.error)
return; return;
fetched = 0; fetched = 0;
@ -138,9 +128,9 @@ public class BoundaryCallbackMessages extends PagedList.BoundaryCallback<TupleMe
} }
}); });
if (server) if (server)
fetched = load_server(); fetched = load_server(state);
else else
fetched = load_device(); fetched = load_device(state);
} catch (final Throwable ex) { } catch (final Throwable ex) {
Log.e("Boundary", ex); Log.e("Boundary", ex);
if (intf != null) if (intf != null)
@ -163,12 +153,12 @@ public class BoundaryCallbackMessages extends PagedList.BoundaryCallback<TupleMe
}); });
} }
private int load_device() { private int load_device(State state) {
DB db = DB.getInstance(context); DB db = DB.getInstance(context);
if (messages == null) { if (state.messages == null) {
messages = db.message().getMessageIdsByFolder(folder); state.messages = db.message().getMessageIdsByFolder(folder);
Log.i("Boundary device folder=" + folder + " query=" + query + " messages=" + messages.size()); Log.i("Boundary device folder=" + folder + " query=" + query + " messages=" + state.messages.size());
} }
int found = 0; int found = 0;
@ -176,10 +166,10 @@ public class BoundaryCallbackMessages extends PagedList.BoundaryCallback<TupleMe
db.beginTransaction(); db.beginTransaction();
String find = (TextUtils.isEmpty(query) ? null : query.toLowerCase()); String find = (TextUtils.isEmpty(query) ? null : query.toLowerCase());
for (int i = index; i < messages.size() && found < pageSize && !destroyed; i++) { for (int i = state.index; i < state.messages.size() && found < pageSize && !state.destroyed; i++) {
index = i + 1; state.index = i + 1;
EntityMessage message = db.message().getMessage(messages.get(i)); EntityMessage message = db.message().getMessage(state.messages.get(i));
if (message == null) if (message == null)
continue; continue;
@ -243,7 +233,7 @@ public class BoundaryCallbackMessages extends PagedList.BoundaryCallback<TupleMe
return found; return found;
} }
private int load_server() throws MessagingException, IOException { private int load_server(State state) throws MessagingException, IOException {
DB db = DB.getInstance(context); DB db = DB.getInstance(context);
final EntityFolder browsable = db.folder().getBrowsableFolder(folder, query != null); final EntityFolder browsable = db.folder().getBrowsableFolder(folder, query != null);
@ -254,7 +244,7 @@ public class BoundaryCallbackMessages extends PagedList.BoundaryCallback<TupleMe
if (account == null) if (account == null)
return 0; return 0;
if (imessages == null) if (state.imessages == null)
try { try {
// Check connectivity // Check connectivity
if (!ConnectionHelper.getNetworkState(context).isSuitable()) if (!ConnectionHelper.getNetworkState(context).isSuitable())
@ -264,28 +254,28 @@ public class BoundaryCallbackMessages extends PagedList.BoundaryCallback<TupleMe
boolean debug = (prefs.getBoolean("debug", false) || BuildConfig.BETA_RELEASE); boolean debug = (prefs.getBoolean("debug", false) || BuildConfig.BETA_RELEASE);
Log.i("Boundary server connecting account=" + account.name); Log.i("Boundary server connecting account=" + account.name);
iservice = new MailService(context, account.getProtocol(), account.realm, account.insecure, debug); state.iservice = new MailService(context, account.getProtocol(), account.realm, account.insecure, debug);
iservice.setPartialFetch(account.partial_fetch); state.iservice.setPartialFetch(account.partial_fetch);
iservice.setSeparateStoreConnection(); state.iservice.setSeparateStoreConnection();
iservice.connect(account); state.iservice.connect(account);
Log.i("Boundary server opening folder=" + browsable.name); Log.i("Boundary server opening folder=" + browsable.name);
ifolder = (IMAPFolder) iservice.getStore().getFolder(browsable.name); state.ifolder = (IMAPFolder) state.iservice.getStore().getFolder(browsable.name);
ifolder.open(Folder.READ_WRITE); state.ifolder.open(Folder.READ_WRITE);
Log.i("Boundary server query=" + query); Log.i("Boundary server query=" + query);
if (query == null) if (query == null)
imessages = ifolder.getMessages(); state.imessages = state.ifolder.getMessages();
else if (query.startsWith(context.getString(R.string.title_search_special_prefix) + ":")) { else if (query.startsWith(context.getString(R.string.title_search_special_prefix) + ":")) {
String special = query.split(":")[1]; String special = query.split(":")[1];
if (context.getString(R.string.title_search_special_unseen).equals(special)) if (context.getString(R.string.title_search_special_unseen).equals(special))
imessages = ifolder.search(new FlagTerm(new Flags(Flags.Flag.SEEN), false)); state.imessages = state.ifolder.search(new FlagTerm(new Flags(Flags.Flag.SEEN), false));
else if (context.getString(R.string.title_search_special_flagged).equals(special)) else if (context.getString(R.string.title_search_special_flagged).equals(special))
imessages = ifolder.search(new FlagTerm(new Flags(Flags.Flag.FLAGGED), true)); state.imessages = state.ifolder.search(new FlagTerm(new Flags(Flags.Flag.FLAGGED), true));
else else
imessages = new Message[0]; state.imessages = new Message[0];
} else { } else {
Object result = ifolder.doCommand(new IMAPFolder.ProtocolCommand() { Object result = state.ifolder.doCommand(new IMAPFolder.ProtocolCommand() {
@Override @Override
public Object doCommand(IMAPProtocol protocol) { public Object doCommand(IMAPProtocol protocol) {
// Yahoo! does not support keyword search, but uses the flags $Forwarded $Junk $NotJunk // Yahoo! does not support keyword search, but uses the flags $Forwarded $Junk $NotJunk
@ -299,7 +289,7 @@ public class BoundaryCallbackMessages extends PagedList.BoundaryCallback<TupleMe
try { try {
// https://tools.ietf.org/html/rfc3501#section-6.4.4 // https://tools.ietf.org/html/rfc3501#section-6.4.4
Argument arg = new Argument(); Argument arg = new Argument();
if (query.startsWith("raw:") && iservice.getStore().hasCapability("X-GM-EXT-1")) { if (query.startsWith("raw:") && state.iservice.getStore().hasCapability("X-GM-EXT-1")) {
// https://support.google.com/mail/answer/7190 // https://support.google.com/mail/answer/7190
// https://developers.google.com/gmail/imap/imap-extensions#extension_of_the_search_command_x-gm-raw // https://developers.google.com/gmail/imap/imap-extensions#extension_of_the_search_command_x-gm-raw
arg.writeAtom("X-GM-RAW"); arg.writeAtom("X-GM-RAW");
@ -342,7 +332,7 @@ public class BoundaryCallbackMessages extends PagedList.BoundaryCallback<TupleMe
Message[] imessages = new Message[msgnums.size()]; Message[] imessages = new Message[msgnums.size()];
for (int i = 0; i < msgnums.size(); i++) for (int i = 0; i < msgnums.size(); i++)
imessages[i] = ifolder.getMessage(msgnums.get(i)); imessages[i] = state.ifolder.getMessage(msgnums.get(i));
return imessages; return imessages;
} else { } else {
@ -367,7 +357,7 @@ public class BoundaryCallbackMessages extends PagedList.BoundaryCallback<TupleMe
term = new OrTerm(term, new FlagTerm( term = new OrTerm(term, new FlagTerm(
new Flags(Helper.sanitizeKeyword(search)), true)); new Flags(Helper.sanitizeKeyword(search)), true));
return ifolder.search(term); return state.ifolder.search(term);
} }
} catch (MessagingException ex) { } catch (MessagingException ex) {
@ -380,13 +370,13 @@ public class BoundaryCallbackMessages extends PagedList.BoundaryCallback<TupleMe
if (result instanceof MessagingException) if (result instanceof MessagingException)
throw (MessagingException) result; throw (MessagingException) result;
imessages = (Message[]) result; state.imessages = (Message[]) result;
} }
Log.i("Boundary server found messages=" + imessages.length); Log.i("Boundary server found messages=" + state.imessages.length);
index = imessages.length - 1; state.index = state.imessages.length - 1;
} catch (Throwable ex) { } catch (Throwable ex) {
error = true; state.error = true;
if (ex instanceof FolderClosedException) if (ex instanceof FolderClosedException)
Log.w("Search", ex); Log.w("Search", ex);
else else
@ -397,11 +387,11 @@ public class BoundaryCallbackMessages extends PagedList.BoundaryCallback<TupleMe
List<EntityRule> rules = db.rule().getEnabledRules(browsable.id); List<EntityRule> rules = db.rule().getEnabledRules(browsable.id);
int found = 0; int found = 0;
while (index >= 0 && found < pageSize && !destroyed) { while (state.index >= 0 && found < pageSize && !state.destroyed) {
Log.i("Boundary server index=" + index); Log.i("Boundary server index=" + state.index);
int from = Math.max(0, index - (pageSize - found) + 1); int from = Math.max(0, state.index - (pageSize - found) + 1);
Message[] isub = Arrays.copyOfRange(imessages, from, index + 1); Message[] isub = Arrays.copyOfRange(state.imessages, from, state.index + 1);
index -= (pageSize - found); state.index -= (pageSize - found);
FetchProfile fp = new FetchProfile(); FetchProfile fp = new FetchProfile();
fp.add(FetchProfile.Item.ENVELOPE); fp.add(FetchProfile.Item.ENVELOPE);
@ -411,20 +401,20 @@ public class BoundaryCallbackMessages extends PagedList.BoundaryCallback<TupleMe
fp.add(IMAPFolder.FetchProfileItem.HEADERS); fp.add(IMAPFolder.FetchProfileItem.HEADERS);
fp.add(FetchProfile.Item.SIZE); fp.add(FetchProfile.Item.SIZE);
fp.add(IMAPFolder.FetchProfileItem.INTERNALDATE); fp.add(IMAPFolder.FetchProfileItem.INTERNALDATE);
ifolder.fetch(isub, fp); state.ifolder.fetch(isub, fp);
try { try {
db.beginTransaction(); db.beginTransaction();
for (int j = isub.length - 1; j >= 0 && found < pageSize && !destroyed; j--) for (int j = isub.length - 1; j >= 0 && found < pageSize && !state.destroyed; j--)
try { try {
long uid = ifolder.getUID(isub[j]); long uid = state.ifolder.getUID(isub[j]);
Log.i("Boundary server sync uid=" + uid); Log.i("Boundary server sync uid=" + uid);
EntityMessage message = db.message().getMessageByUid(browsable.id, uid); EntityMessage message = db.message().getMessageByUid(browsable.id, uid);
if (message == null) { if (message == null) {
message = Core.synchronizeMessage(context, message = Core.synchronizeMessage(context,
account, browsable, account, browsable,
ifolder, (IMAPMessage) isub[j], state.ifolder, (IMAPMessage) isub[j],
true, true, true, true,
rules, null); rules, null);
found++; found++;
@ -457,20 +447,39 @@ public class BoundaryCallbackMessages extends PagedList.BoundaryCallback<TupleMe
return found; return found;
} }
void clear() { void close() {
destroyed = true; final State state = this.state;
this.state = new State();
state.destroyed = true;
executor.submit(new Runnable() { executor.submit(new Runnable() {
@Override @Override
public void run() { public void run() {
Log.i("Boundary destroy"); Log.i("Boundary close");
try { try {
if (iservice != null) if (state.ifolder != null)
iservice.close(); state.ifolder.close();
} catch (Throwable ex) {
Log.e("Boundary", ex);
}
try {
if (state.iservice != null)
state.iservice.close();
} catch (Throwable ex) { } catch (Throwable ex) {
Log.e("Boundary", ex); Log.e("Boundary", ex);
} }
} }
}); });
} }
private class State {
boolean destroyed = false;
boolean error = false;
int index = 0;
List<Long> messages = null;
MailService iservice = null;
IMAPFolder ifolder = null;
Message[] imessages = null;
}
} }

@ -2779,7 +2779,7 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences.
getContext(), getViewLifecycleOwner(), getContext(), getViewLifecycleOwner(),
viewType, type, account, folder, thread, id, query, server); viewType, type, account, folder, thread, id, query, server);
vmodel.setCallback(callback); vmodel.setCallback(getViewLifecycleOwner(), callback);
vmodel.setObserver(getViewLifecycleOwner(), observer); vmodel.setObserver(getViewLifecycleOwner(), observer);
} }

@ -71,10 +71,8 @@ public class ViewModelMessages extends ViewModel {
if (model == null || !model.args.equals(args)) { if (model == null || !model.args.equals(args)) {
Log.i("Creating model=" + viewType + " replace=" + (model != null)); Log.i("Creating model=" + viewType + " replace=" + (model != null));
if (model != null) { if (model != null)
model.list.removeObservers(owner); model.list.removeObservers(owner);
model.clear();
}
DB db = DB.getInstance(context); DB db = DB.getInstance(context);
@ -174,7 +172,7 @@ public class ViewModelMessages extends ViewModel {
if (viewType == AdapterMessage.ViewType.THREAD || lowmem) { if (viewType == AdapterMessage.ViewType.THREAD || lowmem) {
Log.i("Remove model=" + viewType); Log.i("Remove model=" + viewType);
remove(viewType); models.remove(viewType);
} }
dump(); dump();
@ -182,10 +180,10 @@ public class ViewModelMessages extends ViewModel {
}); });
if (viewType == AdapterMessage.ViewType.UNIFIED) { if (viewType == AdapterMessage.ViewType.UNIFIED) {
remove(AdapterMessage.ViewType.FOLDER); models.remove(AdapterMessage.ViewType.FOLDER);
remove(AdapterMessage.ViewType.SEARCH); models.remove(AdapterMessage.ViewType.SEARCH);
} else if (viewType == AdapterMessage.ViewType.FOLDER) } else if (viewType == AdapterMessage.ViewType.FOLDER)
remove(AdapterMessage.ViewType.SEARCH); models.remove(AdapterMessage.ViewType.SEARCH);
if (viewType != AdapterMessage.ViewType.THREAD) { if (viewType != AdapterMessage.ViewType.THREAD) {
last = viewType; last = viewType;
@ -201,15 +199,7 @@ public class ViewModelMessages extends ViewModel {
@Override @Override
protected void onCleared() { protected void onCleared() {
for (AdapterMessage.ViewType viewType : new ArrayList<>(models.keySet())) for (AdapterMessage.ViewType viewType : new ArrayList<>(models.keySet()))
remove(viewType);
}
private void remove(AdapterMessage.ViewType viewType) {
Model model = models.get(viewType);
if (model != null) {
model.clear();
models.remove(viewType); models.remove(viewType);
}
} }
void observePrevNext(LifecycleOwner owner, final long id, final IPrevNext intf) { void observePrevNext(LifecycleOwner owner, final long id, final IPrevNext intf) {
@ -385,20 +375,23 @@ public class ViewModelMessages extends ViewModel {
this.boundary = boundary; this.boundary = boundary;
} }
void setCallback(BoundaryCallbackMessages.IBoundaryCallbackMessages callback) { void setCallback(LifecycleOwner owner, BoundaryCallbackMessages.IBoundaryCallbackMessages callback) {
if (boundary != null) if (boundary != null) {
boundary.setCallback(callback); boundary.setCallback(callback);
owner.getLifecycle().addObserver(new LifecycleObserver() {
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
public void onDestroyed() {
boundary.close();
}
});
}
} }
void setObserver(LifecycleOwner owner, @NonNull Observer<PagedList<TupleMessageEx>> observer) { void setObserver(LifecycleOwner owner, @NonNull Observer<PagedList<TupleMessageEx>> observer) {
//list.removeObservers(owner); //list.removeObservers(owner);
list.observe(owner, observer); list.observe(owner, observer);
} }
private void clear() {
if (this.boundary != null)
this.boundary.clear();
}
} }
interface IPrevNext { interface IPrevNext {

Loading…
Cancel
Save