screen freezing on making big sqlite request using asyncTask - android

I am trying to make a complex SQLite query using AsyncTask and it results in ANR. I can get where the problem is. Here is my AsyncTask class:
private void callBackground(String type, int page, Date date){
SetParamsForQuery params = new SetParamsForQuery(type, page, date);
backgroundTask backgroundTask = new backgroundTask();
backgroundTask.execute(params);
}
private static class SetParamsForQuery {
String type;
int page;
Date sDate;
public SetParamsForQuery(String type, int page, Date date) {
this.type = type;
this.page = page;
this.sDate = date;
}
}
private class backgroundTask extends AsyncTask<SetParamsForQuery, Void, HashMap<String, ArrayList<ArrayList<esProperty>>>>
{
#Override protected void onPreExecute()
{
}
#Override protected HashMap<String, ArrayList<ArrayList<esProperty>>> doInBackground(SetParamsForQuery... params) {
return esData.requestData(params[0].type, params[0].page, params[0].sDate);
}
#Override protected void onPostExecute(HashMap<String, ArrayList<ArrayList<esProperty>>> result)
{
Log.d(TAG, " result "+ result.size());
if(mode == R.string.today)
otherChart(result, TODAY);
if(mode == R.string.week)
otherChart(result, WEEK);
if(mode == R.string.month)
otherChart(result, MONTH);
}
}
here is my requestData method
public HashMap<String,ArrayList<ArrayList<esProperty>>> requestData(final String mode, final int page,final Date second){
es = new esDatabaseHandler();
HashMap<String, ArrayList<ArrayList<esProperty>>> hash = new HashMap<>();
try{
ActiveAndroid.beginTransaction()
for (int i = 0; i < 24; i++) {
if (page == 1) {
Calendar c = Calendar.getInstance();
hash = es.createHash(es.getDayFromToday(c.getTime(), too, page), null, mode);
}
else {
hash = es.createHash(es.getDayFromToday(second, too, page), null, mode);
}
}
Log.d(TAG, " Background "+ hash.size());
ActiveAndroid.setTransactionSuccessful();
}
finally {
ActiveAndroid.endTransaction();
}
}
The method requestData is running queries in loop in SQLite and returning hash. I know the reasons why ANR happens but it seems like I am doing correct

Well, syntax is correct, but you are forcing UI update all at once in your onPostExecute method. You should update the Activity little by little, extracting a bunch of data and passing it to publishProgress() AsyncTask method. Your way, it is too much to handle for the Main Thread.

Related

Android AsyncTask class

Can I use await for async class in android like used in C# ,
int stocktakingId = 0;
if (Stocktaking.SynchCountId == 0) {
stocktakingId = NewStocktaking(Stocktaking, UHFApplication.getInstance().getData("UserID"), _stocktakingType);
Stocktaking.setSynchCountId(stocktakingId);
);
} else {
stocktakingId = Stocktaking.getSynchCountId();
}
I want to await NewStocktakin metod. This callback method. after return NewStocktakin value I must continue below line. its possible ? Because my stocktakingId is return 0.
public int NewStocktaking(Stocktaking stocktaking, String userId, Constants.StocktakingType stocktakingType) {
new NewStocktakingService(count -> count).
execute(
String.valueOf(stocktaking.getName()),
String.valueOf(stocktaking.getRelatedId()),
String.valueOf(userId),
String.valueOf(stocktakingType)
);
return 0;
}
and also inteface metod
public interface AsyncResponseNewStocktaking {
int processFinish(int count);
}
and my async class
public class NewStocktakingService extends AsyncTask<String, Void, ResponseModel> {
AsyncResponseNewStocktaking delegate = null;
NewStocktakingService(AsyncResponseNewStocktaking delegate) {
this.delegate = delegate;
}
#Override
protected ResponseModel doInBackground(String... parameters) {
RequestHandler requestHandler = new RequestHandler();
ResponseModel responseModel = requestHandler.getRequestGet(UHFApplication.getInstance().apiUrl + "/api/MobileService/NewStocktaking?" +
"stocktakingName=" + URLEncoder.encode(parameters[0]) +
"&relatedID=" + parameters[1] +
"&userID=" + parameters[2] +
"&stocktakingType=" + parameters[3]);
return responseModel;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected void onPostExecute(ResponseModel responseModel) {
super.onPostExecute(responseModel);
if (HttpURLConnection.HTTP_OK == responseModel.httpStatus) {
int count = Integer.parseInt(responseModel.responseString);
delegate.processFinish(count);
} else {
delegate.processFinish(0);
}
}
}
if you want the value of Your AsyncTask class. You can await using the .get() method.
So your final code should be like:
public int NewStocktaking(Stocktaking stocktaking, String userId, Constants.StocktakingType stocktakingType) {
return new NewStocktakingService(count -> count).
execute(
String.valueOf(stocktaking.getName()),
String.valueOf(stocktaking.getRelatedId()),
String.valueOf(userId),
String.valueOf(stocktakingType)
).get();
}
But that will require you to make your AsynTask return Integer. Checking your code, you anyways dont use the other properties of the ResponeModel so you can make onBackground() return Integer!

How to manually call observer.onNext in rxJava

I am relatively new to RxJava/RxAndroid. I have been using AsyncTask to do my long running tasks before now.
I have converted most of my AsyncTask to RxJava but this one.
The particular problem I am having is calling something like AsyncTask's publishProgress(params); in
the background thread. I need to do this to update the progress of a ProgressBar.
First this is the code in AsyncTask
private static class AddBooksToDatabase extends AsyncTask<String, String, String> {
//dependencies removed
AddBooksToDatabase(AddBooksDbParams params) {
//Removed assignment codes
}
#Override
protected String doInBackground(String... strings) {
//Initializing custom SQLiteOpenHelper and SQLite database
File mFile = new File(mFolderPath);
int booksSize = getFilesInFolder(mFile).size();
String[] sizeList = {String.valueOf(booksSize)};
//The first publishProgress is used to set the max of the progressbar
publishProgress(sizeList);
for (int i = 0; i < booksSize; i++) {
//publishProgress with current item, current file
publishProgress(String.valueOf(i), getFilesInFolder(mFile).get(i).getName());
//Inserting current items in database. Code removed
}
return null;
}
#Override
protected void onPreExecute() {
//Show ProgressBar
}
#Override
protected void onPostExecute(String s) {
//Hide ProgressBar
}
#Override
protected void onProgressUpdate(String... values) {
super.onProgressUpdate(values);
if (values.length == 1) {
//The first call to publishProgress
mProgressBar.setMax(Integer.parseInt(values[0]));
} else {
//Subsequent calls to publish progress
Log.i(TAG, "Current item is " + values[0] + " and current file is " + values[1]);
infoText.setText(values[1]);
mProgressBar.setProgress(Integer.parseInt(values[0]), true);
}
}
#Override
protected void onCancelled() {
cancel(true);
}
}
Code Using RxJava
final Observable<String[]> addBooksObserver = Observable.create(new Observable.OnSubscribe<String[]>() {
#Override
public void call(Subscriber<? super String[]> subscriber) {
subscriber.onNext(setAddSubscription());
subscriber.onCompleted();
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
private String[] setAddSubscription() {
S//Initializing custom SQLiteOpenHelper and SQLite database
File mFile = new File(mFolderPath);
int booksSize = getFilesInFolder(mFile).size();
String[] sizeList = {String.valueOf(booksSize)};
//The first publishProgress is used to set the max of the progressbar
addBooksObserver.doOnNext(addReturnParams(String.valueOf(sizeList.length), null, null));
for (int i = 0; i < booksSize; i++) {
EpubReader reader = new EpubReader();
//publishProgress with current item, current file*
addBooksObserver.doOnNext(addReturnParams(String.valueOf(sizeList.length),
String.valueOf(i), getFilesInFolder(mFile).get(i).getName()));
//Inserting current item in database. Code removed
}
return null;
}
private String[] addReturnParams(String totalItems, String currentItem, String currentFile) {
return new String[]{totalItems, currentItem, currentFile};
}
The problem is that lines addBooksObserver.doOnNext(addReturnParams( are displaying this error
doOnNext (rx.functions.Action1) cannot be applied to (java.lang.String[])
I don't know have any idea how to fix this because I thought that since setAddSubscription() and addReturnParams(String totalItems, String currentItem, String currentFile) are returning String array then this shouldn't be a problem. Please can you help me out?
you just have to pass the values to the onNext method of your subscriber, not the doOnNext method of your observable!
you also have to subscribe to the service. try something like this for your obserable:
Observable.create(new Observable.OnSubscribe<String[]>() {
#Override
public void call(Subscriber<? super String[]> subscriber) {
setAddSubscription(subscriber);
subscriber.onCompleted();
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<String[]>() {
#Override
public void onCompleted() {
// handle 'oparation is done'
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(String[] values) {
if (values.length == 1) {
//The first call to publishProgress
mProgressBar.setMax(Integer.parseInt(values[0]));
} else {
//Subsequent calls to publish progress
Log.i(TAG, "Current item is " + values[0] + " and current file is " + values[1]);
infoText.setText(values[1]);
mProgressBar.setProgress(Integer.parseInt(values[0]), true);
}
}
});
you also need to modify your private methods a little bit:
private void setAddSubscription(Subscriber<? super String[]> subscriber) {
//Initializing custom SQLiteOpenHelper and SQLite database
File mFile = new File(mFolderPath);
int booksSize = getFilesInFolder(mFile).size();
String[] sizeList = {String.valueOf(booksSize)};
//The first publishProgress is used to set the max of the progressbar
subscriber.onNext(addReturnParams(String.valueOf(sizeList.length), null, null));
for (int i = 0; i < booksSize; i++) {
EpubReader reader = new EpubReader();
//publishProgress with current item, current file*
subscriber.onNext(addReturnParams(String.valueOf(sizeList.length),
String.valueOf(i), getFilesInFolder(mFile).get(i).getName()));
//Inserting current item in database. Code removed
}
}
private String[] addReturnParams(String totalItems, String currentItem, String currentFile) {
return new String[]{totalItems, currentItem, currentFile};
}
You can use Subject to call onNext() manually like this:
Subject<Event> event = Subject.create();
Now call the onNext() for sending event like:
event.onNext("event");
Finally you can return Observable by using this code:
event.toFlowable(BackpressureStrategy.LATEST)
.toObservable();
Your Observer should be like Observable.create(new Observable.OnSubscribe<String>() & in your call method you should loop through the StringArray & pass it to onNext for example:
#Override
public void call(Subscriber<? super String> subscriber) {
for(String val : setAddSubscription()) {
subscriber.onNext(val);
}
subscriber.onCompleted();
}
now onNext shall return you individual items & onCompleted will be called upon the loop is finished.
Edit
myObserver.subscribe(new Subscriber<String>() {
#Override
public void onCompleted() {
// handle completion.
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(String value) {
// do whatever with each value passed to onNext
}
});
Observable.create(emitter -> {
for (int i = 0; i < 10; i++) {
int[] ii = {i, i * 2};
emitter.onNext(ii);
}
emitter.onComplete();
}).observeOn(AndroidSchedulers.mainThread()).subscribeOn(Schedulers.io()).subscribe(o -> {
// update progress
int[] i = (int[]) o;
Toast.makeText(SearchActivity.this, "oftad " + i[0] + " - " + i[1], Toast.LENGTH_SHORT).show();
}, t -> {
// on error
Toast.makeText(SearchActivity.this, t.getMessage(), Toast.LENGTH_SHORT).show();
}, () -> {
// progress tamom shod
Toast.makeText(SearchActivity.this, "completed", Toast.LENGTH_SHORT).show();
});

How to call a asyncTask several times inside a loop- one after another

Actually what i am trying to do is that call an asyncTask several times inside a loop. So, first time the asyncTask will start immediately and from second time onwards, it will check whether the AsyncTask has been finished-if finished than again call it with different values.
Below is my code for the activity:
In onCreate()
btnUpload.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
count_response = 0;
newUploadWithSeparate();
}
});
The newUploadWithSeparate() method:
private void newUploadWithSeparate()
{
responseString_concat = "";
if(filePath.length > 0)
{
for(int i=0;i<filePath.length;i++)
{
count_response = i;
if(i == 0)
{
uploadAsync.execute(filePath[0]);
mHandler = new Handler() {
#Override public void handleMessage(Message msg) {
String s=(String)msg.obj;
Log.d("logIMEI","\n Response from Asynctask: " + s);
str_response_fromAsync = s;
}
};
}
else
{
uploadAsync.getStatus();
while(uploadAsync.getStatus() == AsyncTask.Status.RUNNING) // this while loop is just to keep the loop value waitining for finishing the asyncTask
{
int rx = 0;
}
if(uploadAsync.getStatus() != AsyncTask.Status.RUNNING)
{
if(uploadAsync.getStatus() == AsyncTask.Status.FINISHED)
{
if(str_response_fromAsync != "" || !str_response_fromAsync.equals("") || !str_response_fromAsync.isEmpty())
{
uploadAsync.execute(filePath[i]);
x = i;
mHandler = new Handler() {
#Override public void handleMessage(Message msg)
{
String s=(String)msg.obj;
Log.d("logIMEI","\n Response from Asynctask_" + x + ": " + s);
str_response_fromAsync = s;
}
};
}
}
}
}
}
}
}
And the asyncTask:
private class UploadFileToServer extends AsyncTask<String, Integer, String>
{
#Override
protected void onPreExecute()
{
super.onPreExecute();
}
#Override
protected String doInBackground(String... params)
{
return uploadFile(params[0]);
}
private String uploadFile(String pr)
{
//inside here calling webservice and getting a response string as result.
MyWebsrvcClass mycls = new MyWebsrvcClass();
return responseString_concat = mycls.Call(xxx,yyy) ;
}
#Override
protected void onPostExecute(String result)
{
Log.d("logIMEI" , "\n count_response : "+ count_response + " fileprath_len : " + filePath.length);
Message msg=new Message();
msg.obj=result.toString();
mHandler.sendMessage(msg);
super.onPostExecute(result);
}
}
Now the problem is that its not working as expected. The first time when value of i is equals 0 than the AsyncTask gets called and after that its not getting called anymore.
Plus, when first time AsyncTask is called- its still not directly entering to onPostExecute(). When the loop ends totally and newUploadWithSeparate() method ends then the onPostExecute() is working.
Any solutions for this or any other way to do this job done for using AsyncTask inside loop?
You cannot call execute() on the same object more than once. So create a new instance of UploadFileToServer for each iteration of the loop.

How to sort listview items in descending order

So I have a listview where I wanted to sort the NumberOfRecords in descending order. I have a custom array adapter but I called my sorting class before I place a data in my ArrayList, this my Asynctask receiving JSON:
public class SampleListTask extends AsyncTask<String, Void, String> {
public ProgressDialog pDialog;
#Override
protected void onPreExecute() {
super.onPreExecute();
pDialog = new ProgressDialog(SampleActivity.this);
pDialog.setMessage("Loading...");
pDialog.setCancelable(false);
pDialog.show();
}
#Override
protected String doInBackground(String... path) {
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
Log.d(Constant.TAG_RANKING, path[0]);
String apiRequestReturn = UtilWebService.getRequest(path[0]);
if (apiRequestReturn.equals("")) {
Log.d(Constant.TAG_SAMPLE, "WebService request is null");
return null;
} else {
Log.d(Constant.TAG_SAMPLE, "WebService request has data");
return apiRequestReturn;
}
}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
if (null != pDialog && pDialog.isShowing()) {
pDialog.dismiss();
}
if (null == result || result.length() == 0) {
application.shortToast("No data found from server");
} else {
try {
JSONObject sampleObject = new JSONObject(result);
JSONArray jsonArray = sampleObject
.getJSONArray(Constant.TAG_SAMPLE);
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject objJson = jsonArray.getJSONObject(i);
sample = new ArraySample();
sample.setId(objJson.getInt(Constant.TAG_SONGID));
sample.setThumbUrl(objJson
.getString(Constant.TAG_IMAGEURL));
sample.setTitle(objJson
.getString(Constant.TAG_NAME));
sample.setArtist(objJson
.getString(Constant.TAG_ARTIST));
sample.setDuration(Utility
.changeStringTimeFormat(objJson
.getString(Constant.TAG_MUSICLENGTH)));
sample.setNumberOfRecords(objJson
.getString(Constant.TAG_NUMBEROFRECORDS));
Collections.sort(sampleList, new SortByRecordNumber()); // This where I call the class
sampleList.add(sample);
}
} catch (JSONException e) {
e.printStackTrace();
}
setAdapterToListview();
}
}
public void setAdapterToListview() {
objRowAdapter = new RowAdapterSample(getApplicationContext(),
R.layout.item_sample, sampleList);
sampleListView.setAdapter(objRowAdapter);
}
}
And here's my sorting class:
public class SortByRecordNumber implements Comparator {
public int compare(Object o1, Object o2) {
ArraySample p1 = (ArraySample) o1;
ArraySample p2 = (ArraySample) o2;
return p2.getNumberOfRecords().compareTo(p1.getNumberOfRecords());
}
}
But the result I'm getting is:
5
15
14
0
0
Is my sorting implementation wrong? Or should I parse it to Integer before return?
You can use the following code to sort your integer list is descending order.Here we are overriding compare() so that it sorts in descending order.
//sort list in desc order
Collections.sort(myIntegerList, new Comparator<Integer>() {
public int compare(Integer one, Integer other) {
if (one >= other) {
return -1;
} else {
return 1;
}
}
});
Hope it helps.
Try with this Comparator.
Comparator objComparator = new Comparator() {
public int compare(Object o1, Object o2) {
int no1 = Integer.parseInt((String) o1);
int no2 = Integer.parseInt((String) o2);
return no1 < no2 ? -1 : no1 == no2 ? 0 : 1;
}
};
Collections.sort(myIntegerList, objComparator);
Okay, so I solved this by replacing the:
p2.getNumberOfRecords().compareTo(p1.getNumberOfRecords())
to:
(int) Integer.parseInt(p2.getNumberOfRecords()) - Integer.parseInt(p1.getNumberOfRecords())
So the simple compare of an integer in a String data type would not result correctly but to parse the string first by:
Integer.parseInt(string)
and get the true value of the number string.

I have problems when using sqlite inside AsyncTask in android?

I face two main problems when using a sqlite command inside an AsncTask in android.
When I execute a select command on the first try I get no results but on the second try (loading a activity that has this Asynctask) I do get results.
Sometimes I get an error that the database is not closed despite that it is already closed/
What is the problem with this?
UPDATE:
This is the code that retrive data from database (db.getAllMessage)
private ArrayList<FriendMessagesResulted> getMessagesFromCach(Context c){
FriendMessagesResulted messagesResulted1 = new FriendMessagesResulted();
DBAdapter db = new DBAdapter(c);
Cursor c1;
db.open();
c1 = db.getAllMessage(Settings.getCurrentUserId(c),Integer.parseInt(fId));
Log.d("***Database count",c1.getCount()+" from: "+Settings.getCurrentUserId(c)+" to:"+Integer.parseInt(fId));
c1.moveToFirst();
if(c1.getCount()>0)
status=true;
if (messagesResultedList == null) {
messagesResultedList = new ArrayList<FriendMessagesResulted>();
}
else
messagesResultedList.clear();
while (c1.isAfterLast() == false) {
if(Integer.parseInt(c1.getString(0))>maxId)
maxId=Integer.parseInt(c1.getString(0));
messagesResulted1.set_mId(Integer.parseInt(c1.getString(0)));
messagesResulted1.set_msgTxt("MD:"+c1.getString(3));
messagesResulted1.set_MessageTime(c1.getString(4));
messagesResulted1.set_dir(c1.getString(5));
messagesResultedList.add(messagesResulted1);
c1.moveToNext();
}
db.close();
c1.close();
return messagesResultedList;
}
and this the code for AsyncTask, where I call get getMessagesFromCach method
private void getMessages(final Context c)
{
handler = new Handler();
r=new Runnable() {
public void run() {
class RecentMessageLoader extends AsyncTask<Void, Void, ArrayList<FriendMessagesResulted>>{
ArrayList<FriendMessagesResulted> messagesResultedList=null;
#Override
protected ArrayList<FriendMessagesResulted> doInBackground(Void... params) {
if(!finishLoadingPastMessages)
{
messagesResultedList=getMessagesFromCach(c);
if(!status){
Log.d("Where are you","I'm in JSON");
messagesResultedList=getMessagesFromJSON(c);
}
}
else{
Log.d("Where are you","I'm in Recent messages");
messagesResultedList=getRecentMessages(c,Settings.getCurrentUserId(c),Integer.parseInt(fId));
}
return messagesResultedList;
}
protected void onPostExecute( ArrayList<FriendMessagesResulted> FMRList ) {
// to disappear loading message
d.dismiss();
finishLoadingPastMessages=true;
if(FMRList!=null){
for(int i=FMRList.size()-1;i>=0;i--)
addMessage(FMRList.get(i),c);
}
handler.postDelayed(r, 2000);
}
}
new RecentMessageLoader().execute();
}
};
handler.post(r);
}
UPDATE 2 : Cach class ..
public class Cach {
static DBAdapter db;
public Cach(Context c)
{
}
public static void AddMessages(Context c,
int id,
int fromId,
int toId,
String message,
String dir,
String MessageTime)
{
db = new DBAdapter(c);
db.open();
long id2;
id2 = db.insertMessage(id, fromId, toId, message, dir,MessageTime);
db.close();
}
}
It seems the problem is with the type of variables you are using.. there must be Static variables of instance variables which are getting set from many sources... try not to use static variables and use local variables I mean in the methods implicitly.

Categories

Resources