Android: setRefreshing(false) on DDP callback hangs app - android

Here is the code:
public void onRefresh() {
MyDDPState.getInstance().getItems(null, new DDPListener() {
#Override
public void onResult(Map<String, Object> json) {
Log.d(TAG, "refreshed");
if (json.get("result") == null) {
/*
* Result is null action
*/
Log.d(TAG, "Null");
swipeLayout.setRefreshing(false);
return;
}
List<Map<String, Object>> temp = (ArrayList) json.get("result");
Log.d(TAG, temp.toString());
MyDDPState.getInstance().initItems(temp);
Log.d(TAG, "converted" + MyDDPState.getInstance().getItems().toString());
Log.d(TAG, swipeLayout.toString());
Log.d(TAG, "Finished refreshing");
swipeLayout.setRefreshing(false);
Log.d(TAG, "Refresh closed");
}
});
}
swipeLayout refers to a private variable in the class that holds the SwipeRefreshLayout. On the callback, I try to call setrefreshing(false) to get rid of the progress indicator, but this call hangs the async function. All the other Logs work except for the "Refresh closed" log.
For some reason, I think because of the library I'm using, errors inside DDP Listeners are not logged either, so I can't trace it. swipeLayout.setRefreshing when called outside of the DDP call work fine.

Anyway, I managed to solve the issue.
When I tried to change a ui variable, the function would stop running.
Turns out the issue was that I was changing UI variables on the wrong thread. To fix it, you have to make the UI calls inside a ui thread by calling:
activity.runOnUiThread(new Runnable() {
#Override
public void run () {
/*
* UI code
*/
}
});
More info here: http://developer.android.com/tools/testing/activity_testing.html

Related

How to wait for async APIs in Android?

In android, there are many async APIs such as WebView's evaluateJavascript, which will Asynchronously evaluates JavaScript in the context of the currently displayed page. Usually an execution will just proceed to the successive statements after the call of an async API without any waiting.
But how can I wait until this call finishes its executing, before proceeding to the successive statements. For example,
webview.evaluateJavascript("JS code", new ValueCallback<String> {
public void onReceiveValue(String value) {
//get JS return here
}
});
//Remaining code
How can I make sure the remaining code is executed after webview.evaluateJavascript has finished its executing (i.e., its callback onReceiveValue has finished its executing).
Edit: To be more precise, what I want is that remaining code should be executed after onReceiveValue has finished executing.
I find out a workaround by using JavaScript interface. The idea is that we create a bridge class that contains a method that takes the javascript execution result as input. Then we can obtain the result at the Java end. This method works because bridge methods are invoked by JavaScript code, which is run on another thread. We only need to wait on the UI thread for a little milliseconds, then the result is here for you. The following code is an illustration:
class Bridge {
public String result = null;
#JavascriptInterface
public void putJsResult(String result) {
this.result = result;
}
public String getJsResult() {
return this.result;
}
}
Bridge bridge = new Bridge();
wv.addJavascriptInterface(bridge, "bridge");
webview.evaluateJavascript("bridge.putJsResult(func())", null);
Thread.sleep(100);
//Result is there
String result = bridge.getJsResult();
When you have to wait for code execution, a simple class to use is CountDownLatch.
An example for your problem can be:
public class AboutActivity extends Activity {
private volatile CountDownLatch jsLatch = new CountDownLatch(1);
private volatile String jsReceivedValue = null
initWebView() {
// webview init
...
webview.evaluateJavascript("JS code", new ValueCallback<String> {
public void onReceiveValue(String value) {
//get JS return here
jsReceivedValue = value
jsLatch.countDown();
}
});
try {
// wait 60 seconds or assume there was some problem during the loading
jsLatch.await(60, TimeUnit.SECONDS);
} catch (InterruptedException e) {
// thread interrupted or time elapsed
}
if (jsReceivedValue == null) {
// show "problem during loading"
} else {
//Remaining code
}
}
}
Note that waiting for code execution on main thread, can lead to unresponsive app.
You can show a loading spinner while using a simple thread to avoid this:
new Thread(new Runnable() {
#Override
public void run() {
initWebView();
}
}).start();

OKHttp crashes on when switching Fragments

I have no formal coding education so please let me know if something I did is considered bad coding
I am using a bottom navigation view for a train schedule application I am making. One of the options is a nearby functionality - when nearby is clicked on the bottom navigation drawer, the NearbyFragment is launched and my server gets a request via OKHttp automatically without additional user interaction
The problem is that if you switch from Nearby to another button on the bottom navigation view OR if you tapped the nearby button multiple times, the app would crash. Initially, this was because the runOnUiThread method wouldn't be able to find the UI thread since I moved on to a different fragment (and presumably the main thread no longer existed). I tried fixing this by creating a 1 second delay before the getLocation method was called in onViewCreated
// Delay before calling on location to prevent crashing when rapidly switching from Nearby Fragment
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
if (getActivity() != null) {
getLocation();
}
}
}, 1000);
I also wrapped the above, as well as OKHttp onSuccess and onFailure methods in an if statement (getActivity != null) to check that the UI thread still existed before proceeding
#Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
final String myResponse = response.body().string();
if (getActivity() != null) {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
processJSON(myResponse);
}
});
}
}
}
I would still get crashes associated with the onFailure method with NullPointerExceptions and cannot find views (such as the snackbar) IF I changed from the NearbyFragment to a different fragment after 1 second of loading but before the loading had finished, so I wrapped the onFailure in a try/catch to catch all exceptions and ignore them. This feels like a hack, but my app no longer crashes/acts as expected when switching from from the NearbyFragment to another fragment via bottom navigation.
#Override
public void onFailure(Call call, IOException e) {
try {
mSwipeToRefresh.setRefreshing(false);
Log.e("Main", e.toString());
mSnackbar = Snackbar.make(getActivity().findViewById(R.id.swipe_container_nearby), "ERROR!", Snackbar.LENGTH_INDEFINITE);
snackBarCoordinator(mSnackbar);
mSnackbar.show();
} catch (Exception j) {
Log.e("Failed Get Closest Stations ", j.toString());
}
}
Is there a cleaner was to stop the crashes without the combination of three failsafes i.e., handler.postDelayed, (getActivity() != null), and an empty try/catch block?
To know whether activity or fragment is on UI.
For Activity, you can call isFinishing()
For Fragment call isAttached().
i.e before show loader, snack bar or any widgets wrap with if(...) {}
if(isAttached()) { snackbar.show(); }

How to call method in thread time after time?

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.

AsyncTask in Fragments to perform Database update

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)

How to wait for webview.post to complete?

In the below code, outside is printed before inside. I want the order to be inside first and then outside. So how do I ensure that the Runnable is finished before the second Log is reached?
webView.post(new Runnable() {
#Override
public void run() {
Log.d("check", "inside");
}
});
// some code needed here
Log.d("check", "outside");
Some code must be inserted where to comment is to achieve this.
EDIT: I am doing all the work in a background service.
[P.S.: Those who are curious as to why I am doing this, it is so because unless I add the webview.post, I keep getting the following error: "All WebView methods must be called on the same thread.". Anyway, this shouldn't affect you from answering the question.]
You might try using a CountDownLatch:
final CountDownLatch latch = new CountDownLatch(1);
webView.post(new Runnable() {
#Override
public void run() {
Log.d("check", "inside");
latch.countDown();
}
});
// some code needed here
latch.await();
Log.d("check", "outside");
However, you wouldn't want to use latch.await() on the UI thread, as that's a blocking method. If you wanted to run this on the UI thread it would be best to replace latch.countDown() with a call to a callback method, which would in turn run Log.d("check", "outside").

Categories

Resources