Unity Firebase SetCurrentScreen must be called from Main Thread - android

This is Strange, I used to do GoogleAnalytics for my Unity Projects because I had two major uses, one was to see the number of users on Screen and second for some events. I heard about Firebase and wanted to explore it, I was successful in initializing the sdk and logging different events, but now there is one major problem which I can't seem to get over. Apparently when I try to Log my Current Screen using:
Firebase.Analytics.FirebaseAnalytics.SetCurrentScreen ("MainActivity", "MainMenu");
and then read somewhere to use it like this:
FirebaseAnalytics.SetCurrentScreen ("MainActivity", "MainMenu");
both of these functions gave the same error when viewed in Monitor(ddms)
SetCurrentScreen must be called from Main Thread
Everytime I called the function this came up, I don't know why this is happening and can't find a solution for this anywhere.
The Function calling the Method is:
public void AnalyticsLogScreen() {
// Log an event with a float.
DebugLog("Logging a Screen");
Firebase.Analytics.FirebaseAnalytics.SetCurrentScreen ("MainActivity", "MainMenu");
}

It appears that you are calling SetCurrentScreen from worker thread. You can check this by looking at the name of the thread making the call or comparing the TID from the logcat to the process PID. The main thread with have name "main" and TID that matched the app PID.
Unity SetCurrentScreen is wrapped on Java FirebaseAnalytics.setCurrentScreen() method .
setCurrentScreen can only be called from the main thread.
The reason for this requirement is that Activities in Android can only be displayed displayed from the UI thread and allowing SetCurrentScreen from worker thread creates a race condition between the Activity displayed in the UI thread and the worker thread executing the call to setCurrentScreen. To avoid this race condition Firebase requires the call to setCurrentScreen to be made on the UI thread. If you still like to set the screen from worker thread you can just call runOnUiThread though this will create the race condition so some event can be attributed to the wrong screen or appear logged not on any screen.

https://developers.google.com/tag-manager/android/v4/ua#send-screen-views this is container settings. After that you can log screen :
Firebase.Analytics.FirebaseAnalytics.LogEvent("openScreen", "screenName", "main_screen");
Tag settings:
Trigger :
Varible:

Related

Why are we using RunOnUiThread in the app

I am new to android and I have a question, it looks silly but i need to know.
I was looking in a source code of an open project that was uploaded in git the app starts with an authentication activity
user enters his/her username password, and if it didn't exists in database, it will show a snack bar, but if it was exist in database the app will do this:
RunOnUiThread(() => { TransitToServiceListActivity();});
and in TransitToServiceListActivity() method, the code defines a new intent and starts navigate to the activity related to that intent.
So my question is, why in the first place we didn't start to navigate to other Activities.
why we need to do first:
RunOnUiThread(() => { TransitToServiceListActivity();});
and then star to navigate between activities?
Why not create an intent from that activity we are going to transit to, and call that activity
what problem this RunOnUiThread(() => { TransitToServiceListActivity();}); solves that we are using it?
The purpose of RunOnUiThread is to ensure that a given Runnable is executed in the UI thread.
So, the method RunOnUiThread is meant to be called when running in a different thread other than the main one, and it is required that some specific code gets executed in the main thread. Although is also perfectly valid to be called in an ambiguous code block, which could be called in both the UI thread or a background thread, for which scenario RunOnUiThread will resolve if the runnable must be executed immediately or passed to the main thread.
I am guessing that probably the code you are looking at, is performing the authentication in a background thread. If it doesn't and there is no chance that the authentication is never executed in a background thread, then there is no point to use RunOnUiThread.

Is it okay to read data from UI elements in another thread?

I just found out that some of my code is (unindentionally) running in a worker thread and reads some data from UI elements:
e.g. checkbox.isChecked(), textView.getText()
and to my surprise, this works just fine...
I expected that it would crash with an exception (like the following exception that I get when I want to write to UI elements):
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
According to the Android docs:
Only objects running on the UI thread have access to other objects on
that thread. Because tasks that you run on a thread from a thread pool
aren't running on your UI thread, they don't have access to UI
objects.
So, is it really okay to read data from UI elements from other threads?
If not: why is there no exception?
is it really okay to read data from UI elements from other threads?
No, but not for the reasons that you may be thinking.
First, as soon as you fork the background thread, the user might press BACK or otherwise do something that destroys your activity. It is not safe to try to use widgets of a destroyed activity.
Second, there is little to no thread synchronization logic in the View class or its subclasses. The main application thread may be modifying the data at the same time that you are trying to use it, resulting in possible concurrency issues.
I would pass the data from the widgets into the thread (e.g., thread constructor).
why is there no exception?
AFAIK, that specific exception is only thrown on setters or other things that modify the contents of the widget. This does not mean that any code that does not raise that exception is guaranteed to be safe.
You can't redraw (invalidate) your Views outside main thread which is also UI thread. Setting text for TextView also causes redrawing view ofc... But getting text just returns String (or Editable?), so its working...
Note that there are some other restrictions and limitations, e.g. when you pass EditText to some AsyncTask, but while it's working the main Activity (holding passed view) finish then you might get NPE, because view is also gone... In this and similar cases WeakReferences are very usefull

Is using runOnUiThread inside AsyncTask inefficient and bad?

I know it sounds crazy that someone is using runOnUiThread inside AsyncTask. Somehow, it is working for me but I wanna know if it is an acceptable and robust approach or not. Here is the scenario:
I have an app in which after successful login, user is rendered to next screen. In this new screen, 3 different methods are loading different types of data from a web server. These methods are:
getMembersForList() : It loads the list of community members and shows it in a listview.
getProfileData() : It loads the profile of logged in user and shows his name , image etc on the screen.
getNotificationCounts : It loads the count of new notifications for the user.
I applied 3 different approaches for it :
(1) Calling all 3 methods simply in onCreate i.e. no exclusive thread is being used for any of the methods . In this case , the transition from login screen to this screen becomes very slow and black screen shows up for some time before this activity shows up.
(2) Calling getMembersForList() on UI thread and the other 2 methods on exclusive threads. In this case transition becomes fast and list shows up quickly but Notification counts and username etc. don't show up because WrongThreadException occurs saying that this thread can't touch other thread's views (TextViews for username, notification count etc. which are declared globally) . The same thing happens when I start these threads from an AsyncTask as well.
(3) Calling getMembersForList() on UI thread and then starting an AsyncTask in which the other 2 methods are being called in "runOnUiThread" inside doInBackground() method. This solves both the above issues. Now the screen transition is faster and the WrongThread exception is also not occuring.
So far the approach-(3) is working good for me but I am not sure if this is the right way to do it because runOnUiThread and AsyncTask are 2 completely opposite things. Can anyone please clear my doubts about this scenario. Thanx in advance.
Yes, use-cases like this are a big reason why the runOnUiThread() method exists in the first place. The idea is you allow your background thread(s)/AsyncTask instance(s) to run your lengthy operations in the background, and then provide a simple hook that they can use to update the interface when they have the result (or at arbitrary intervals, as different pieces of the result become available).
As long as that's what you're doing, then your usage is fine. What you want to avoid doing is performing a lengthy operation on the main thread, either directly or indirectly by passing in some lengthy operation from a background thread.
Of course you don't have to do it that way if you don't want to. You could use postExecute() instead. Or you could store the result somewhere and then use any sort of message-passing API to notify the main thread that the result is ready, and so on.
I would advice to run all the 3 calls in the asyncTask, and update the UI in the postExecute() of the AsyncTask after the background taks is complete, postExecute runs on UIthread so you need not call anything explicit to run them on UIthread.

Long operations on Activity's onCreate()

I have an Activity that retrieves information from a remote server and displays it in a TableLayout.
The function that retrieves the information from the server has its own timeout, and exception is thrown when the timeout gets exceeded.
Now, when the activity is loaded, I want the function to be fired, and a progressDialog to be shown while the function works, and to be hided if the function is done working, or if a timeout exception was thrown.
The problem: I've put the code that do all the functionality described above in the onCreate() function. Nothing is shown on the emulator screen, since the onCreate() function hasn't finished running...
I've also tried to put the code in the onStart() function... same unwanted results...
I'm trying to avoid using of threads, because the functionality needs many variables that the thread will not has access to them...
How can i implement the wanted behavior??
Thanks.
Use AsyncTask with ProgressDialog bounded:
http://it-projects.spb.ru/?p=150&lang=en
Create a class implementing Runnable and put all your load logic in there. Call a function in the activity when finished (lets say onFinished(params...))
Create a UI Handler and get the handler to update UI in onFinished(params...)
Create a thread in onCreate and start it there to call your Runnable.

ResourceNotFound on layout inflation

My app may launch a sub-activity for a specific purpose. When that activity finishes, I get the results in onActivityResult. These results are then processed in the subsequent onResume. This consists of a setContentView and also starting an AsyncTask that puts up a ProgressDialog.
This all works well when initiated the normal way, which is via a user request (i.e., menu selection) after the app is up and running. However, under some conditions I need to do this right as the app is starting up, so I initiate this sequence right from my onCreate. What then happens is that I get fatal ResourceNotFound errors within any o/s call that implicitly calls the layout inflater. I got around this with setContentView by pre-inflating the view in my onCreate method, but the AsyncTask's onPreExecute still fails on ProgressDialog.show() as it "fails to find" Android's own progress_dialog.xml!
Anyone know what's happening here?
I suspect it's something to do with the timing, where this is occurring before the main activity has even had a chance to display its screen. These calls are all being made on the main UI thread, but maybe something hasn't completed within the o/s under these conditions.
As a closeout, the problem turned out to be totally unrelated to what I described in my post. Turns out it was due to blindly using some code that had been posted in some online forum showing how to get and use AssetManager. Trouble is, at the end of the block of code he had put "assMan.close()". Well, this closes the asset manager for the entire activity and resources can no longer be accessed!
It took a while to find it since it was not something that I did via my own understanding.

Categories

Resources