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.
Related
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.
I'm working on an Android app by adding a new functionality that fetch and save data with API calls.
These calls are made in a Fragment. There is a call made in an AsyncTask, and I don't want to create an AsyncTask for every call, so I just try send parameters to my controlles in some function, but when I debug every time I try to make a call without using an AsyncTask, I got an IOException "Cancelled". Is there a way to do this without using AsyncTasks in the same Fragment?
This is the AsyncTask:
private void validateUnit(#NonNull String unitCode, final int routeId, final boolean goodCondition) {
mUnitDetails = new UnitDetails();
if (mFindUnitAysncTask != null) {
mFindUnitAysncTask.cancel(true);
}
mFindUnitAysncTask = new AsyncTask<String, Void, FindUnitResponse>() {
#Override
protected void onPreExecute() {
super.onPreExecute();
showProgressDialog();
}
#Override
protected FindUnitResponse doInBackground(String... params) {
FindUnitResponse unitResponse = mUnitController.findUnit(params[0], routeId);
FindUnitDetailsResponse unitDetailsResponse = mUnitController.getUnitDetails(
unitResponse.getUnits().get(0), mUser);
if(unitDetailsResponse.isSuccess()) {
mUnitDetails.setBranchCode(unitDetailsResponse.getBranchCode());
mUnitDetails.setBranchName(unitDetailsResponse.getBranchName());
mUnitDetails.setCompanyId(unitDetailsResponse.getCompanyId());
mUnitDetails.setEconomicNumber(unitDetailsResponse.getEconomicNumber());
mUnitDetails.setFuelType(unitDetailsResponse.getFuelType());
mUnitDetails.setFuelTypeId(unitDetailsResponse.getFuelTypeId());
mUnitDetails.setFuelPrice(unitDetailsResponse.getFuelPrice());
mUnitDetails.setModel(unitDetailsResponse.getModel());
mUnitDetails.setBrand(unitDetailsResponse.getBrand());
mUnitDetails.setUnitType(unitDetailsResponse.getUnitType());
mUnitDetails.setRouteCode(unitDetailsResponse.getRouteCode());
mUnitDetails.setRealTrips(unitDetailsResponse.getRealTrips());
mUnitDetails.setMaximumMileageRange(unitDetailsResponse.getMaximumMileageRange());
}
else {
showMessage(unitDetailsResponse.getMessage());
}
return unitResponse;
}
#Override
protected void onPostExecute(FindUnitResponse response) {
super.onPostExecute(response);
dismissProgressDialog();
if (response != null && response.isSuccess()) {
//Unit unit = response.getUnits().get(0);
unit = response.getUnits().get(0);
finishChecklist(unit, goodCondition);
} else {
showMessage(response.getMessage());
saveChecklist();
}
}
#Override
protected void onCancelled() {
super.onCancelled();
dismissProgressDialog();
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, unitCode);
}
With that I fetch the details of a vehicle. Then I have a method called validateMileage.
private void validateMileage(#NonNull Unit unit, #NonNull User user, #NonNull int mileage, int travels,
final boolean dayFinished) {
List<Incident> incidents = mIncidentController.getIncidentList();
Incident suspiciousMileageIncident = mIncidents.get(2);
List<Manager> managers = mManagersController.findByIncidentId(suspiciousMileageIncident.getId());
.....
}
If I just try to make calls like .getIncidentsList or .findByIncidentId I got an IOException when I wait for the response. But if I make the call in an AsyncTask, there is not errors.
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);
}
I am trying to use ProgressDialog. when i run my app the Progress Dialog box show and disappear after 1 second. I want to show it on completion of my process.. Here is my code:
public class MainActivity extends Activity {
android.view.View.OnClickListener mSearchListenerListener;
private ProgressDialog dialog;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new YourCustomAsyncTask().execute(new String[] {null, null});
}
private class YourCustomAsyncTask extends AsyncTask <String, Void, Void> {
protected void onPreExecute() {
dialog = new ProgressDialog(MainActivity.this);
dialog.setMessage("Loading....");
dialog.setIndeterminate(true);
dialog.setCancelable(true);
dialog.show(); //Maybe you should call it in ruinOnUIThread in doInBackGround as suggested from a previous answer
}
protected void doInBackground(String strings) {
try {
// search(strings[0], string[1]);
runOnUiThread(new Runnable() {
public void run() {
// updateMapWithResult(); //Or call it onPostExecute before progressDialog's dismiss. I believe this method updates the UI so it should run on UI thread
}
});
} catch(Exception e) {
}
}
#Override
protected void onPostExecute(Void params) {
dialog.dismiss();
//result
}
#Override
protected Void doInBackground(String... params) {
// TODO Auto-generated method stub
return null;
}
}
}
Updated Question:
#Override
public void onCreate(SQLiteDatabase db) {
mDatabase = db;
Log.i("PATH",""+mDatabase.getPath());
mDatabase.execSQL(FTS_TABLE_CREATE);
loadDictionary();
}
/**
* Starts a thread to load the database table with words
*/
private void loadDictionary() {
new Thread(new Runnable() {
public void run() {
try {
loadWords();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}).start();
}
private void loadWords() throws IOException {
Log.d(TAG, "Loading words...");
for(int i=0;i<=25;i++)
{ //***//
final Resources resources = mHelperContext.getResources();
InputStream inputStream = resources.openRawResource(raw_textFiles[i]);
//InputStream inputStream = resources.openRawResource(R.raw.definitions);
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
try {
StringBuilder sb = new StringBuilder();
while ((word = reader.readLine()) != null)
{
sb.append(word);
// Log.i("WORD in Parser", ""+word);
}
String contents = sb.toString();
StringTokenizer st = new StringTokenizer(contents, "||");
while (st.hasMoreElements()) {
String row = st.nextElement().toString();
String title = row.substring(0, row.indexOf("$$$"));
String desc = row.substring(row.indexOf("$$$") + 3);
// Log.i("Strings in Database",""+title+""+desc);
long id = addWord(title,desc);
if (id < 0) {
Log.e(TAG, "unable to add word: " + title);
}
}
} finally {
reader.close();
}
}
Log.d(TAG, "DONE loading words.");
}
I want to show ProgressDialogue box untill all words are not entered in the database. This code is in inner calss which extends SQLITEHELPER. so how to can i use ProgressDialogue in that inner class and run my addWords() method in background.
You cannot have this
runOnUiThread(new Runnable() {
public void run() {
// updateMapWithResult(); //Or call it onPostExecute before progressDialog's dismiss. I believe this method updates the UI so it should run on UI thread
}
});
in your doInBackground().
Progress dialog doesn't take priority when there is some other action being performed on the main UI thread. They are intended only when the actions are done in the background. runonUIthread inside doInBackground will not help you. And this is normal behavior for the progressdialog to be visible only for few seconds.
You have two doInBackground() methods inside your AsyncTask Class. Remove the runOnUiThread() from First doInBackground() and move it to second doInBackground() which has #Override annotation.
I don't know whether you wantedly written two doInBackground() methods or by mistake but it is not good to have such confusion between the Method. Your AsyncTask is not calling the first doInBackground() and it will call doInBackground() which has #Override annotation. So your ProgressDialog is dismissed in 1 second of time as it returns null immediately.
In my app I performing loading data from web and then displaying it to user. Before loading data app shows progress dialog. I have problem if user locks phone in the middle of loading operation, or server is overloaded and can't respond in time my application freezes, because it doesn't dismiss progress dialog, or in some cases it crashes because lack on needed data.
If some error happened while loading data I want show some dialog to user to let him know about error and ask him should application repeat last request. I tried to use AlertDialog for it, but I haven't succeed.
Here is code of one activity (There is no progress dialog here, but it demonstrates how I loading data):
#EActivity(R.layout.layout_splash)
#RoboGuice
public class SplashScreenActivity extends Activity {
#Inject
private AvtopoiskParserImpl parser;
#Bean
BrandsAndRegionsHolder brandsAndRegionsHolder;
#ViewById(R.id.splash_progress)
ProgressBar progressBar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
loadData();
}
#Background
protected void loadData() {
publishProgress(10);
LinkedHashMap<String, Integer> brands = null;
try {
brands = parser.getBrands();
} catch (IOException e) {
Log.e(e.getMessage());
}
publishProgress(50);
LinkedHashMap<String, Integer> regions = null;
try {
regions = parser.getRegions();
} catch (IOException e) {
Log.e(e.getMessage());
}
publishProgress(70);
populateData(brands, regions);
}
#UiThread
protected void populateData(LinkedHashMap<String, Integer> brands, LinkedHashMap<String, Integer> regions) {
Intent intent = new Intent(SplashScreenActivity.this, SearchActivity_.class);
brandsAndRegionsHolder.brandsMap = brands;
brandsAndRegionsHolder.regionsMap = regions;
publishProgress(100);
startActivity(intent);
finish();
}
#UiThread
void publishProgress(int progress) {
progressBar.setProgress(progress);
}
}
parser.getBrands() and parser.getRegions() are loading data from the web.
I want to do something like this:
boolean repeatRequest = true;
while (repeatRequest) {
try {
brands = parser.getBrands();
repeatRequest = false;
} catch (IOException e) {
Log.e(e.getMessage());
repeatRequest = showErrorDialog();
}
}
But I didn't manage to do so because this code executes in background thread, but dialog should be shown in UI thread.
I believe that it should be standard approach of doing so, but didn't manage to find it.
Any ides how can I implement this?
The best way is to use AsyncTask.
private class LoadDataTask extends AsyncTask<Void, Integer, Object> {
private ProgressDialog mProgress;
protected Object doInBackground(Void... params) {
// This method runs in background
Object result = null;
try {
result = parser.parse();
} catch (Exception e) {
result = e.getMessage();
}
return result;
}
protected void onProgressUpdate(Integer... progress) {
// This method runs in UI thread
mProgress.setProgress(progress[0]);
}
protected void onPreExecute() {
// This method runs in UI thread
mProgress = new ProgressDialog(context);
mProgress.show();
}
protected void onPostExecute(Object result) {
// This method runs in UI thread
mProgress.dismiss();
if (result instance of String) {
// Here you can launch AlertDialog with error message and proposal to retry
showErrorDialog((String) result);
} else {
populateData(result);
}
}
}