ObjectBox: Abort Transaction? - android

How can I abort my write Transaction in ObjectBox if one of my actions fails? I do not see anything available from within the Runnable or the boxStore or one of the boxes to abort a transaction. I don't want half of my actions to get applied and half to not get applied if, for example, I get an unrelated I/O error while running my transaction.

Throw an exception. E.g.
try {
boxStore.runInTx(() -> {
for(User user: allUsers) {
if(user.isValid()) box.put(user);
else throw new UserInvalidException();
}
});
} catch (UserInvalidException e) {
// TX is aborted
}

Related

Best way to handle network response in jetpack compose when using SRP

When using the Single Responsibility pattern, I wonder what would be the best approach to show network response (Success, error, progress). storing state for every request would create so many states in viewModel and will have to pass so many states to a component. Any other way, worth a try?
I use this in my main Activity's onCreate function to capture all unhandled exceptions but only when the app is released for production. During debug, I want the app to crash so that the exception stack is printed to LogCat:
if (!BuildConfig.DEBUG) {
Thread.setDefaultUncaughtExceptionHandler { paramThread, paramThrowable ->
val message: String
if (paramThrowable is HttpException) {
message = R.string.problem_processing_request.stringRes()
} else {
message = R.string.unknown_problem.stringRes()
}
App.mainViewModel.displaySnackbar(sbInfo = SnackbarInfo(message = message, isCritical = true))
}
}

How to effectively group non fatal exceptions in Crashlytics (Fabrics)?

We are using Crashlytics in our app as the crash reporting tool.
For Android native crashes, it's working fine and grouping the crashes correctly.
Our app also has few components in react-native. For the crashes which occur in these components, we catch them and then log them to Crashlytics as non-fatal exceptions.
public class PlatformNativeModuleCallExceptionhandler implements
NativeModuleCallExceptionHandler {
#Override
public void handleException(Exception e) {
try {
.
.
.
Crashlytics.logException(new Exception(exceptionString));
} catch (Exception ex) {}
}
Crashes are getting logged in Crashlytics dashboard, but it's showing all the crashes inside a single tab. These might be different crashes of the same or different react-native components.
Due to this we are not able to find out the instances of a particular crash. Need to manually go through each instance of the crash.
I guess it takes the name of the class where exception gets created, in this case PlatformNativeModuleCallExceptionHandler.
I tried creating my own custom exception class but that also did not help.
Does anybody know how we can group the non fatal exceptions better here?
All the similar crashes should be grouped together with their total instances.
Crashlytics uses the method and crash line number to group crashes, so if you have an exception handler method for all of your non-fatals, they'll be grouped together. There isn't currently a workaround for this.
Best way I've found to do this is to manually chop the shared parts of the stacktrace off:
private fun buildCrashlyticsSyntheticException(message: String): Exception {
val stackTrace = Thread.currentThread().stackTrace
val numToRemove = 8
val lastToRemove = stackTrace[numToRemove - 1]
// This ensures that if the stacktrace format changes, we get notified immediately by the app
// crashing (as opposed to silently mis-grouping crashes for an entire release).
check(lastToRemove.className == "timber.log.Timber" && lastToRemove.methodName == "e",
{ "Got unexpected stacktrace: $stackTrace" })
val abbreviatedStackTrace = stackTrace.takeLast(stackTrace.size - numToRemove).toTypedArray()
return SyntheticException("Synthetic Exception: $message", abbreviatedStackTrace)
}
class SyntheticException(
message: String,
private val abbreviatedStackTrace: Array<StackTraceElement>
) : Exception(message) {
override fun getStackTrace(): Array<StackTraceElement> {
return abbreviatedStackTrace
}
}
This way the message can be parameterized Timber.e("Got a weird error $error while eating a taco") and all of that line's calls will be grouped together.
Obviously, numToRemove will need to change depending on your exact mechanism for triggering nonfatals.
I resolved this by setting a custom stack trace to the exception. A new Exception(exceptionMessage) will create the exception there itself, what we did was to throw an exception which in catch called my counterpart of handleException() with the actual stack trace furnished in the exceptionMessage. Some parsing and the exceptionMessage can be used to set the stack trace on the newly created exception using exception.setStackTrace(). Actually, this was required in my project only because it is cross-language, for regular projects, simply passing the exception thrown and caught at the place of interest should work.
Crashlytics groups by the line number that the exception was generated on and labels it with the exception type. If you know all the types of the exceptions you can generate each one on a different line. And you could also map your strings to custom Exception types to make it more easy to identify them in Crashlytics.
Here's an example:
public void crashlyticsIsGarbage(String exceptionString) {
Exception exception = null;
switch(exceptionString) {
case "string1": exception = new String1Exception(exceptionString);
case "string2": exception = new String2Exception(exceptionString);
case "string3": exception = new String3Exception(exceptionString);
case "string4": exception = new String4Exception(exceptionString);
default: exception = new Exception(exceptionString);
}
Crashlytics.logException(exception);
}
class String1Exception extends Exception { String1Exception(String exceptionString) { super(exceptionString); } }
class String2Exception extends Exception { String2Exception(String exceptionString) { super(exceptionString); } }
class String3Exception extends Exception { String3Exception(String exceptionString) { super(exceptionString); } }
class String4Exception extends Exception { String4Exception(String exceptionString) { super(exceptionString); } }
BTW, Crashlytics will ignore the message string in the Exception.
I was looking into this just now, because the documentation says:
Logged Exceptions are grouped by Exception type and message.
Warning:
Developers should avoid using unique values, such as user ID, product ID, and timestamps, in the Exception message field. Using unique values in these fields will cause a high cardinality of issues to be created. In this case, Crashlytics will limit the reporting of logged errors in your app. Unique values should instead be added to Logs and Custom Keys.
But my experience was different. From what I found out, what Alexizamerican said in his answer is true, with a small caveat:
Issues are grouped by the method and line where the exception was created, with the caveat that it is the root cause of the exception that is being taken into account here.
By root cause I mean this:
public static Throwable getRootCause(Throwable throwable) {
Throwable cause = throwable;
while (cause.getCause() != null) {
cause = cause.getCause();
}
return cause;
}
Therefore, if you did:
#Override
public void handleException(Exception e) {
// ...
Crashlytics.logException(e);
}
That should correctly group the exceptions together.
Furthermore, if you did:
#Override
public void handleException(Exception e) {
// ...
Crashlytics.logException(new Exception(exceptionString, e));
}
That would also correctly group the exceptions, because it would look at e or its cause, or the cause of that, and so on, until it reaches an exception that doesn't have any other cause, and look at the stack trace where it was created.
Finally, unlike what miguel said, exception type or message doesn't affect grouping at all in my experience. If you have FooException("foo") at some particular line in a particular method, and you replace it with BarException("bar"), the two will be grouped together, because the line and method didn't change.

Subscribe to observable after dispose

i am building my app on android repository by Fernando Cejas and i have a problem with subscribing to observable after calling dispose.
When i come to dashboard, i call method subscribeOnUserMessages.execute(new Subscriber(), new Params(token)), which is method in UseCase class
public void execute(DisposableObserver<T> observer, Params params) {
Preconditions.checkNotNull(observer);
final Observable<T> observable = this.buildUseCaseObservable(params)
.subscribeOn(Schedulers.from(threadExecutor))
.observeOn(postExecutionThread.getScheduler());
addDisposable(observable.subscribeWith(observer));
}
In child class SubscribeOnUserMessages i simply call repository like this
return messageRepository.subscribeOnUserMessages(params);
In my socket implementation i create like this
return Observable.create(emitter -> {
if (!isThereInternetConnection()) {
Timber.w("Network connection exception");
emitter.onError(new NetworkConnectionException());
return;
}
/*
* Open socket if not opened
*/
openSocket(params.getToken());
String channelName = CHANNEL_PRIVATE_USER + params.getAuthenticated().getUuid();
if (subscribedChannels.contains(channelName)) {
Timber.d("Channel %s is already subscribed", channelName);
return;
}
JSONObject auth;
try {
auth = createAuthJson(CHANNEL, channelName, params.getToken());
} catch (JSONException e) {
Timber.e("Couldn't create auth json");
emitter.onError(e);
return;
}
mSocket.emit(SUBSCRIBE, auth);
Timber.d("Emitted subscribe with channel: %s ", CHANNEL_PRIVATE_USER + params.getAuthenticated().getUuid());
subscribedChannels.add(CHANNEL_PRIVATE_USER + params.getAuthenticated().getUuid());
Timber.d("Subscribing on event: %s\n with user: %s", EVENT_USER_NEW_MESSAGE, params.getAuthenticated().getUuid());
if (mSocket.hasListeners(EVENT_USER_NEW_MESSAGE)) {
Timber.v("Socket already has listener on event: %s", EVENT_USER_NEW_MESSAGE);
return;
}
mSocket.on(EVENT_USER_NEW_MESSAGE, args -> {
if (args[1] == null) {
emitter.onError(new EmptyResponseException());
}
Timber.d("Event - %s %s", EVENT_USER_NEW_MESSAGE, args[1].toString());
try {
MessageEntity messageEntity = messageEntityJsonMapper.transform(args[1]);
emitter.onNext(messageEntity);
} catch (JSONException e) {
Timber.e(e, "Could not parse message json");
emitter.onError(e);
}
});
});
Symptoms are that first time i subscribe everything is going through to presentation layer. When i dispose after going to second screen and come back i only see logs coming to socket implementation, but not going through.
My question is: Is there a method for subscribing to same observable again? I've already tried to save that observable in my use case in singleton and subscribe to that observable, didn't help.
Without additional info and details regrading socket implementation it is hard to spot the problem exactly, but, from the code you've posted, you don't have dispose logic, so while you might properly call dispose() to the Observable at the correct lifecycle event, your socket will actually stay open, and it might not got disconnected/closed properly ever.
That might lead to a problems opening and connecting to the socket at the 2nd time, as you might try to reopen already open socket and depends on your internal socket impl that might be a problem.
(I can see in the comment that openSocket if not already opened, but still there might be problem elsewhere calling some method on the socket multiple times or setting listeners, again depends on the socket impl)
As a general guidelines, you should add dispose logic using emitter.setCancellable()/emitter.setDisposable() in order to dispose properly the socket resources when you no longer need them, thus - when applying subscribe again (whether the same object or not) will invoke your subscription logic again that will reopen the socket and listen to it.
It is not clear to me if you like to keep the socket open when you moving to a different screen (I don't think it is a good practice, as you will keep this resource open and might never get back to the screen again to use it), but if that's the case as #Phoenix Wang mentioned, you can use publish kind operators to multicast the Observable, so every new Subscriber will not try to reopen the socket (i.e. invoking the subscription logic) but will just get notify about messages running in the already opened socket.

Android: How to check for a specific exception?

How to check for a specific exception, e.g. SocketException with message "Socket closed"? We can compare strings like this:
if (exception.getMessage().equals("Socket closed"))...
but is there some more elegant method, like comparing error codes, or comparison with constant exception value?
Except if SocketException is always "Socket closed", but in docs it states that this class is a superclass for all socket exceptions, so there is more than one.
UPDATE:
I don't want to check for exception class. If I do, I would use specialized catch rather than to check tor a class explicitly:
catch (SocketException ex) { ... }
I want some more elegant method to distinct two exceptions which are instances of the same class, not by comparing strings like this:
try {
int i = 2;
if (i == 1) throw new SocketException("one");
else if (i == 2) throw new SocketException("two");
}
catch (SocketException ex) {
if (ex.getMessage().equals("one")) { ... }
}
In this particular case I throw exceptions to show what is it about, but in reality it can be code not controlled by me.
Also I noticed that exception message in one particular case method threw "Socket closed", in another different method threw "Socket is closed". So it's not so reliable to stick to the message either.
Your question has different approaches, depending on what you are trying to achieve. The simplest method for determining if you have the exception you want is to use instanceof since an Exception is a class as well, i.e.:
if (myException instanceof SocketException) {
...
}
However, you then add the requirement of the contents of the message or the possibility that the Exception thrown is actually a subclass of the Exception of interest to you. In the case of a "subclass" you can check if it is a subclass with:
if (myException instanceof SocketException &&
myException.getClass() != SocketException.class) {
// then I'm an instance of a subclass of SocketException, but not SocketExcpetion itself
}
Or conversely, only evaluate for the parent class:
if (myException instanceof SocketException &&
myException.getClass() == SocketException.class) {
// then I'm an instance of the class SocketException, and not a cubclass of SocketExcpetion!!
}
These serve as the "error codes" you seem to be looking for - the identification of the class, with certainty.
However, if you really are interested in the human-readable error contents, then the solution you have should be your implementation. That seems unlikely, but sometimes that is what is required.
You can use:
exception.getClass().getSimpleName() and compare it to SocketException
Hope this helps.

RxJava: Can I override OnError or create a custom Observable which handles a specific error

I've come across an issue in my application where I am checking for a specific error (lets say error 9000) in the onError of many different subscriptions. All of them may or may not handle the error in the same way. Rather than doing a check if(error == 9000) in the OnError of these subscriptions is there a way to create a custom Observable or operator that checks for this error specifically or maybe something like a .doOn9000Error()
You could write a simple function handleErr9000 which takes an Observable, and transforms it into one which correctly deals with error 9000. The onErrorResumeNext operator is what you need: It takes a function which gets the error which occurred, and can decide, depending on the kind of error, what Observable sequence to continue with.
public static <T> Observable<T> handleErr9000(Observable<T> o) {
return o.onErrorResumeNext(new Func1<Throwable, Observable<T>>() {
public Observable<T> call(Throwable err) {
if (err instanceof NumberedException
&& ((NumberedException) err).number == 9000)
{
// Handle this specific error ...
// Then return Observable.error(err) if you want to keep
// the error, or Observable.just(someDefaultValue) to
// substitute the error by a default value,
// or Observable.empty() to swallow the error
return Observable.empty();
} else {
// just pass on the error if it's a different error
return Observable.error(err);
}
}
});
}
[I invented an exception class named NumberedException for this example, you probably already have your own exception class for this.]

Categories

Resources