I'm developing an Android+Firebase app but since I'm new to both techlogies I'm having a problem regarding async calls and I haven't found the solution yet so hope you can help me.
I have the following snippet in my Activity's onCreate method:
final ArrayList<AssetLocation> assetsLocations = new ArrayList<>();
DatabaseReference assetsLocationReference = database.getReference(FirebaseReferences.ASSETSLOCATION_REFERENCE);
assetsLocationReference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
ArrayList<String> assetsLocationSpinner = new ArrayList<String>();
// Create an ArrayAdapter using the string array and a default spinner layout
for (DataSnapshot postSnapshot : snapshot.getChildren()) {
//Getting the data from snapshot
AssetLocation assetsLocation = postSnapshot.getValue(AssetLocation.class);
assetsLocations.add(AssetLocation);
}
}
#Override
public void onCancelled(DatabaseError firebaseError) {
System.out.println("The read failed: " + firebaseError.getMessage());
}
});
I also have a pretty similar call but instead of getting Locations, I'm getting Types.
After this code (inside the onCreate method as well), I'm calling setScreenInfo which is a function to fill both spinners (and do more stuff) with said data but since it is an async call, the spinners are blank when I execute it.
How can I execute setScreenInfo once the calls are made? I tried with .once()/.on() but it's not being recognised by Android Studio as a function.
Thanks for your help!
After this code (inside the onCreate method as well), I'm calling setScreenInfo which is a function to fill both spinners (and do more stuff) with said data but since it is an async call, the spinners are blank when I execute it.
You aren't allowed to modify the UI from a background thread. Normally I would make a call to runOnUIThread, passing a new Runnable() and a final copy of the data I want to pass.
final String myData = "updateData";
ActivityName.this.runOnUiThread(new Runnable() {
public void run() {
// use myData to update UI
}
});
However, it seems like there's a migration to AsyncTask : Converting runOnUiThread to AsyncTask
I personally still use runOnUIThread. It's more explicit.
Because of the asynchronously behaviour, you need to move the declaration of the assetsLocations ArrayList:
final ArrayList<AssetLocation> assetsLocations = new ArrayList<>();
inside the onDataChange() method, as assetsLocationSpinner ArrayList is. So remember, onDataChange is always called asynchronously. This means that the statement that adds objects of AssetLocation class to the list is executed before onDataChange has been called. That's why your list is empty outside that method.
For other approach, please visit this post and this post.
Hope ot helps.
You can use thread:
thread = new Thread() {
#Override
public void run() {
try {
//background tasks
runOnUiThread(new Runnable() {
#Override
public void run() {
//UI tasks
}
});
}
catch (JSONException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
};
thread.start();`
Or you can use Firebase Multi Query method.
link: Only load layout when firebase calls are complete
Related
I'm building a simple Android Application. I want to parse JSON. I have everything working using Button. But I want call it without using Button. It need to be get the JSON first from url and then Parse it. I have function getJson() and parseJSON(). First I want getJSON to be called and after 5 second parseJSON(). here is my code:
Runnable[] methods = new Runnable[]{
new Runnable() {
#Override
public void run() {
Toast.makeText(SheduleActivity.this, "This is gettin JSON", Toast.LENGTH_LONG).show();
getJSON();
}
},
new Runnable() {
#Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},
new Runnable() {
#Override
public void run() {
Toast.makeText(SheduleActivity.this, "This is Parsing JSON", Toast.LENGTH_LONG).show();
parseJSON();
}
}
};
for(Runnable r : methods)
//r.run();
service.submit(r);
service.shutdown();
while (!service.isTerminated()) {}
Toast.makeText(SheduleActivity.this,"Finished all threads", Toast.LENGTH_LONG).show();
Everytime I run my app only getJSON() method calls and stops. Any suggetions?
I think you need to use AsyncTask. put getJson() in doInBackground block and parseJSON() on onPostExecute block. hope this help.
Android does not suggest to use sleep(5000) for delay. Use handler to do this. See the documentation of Handler.
For example in your code,
Handler handler = new Handler( /* --- */ );
handler.postDelayed(methods[i], 5000);
Also for getting json from web, it is suggested to use Asynctask to make the UI responsive. Best tutorial for Asynctask is here.
I have a method that loads data from Firebase into ArrayList. After this,I use that ArrayList to construct RecyclerView. I've decided to load data on another thread. Below is my code:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_just);
citiesRecyclerView =
(RecyclerView)findViewById(R.id.recyclerView);
handler = new Handler()
{
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if(msg.what==1)
{
cityAdapter = new
CityAdapter(MainActivity.this,cities) ;
citiesRecyclerView.setAdapter(cityAdapter);
}
}
};
t = new Thread(new Runnable() {
#Override
public void run() {
//method that loads data into List.If this method was
//successfully done,then I send message 1 to Handler
loadDataFromFirebase();
}
});
t.start();
//other operations below
}
Hope,that everything understandable. Code works fine. And my problem is that I need to use loadDataFromFirebase method in thread again. I wanted to call t.start() again in order to call loadDataFromFirebase method,but there was error that thread already started. I checked that by writing this code:
if(t.getState()== Thread.State.NEW)
t.start();
else
someMethod();
else statement worked above.
And my questions are:
1) Does loadDataFromFirebase method work really on another thread by this way?
2) How to call loadDataFromFirebase method again in another thread, if something happened? Do I need to create another variable for Thread again?
It's not a good idea to handle all low-level thread work by your own.
Accroding to Android you could:
Use AsyncTask (but notice that they have many drawbacks such as context leak in some cases etc),
I could suggest you to get into RxJava - it's a painless way to use async work in your app.
To 'download' data from Firebase you could probably use FCM (push notifications) to load data on demand.
And what about your question:
"It is never legal to start a thread more than once. In particular, a thread may not be restarted once it has completed execution."(c) http://docs.oracle.com/javase/6/docs/api/java/lang/Thread.html#start()
If you are using firebase SDK you can use realtime database feature, so do not need to query it each time.
You should just subscribe one time and get updates. For example:
firebaseReference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// This method is called once with the initial value and again
// whenever data at this location is updated.
YourDataObject value = dataSnapshot.getValue(YourDataObject.class);
Log.d(TAG, "Value is: " + value);
}
#Override
public void onCancelled(DatabaseError error) {
// Failed to read value
Log.w(TAG, "Failed to read value.", error.toException());
}
});
You can read docs here.
I have over 10 fragments that execute the same kind of task which is :
Retrieving the Data from the server using Retrofit
Starting an Async Task to update the Database (Using ORMLite)
Once the Database is updated, retrieving the new data from the Database
Notify Dataset has changed in the adapter
I'm wondering if it's useless to put the update database code inside an AsyncTask within my fragment once I retrieve the data from the server?
I have trouble understanding what run on the UI thread and what doesn't and should be started as his own thread through an AsyncTask
Here my code:
private void getLocalIncidentTemplate() {
mIncidentTemplate.clear();
mIncidentTemplate.addAll(GenericDAO.getInstance(EntityGroup.class).queryForAll());
Collections.sort(mIncidentTemplate);
Log.e(TAG, "Incident Template count:" + mIncidentTemplate.size());
mListAdapter.notifyDataSetChanged();
spinner.setVisibility(View.GONE);
}
private void getRemoteIncidentTemplate() {
Call<EntityIncident> call = meepServices.getIncidentTemplate();
call.enqueue(new Callback<EntityIncident>() {
#Override
public void onResponse(Call<EntityIncident> call, Response<EntityIncident> response) {
if (response.isSuccessful()) {
new updateIncidentTemplateTask().execute(response.body());
}
}
#Override
public void onFailure(Call<EntityIncident> call, Throwable t) {
t.getStackTrace();
Log.e(TAG, t.toString());
Utils.showToastMessage(getActivity(), "Error retrieving Incidents", true);
}
});
}
private class updateIncidentTemplateTask extends AsyncTask<EntityCategories, Void, Boolean> {
#Override
protected Boolean doInBackground(EntityCategories... params) {
updateIncidents(params[0]);
return true;
}
protected void onPostExecute(Boolean b) {
getLocalIncidentTemplate();
spinner.setVisibility(View.GONE);
}
}
Here is the Database Update Using ORMlite:
private void updateIncident(EntityCategories categories) {
try {
categories.setId("MobilePlan");
//Update base categories
GenericDAO.getInstance(EntityCategories.class).addOrUpdate(categories);
for (EntityCategories.EntityCategory currentCategory : new ArrayList<>(categories.getCategories())) {
if (currentCategory.getmPlans() != null) {
for (EntityPlan myPlan : new ArrayList<>(currentCategory.getmPlans())) {
EntityPlan oldPlan = GenericDAO.getInstance(EntityPlan.class).queryById(String.valueOf(myPlan.getmId()));
myPlan.setCategories(currentCategory);
if (oldPlan != null) {
if (!myPlan.getmDateModification().equals(oldPlan.getmDateModification())) {
GenericDAO.getInstance(EntityPlan.class).addOrUpdate(myPlan);
}
} else {
GenericDAO.getInstance(EntityPlan.class).addOrUpdate(myPlan);
}
}
} else {
continue;
}
GenericDAO.getInstance(EntityLabel.class).addOrUpdate(currentCategory.getmLabel());
currentCategory.setCategories(categories);
GenericDAO.getInstance(EntityCategories.EntityCategory.class).addOrUpdate(currentCategory);
}
} catch (Exception e) {
e.printStackTrace();
}
Log.d(TAG, "DATA updated");
}
For your particular case, you should use the AsyncTask to retrieve data from the backend and place it in the database.
Remember that AsyncTask has three main methods:
onPreExecute() that runs on the UI thread. Useful when you need to prep something that requires UI thread (touching views and whatnot)
doInBackGround() this runs on background thread
onPostExecute() runs also on the UI thread.
In onPostExecute() you could notify your adapter of the new data.
If I were you, I'd use loaders to get notified and retrieve the data off the database. So that the complete chain would be some:
AsyncTask pulls data from the backend and stores it in the database
Your loader will get notified that something changed inside the database and will pull the data from it and call onLoadFinished() method inside your activity/fragment
onLoadFinished() passes the data to the view adapter.
I haven't gone into detail as to how to implement this. I just presented the overall architecture.
I have trouble understanding what run on the UI thread and what doesn't and should be started as his own thread
The short answer:
Everything that might block the UI thread (in other words, might take time) should run on a worker thread (threadpool or dedicated)
DB actions and network requests are classic examples for actions that should always run asynchronously.
In your case I would just use an ORM to wrap all the interaction with the DB, such as ORMlite or any other you find more suitable, in that case you will not have to concern yourself with the inner workings and just provide callbacks for when your calls have finished (successfully or not)
I having 2 classes ,
1.Activity class
2.Service class
I need to update my list view in my activity,when service got any updates.
Actually i trying like an chat application , My services always checking my db and if it got any new string , i need to update in my activity without rebuild the again only i need to refresh the list view. i found it will be manipulated using iBinder , But i don't how to use it. Can any one suggest me with some examples of code .
referred pages
You should use a Bound Service. I did the something similar in my application. Where upon clicking refresh, I invoke a service which gets data in background and updates the UI.
Check out my service here:
https://github.com/madhur/GAnalytics/blob/develop/src/in/co/madhur/ganalyticsdashclock/AnalyticsDataService.java
#Override
protected void onPostExecute(AnalyticsAccountResult result) {
super.onPostExecute(result);
App.getEventBus().post(result);
}
Activity:
https://github.com/madhur/GAnalytics/blob/develop/src/in/co/madhur/ganalyticsdashclock/MainActivity.java
#Subscribe
public void UpdateUI(AnalyticsAccountResult result) {
ProgressBar progressbar = (ProgressBar) findViewById(R.id.pbHeaderProgress);
LinearLayout spinnerLayout = (LinearLayout) findViewById(R.id.spinnerslayout);
TextView statusMessage = (TextView) findViewById(R.id.statusMessage);
switch (result.getStatus()) {
case STARTING:
statusMessage.setVisibility(View.GONE);
progressbar.setVisibility(View.VISIBLE);
spinnerLayout.setVisibility(View.GONE);
break;
case FAILURE:
statusMessage.setVisibility(View.VISIBLE);
progressbar.setVisibility(View.GONE);
spinnerLayout.setVisibility(View.GONE);
statusMessage.setText(result.getErrorMessage());
break;
case SUCCESS:
statusMessage.setVisibility(View.GONE);
progressbar.setVisibility(View.GONE);
spinnerLayout.setVisibility(View.VISIBLE);
if (result.getItems() != null)
{
this.acProfiles = result.getItems();
MyAdapter myAdapter = new MyAdapter(acProfiles, this);
listView.setAdapter(myAdapter);
UpdateSelectionPreferences();
if (result.isPersist() && acProfiles.size() > 0)
{
if (App.LOCAL_LOGV)
Log.v(App.TAG, "saving configdata");
try
{
appPreferences.saveConfigData(acProfiles, credential.getSelectedAccountName());
}
catch (JsonProcessingException e)
{
Log.e(App.TAG, e.getMessage());
}
}
}
break;
}
}
It would also helpful to use Otto library:
http://square.github.io/otto/
Let's suppose you have the activity class named MainActivity where you initialized your ListView with the adapter named listviewAdapter. Put this code inside MainActivity:
public static Handler UIHandler;
static {
UIHandler = new Handler(Looper.getMainLooper());
}
public static void runOnUI(Runnable runnable) {
UIHandler.post(runnable);
}
When you made changes to your listview data inside your service class, write this code to apply new data to the ListView:
MainActivity.runOnUI(new Runnable()
{
public void run()
{
try
{
MainActivity.listviewAdapter.notifyDataSetChanged();
}
catch (Exception e)
{
e.printStackTrace();
}
}
});
Without more information I cannot provide any useful code examples, however I think what you may be looking for is a ListAdapter. A ListAdapter takes a listview and a dataset (in your case maybe an array of strings) and combines the 2. Whenever the dataset changes (in your case this would be when your service detects a new string and adds it to the array) you just call ListAdapter.notifyDataSetChanged() and the listview will be automatically updated with your new information.
Check http://developer.android.com/reference/android/widget/ArrayAdapter.html for more info on the specific ListAdapter you might use.
I have one function: fillFromDB which fills an instances of ArrayList called list from the SQLite DB.
I also have another Thread which is suppose to clear that list and re-populate it.
I'm having some issues with the list, since sometimes the other Thread clear the list while the first one is still filling it, causing IndexOutOfBoundsException on an Adapter.
// This occurs onCreate()
fillFromDb(list);
downloadAndRepopulateList(list);
private void downloadAndRepopulateList(List list) {
new Thread(new Runnable() {
#Override public void run() {
list.clear();
// Some other clode
}
}
}
I know that the issue is cause they are not Thread-safe, but I have never used synchronized before.
So my question is:
if I change the download function like this:
private void downloadAndRepopulateList(List list) {
new Thread(new Runnable() {
#Override public void run() {
synchronized(list) {
list.clear();
// Some other clode
}
}
}
}
will it wait for the UIThread to finish filling the list on fillFromDb() and then proceed to clear and repopulate it?
if not, how should I do this?
Though I would rather recommend using LinkedBlockingQueue. i.e.
list = new LinkedBlockingQueue<SomeType>();
fillFromDb(list);
downloadAndRepopulateList(list);
private void downloadAndRepopulateList(List list) {
new Thread(new Runnable() {
#Override public void run() {
list.clear();
// Some other clode
}
}
}
LinkedBlockingQueue will itself manage synchronization for you and you dont need to hold any lock etc.
If you wish to make it using Arraylist, you will have to make it atomic from both the sides. i.e. to access list, they should first hold the lock to object and the proceed. If someone else holds the lock, then wait.
synchonized(list){
fillFromDb(list);
downloadAndRepopulateList(list);
}
private void downloadAndRepopulateList(List list) {
new Thread(new Runnable() {
#Override public void run() {
synhronized(list){
list.clear();
}
// Some other clode
}
}
}
To make it work you also have to make synchronization on your fillFromDb method. So in the end you would have two synchronized parts, one that synchronizes your filling of the list and another synchronizes clear of the list. Also you can use your list as a mutex, but I would recommend you to have a separate object as mutex, it could me just like Object mutex = new Object();.