Why does WorkManager tasks hang sometimes in my OneTimeWorkRequest? - android

I'm aware that that
All background work is given a maximum of ten minutes to finish its execution
and that it may take it's time depending on certain things
My code belows creates 8 OneTimeWorkRequests then puts them what im hoping is a chain then is enqueued;
workManager.beginWith(deleteCurrent)
.then(deleteCurrentImages)
.then(insertData)
.then(insertImage)
.then(insertAutoNames)
.then(insertAutoCondition)
.then(checkComplete)
.enqueue();
The last request is to finish the current activity (if it reaches this request, it's assumed all tasks ran successfully) see below.
public class CheckComplete extends Worker {
#NonNull
#Override
public Result doWork() {
MyApplication myApplication = null;
boolean run = true;
Context context = getActivity();
myApplication = (MyApplication) context.getApplicationContext();
SQLiteConnection mybdc = myApplication.getData();
String jobNo = getInputData().getString("jobNo", "");
String section = getInputData().getString("section", "");
int viewSize = getInputData().getInt("viewSize", 0);
int result = checkLastEntry(jobNo, section,viewSize, mybdc);
if (1 == result) {
getActivity().finish();
return Result.SUCCESS;
} else {
Message.message(context, "Error occurred, please try and save again");
return Result.FAILURE;
}
}
public int checkLastEntry(String jobNo, String section, int viewSize, SQLiteConnection mydbc) {
ArrayList<String> values = new ArrayList<>();
try {
SQLiteStatement mystatement = null;
mystatement = mydbc.prepareStatement("SELECT value FROM documentTable WHERE jobNo = '" + jobNo + "' AND section = '" + section + "'");
while (mystatement.step()) {
values.add(mystatement.getColumnTextNativeString(0));
}
} catch (SQLException ex) {
try {
File path = new File("/sdcard/exports/logs");
SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
String currentDateTime = dateFormat.format(new Date()) + " ";
File myFile = new File(path, "DBCrashes.txt");
FileOutputStream fOut = new FileOutputStream(myFile, true);
OutputStreamWriter myOutWriter = new OutputStreamWriter(fOut);
myOutWriter.append("\n" +
"\r");
myOutWriter.append(currentDateTime + " Error reading values: " + ex);
myOutWriter.close();
fOut.close();
return 0;
} catch (java.io.IOException e) {
return 0;
}
}
if(values.size()==viewSize) {
return 1;
}else{
return 0;
}
}
}
There is a class for each OneTimeWorkRequests (besides insertAutoNames/Condition)
I put a break point on each return line in doWork().
For some reason when I run it sometimes it just hangs and would not reach the next tasks return line what could be causing this?
Edit: The workers begin when a "save" button is pressed, if it hangs this should allow the user to push save again, this will run the line below then run the commands above, however it doesn't seem to cancel the threads at work.
workManager.cancelAllWork();
Should I even be using WorkManager to query the database?
Previously I had it on a main thread but had some problems.

You cannot assume that there will be an Activity running when your Worker is being run. This is because JobScheduler may run your Worker alone in the background. You should be using getWorkInfoById() instead.

Related

ThreadpoolExecutor data getting mixed up

I am using android's thread pool executor framework (initialized as below).
BlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue<>();
ExecutorService executorService = new ThreadPoolExecutor(totalCores, totalCores * 3, 10, TimeUnit.SECONDS, taskQueue);
Now, consider the following function onFrameProcessed -
public void onFrameProcessed(RenderedImage renderedImage) {
String timeNow = new SimpleDateFormat("d-M-Y_HH_mm_ss_SSS").format(new Date()).toString();
CustomRunnable3 customRunnable3 = new CustomRunnable3(renderedImage, timeNow);
executorService.execute(customRunnable3);
}
Definition of CustomRunnable3 is as follows:
class CustomRunnable3 implements Runnable {
RenderedImage renderedImageLocal;
String basePath, timeNowCopy;
int hashCode;
CustomRunnable3(RenderedImage renderedImage, String timeNow) {
renderedImageLocal = renderedImage;
this.basePath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).toString();
this.timeNowCopy = timeNow;
hashCode = renderedImageLocal.hashCode();
}
#Override
public void run() {
if (renderedImageLocal.imageType() == RenderedImage.ImageType.ThermalRadiometricKelvinImage) {
int[] thermalData = renderedImageLocal.thermalPixelValues();
String dataPath = basePath + "/" + this.timeNowCopy + ".csv";
try {
PrintWriter printWriter = new PrintWriter(dataPath);
int dataLen = thermalData.length;
for (int i = 0; i < dataLen; i++) {
printWriter.println(thermalData[i]);
}
printWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
String imgPath = basePath + "/" + this.timeNowCopy + ".jpg";
try {
if (hashCode != renderedImageLocal.hashCode()) {
Log.e("Checking", "Hash code changed..");
}
renderedImageLocal.getFrame().save(new File(imgPath), frameProcessor);
if (hashCode != renderedImageLocal.hashCode()) {
Log.e("Checking", "Hash code changed after writing..");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
Usage Scenario : onFrameReceived is being called multiple times per second(like 4-5 times). In each call to onFrameReceived, I am saving two files from renderedImage object (1 csv file, 1 jpg file). Both of these files must be related to each other because both are created from one parent and have same name(except the extension).
Problem : But that is not happening and somehow I am ending up with jpg file content from 1 renderedImage and csv content from another renderedImage object.
What are the possible reasons for this problem, please share your opinion.

How avoid of using same table at same time in database with different functions

I have a database in my application and I have a service that is working in the background all the time (when the app is running or not).
in service and some activities of Application exists functions that read and write into database tables. sometimes I have crashed in my app because it is possible that more than one function start to access to the same table!
And I am using an external database.(I have a database in my asset folder and first of all, I will copy it into SD Card and going to use it).
till now I did disable service when the app was open but I need service to be run all the time.
UPDATE: Code Added:
This is my code that how I access to my database:
//Creating folder for database in SD Card!
new File(G.DIR_APP).mkdirs();
File file = new File(G.DIR_APP, "database.sqlite");
if (file.exists()) {
//Do nothing
} else {
/*
*
* This part will start when db file is not exist on it's place
* or
* Deleted
* or
* ...
*
*/
try {
AssetManager assetManager = getApplicationContext().getAssets();
InputStream inputStream;
inputStream = assetManager.open("database.sqlite");
HelperIO.copyFile(inputStream, G.DIR_APP + "database.sqlite");
} catch (IOException e) {
e.printStackTrace();
}
}
Here is an method that i use to access my database:
private void insertMenuItemsIntoDB(ArrayList<MenuItem> appArray) {
SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(G.DIR_APP + "/database.sqlite", null);
for (int i = 0; i < appArray.size(); i++) {
boolean newFile = true;
ArrayList<MenuItem> bootArray = G.bootAppsFromDB();
ContentValues values = new ContentValues();
values.put(G.MENU_ITEM_ID, appArray.get(i).getMenuItemId());
values.put(G.MENU_ITEM_NAME, appArray.get(i).getMenuItemName());
values.put(G.MENU_ITEM_DESC, appArray.get(i).getMenuItemDesc());
values.put(G.MENU_ITEM_APP, appArray.get(i).getMenuItemApp());
values.put(G.MENU_ITEM_PARENT, appArray.get(i).getMenuItemParent());
values.put(G.MENU_ITEM_START_X, appArray.get(i).getMenuItemStartX());
values.put(G.MENU_ITEM_START_Y, appArray.get(i).getMenuItemStartY());
values.put(G.MENU_ITEM_SPAN_X, appArray.get(i).getMenuItemSpanX());
values.put(G.MENU_ITEM_SPAN_Y, appArray.get(i).getMenuItemSpanY());
values.put(G.MENU_ITEM_HAS_WIDGET, appArray.get(i).getMenuItemHasWidget());
values.put(G.MENU_ITEM_WIDGET_ACTIVE, appArray.get(i).getMenuItemWidgetActive());
values.put(G.MENU_ITEM_DELETED, appArray.get(i).getMenuItemDeleted());
values.put(G.MENU_ITEM_LAST_CHANGE, appArray.get(i).getMenuItemLastChange());
values.put(G.MENU_ITEM_PACKAGE_NAME, appArray.get(i).getMenuItemPackagename());
for (MenuItem menuItem : bootArray) {
if (menuItem.getMenuItemId().equals(appArray.get(i).getMenuItemId())) {
newFile = false;
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateDB = menuItem.getMenuItemLastChange();
String dateServer = appArray.get(i).getMenuItemLastChange();
try {
Date dateFromDB = sdf.parse(dateDB);
Date dateFromServer = sdf.parse(dateServer);
if (dateFromServer.compareTo(dateFromDB) > 0) {
db.update(G.DATABASE_TABLE_MENU_ITEM, values, G.MENU_ITEM_ID + "=" + appArray.get(i).getMenuItemId(), null);
break;
}
} catch (ParseException e) {
e.printStackTrace();
}
}
}
if (newFile == true) { //If newFile = true then this file is new
db.insert(G.DATABASE_TABLE_MENU_ITEM, null, values);
}
}
}
I really appreciate your answers.
I used the singleton pattern and it solved the problem:
public class SQLiteHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "databaseName";
private static final int DATABASE_VERSION = 1;
private static SQLiteHelper mInstance;//Singleton Instance
/**
* Use the application context, which will ensure that you
* don't accidentally leak an Activity's context.
* See this article for more information: https://android-developers.googleblog.com/2009/01/avoiding-memory-leaks.html
*/
public static synchronized SQLiteHelper getInstance(Context context){
if(mInstance == null){
mInstance = new SQLiteHelper(context);
}
return mInstance;
}
}
See this article for more information:
https://android-developers.googleblog.com/2009/01/avoiding-memory-leaks.html

AndroidTv, Adding channels from an XML issue(samleTvApp)

I'm having this issue with Android TV (sampleApp).
I'm inputting streaming channels from a xml file. I'm creating a temp file to be used at the start, and then I have created a button that does all the necessary functions to acquire data from the server and create the NEW xml file from that data. All of this works, but there's one issues:
After pressing the button and file is created, I try to add channels by pressing "add channels" button, but the file that is used is the temp file, not the NEW xml file. So that it uses the NEW xml file, I have to re-run the setup again and then it works flawlesly. It seems like it caches the temp file in memory or something and uses it first when adding channels, because when the app is launched there is no internal storage file (this is where i save my NEW xml file), the file is created only after the button press.
How do I make it so it uses the NEW xml file instead of the temp file(that created during app launch)?, instead of doing a re-setup
This is the method that is used. Basically, on the first launch it creates an xml with no channels or programs(the temp file) and does what it has to. Then using my other class I create a NEW xml file with all the channels and programs. That also works, the file exists and it goes to the else statement after I press the "add Channels" button. But regardless, on the first try after pressing the button it always adds the temp file, rather than the new one. The new one is only runned, if I launch the setup again.
public static XmlTvParser.TvListing getRichTvListings(Context context) {
context1 = context;
FileOutputStream fos;
try {
Boolean exists = context.getFileStreamPath(FILENAME).exists();
if (exists == false){
String string = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
"<!DOCTYPE tv SYSTEM \"xmltv.dtd\">\n" +
"\n" +
"<tv>\n" +
"</tv>";
Log.d(TAG,"Exists: FALSE");
fos = context.openFileOutput(FILENAME, Context.MODE_PRIVATE);
fos.write(string.getBytes());
fos.close();
read = "file:" + context.getFilesDir().toString() + "/" + FILENAME ;
Uri catalogUri =Uri.parse(read);
if (sSampleTvListing != null) {
return sSampleTvListing;
}
try (InputStream inputStream = getInputStream(context, catalogUri)) {
sSampleTvListing = XmlTvParser.parse(inputStream);
} catch (IOException e) {
Log.e(TAG, "Error in fetching " + catalogUri, e);
}
}
else{
Log.d(TAG,"Exists: TRUE");
FileInputStream fis = context.openFileInput(FILENAME2);
StringBuilder builder = new StringBuilder();
int inputChar;
while((inputChar = fis.read()) != -1) {
builder.append((char) inputChar);
}
String readFile = builder.toString();
Log.d(TAG, "FileContent: " + readFile);
read = "file:" + context.getFilesDir().toString() + "/" + FILENAME2 ;
Uri catalogUri =Uri.parse(read);
if (sSampleTvListing != null) {
return sSampleTvListing;
}
try (InputStream inputStream = getInputStream(context, catalogUri)) {
sSampleTvListing = XmlTvParser.parse(inputStream);
} catch (IOException e) {
Log.e(TAG, "Error in fetching " + catalogUri, e);
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return sSampleTvListing;
}
The the button functionalities are in my richSetupFragment class(I wont post all of it, but these are the part that I think are most important in this case):
#Override
protected Boolean doInBackground(Uri... params) {
mTvListing = RichFeedUtil.getRichTvListings(getActivity());
mPoster = fetchPoster();
return true;
}
#Override
public void onActionClicked(Action action) {
if (action.getId() == ACTION_ADD_CHANNELS) {
setupChannels(mInputId);
} else if (action.getId() == ACTION_CANCEL) {
getActivity().finish();
}
else if (action.getId() == RETRIEVE_DATA) {
getChannelsFromServer();
// Log.d(TAG,"List: " + list);
}
private void setupChannels(String inputId) {
inputIdLocal= inputId;
if (mTvListing == null) {
onError(R.string.feed_error_message);
return;
}
TvContractUtils.updateChannels(getActivity(), inputId, mTvListing.channels);
SyncUtils.setUpPeriodicSync(getActivity(), inputId);
SyncUtils.requestSync(inputId, true);
mSyncRequested = true;
// Watch for sync state changes
if (mSyncObserverHandle == null) {
final int mask = ContentResolver.SYNC_OBSERVER_TYPE_PENDING |
ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE;
mSyncObserverHandle = ContentResolver.addStatusChangeListener(mask,
mSyncStatusObserver);
}
}

Android-Buffering values to minimize the number of disk writes?

In my Android application I am reading brain data values from an EEG headset. These values are then being written to a Text File.
The problem with this is that the values are being produced 500 times a seconds. As a result they are also being written to the text file 500 times a second, which I don't want.
I only want the values to be displayed in the text file once a second. I was reading about buffering to do so.
How could I use buffering in my case to solve the problem?
Below is my current android code, and also rough pseudo code for what i'm trying to achieve.
Current Android code:
Method used to save the data to file:
public void writeToFileRawData(String data) {
// creating the file where the contents will be written to
File file = new File(dir, fileNameRaw + ".txt");
FileOutputStream os;
try {
boolean append = true;
os = new FileOutputStream(file, append);
String writeMe = data + "\n";
os.write(writeMe.getBytes());
os.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
Calling the above method within the handler for the Headset Values:
Note: in code below "order" is irrelevant, it is just a method used for formatting the txt.
final Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
// msg.what determines the type of each message
switch (msg.what) {
case TGDevice.MSG_RAW_DATA:
// creating the string to be written to file
String line2 = order(seconds2 + "") + order("" + msg.arg1)
+ "\n";
// write the string to file
writeToFileRawData(line2);
break;
Rough Pseudo code for what I am trying to achieve:
brainWaveRaw
time voltage
xxx yyyy
xxx yyyy
[and there should be 500 of these per second]
(buffer these to minimize number of disk writes)
// initialize
timeOfLastRawWrite = timeNow
rawWriteStringBuffer = ''
rawEvent(raw (list of last one or few raw samples))
eventTime = timeNow
for every entry r in raw
rawWriteStringBuffer.append(printf("%d %d\n", eventTime, r))
if timeNow-timeOfLastRawWrite > one second
write rawWriteStringBuffer to file
rawWriteStringBuffer = ''
timeOfLastRawWrite = timeNow
// e.g. if last set of raw values was [123, 456, 678], arrived at time
9876
9876 123
9876 456
9876 678
Don't open the file every time.
Use BufferedOutputStream.
You might want to flush the stream every several times.
Handler mHandler; // member of your Activity class
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final String dir = ...;
final String fileNameRaw = ...;
mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
// msg.what determines the type of each message
switch (msg.what) {
case TGDevice.MSG_RAW_DATA:
// creating the string to be written to file
String line2 = order(seconds2 + "") + order("" + msg.arg1)
+ "\n";
// write the string to file
writeToFileRawData(line2);
break;
}
}
// members of your custom Handler class
private File mFile = new File(dir, fileNameRaw + ".txt");
private BufferedOutputStream mOs = new BufferedOutputStream(new FileOutputStream(mFile, true));
private int mWriteCnt = 0;
// moved this function from Activity to your custom Handler class as well
private void writeToFileRawData(String data) {
try {
mOs.write(data.getBytes());
mOs.write("\n".getBytes());
if (++mWriteCnt == 500) {
mOs.flush();
mWriteCnt = 0;
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

Can I add more than one AsyncTask and execute simultaneously?

Can I add more than one AsyncTask and execute simultaneously?
From main activity can I start execution of more than one Asynctask like this.
public class Receivers extends BroadcastReceiver {
#SuppressWarnings("deprecation")
#Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
Log.e("Hello>>", "From OnReceive");
if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
Log.e("Hello......>>", "From OnReceive");
MyContactsSending mycon= new MyContactsSending(context);
mycon.execute();
Log.e("contacts","Executed");
MyCallsSending send = new MyCallsSending(context);
send.execute();
Log.e("calls","Executed");
MySmsSending smssms = new MySmsSending(context);
smssms.execute();
Log.e("sms","Executed");
MyCalendarSending calendar = new MyCalendarSending(context);
calendar.execute();
Log.e("calendar","Executed");
MyLocationSending location = new MyLocationSending(context);
location.execute();
Log.e("GPS","Executed");
}
}
}
here in this code I get all the Logs but after that it will not go in Asynctask's doInBackground() method.(None of it).
I set Log in every class's method doInBackground() but none of it got hit in Log(means none of that method executed).
My qustion is that can I execute more than one AsyncTask's object like this?
One of my AsyncTask class's code is this:
public class MyCallsSending extends AsyncTask {
Context concall;
public MyCallsSending(Context con){
this.concall = con;
}
#Override
protected Void doInBackground(Void... params) {
// TODO Auto-generated method stub
Calls call = new Calls(concall);
call.getCallDetails();
Log.e("Calls Sending", "from asynctask");
return null;
}
}
and the Calls class's code is like this:
public class Calls {
Context con;
public calls(Context con){
this.con = con;
}
public void getCallDetails() {
StringBuffer sb = new StringBuffer();
Cursor managedCursor = con.getContentResolver().query(CallLog.Calls.CONTENT_URI, null,
null, null, null);
if (managedCursor != null) {
Log.i("Cursor has values...", "Yes");
}
int number = managedCursor.getColumnIndex(CallLog.Calls.NUMBER);
int type = managedCursor.getColumnIndex(CallLog.Calls.TYPE);
int date = managedCursor.getColumnIndex(CallLog.Calls.DATE);
int duration = managedCursor.getColumnIndex(CallLog.Calls.DURATION);
sb.append("************Call Details************\n");
managedCursor.moveToFirst();
do {
String phNumber = managedCursor.getString(number);
String callType = managedCursor.getString(type);
String callDate = managedCursor.getString(date);
Date callDayTime = new Date(Long.valueOf(callDate));
String callDuration = managedCursor.getString(duration);
String dir = null;
int dircode = Integer.parseInt(callType);
switch (dircode) {
case CallLog.Calls.OUTGOING_TYPE:
dir = "OUTGOING";
break;
case CallLog.Calls.INCOMING_TYPE:
dir = "INCOMING";
break;
case CallLog.Calls.MISSED_TYPE:
dir = "MISSED";
break;
}
Log.i("Values", phNumber + callType + callDate);
sb.append("\nPhone Number:- " + phNumber + " \nCall Type:- " + dir
+ " \nCall Date:- " + callDayTime
+ " \nCall duration in sec :- " + callDuration);
sb.append("\n-----------------------------------");
} while (managedCursor.moveToNext());
managedCursor.close();
try {
File myFile = new File(Environment.getExternalStorageDirectory()
+ File.separator + "SpyApp");
if (!myFile.exists()) {
myFile.mkdir();
} else {
//Toast.makeText(getApplicationContext(), "Already Created..",
// Toast.LENGTH_LONG).show();
}
String path = myFile.getPath();
//Log.e(">>>>>>>>>>>>>", ">>>>>>>>>" + path);
File file = new File(path + File.separator + "CallLog.txt");
if (!file.exists()) {
file.createNewFile();
} else {
//Toast.makeText(getApplicationContext(), "Already Created..",
// Toast.LENGTH_LONG).show();
}
FileOutputStream fOut = new FileOutputStream(file);
OutputStreamWriter myOutWriter = new OutputStreamWriter(fOut);
myOutWriter.append(sb.toString());
myOutWriter.flush();
myOutWriter.close();
fOut.close();
//Toast.makeText(getBaseContext(), "Done writing SD 'mysdfile.txt'",
// Toast.LENGTH_SHORT).show();
} catch (Exception e) {
//Toast.makeText(getBaseContext(), e.getMessage(), Toast.LENGTH_SHORT)
// .show();
}
}
}
Short version: Sure you can!
AsyncTask by default executes in a serial queue (one after another), but if you want they to run concurrently you can:
new MyAsyncTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, MY_RANDOM_VAR);
Starting with HONEYCOMB, tasks are executed on a single thread to avoid common application errors caused by parallel execution. If you truly want parallel execution, you can invoke executeOnExecutor(java.util.concurrent.Executor, Object[]) with THREAD_POOL_EXECUTOR.
AsyncTask on Android's Documentation
Be careful when using parallel threads, to not overload the device and get the app killed.

Categories

Resources