Making an app Truly Async (Android) - android

I am building an application that is pretty dependent on async requests to function.
I have the main Activity called MainActivity. This really doesn't do much apart from contain layouts, however it does have a recycleviewer.
I then have a couple of http requests that are done to populate the recycle viewer.
To do this I have wrote a java class as follows:
public class dataforUi extends AsyncTask<String, String, JsonObject> {
private ArrayList<UiElements> els;
protected void onPreExecute() {
progressDialog.setMessage("Downloading your data...");
progressDialog.show();
progressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
public void onCancel(DialogInterface arg0) {
RedditRequests.this.cancel(true);
}
});
}
protected JsonObject doInBackground(String... params) {
Do the http request here, get the result and populate uiElements with it
}
#Override
protected void onPostExecute(JsonObject jsonObject) {
super.onPostExecute(jsonObject);
progressDialog.hide();
}
I have a few more classes like this but hopefully it serves as an example of what I'm trying to do.
Then back in Main Activity, I have this code:
public void getUiElements() {
dataforUi ui = new dataforUi(findViewById(android.R.id.content));
try {
ui.execute("https://t").get();
ArrayList<UiElements> r = ui.getEls();
Log.d("MainFeedScreen", "Size of r is:" + r.size());
UiAdapter = new UiAdapter(r);
mRecyclerView.setAdapter(UiAdapter);
} catch (Exception e) {
}
}
This works fine, but it is very jolty due to the use of .get() on execute to make it blocking. If i remove .get() the progress bar shows up and disappears when the task is done, but my ui thread has progressed past this and ha tried to populate my view with an Empty Array and therefore nothing shows.
I have done a bit of looking into it but cant find a conclusive way of managing the notification of the UI thread that an activity is done.
Would really appericiate any advice on this one.

You need to fix your design.
On post execute, use local broadcast to notify your MainActivity that the AsyncTask is done.

Try using a separate thread to process your data. I use a ListView in stead of a RecyclerView, but the principle is the same.
I have no problems with jolting views.
protected void onPostExecute(String result) {
final String value = result;
// dismiss the dialog after getting all data
progressDialog.dismiss();
if (!value.isEmpty()) {
// updating UI from a new thread
runOnUiThread(new Runnable() {
public void run() {
// ListData is my custom class that holds my data
ArrayList<ListData> arrayDriverListData = new ArrayList<ListData>();
ListDataAdapter adapter = new ListDataAdapter(ListActivity.this, arrayListData);
ListData data;
boolean b = true;
try {
// My data is from a Json source from node 'history'
JSONObject object = new JSONObject(value);
JSONArray array = object.getJSONArray("history");
int len = array.length();
if (len > 0) {
for (int i = 0; i < len; i++) {
final JSONObject o = array.getJSONObject(i);
// Parse my data and add it to my adapter
adapter.add(data);
}
}
} catch (JSONException jex) {
Log.e(TAG, "" + jex.getMessage());
}
// setListAdapter is my call to update my list view
setListAdapter(adapter);
}
});
}
}
Now just update the UI thread
private void setListAdapter(ListDataAdapter adapter){
// my ListView
lvHistory.setAdapter(adapter);
}

Related

SQL query with listview

I am busy with an application where i am getting data from my azure database with sql and storing it in an array. I created a separate class where i get my data and my main activity connects to this class and then displays it.
Here is my getData class:
public class GetData {
Connection connect;
String ConnectionResult = "";
Boolean isSuccess = false;
public List<Map<String,String>> doInBackground() {
List<Map<String, String>> data = null;
data = new ArrayList<Map<String, String>>();
try {
ConnectionHelper conStr=new ConnectionHelper();
connect =conStr.connectionclass(); // Connect to database
if (connect == null) {
ConnectionResult = "Check Your Internet Access!";
} else {
// Change below query according to your own database.
String query = "select * from cc_rail";
Statement stmt = connect.createStatement();
ResultSet rs = stmt.executeQuery(query);
while (rs.next()) {
Map<String,String> datanum=new HashMap<String,String>();
datanum.put("NAME",rs.getString("RAIL_NAME"));
datanum.put("PRICE",rs.getString("RAIL_UNIT_PRICE"));
datanum.put("RANGE",rs.getString("RAIL_RANGE"));
datanum.put("SUPPLIER",rs.getString("RAIL_SUPPLIER"));
datanum.put("SIZE",rs.getString("RAIL_SIZE"));
data.add(datanum);
}
ConnectionResult = " successful";
isSuccess=true;
connect.close();
}
} catch (Exception ex) {
isSuccess = false;
ConnectionResult = ex.getMessage();
}
return data;
}
}
And in my Fragmentactivity.java I simply just call the class as shown here:
List<Map<String,String>> MyData = null;
GetValence mydata =new GetValence();
MyData= mydata.doInBackground();
String[] fromwhere = { "NAME","PRICE","RANGE","SUPPLIER","SIZE" };
int[] viewswhere = {R.id.Name_txtView , R.id.price_txtView,R.id.Range_txtView,R.id.size_txtView,R.id.supplier_txtView};
ADAhere = new SimpleAdapter(getActivity(), MyData,R.layout.list_valence, fromwhere, viewswhere);
list.setAdapter(ADAhere);
list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
HashMap<String,Object> obj=(HashMap<String,Object>)ADAhere.getItem(position);
String ID=(String)obj.get("A");
Toast.makeText(getActivity(), ID, Toast.LENGTH_SHORT).show();
}
});
My problem comes when I want to include the onPreExecute and onPostExecute because I am relatively new to android studio and I do not know where to put the following lines of code:
#Override
protected void onPreExecute() {
ProgressDialog progress;
progress = ProgressDialog.show(MainActivity.this, "Synchronising", "Listview Loading! Please Wait...", true);
}
#Override
protected void onPostExecute(String msg) {
progress.dismiss();
}
You need to get the data from your azure database using a background service or AsyncTask. However, you are defining a class GetData which does not extend AsyncTask and hence the whole operation is not asynchronous. And I saw you have implemented doInBackground method which is not applicable here as you are not extending AsyncTask. I would suggest an implementation like the following.
You want to get some data from your azure database and want to show them in your application. In these kind of situations, you need to do this using an AsyncTask to call the server api to get the data and pass the data to the calling activity using an interface. Let us have an interface like the following.
public interface HttpResponseListener {
void httpResponseReceiver(String result);
}
Now from your Activity while you want to get the data through an web service call, i.e. AsyncTask, just the pass the interface from the activity class to the AsyncTask. Remember that your AsyncTask should have an instance variable of that listener as well. So the overall implementation should look like the following.
public abstract class HttpRequestAsyncTask extends AsyncTask<Void, Void, String> {
public HttpResponseListener mHttpResponseListener;
private final Context mContext;
HttpRequestAsyncTask(Context mContext, HttpResponseListener listener) {
this.mContext = mContext;
this.mHttpResponseListener = listener;
}
#Override
protected String doInBackground(Void... params) {
String result = null;
try {
// Your implementation of getting data from your server
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
#Override
protected void onPostExecute(final String result) {
mHttpResponseListener.httpResponseReceiver(result);
}
#Override
protected void onCancelled() {
mHttpResponseListener.httpResponseReceiver(null);
}
}
Now you need to have the httpResponseReceiver function implemented in the calling Activity. So the sample activity should look like.
public class YourActivity extends AppCompatActivity implements HttpResponseListener {
// ... Other code and overriden functions
public void callAsyncTaskForGettingData() {
// Pass the listener here
HttpRequestAsyncTask getDataTask = new HttpRequestGetAsyncTask(
YourActivity.this, this);
getDataTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
#Override
public void httpResponseReceiver(String result) {
// Get the response callback here
// Do your changes in UI elements here.
}
}
To read more about how to use AsyncTask, you might consider having a look at here.

Freezing app when using AsyncTask

I download a high amount of data from API and want to make it efficient so I get first 100 record in one asyncTask and then in another asyncTask get another several thousands(in 500 hundred portions) The loadListAsynchronously(); looks identicall as loadData function without content,progress,loadContent(); function but this functions are not the problem - without loadListAsynchronously(); app runs smoothly after frezee when download first data. I tried add transaction but that does not help me.
private void loadData() {
DottedProgressBar progressBar = (DottedProgressBar) findViewById(R.id.loadIngDots);
progressBar.startProgress();
content = (LinearLayout)findViewById(R.id.activity_main) ;
progress = (RelativeLayout) findViewById(R.id.progressPage) ;
AsyncTask<String, Void, String> read =new AsyncTask<String, Void, String>() {
SharedPreferences keyValues;
#Override
protected void onPreExecute() {
content.setVisibility(View.GONE);
keyValues = getSharedPreferences(Settings.MODEL_LAST_CALL, Context.MODE_PRIVATE);
DisplayMetrics displaymetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
height = displaymetrics.heightPixels;
width = displaymetrics.widthPixels;
super.onPreExecute();
}
#Override
protected String doInBackground(String... params) {
modelList = new ArrayList<>();
Map<String,String> options= new HashMap<>();
options.put("limit",String.valueOf(AMOUNT_OF_LOADED_modelS));
ApiHelper.getModelWithParams(new Callback<ModelApiEnvelope>() {
#Override
public void onResponse(Call<ModelApiEnvelope> call, Response<ModelApiEnvelope> response) {
Log.i(TAG,"First call model Get response");
final ModelApiEnvelope envelope = response.body();
if(envelope==null)
Toast.makeText(MainActivity.this,getString(R.string.server_down_explanation),Toast.LENGTH_SHORT).show();
else{
try {
final Dao<Model,Integer> modelDAO = getHelper().getmodelDAO();
final Dao<Submodel,Integer> submodelDAO=getHelper().getsubmodelDAO();
TransactionManager.callInTransaction(getHelper().getConnectionSource(),
new Callable<Void>() {
public Void call() throws Exception {
modelList=envelope.getData();
Log.i(TAG,"LoadData loop Start");
for( final model m: modelList){
m.setLogo(m.getLogo()+"?width="+width/2+"&height="+height);
m.setLanguage(m.getLanguage().substring(0,2));
if(m.getLanguage().equals("uk"))
m.setLanguage("ua");
if(m.getsubmodels().size()!=0){
for(final submodel e: m.getsubmodels()){
e.setLanguage(m.getLanguage());
submodelDAO.createOrUpdate(e);
}
}
try {
modelDAO.createOrUpdate(m);
}catch (SQLException e) {e.printStackTrace();}
}
return null;}
});
if(envelope.getData().isEmpty()){
SharedPreferences.Editor editor = sharedPreferences.edit();
long time = System.currentTimeMillis();
editor.putString(Settings.model_LAST_CALL , Long.toString(time));
editor.apply();
}
else
loadListAsynchronously();
} catch (SQLException e) {
Log.i(TAG," message "+e.getMessage()) ; e.printStackTrace();
}}
loadContent();
content.setVisibility(View.VISIBLE);
progress.setVisibility(View.GONE);
}
#Override
public void onFailure(Call<modelApiEnvelope> call, Throwable t) {
Log.i(TAG,"ERROR"+ t.getMessage());
Toast.makeText(MainActivity.this,getString(R.string.server_down_explanation),Toast.LENGTH_LONG).show();
loadContent();
}
},MainActivity.this,options, keyValues.getString(lang,"0"));
return null;
}
#Override
protected void onProgressUpdate(Void... values) {
super.onProgressUpdate(values);
}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
}
};
read.execute();
}
UPDATE: Method Trace added
UPDATE 2: Removing the transaction solve my problem. It seems that the making transaction for thousands saveings into database freeze Ui.
Callback in Retrofit1 and AsyncTask are not compatible. You have to modify your API interface from something like this :
public interface Api {
void getModelWithParams(Callback<Something> callback);
}
To this :
public interface Api {
Something getModelWithParams();
}
Then Retrofit will not provide async execution support and you can execute that row method inside AsyncTask.doInBackground method.
Other option is to stay with that interface definition and just call Retrofit method directly (without AsyncTask wrapping). The question is if your further logic is not heavy, because onResponse will be executed on UI Thread which cause your freezes and in general is root cause of your problem.

How to show array element through timer

I have a String type array. I want to show each element in a TextView after a little time slice that means after certain time the element of array is showing in a TextView again and again. So I am using timer, but it is showing only last element of array. Please solve my problem as soon as possible.
protected void onPostExecute(Void result) {
try {
super.onPostExecute(result);
listAdapter = new ExpandableListAdapter(getActivity(), listDataHeader, headerChild, expListView);
expListView.setAdapter(listAdapter);
int j=0, k=1;
if (prog.isShowing()) {
prog.dismiss();
expListView.setBackgroundColor(0xFFFFFFFF);
String af="";
String cf="";
for(int i=0; i<breakstore2.length; i++){
cf=breakstore2[j];
af= breakstore2[k];
new Handler().postDelayed(new Runnable() {
public void run() {
marquee.setText("Breaking News :" + af);
marquee.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Intent mainIntent = new Intent(getActivity(),h_nextpage2.class);
mainIntent.putExtra("listitem", cf);
startActivity(mainIntent);
}
});
}
}, DISPALY_LENGTH);
j=j+2;
k=k+2;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
The method is updating the UI faster than you wanted...what you can do is that you can create an IntentService and send that service the data that need to be put on your screen.
Create Messenger in your main activity where you need to update the UI and also create an Handler in the same. And from your AsyncTask post the updates to the intentService in that you can delay the updation by sleeping the intentServices' thread and then sending a message to the messenger on the main activity containing the data which you need to show (in your case breaking news) which in turn will do the updation for you.

"Can't create handler inside thread that has not called Looper.prepare()" in AsyncTask

I got a weird problem with an android activity : I re-used one of my previous activity that works well, but this time all I got is "Can't create handler inside thread that has not called Looper.prepare()"
I tried to debug, and everything in the async task is performing well but when I reach then end of onPostExecute() the error is raised.
So I tried to disable my process about the process dialog, the only change is that it's crashing on line upper.
Here is the code :
public class DateActivity extends ActionBarActivity{
ProgressDialog mProgressDialog;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_date);
ActionBar actionBar = this.getSupportActionBar();
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setTitle(getResources().getString(R.string.actionbar_titre_date));
if (VerifConnexion.isOnline(this)) {
this.mProgressDialog = ProgressDialog.show(this, getResources().getString(R.string.loading),
getResources().getString(R.string.loading), true);
new QueryForDateTask().execute(this.mProgressDialog, this, this.getApplicationContext());
} else {
...
}
});
alertDialog.show();
}
}
private class QueryForDateTask extends
AsyncTask<Object, Void, ArrayList<String>> {
private ProgressDialog mProgressDialog;
private Activity act;
private Context context;
protected ArrayList<String> doInBackground(Object... o) {
this.mProgressDialog = (ProgressDialog) o[0];
this.act = (Activity) o[1];
this.context = (Context) o[2];
ArrayList<String> listeDate = this.parseJSON(this.startQuerying());
return listeDate;
}
public JSONObject startQuerying() {
JSONRequest jr = new JSONRequest();
String from = getResources().getString(R.string.api_param_from);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd", Locale.FRANCE);
from += "=" + sdf.format(new Date());
String url = getResources().getString(
R.string.api_dates_json);
JSONObject jo = jr.getJSONFromUrl(url + "?" + from);
return jo;
}
public ArrayList<String> parseJSON(JSONObject jsonObject) {
ArrayList<String> l = new ArrayList<String>();
try {
JSONArray array = jsonObject.getJSONArray("dates");
if (array != null) {
for (int i = 0; i < array.length(); i++) {
String type = array.getString(i);
l.add(type);
} // fin parcours JSONArray
}
} catch (Exception _e) {
}
return l;
}
protected void onProgressUpdate(Integer... progress) {
// setProgressPercent(progress[0]);
}
protected void onPostExecute(ArrayList<String> lDate) {
// Create items for the ListView
DateAdapter adapter = new DateAdapter(this.context, R.layout.searchitem_date, lDate, this.act);
// specify the list adaptor
((ListView)findViewById(R.id.list)).setAdapter(adapter);
this.mProgressDialog.dismiss();
}
} // fin async
}
I tried this to replace the call to the AsyncTask :
runOnUiThread(new Runnable() {
public void run() {
QueryForDateTask task = new QueryForDateTask();
task.execute(DateActivity.this.mProgressDialog, DateActivity.this, DateActivity.this.getApplicationContext());
}
});
(like explained in Asynctask causes exception 'Can't create handler inside thread that has not called Looper.prepare()' as far as I understood), but the result is exactly the same.
So I can't understand why it is not working in this activity despite all is ok for the other ones of the project.
Any clue ?
Thank a lot for all ideas :)
Just a post to mark the trouble as resolved :
the adapter i used was buggy in parsing parameters and throwed a NullPointerException.
I just fixed it, the AsyncTask is now running without problem.

Delivering AsyncTask result to an other activty

I am trying to retrieve my data from Parse.com during splash screen.
I'm making the query in the DoInBackground method and add all objects retrieved in an object vector (which is in an other class).
when moving to the MainActivty all data get loss.
Here's my code:
private class loadDataTask extends AsyncTask<Void, Void, Vector<PartyObj>>{
#Override
protected Vector<PartyObj> doInBackground(Void... params)
{
ParseQuery query = new ParseQuery("partyObj");
query.whereExists("Name");
query.findInBackground(new FindCallback() {
#Override
public void done(List<ParseObject> mNameList, ParseException e) {
if (e == null) {
adapter.partyVector = new Vector<PartyObj>();
for(int i=0; i<mNameList.size(); i++){
PartyObject party = new PartyObject();
party.setIndex(i);
party.setmName(mNameList.get(i).getString("Name").toString());
adapter.partyVector.add(party);
}
}else {
Log.d("mNameList", "Error: " + e.getMessage());
}
}
});
return adpater.partyVector;
}
#Override
protected void onPostExecute(Vector<PartyObj> result) {
progressDialog.dismiss();
setContentView(R.layout.activity_main_menu);
}
}
It seems like you're using the AsyncTask all wrong. query.findInBackground() is as far as I can tell an asynchronous call so you're essentially wrapping one async call in another. The problem with this is that code execution continues immediately after query.findInBackground(), thus ending the doInBackground() method. Since you move to the main activity immediately when the async task is finished it may be that query.findInBackground() hasn't finished yet (thus resulting in an empty/partial list).
Are you really sure you need to wrap everything in an async call? Does either of
ParseQuery query = new ParseQuery("partyObj");
query.whereExists("Name");
take time to execute or is the bulk of the work already in the async findInBackground() call? If so, I'd say skip the AsyncTask and do the view transition in the done() method of the FindCallback.
UPDATE: I've read up on ParseQuery and it clearly says:
Using the callback methods is usually preferred because the network
operation will not block the calling thread. However, in some cases it
may be easier to use the find, get or count calls, which do block the
calling thread. For example, if your application has already spawned a
background task to perform work, that background task could use the
blocking calls and avoid the code complexity of callbacks.
And in findInBackground() it clearly states that:
Retrieves a list of ParseObjects that satisfy this query from the
server in a background thread. This is preferable to using find(),
unless your code is already running in a background thread.
Since you are already wrapping everything in an AsyncTask you should either skip the async task completely since findInBackground( is already running in a background thread or switch to the blocking find() method.
Example without AsyncTask but using async findInBackground():
ParseQuery query = new ParseQuery("partyObj");
query.whereExists("Name");
query.findInBackground(new FindCallback() {
#Override
public void done(List<ParseObject> mNameList, ParseException e) {
if (e == null) {
adapter.partyVector = new Vector<PartyObj>();
for(int i=0; i<mNameList.size(); i++){
PartyObject party = new PartyObject();
party.setIndex(i);
party.setmName(mNameList.get(i).getString("Name").toString());
adapter.partyVector.add(party);
}
progressDialog.dismiss();
setContentView(R.layout.activity_main_menu);
}
else {
Log.d("mNameList", "Error: " + e.getMessage());
}
}
});
Example with AsyncTask and blocking find()
private class loadDataTask extends AsyncTask<Void, Void, Vector<PartyObj>>{
#Override
protected Vector<PartyObj> doInBackground(Void... params)
{
ParseQuery query = new ParseQuery("partyObj");
query.whereExists("Name");
Vector<PartyObject> v = new Vector<PartyObject();
try {
List<ParseObject> queryResult = query.find();
for(ParseObject po : queryResult) {
PartyObject party = new PartyObject();
party.setIndex(partyVector.size());
party.setmName(po.getString("Name").toString());
v.add(party);
}
}
catch(ParseException e) {
Log.d("mNameList", "Error: " + e.getMessage());
}
return v;
}
#Override
protected void onPostExecute(Vector<PartyObj> result) {
adapter.partyVector.addAll(result);
progressDialog.dismiss();
setContentView(R.layout.activity_main_menu);
}
}
I have implemented similar async class for getting parse server data
private class Getneworders_list extends AsyncTask<Void, Void, List<ParseObject>> {
ArrayList<HashMap<String, String>> neworders_hashmap;
#Override
protected void onPreExecute() {
super.onPreExecute();
neworders_recyclerView.getRecycledViewPool().clear();
}
#Override
protected List<ParseObject> doInBackground(Void... params) {
// Create the array
try {
ParseQuery<ParseObject> query = ParseQuery.getQuery("Orders");
ParseObject obj = ParseObject.createWithoutData("ShopLocations", idd);
query.whereEqualTo("shop", obj);
query.whereEqualTo("orderStatus",0);
query.setCachePolicy(ParseQuery.CachePolicy.IGNORE_CACHE);
query.orderByDescending("updatedAt");
object11 = query.find();
} catch (ParseException e) {
Log.d("Error", e.getMessage());
e.printStackTrace();
}
return object11;
}
#Override
protected void onPostExecute(List<ParseObject> result) {
neworders_hashmap = new ArrayList<HashMap<String, String>>();
if(result != null) {
if (result.size() > 0) {
chec(result.size());
m_Runnable.run();
}
}
}
}
use full for some one

Categories

Resources