Flutter and external JAR Library: android.os.NetworkOnMainThreadException - asynchronous

Im trying to use olingo with Flutter on Android. I set up my channel and I can call the library but I keep getting this message:
E/AndroidRuntime(28391): FATAL EXCEPTION: main
E/AndroidRuntime(28391): Process: com.example.odata, PID: 28391
E/AndroidRuntime(28391): org.apache.olingo.client.api.http.HttpClientException: android.os.NetworkOnMainThreadException
E/AndroidRuntime(28391): at org.apache.olingo.client.core.communication.request.AbstractODataRequest.doExecute(AbstractODataRequest.java:312)
So it looks like it is running on the main thread - which is a no go as this would block. I tried the looper to ask Java to run on the UI Thread:
public void onMethodCall(MethodCall call, Result result) {
// Note: this method is invoked on the main thread.
Log.i("test", "using " + call.method);
String serviceUrl = "http://services.odata.org/OData/OData.svc/";
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
if (call.method.equals("getMetaData")) {
String metadata;
final Edm edm = ODataClientFactory.getClient().getRetrieveRequestFactory().getMetadataRequest(serviceUrl).execute().getBody();
metadata = edm.toString();
if (metadata != "") {
result.success(metadata);
} else {
result.error("UNAVAILABLE", "Metadata cannot read.", null);
}
} else {
result.notImplemented();
}
}
});
But Im still getting the same error.
So how exactly can I deal with external JAR Library which are doing blocking operations ? To my understanding an external call is a Future anyway so it will not block my Flutter thread anyway - but Android Java does not think so ...
This is my method call in flutter
Future<void> _getMetaData() async {
String metadata;
try {
final String result = await platform.invokeMethod('getMetaData');
metadata = result;
} on PlatformException catch (e) {
metadata = e.message;
}
setState(() {
_metadata = metadata;
});
}

Thanks for the answer, this is the solution for anyone that may be interested:
public void onMethodCall(MethodCall call, Result result) {
if (call.method.equals("getMetaData")) {
class MetadataLoader extends AsyncTask<String , Integer, String> {
#Override
protected String doInBackground(String... urls) {
// call your Java library method here, including blocking methods
return your_return_value;
}
protected void onPostExecute(String _result) {
// your_return_value is now passed in _result
result.success(_result);
}
}
new MetadataLoader().execute(); // Start the Async
}
On the flutter side,
Future<void> _getMetaData() async {
String metadata;
try {
final String result = await platform.invokeMethod('getMetaData');
// do something with the result
// the Flutter thread will stop at the await and resume when the Java
// will call result.success
}
}

You will need to create a new Java thread or Worker. (Note that the "main" thread and the "UI" thread are the same thing - so by posting to the main looper you've ended up in the same place - trying to do network i/o on the main thread.)
Yes, the Flutter engine is running in different threads, but you still need to leave the main native thread unblocked as it is responsible for detecting user input, etc.
Also note that when your blocking activity completes - on its non-main thread - it will likely want to deliver the response to Dart. To do this it will need to use part of your code above - to post the results back to the main thread, which can then invoke method channel operations.
You'll probably want to use your method channel bi-directionally. From flutter to native to request an operation (returning, say, a sequence number), and from native to flutter to deliver the results (quoting the sequence number so that the result can be tied back to the request).

Related

Unity async/await and IO operations with Firestore (Firebase)

I've developed a Unity App that uses Firebase as a BaaS and Firestore as a Database.
Firebase has a Client SDK to make calls that are usually called from client to server by an URL endpoint.
My concern is how my methods should be implemented to correctly work on client without blocking the user experience, cause if I made a heavy request, my Unity App is blocked, and no interaction is allowed to the user.
This is the code of my client DatabaseManager with the methods to retrieve a User from Firestore:
public class DatabaseManager
{
public DatabaseManager(FirebaseFirestore db)
{
this.db = db;
}
public async Task<User> GetUserByUIDAsync(string uid)
{
string documentID = uid;
return await AsyncGetDocumentFromCollection<User, User_FirestoreData>(COL_ID_USERS, documentID);
}
public async Task<PlainData> AsyncGetDocumentFromCollection<PlainData, FirestoreData>(string collectionID, string documentID) where PlainData : IConvertToFirestore<FirestoreData> where FirestoreData : IConvertToPlainData<PlainData>
{
try
{
DocumentReference docRef = db.Collection(collectionID).Document(documentID);
DocumentSnapshot documentSnapshot = await docRef.GetSnapshotAsync();
if (documentSnapshot.Exists)
{
Debug.Log("Get Document data for document:" + documentSnapshot.Id);
FirestoreData firestoreData = documentSnapshot.ConvertTo<FirestoreData>();
return firestoreData.ToPlainData();
}
else
{
Debug.Log($"Document {documentSnapshot.Id} does not exist!");
}
}
catch (Exception e)
{
Debug.Log(e);
}
return default(PlainData);
}
}
This is a simple call and when it's called from any MonoBehaviouryou couldn't notice the load difference when you call it like:
using UnityEngine.UI;
public class MyMono : MonoBehaviour
{
private void DatabaseManager db;
[SerializedField] private Button button = null;
private void Awake()
{
button.onClick.AddListener(async ()=> await CustomAwakeAsync(db));
}
private async Task CustomAwakeAsync(DatabaseManager db)
{
//if this Async method is heavier, this will block the main UI thread when the button is pressed
await db.GetUserByUIDAsync("xdfipñfajrfiñar");
}
}
But if instead of GetUserByUIDAsync I make a heavy call, or multiple recursive calls my application UI will freeze until it's finished...which is bad.
How should I build my code to avoid these case?
Note:
My easy way to test if it's blocking UI thread is having this class attached to a GameObject with Image component:
using UnityEngine;
public class InfiniteRotate : MonoBehaviour
{
public float speed = 1;
// Update is called once per frame
private void Update()
{
this.gameObject.transform.Rotate(0, 0, 1 * Time.deltaTime * speed);
}
}
If the image stop spinning, means that async/await is blocking the UI thread.
Your code as shown:
private void CustomAwake(DatabaseManager db)
{
await db.GetUserByUIDAsync("xdfipñfajrfiñar");
}
...should be producing the following error:
error CS4033: The 'await' operator can only be used within an async method. Consider marking this method with the 'async' modifier and changing its return type to 'Task'.
Even if somehow you managed to silence this error the method signature private void CustomAwake(DatabaseManager db) clearly indicates that this method is synchronous to the caller.
OP:
But if instead of GetUserByUIDAsync I make a heavy call, my application UI will freeze until it's finished...which is bad.
So if you are calling this from the same thread as Update, FixedUpdate etc (which by the looks of it you are) then you are going to block Unity and thus slow down your game.
If you are going to use async/await then you need to do so all the way back to the original caller.
Make it asynchronous
Change the method to:
private async Task<User> CustomAwake(DatabaseManager db) // Note the async Task
{
return await db.GetUserByUIDAsync("xdfipñfajrfiñar");
}
...and ensure that whatever calls it uses await in order to get the User.

Blazor WebAssembly SignalR HubConnection causing javascript error on reload

I have a SignalR HubConnection within my Blazor WebAssembly application and whilst it works most of the time, if I reload the page (via the browser reload) then I often am getting the following error in the console and the connection is not made:
Uncaught Error: The delegate target that is being invoked is no longer available. Please check if it has been prematurely GC'd.
at Object.invoke_delegate (dotnet.5.0.4.js:1)
at WebSocket. (dotnet.5.0.4.js:1)
Here's a rough, simplified view of the code where I create the HubConnection (and dispose it).
#inherits LayoutBase
#attribute [Authorize]
<AuthorizeView>
<Authorized>
//...
</Authorized>
<NotAuthorized>
//...
</NotAuthorized>
</AuthorizeView>
public class LayoutBase : LayoutComponentBase, IAsyncDisposable
{
[Inject] public IAccessTokenProvider AccessTokenProvider { get; set; }
private readonly HubConnection _hubConnection;
protected override async Task OnInitializedAsync()
{
_hubConnection = new HubConnectionBuilder()
.AddNewtonsoftJsonProtocol(c =>
{
//...
})
.WithUrl(notificationHubUrl, option => option.AccessTokenProvider = GetAccessToken)
.WithAutomaticReconnect()
.Build();
_hubConnection.Closed += HubConnectionOnClosed;
_hubConnection.Reconnected += HubConnectionOnReconnected;
_hubConnection.Reconnecting += HubConnectionOnReconnecting;
await _hubConnection.StartAsync()
await base.OnInitializedAsync();
}
private async Task<string> GetAccessToken()
{
var tokenResult = await AccessTokenProvider.RequestAccessToken(...)
// etc...
}
// .. Event Handlers
public ValueTask DisposeAsync()
{
_logger.LogInformation($"Disposing Hub: {_hubConnection.ConnectionId}");
_hubConnection.Closed -= HubConnectionOnClosed;
_hubConnection.Reconnected -= HubConnectionOnReconnected;
_hubConnection.Reconnecting -= HubConnectionOnReconnecting;
return _hubConnection.DisposeAsync();
}
}
Previously I had it as an injected service but I eventually simplified it to this structure but it continues to get this error on reload. It's not every time I reload but most times.
I have tried changing the dispose pattern without success. I can't find any information on the error anywhere else.
Any ideas?
I don't have a definitive answer as to the underlying reason but I suspect that this is a bug somewhere in the SignalR/dotnet framework resulting in the GCing of a delegate because something drops a reference to it.
One way I've managed to provoke this error reasonably consistently is to have a handler returning just a Task, e.g.
_hubConnection.On<TEvent>(eventType.Name, OnEvent);
where OnEvent looks like this:
// THIS IS THE BROKEN SIGNATURE - DO NOT USE
private async Task OnEvent<TEvent>(TEvent e)
{
}
A workaround which appears to have fixed it for me is to make the handler actually return something. This seems to make something deeper in the framework hold a reference for longer so that it doesn't get GC'ed. E.g.
// WORKS ON MY MACHINE - Note the return type of Task<object>
private async Task<object> OnEvent<TEvent>(TEvent e)
{
// ... Do stuff
return null;
}

Why does _session.Use method not work properly?

I have a job like this:
[UnitOfWork]
public override void Execute(CompleteIRHJobArgs args)
{
var robotUserId = _userRepo.GetAll().Where(p => p.UserName == TestaLIMSWPConsts.LIMSRobot).Select(p => p.Id).First();
using (_session.Use(args.TenantId, robotUserId))
{
_instanceReciptHeaderDomainService.SetIRHToCompleteState(args.IRHIds);
}
}
I find robotUserId and set it as the current user. But after I step into method SetIRHToCompleteState, _session.UserId.Value is null. I think it is wrong behavior. My ABP version is 4.0.0.
public async Task SetIRHToCompleteState(List<int> irhIds)
{
var irhs = await _instanceHeaderRepo.GetAll().Where(p => irhIds.Contains(p.Id)).ToListAsync();
foreach (var t in irhs)
{
t.FlowState = FlowState.Completed;
t.CompleteDate = Clock.Now;
t.CompleteUserId = _session.UserId.Value;
}
}
And sometimes,
var irhs = await _instanceHeaderRepo.GetAll()...
throws exception:
System.Transactions.TransactionInDoubtException: The transaction is in doubt. ---> System.Data.SqlClient.SqlException: There is already an open DataReader associated with this Command which must be closed first. ---> System.ComponentModel.Win32Exception: The wait operation timed out
But after step into method SetIRHToCompleteState, _session.UserId.Value is null.
SetIRHToCompleteState is async and continued running after the using scope was disposed.
Since Execute is not async, you cannot await but you can call AsyncHelper.RunSync instead.
// using Abp.Threading;
using (_session.Use(args.TenantId, robotUserId))
{
AsyncHelper.RunSync(() => _instanceReciptHeaderDomainService.SetIRHToCompleteState(args.IRHIds));
}
This would also avoid the "open DataReader" error.
From aspnetboilerplate/aspnetboilerplate#1646:
it's called in a background thread which is not inside an async context. But it's not a problem since background job manager is already single threaded and does not cause to block many threads.
Hangfire implementation is also like that.

Rebus - Exit application from Handle method

I'm implementing an enricher pattern (https://www.enterpriseintegrationpatterns.com/patterns/messaging/DataEnricher.html) using a command/consumer queue where the consumer is the enricher and publishes the enriched message to a separate endpoint (SQL database in this case). The consumer is running as a HostedService which implements cancellation token.
Because I'm consuming commands from one transport and publishing events to another there is a possibility that the transport I'm publishing to is down while the one I'm consuming from is up. In that case I'd like to log an error and stop my Hosted service. However, I cannot see how that would work since whatever calls the Handle method already handles exceptions, and I cannot access my cancellation token. Does anyone have any ideas?
This is a draft of what I want to do.
public async Task Handle(EditedEventData message)
{
var enricher = _enricherFactory.GetEnricher(message);
object #event = await enricher.EnrichAsync(message);
var transformers = _transformerFactory.GetTransformers(message);
var messages = new List<object>();
foreach (var transformer in transformers)
{
messages.AddRange(transformer.Transform(#event, message));
}
foreach (var item in messages)
{
try
{
await _bus.Publish(item);
}
catch (Exception ex)
{
_logger.LogCritical("Publishing event message {#item} failed with error {ex}", item, ex);
//how do I exit from here?
}
}
}
If I were you, I would come up with some kind of application service, e.g. IApplicationControlService, which you can configure to be injected into your handlers using whichever IoC container you're using.
It could look somewhat like this:
public interface IApplicationControlService
{
void RequestApplicationShutdown();
}
and then your code could simply
public class YourHandler : IHandleMessages<EditedEventData>
{
readonly IApplicationControlService applicationControlService;
public YourHandler(IApplicationControlService applicationControlService)
{
this.applicationControlService = applicationControlService;
}
public async Task Handle(EditedEventData message)
{
// (...)
foreach (var item in messages)
{
try
{
await _bus.Publish(item);
}
catch (Exception ex)
{
_logger.LogCritical("Publishing event message {#item} failed with error {ex}", item, ex);
applicationControlService.RequestApplicationShutdown();
}
}
}
}
to request the application be stopped, when an error occurs.
An implementation of IApplicationControlService could then be something like
public class BruteForceApplicationControlService : IApplicationControlService
{
public void RequestApplicationShutdown()
{
Environment.FailFast("you should probably not do THIS 😉");
}
}
or something more gentle 😁 – the point is, that you will be able to provide a way to request your application to shut down "from the outside", most likely from the place where your application is assembled (i.e. the "composition root")

Regulate network calls in SyncAdapter onPerformSync

I m sending several retrofit calls via SyncAdapter onPerformSync and I m trying to regulate http calls by sending out via a try/catch sleep statement. However, this is blocking the UI and will be not responsive only after all calls are done.
What is a better way to regulate network calls (with a sleep timer) in background in onPerformSync without blocking UI?
#Override
public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) {
String baseUrl = BuildConfig.API_BASE_URL;
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.build();
service = retrofit.create(HTTPService.class);
Call<RetroFitModel> RetroFitModelCall = service.getRetroFit(apiKey, sortOrder);
RetroFitModelCall.enqueue(new Callback<RetroFitModel>() {
#Override
public void onResponse(Response<RetroFitModel> response) {
if (!response.isSuccess()) {
} else {
List<RetroFitResult> retrofitResultList = response.body().getResults();
Utility.storeList(getContext(), retrofitResultList);
for (final RetroFitResult result : retrofitResultList) {
RetroFitReview(result.getId(), service);
try {
// Sleep for SLEEP_TIME before running RetroFitReports & RetroFitTime
Thread.sleep(SLEEP_TIME);
} catch (InterruptedException e) {
}
RetroFitReports(result.getId(), service);
RetroFitTime(result.getId(), service);
}
}
}
#Override
public void onFailure(Throwable t) {
Log.e(LOG_TAG, "Error: " + t.getMessage());
}
});
}
}
The "onPerformSync" code is executed within the "SyncAdapterThread" thread, not within the Main UI thread. However this could change when making asynchronous calls with callbacks (which is our case here).
Here you are using an asynchronous call of the Retrofit "call.enqueue" method, and this has an impact on thread execution. The question we need to ask at this point:
Where callback methods are going to be executed?
To get the answer to this question, we have to determine which Looper is going to be used by the Handler that will post callbacks.
In case we are playing with handlers ourselves, we can define the looper, the handler and how to process messages/runnables between handlers. But this time it is different because we are using a third party framework (Retrofit). So we have to know which looper used by Retrofit?
Please note that if Retrofit didn't already define his looper, you
could have caught an exception saying that you need a looper to
process callbacks. In other words, an asynchronous call needs to be in
a looper thread in order to post callbacks back to the thread from
where it was executed.
According to the code source of Retrofit (Platform.java):
static class Android extends Platform {
#Override CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) {
if (callbackExecutor == null) {
callbackExecutor = new MainThreadExecutor();
}
return new ExecutorCallAdapterFactory(callbackExecutor);
}
static class MainThreadExecutor implements Executor {
private final Handler handler = new Handler(Looper.getMainLooper());
#Override public void execute(Runnable r) {
handler.post(r);
}
}
}
You can notice "Looper.getMainLooper()", which means that Retrofit will post messages/runnables into the main thread message queue (you can do research on this for further detailed explanation). Thus the posted message/runnable will be handled by the main thread.
So that being said, the onResponse/onFailure callbacks will be executed in the main thread. And it's going to block the UI, if you are doing too much work (Thread.sleep(SLEEP_TIME);). You can check it by yourself: just make a breakpoint in "onResponse" callback and check in which thread it is running.
So how to handle this situation? (the answer to your question about Retrofit use)
Since we are already in a background thread (SyncAdapterThread), so there is no need to make asynchronous calls in your case. Just make a Retrofit synchronous call and then process the result, or log a failure. This way, you will not block the UI.

Resources