Coroutine cancellation with HMS - java

I'm adapting my application to HMS. When I cancel launched coroutine, catch and trying to log CancellationException to HMS crashlytics, my app crashed with strange logs. If I omit this exception and didn't log it to crashlytics using method AGConnectCrash.getInstance().recordException(throwable) coroutine cancellation doesn't lead to app crash. Can someone help with that problem?
Crash logs
java.lang.ArrayIndexOutOfBoundsException: length=0; index=0 at com.huawei.agconnect.crash.internal.bean.Event$Builder.summary(Unknown Source:5) at com.huawei.agconnect.crash.internal.log.AGCCrashNonFatal.collectInfo(Unknown Source:41) at com.huawei.agconnect.crash.internal.log.AGCCrashNonFatal.logException(Unknown Source:0) at com.huawei.agconnect.crash.internal.log.AGCCrashCore$4.run(Unknown Source:6) at java.util.concurrent.ThreadPoolExecutor.processTask(ThreadPoolExecutor.java:1187) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1152) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) at java.lang.Thread.run(Thread.java:784)
SomeCodeExample
private fun loadData() {
job?.cancel()
job = viewModelScope.launch {
try {
data.value = someApiCall()
} catch (e: Exception) {
Logger.log(e)
}
}
}
Where
object Logger {
fun log(e: Throwable?) {
if (e != null) {
AGConnectCrash.getInstance().recordException(e)
}
}
}

I think your input parameter type is incorrect. You put the string instead of the stack information. You are advised to enter the document type. If a character string is forcibly entered, the system crashes. And in later versions, a judgment protection mechanism will be added.
For Details,pls kindly refer Docs.

Related

ContextException: Unknown Ban can't be caught (Discord Java)

My unban command sometimes throws a ContextException, when you unban a person who wasn't banned.
I wanted to catch it with a try catch block to notify the user that the person they are trying to unban isn't banned. This is what I tried:
try {
event.getGuild().unban(event.getMessage().getContentRaw().substring(8)).queue();
} catch(ContextException e) {
event.getChannel().sendMessage("This user isn't banned!").queue();
return;
}
But the catch() line just says Exception 'net.dv8tion.jda.api.exceptions.ContextException' is never thrown in the corresponding try block.
Your exception, in this case isn't even a ContextException but an ErrorResponseException. Since queue(...) does asynchronous operations in a different thread, the exceptions cannot be thrown from here. Instead, you should use the failure callback as described by the documentation.
You can use ErrorHandler to handle specific ErrorResponses.
Example:
String userId = event.getMessage().getContentRaw().substring(8);
ErrorHandler handler = new ErrorHandler().handle(ErrorResponse.UNKNOWN_BAN, (error) -> {
event.getChannel().sendMessage("This user isn't banned!").queue();
});
event.getGuild()
.unban(userId)
.queue(null, handler);
The ContextException is only there to tell you where in your code the error originated from. Since the actual exception happens on other threads which give you no context to find the issue.
ContextException handles async exception. So your try block cannot catch the exception.
You can change your code like this.
event.getGuild().unban(event.getMessage().getContentRaw().substring(8)).queue(
null,
(error) -> {
if (error.getMessage().equals("10026: Unknown Ban")) {
event.getChannel().sendMessage("This user isn't banned!").queue();
}
}
);

How do I catch an NPE and send custom error to Publisher in RxJava

I am doing an rxified version of what is shown here: (https://vertx.io/docs/vertx-amqp-client/java/#_creating_a_receiver).
Basically, the code below gets messages whenever they are received on a connection. Behind the scenes I am simulating a broken connection by stopping the message broker service. This results in the AMQP code throwing an NPE. I would like to catch the NPE and send a better error to the subscriber.
When the NPE occurs I can't seem to capture the error, so I'm unsure how to accomplish this.
public Publisher<Object> receiveAmqpMessages(AmqpConnection connection, String address) {
return connection
.rxCreateReceiver(address)
.flatMapPublisher(receiver ->
receiver.toObservable()
.doFinally(receiver::rxClose)
.onErrorReturn(e-> {
System.out.println("I never see this message");
return null;
}.toFlowable(BackpressureStrategy.BUFFER)
);
}
I tried wrapping it in a try/catch like so but it doesn't go into the catch block
public Publisher<Object> receiveAmqpMessages(AmqpConnection connection, String address) {
try {
// same code shown above
} catch (NullPointerException e) {
System.out.println("I never see this message either");
return Observable.error(new Exception("foo")).toFlowable(BackpressureStrategy.BUFFER);
}
}
FWIW the console looks like this (Note - The console never outputs anything that references my own class file):
SEVERE: Unhandled Exception
java.lang.NullPointerException
at io.vertx.ampq.impl.AmqpConnectionImpl.lambda$createReceiver$8(Line 251)
...
If you open their AmqpConnectionImpl and go to that line of code you see
ProtonReceiver receiver = connection.get().createReceiver(address,opts)
which causes the NPE since connection.get() is null

java.lang.RuntimeException: Unable to resume activity with java.lang.IllegalArgumentException

Recently I sometimes got this exception when MainActivity called onResume().
java.lang.RuntimeException: Unable to resume activity {com.qau4d.c35s3.androidapp/com.xxx.XXX.XXX.MainActivity}: java.lang.IllegalArgumentException
at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3400)
at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3440)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1510)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6077)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:865)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755)
Caused by: java.lang.IllegalArgumentException
at android.os.Parcel.readException(Parcel.java:1687)
at android.os.Parcel.readException(Parcel.java:1636)
at android.app.ActivityManagerProxy.isTopOfTask(ActivityManagerNative.java:5475)
at android.app.Activity.isTopOfTask(Activity.java:5961)
at android.app.Activity.onResume(Activity.java:1252)
at com.qau4d.c35s3.androidapp.onResume(XActivity.java:29)
at com.qau4d.c35s3.androidapp.onResume(MainActivity.java:196)
at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1269)
at android.app.Activity.performResume(Activity.java:6768)
at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3377)
Both in MainActivity and super class XActivity, only super.onResume(); is called. It's really strange to get this exception after a long time normal development.I checked some relative reference material but nothing got.
In the method Activity#isTopOfTask we can see:
private boolean isTopOfTask() {
if (mToken == null || mWindow == null) {
return false;
}
try {
return ActivityManager.getService().isTopOfTask(getActivityToken());
} catch (RemoteException e) {
return false;
}
}
And in ActivityManagerService#isTopOfTask we can found:
#Override
public boolean isTopOfTask(IBinder token) {
synchronized (this) {
ActivityRecord r = ActivityRecord.isInStackLocked(token);
if (r == null) {
throw new IllegalArgumentException();
}
return r.task.getTopActivity() == r;
}
}
So, I think that ActivityRecord is null.But I don't know why it is null....
There is insufficient information in your question to determine the cause of the java.lang.IllegalArgumentException, Unfortunately the android ActivityThread doesn't log the stacktrace of that exception, and the exception message appears to be empty.
However, it looks like there is a way forward. The exception is handled by the following code in the ActivityThread::performResumeActivity method:
} catch (Exception e) {
if (!mInstrumentation.onException(r.activity, e)) {
throw new RuntimeException(
"Unable to resume activity "
+ r.intent.getComponent().toShortString()
+ ": " + e.toString(), e);
}
}
If you register an Instrumentation class for your activity, it should be possible to use an onException method to log the stacktrace for the causal exception. Another possibility is to use Thread.setUncaughtExceptionHandler to set a handler for the thread in which the IllegalArgumentException is thrown.
These won't solve the problem (!) but it will get you a step closer to a solution.

Android rxJava Error handling with retrofit

I'm using the newer RX java where instead of
Observable.create(new Observable.OnSubscribeFunc<T>() {...});
this is used: (due to deprecation)
Observable.create(new Observable.OnSubscribe<T>() {...});
(This can be important as most example, tutorial, explonation uses the old one...)
Well, lets see my problem. I have a Java class, relevant parts from it:
private interface ApiManagerService {
#FormUrlEncoded
#POST("/login")
User getUser(#Field("username") String userName, #Field("password") String password);
}
private static RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(HOST)
.setLogLevel(RestAdapter.LogLevel.FULL)
.build();
private static ApiManagerService apiManager = restAdapter.create(ApiManagerService.class);
public static Subscription login(final String userName, final String password, Observer<User> observer) {
return Observable.create(new Observable.OnSubscribe<User>() {
#Override
public void call(Subscriber<? super User> subscriber) {
try {
User user = apiManager.getUser(userName, password);
subscriber.onNext(user);
subscriber.onCompleted();
} catch (RetrofitError e) {
subscriber.onError(e);
} catch (Throwable e) {
subscriber.onError(e);
}
}
}
).subscribeOn(Schedulers.io())
.retry(3)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(observer);
}
This code almost works perfectly, if everything is ok. But if I make an intentional error, like I turn off the WiFi.. than retrofit get the "UnKnownHostException"... as it should happen at the retrofit call (getUser) in the try catch block. But instead of handling the error to onError(Throwable t) --> where I could handle, it just crashes the app. So it is like if the error never gets to the catch block.
What is strange that HTTP errors (like 404, 401 etc.) is catched, got by onError(...) and everything is just fine.
Everything goes for 3 times before crash, as of .retry(3) but none gets into catch clause.
EDIT 1
LogCat Output:
01-08 16:19:31.576 15285-16162/asd.bdef.gh D/Retrofit﹕ ---- ERROR https://testapi.com/api/login
01-08 16:19:31.606 15285-16162/asd.bdef.gh D/Retrofit﹕ java.net.UnknownHostException: Unable to resolve host "testapi.com": No address associated with hostname
at java.net.InetAddress.lookupHostByName(InetAddress.java:394)
at java.net.InetAddress.getAllByNameImpl(InetAddress.java:236)
at java.net.InetAddress.getAllByName(InetAddress.java:214)
at com.squareup.okhttp.internal.Network$1.resolveInetAddresses(Network.java:29)
at com.squareup.okhttp.internal.http.RouteSelector.resetNextInetSocketAddress(RouteSelector.java:259)
at com.squareup.okhttp.internal.http.RouteSelector.nextProxy(RouteSelector.java:233)
at com.squareup.okhttp.internal.http.RouteSelector.nextUnconnected(RouteSelector.java:159)
at com.squareup.okhttp.internal.http.RouteSelector.next(RouteSelector.java:133)
at com.squareup.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:314)
at com.squareup.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:237)
at com.squareup.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:423)
at com.squareup.okhttp.internal.huc.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:105)
at com.squareup.okhttp.internal.huc.HttpURLConnectionImpl.getOutputStream(HttpURLConnectionImpl.java:239)
at com.squareup.okhttp.internal.huc.DelegatingHttpsURLConnection.getOutputStream(DelegatingHttpsURLConnection.java:218)
at com.squareup.okhttp.internal.huc.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java:25)
at retrofit.client.UrlConnectionClient.prepareRequest(UrlConnectionClient.java:68)
at retrofit.client.UrlConnectionClient.execute(UrlConnectionClient.java:37)
at retrofit.RestAdapter$RestHandler.invokeRequest(RestAdapter.java:321)
at retrofit.RestAdapter$RestHandler.access$100(RestAdapter.java:220)
at retrofit.RestAdapter$RestHandler$1.invoke(RestAdapter.java:265)
at retrofit.RxSupport$2.run(RxSupport.java:55)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:390)
at java.util.concurrent.FutureTask.run(FutureTask.java:234)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
at retrofit.Platform$Android$2$1.run(Platform.java:142)
at java.lang.Thread.run(Thread.java:841)
01-08 16:19:31.606 15285-16162/asd.bdef.gh D/Retrofit﹕ ---- END ERROR
01-08 16:19:31.977 15285-15285/asd.bdef.gh D/AndroidRuntime﹕ Shutting down VM
01-08 16:19:31.977 15285-15285/asd.bdef.gh W/dalvikvm﹕ threadid=1: thread exiting with uncaught exception (group=0x41c9d8b0)
01-08 16:19:31.977 15285-15285/asd.bdef.gh E/AndroidRuntime﹕ FATAL EXCEPTION: main
the given api address is not the real one, but the real one is reachable. I just turned off the WiFi to test error handling.
And one more use-case: If I add to the observable .onExceptionResumeNext([2nd observable]) than it goes to the 2nd observable, and it not crashes. But this is not the solution of the problem.
EDIT 2
ApiManager.login(userName, pass, new Observer<User>() {
#Override
public void onCompleted() { }
#Override
public void onError(Throwable e) {
DialogManager.showBasicErrorDialog(getApplicationContext(), e.getLocalizedMessage());
logger.showLog("Login Not ok");
e.printStackTrace();
}
#Override
public void onNext(User user) {
logger.showLog("login ok, user: " + user.getName().toString());
{...}
}
});
EDIT 3
FATAL EXCEPTION: main
rx.exceptions.OnErrorFailedException: Error occurred when trying to propagate error to Observer.onError
at rx.observers.SafeSubscriber._onError(SafeSubscriber.java:175)
at rx.observers.SafeSubscriber.onError(SafeSubscriber.java:97)
at rx.internal.operators.NotificationLite.accept(NotificationLite.java:144)
{...}
Caused by: java.lang.NullPointerException: Attempt to read from field 'rx.functions.Action0 rx.schedulers.TrampolineScheduler$TimedAction.action' on a null object reference
at rx.schedulers.TrampolineScheduler$InnerCurrentThreadScheduler.enqueue(TrampolineScheduler.java:85)
Thanks in advance for helping.
You don't have to build your own Observable with Retrofit, as Retrofit can directly return Observable:
http://square.github.io/retrofit/
Retrofit also integrates RxJava to support methods with a return type
of rx.Observable
#GET("/user/{id}/photo") Observable getUserPhoto(#Path("id")
int id);
(You won't have to handle errors by yourself)
Can you post the stacktrace of your crash ? As I think like you, that your application shouldn't crash.
It looks like you may be running into an issue has been fixed as of RxJava 1.0:
TrampolineScheduler NullPointerException

How to include timeout on Parse queries?

my question is on parse.com queries for Android and how to set a timeout if queries are taking too long to respond.
For example, I have a query where I am getting a list of strings from parse.com. If this query takes too long to be received from parse.com (say, ten seconds), I'd like the user to be able to cancel the query (with an on-screen pop-up, for example). Instead, the app crashes after 30+ seconds of waiting.
So, is there a way to set my own timeout for queries, then handle them appropriately?
Https is the protocol for connections with parse.
Http(s) allows full control of the following:
Socket timeout
getConnection timeout
connectionRequest timeout
In order to manipulate the headers, i know that with parse.com you can use the Rest API and then do anything u want with the headers in the builder....
public void create(int method, final String url, final String data) {
this.method = method;
this.url = url;
this.data = data;
if(method == GET){
this.config = RequestConfig.custom()
.setConnectTimeout(6 * 1000)
.setConnectionRequestTimeout(30 * 1000)
.setSocketTimeout(30 * 1000)
.build();
} else{
this.config = RequestConfig.custom()
.setConnectTimeout(6 * 1000)
.setConnectionRequestTimeout(30 * 1000)
.setSocketTimeout(60 * 1000)
.build();
}
this.context = HttpClientContext.create();
If you use only android sdk, then you will need docs at parse.com to figure out how ( or whether possible ) to set the http connection config listed above.
My solution was to use RxJava Observable.timer(long delay, java.util.concurrent.TimeUnit unit) method.
I declare a RxJava Subscription field, and then init it to an Observable.timer(20, TimeUnit.SECONDS) call, just before any Parse.InBackground() code.
Inside the Observable.timer() method, I invoke another method that'll throw a Java Exception within a try{...} block, and then handle the thrown exception within the following catch {...} block. What this does is have the Observable.timer() call invoke the exception-throwing method as soon as the set time (e.g. 20 seconds in the example above) is exhausted. By handling it in the catch {...} block, you can show a dialog/alert informing user that the operation has timed out.
Here's a code snippet showing this:
Subscription timeoutWatcher;
public void loginWithEmailAndPassword(#NonNull String email, #NonNull String password) {
timeoutWatcher = Observable.timer(10, TimeUnit.SECONDS).subscribe(aLong -> {
// Login timed out. Notify user to check Internet connection is chooppy.
throwNetworkException("Timeout! Your Internet connection is unstable and preventing your sign in from completing on time. Please try again.");
});
ParseUser.logInInBackground(email, password, (user, e) -> {
// if Parse operation completes before timeout, then unsubscribe from the Observable.timer() operation
timeoutWatcher.unsubscribe();
if (user != null) {
// Hooray! The user is logged in.
} else {
// Signup failed. Look at the ParseException to see what happened.
}
});
}
}
private void throwNetworkException(String exceptionMessage) {
try {
throw new NetworkErrorException(exceptionMessage);
} catch (NetworkErrorException e) {
// show alert dialog
}
}
Not the neatest piece of code, but it works for me.
Unfortunately, there is no way to specify a timeout for Parse requests.
Instead, you can catch the timeout exception & take necessary action
try{
...
}catch (ParseException e) {
String mesg = e.getMessage();
if(mesg!= null && mesg.contains("java.net.SocketTimeoutException")){
// Do something here...
}
}
Note: If you are using inBackground Parse methods, then you need to check for the exception in the callback method.

Categories