Android stuck with LoaderManager - android

I have an application with database and content provider. In order to display some data from the database in ListView's, I useLoaderManager.
It's simple and works perfectly well in most of case.
But, when to many change notifications from the content provider arrives, the UI is totally overloaded. It happens when there are many inserts, about 15 per seconds, on the uri observed by the cursor. I think even if the cursor load is in background, the large amount of bindView() calls is enough to overload the UI.
So I would like, in some case as above, limit the number of loads (and so onLoadFinished() calls) to something like one per seconds.
Is there a way to achieve that with LoaderManager ? I tried to play with startLoading() and stopLoading but without success, and anyway it's not recommended.
Or maybe I have to manage a Loader by myself, but I will be surprised if this situation is not common.
Thanks for your advice.
EDIT
I actually missed a method... setUpdateThrottle(long delayMS) do exactly what I want.

You can override applyBatch(...) in your content provider and do something like this:
#Override
public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
throws OperationApplicationException {
final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
db.beginTransaction();
try {
final int numOperations = operations.size();
final ContentProviderResult[] results = new ContentProviderResult[numOperations];
for (int i = 0; i < numOperations; i++) {
results[i] = operations.get(i).apply(this, results, i);
}
db.setTransactionSuccessful();
return results;
} finally {
db.endTransaction();
}
}
You can use this to batch your inserts into one transaction so notification only happens once.

Related

How can I update the data on my listview using AsyncTask in android?

I am a newbie here, and I have searched online (and on stackoverflow) for the answer but I am still struggling to make it work.
What I am trying to do, is (1) update my list with fresh "posts" using an asyncronous task - thus allowing the user to continue using the app as I download new posts to their android.
I am struggling to get the updating task to work, and it could be tied to an obvious implementation problem (or not).
What I have is (1) A customized list (it allows "pull to refresh" - but as stated it is a problem that it is not refreshing any data at this point). (2) a custom AsyncTask that will theoretically populate more posts for the main list on the main screen.
Here is problem point in the list:
listView.setOnRefreshListener(new PullToRefreshListView.OnRefreshListener(listView) {
#Override
public void onRefresh() {
mylistAdapter.loadNewData(); //loads new data <-------------------
m_ptrlistView.postDelayed(new Runnable() {
#Override
public void run() {
m_ptrlistView.onRefreshComplete();
}
}, 2000);
////////////
Here is the function "loadNewData" in the adapter
public void loadNewData(PullToRefreshListView List){
//load new stuff
new AsyncFetchMore(list).execute();
// MANDATORY: Notify that the data has changed
notifyDataSetChanged();
return;
}
And... my asyncTask that is extended's implementation
#Override
protected Object doInBackground() {
try {
//simulating a long task
Thread.sleep(5000);
} catch (InterruptedException e) {
Log.d("Notes", "Thread failed to sleep");
}
//create dummy posts for testing
for ( int i = 12; i < 24; i++ ) {
Post pNewPost = new Post();
pNewPost.setText("POST # " + i);
m_alNewPosts.add(pNewPost);
}
return null;
}
///////////////////////////////
Perhaps I'm approaching it all wrong. I'm having doubts... But I just don't know what the right next step is and I am pretty lost here! Can you give me any tips?
It looks like you arent doint anything with results that are returned by your async task. But before you go to fix it, please consider using Loaders - they are asyncronous and made specifically for your purpouse - getting data for Fragment/Activity. Here is documentation for them. It looks like you might have a fair amount of refactoring to do if you want to implement them, but its well worth it - its the right way. hope this helps.
You are calling notifyDataSetChanged() right after you start your Async thread, which hasn't populated your data. Call notifyDataSetChanged() after the data has been updated. Check out the examples in ApiDemos in the samples directory of the SDK.

Best practices synchronizing CursorLoader test cases

I've just switched to using CursorLoaders and I'm having trouble writing tests that utilize them. Since using the CursorLoader methodology takes the querying off of the main thread getInstrumentation().waitForIdleSync() is returning before the adapter is being updated (or at least this is my theory). I'm trying to avoid this is all my tests
public void testUpdateList() throws InvalidRecord, InterruptedException {
ListView listView = frag.getListView();
// Verify list is empty
assertEquals(0, listView.getCount());
// Add transaction directly into database
transTable.addOccurrences(resolver, TestUtils.createMockTrans());
//Don't want to do this but it works.
synchronized (this) {
wait(500);
assertEquals(1, listView.getCount());
}
}
So my question is, is there a better way to test this functionality within the Android testing framework?
The solution I settled on was using the waitForCondition method within Robotium. Here is an example.
...
// Waits for 500 milliseconds for the condition to be meet.
// If it isn't meet within this time limit the result is false.
boolean isSatisfied = solo.waitForCondition( new Condition() {
public boolean isSatisfied() {
return listView.getCount() == 1;
}, 500);
//Then I check if the condition has been meet.
assertTrue(isSatisfied);
...

Android AsyncTask strangely publishing duplicates

I think this is not an easy question.
I'll be brief and give a little example of what is happening.
Let's say we have a source of data in file Byron.txt:
SHE walks in beauty, like the night
Of cloudless climes and starry skies;
And all that 's best of dark and bright
Meet in her aspect and her eyes:
Thus mellow'd to that tender light
Which heaven to gaudy day denies.
And this code execute inside an AsyncTask:
final ArrayList<Record> poem = new ArrayList<Record>();
final Object objectLock = new Object();
private Record rec = new Record();
#Override
protected Void doInBackground(Void... args) {
String line = null;
int i;
int last;
try {
process = Runtime.getRuntime().exec("cat Byron.txt");
bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()), 8192);
synchronized (objectLock) {
poem.clear();
last = i = poem.size() - 1;
}
while(line = bufferedReader.readLine()) != null) {
rec.setString(line);
synchronized (objectLock) {
last++;
poem.add(last, rec);
}
while(!bPause && i < last) {
i++;
publishProgress(poem.get(i));
}
}
}
catch (Exception e) {
e.printStackTrace();
}
return null;
}
#Override
protected synchronized void onProgressUpdate(Record... m) {
if(m.length > 0) {
mContext.mTable.appendRow(m[0]);
}
}
where there is a TableLayout in the UI and each time we get a new line we add a new TableRow to it.
And this is the output we see in the UI:
SHE walks in beauty, like the night
Of cloudless climes and starry skies;
Of cloudless climes and starry skies;
Of cloudless climes and starry skies;
Thus mellow'd to that tender light
Thus mellow'd to that tender light
And we go into the debugger and we see why it happens.
Sometimes the synchronized (objectLock) is skipped and the loop continues.
There is no publishing because i already catched last.
Later the block is executed as many times as it was skipped,
, but the original line is lost and the current line is added instead to poem several times
Then, all the new lines are published until i catches last again.
So you see that I followed the code and I can explain what's happening, the question here is: Why the block is skipped?, Why?
I expected the synchronized block to stall until it can be executed.
At least this how I understood the function of synchronized (objectLock)
even without using wait() and notify()
I don't pretend to open a discussion here (although if you want we can open one in the chat area)
If you see some fault in the code, then, answer the question to let me know.
NOTES:
synchronized is needed because somewhere else in the app, the user may want to email the lines he got so far.
The user may pause the publishing (bpause); that's the while loop and i follows last only when bPause is false.
I decided to publish the answer. Even though I feel very embarrassed by its simplicity.
I discovered it only after I had already dug deep into AsyncTask class and message handling and whatnot.
I publish it in hope it will help people to check the basic things before jumping to
conclusions, and that someone out there will save himself half a day debugging because of
this post.
The Record rec was the same one each time. The poem ArrayList had the same element id for each entry. And the content changed on all of them at once, since they were all the same.
When the progress was published immediately it printed the right string, the last one. But if some delay cause the progress to publish later, then retrieving the poem.get(i) records retrieved a different entry but with the same pointer, thus, the same content.
The solution was to create a new Record each loop.
Do the synchronization for last object.
synchronized (last) {
last++;
poem.add(last, line);
}

Cancelling ORMLite Write in Background Thread Safely

I'm designing out a module in Android that does some processing and then writes to the database using ORMLite transactions. In particular, my background code will be something like:
public class BackgroundOperation implements Runnable {
#Override
public void run() {
//Do some stuff
//Write to the database in a transaction
try {
ORMHelper h = ORMHelper.getDefaultOrmHelper();
final MyModel modelObj = h.myModelDao.queryForId(someId);
TransactionManager.callInTransaction(
h.getConnectionSource(),
new Callable<Void>() {
public Void call() throws Exception {
modelObj.col1 = 10;
modelObj.col2 = "hello";
h.myModel2Dao.update(modelObj);
h.myModel2Dao.create(new MyModel2("a", "b"));
return null;
}
}
);
}
catch (Exception e) {
return null;
}
}
}
This runnable will then be executed by being submitted to a ThreadPoolExecutor. I want to be able to cancel the background thread if needed and am trying to make sure that if the operation is cancelled, then the transaction will simply fail and do nothing. For example, if I do this:
Future f = myThreadPoolExecutor.submit(new BackgroundOperation());
//Some time later
f.cancel(true);
Can I be sure that it will be an all or nothing deal with the transaction in ORMLite. That is, there is no cleanup needed and my modelObj will have either both col1 and col2 set or neither set? Do I have to do anything special when catching the InterruptedException in the Runnable to handle the case when a task is cancelled in this way, or can I simply exit?
If you call f.cancel(true), all that does is interrupt the Thread which causes wait(), sleep(), and some other methods to throw InterruptedException. It will not cancel the database transaction underway.
If you want, you can check for the interrupted bit in the middle of your IO operations:
h.myModel2Dao.update(modelObj);
if (Thread.currentThread().isInterrupted()) {
throw new RuntimeException("Thread was interrupted");
}
h.myModel2Dao.create(new MyModel2("a", "b"));
For more information about what happens when a thread is interrupted see here:
What does java.lang.Thread.interrupt() do?
Transactions are for when you are updating multiple objects as a single unit or writing to multiple tables. See the documentation about transactions which has an example of updating an Account and an Order inside of a transaction.
Also, you do not need to use a transaction if you are updating multiple fields in the same row. The update statement is considered to be a single unit and the database should ensure that the row gets updated atomically. Only if you are updating multiple different rows either in the same table or in separate tables do you need a transaction.
ORMLite will utilize the sqlite transactions under the covers. This is most likely a double phase commit which only allows you to commit a transaction as an entire unit.
In short, you can be assured that col1 and col2 will only be modified as a single atomic unit. Also, it if is interrupted the commit will fail and the changes to col1 and col2 will be rolled back.

Android static method plots background thread data nicely in real-time, but is it a good solution?

I've been asking a series of evolving questions regarding my Android project that continually plots Bluetooth data in real-time. And I haven't done a very good job of asking questions.
So what I need to do is edit this question, clean it up, add important detail, and most importantly I need to add code fragments of relevant sections of code, especially sections I've hacked on quite a bit, and provide explanation about these sections of code. That way maybe I might get an answer to my question/concerns which are: Is my current solution an OK one? Is it going to hold up as I add new features?
Basically what I've already done is create a first version of my app by cobbling together some open source code Blueterm and OrientationSensor.
It's been suggested that I add a thread, a handler, a Service, or use Async Task, or AIDL, etc. But I have decided I don't want to modify or replace my existing solution unless I really should. Mainly I want to know if it's good enough to move forward and extend it to add other features.
By the way what I have previously referred to as BluetoothData is just bluetooth data: it's 16 bit data received from a remote Bluetooth device at the rate of 2 to 10 samples/second. My app is basically a data acquisition system that acquires/receives bluetooth data and plots it.
Here's a description of the Blueterm open source code I started with (see link above). Blueterm is basically a terminal emulator program that communicates over Bluetooth. It consists of several activities with Blueterm being the most important. It discovers, pairs, and connects with a remote Bluetooth device that supports SPP/RfComm. When connected I can use Blueterm to configure the remote device by sending it commands to turn on sampling, change the number of channels to sample (to one channel), change to format of the incoming data (I like comma separated data), etc
Here's a description of the OrientationSensorExample open source code I started with (see link above). It's basically an example application of the AnroidPlot library. The OrientationSensor activity implements SensorEventListener. This includes overriding onSenorChanged() which is called whenever new orientation sensor data is taken, and it redraws the graph.
Having cobbled together these two open source projects (Blueterm and OrientationSensorExample) into one application (Blueterm) here's a description of how the overall application (Blueterm) works. When I start Blueterm the whole screen emulates a nice blue terminal. From the Options Menu I discover, pair with, connect to, and configure a remote bluetooth device as described above. Once I have configured the remote device, I go again to the Options Menu and select "Plot data" which launches the Plot activity. The terminal emulator goes away, and a nice scrolling real-time plot from the Plot activity shows up.
Here's how I did this. In onOptionsItemSelected() I added a case to launch the Plot activity as follows:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.connect:
if (getConnectionState() == BluetoothSerialService.STATE_NONE) {
// Launch the DeviceListActivity to see devices and do scan
Intent serverIntent = new Intent(this, DeviceListActivity.class);
startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE);
}
else
if (getConnectionState() == BluetoothSerialService.STATE_CONNECTED) {
mSerialService.stop();
mSerialService.start();
}
return true;
case R.id.preferences:
doPreferences();
return true;
case R.id.menu_special_keys:
doDocumentKeys();
return true;
case R.id.plot_data:
doPlotData();
return true;
}
return false;
}
private void doPlotData() {
Intent plot_data = new Intent(this, com.vtrandal.bluesentry.Plot.class);
startActivity(plot_data);
}
Then in the bluetooth background thread I added a call to update() to call plotData() as follows:
/**
* Look for new input from the ptty, send it to the terminal emulator.
*/
private void update() {
int bytesAvailable = mByteQueue.getBytesAvailable();
int bytesToRead = Math.min(bytesAvailable, mReceiveBuffer.length);
try {
int bytesRead = mByteQueue.read(mReceiveBuffer, 0, bytesToRead);
append(mReceiveBuffer, 0, bytesRead);
//VTR use existing handler that calls update() to get data into plotting activity
//OrientationSensor orientationSensor = new OrientationSensor();
Plot.plotData(mReceiveBuffer, 0, bytesRead);
} catch (InterruptedException e) {
//VTR OMG their swallowing this exception
}
}
Then in the Plot activity I basically cleaned house, removed "implements SensorEventListener" and some related methods and variables, and wrote plotData() to be called as shown above. Here's what plotData() and it's helper methods splitData() and nowPlotData() currently look like:
private static StringBuffer strData = new StringBuffer("");
public static void plotData(byte[] buffer, int base, int length) {
Log.i("Entering: ", "plotData()");
/*
byte[] buffer = (byte[]) msg.obj;
int base = msg.arg1;
int length = msg.arg2;
*/
for (int i = 0; i < length; i++) {
byte b = buffer[base + i];
try {
if (true) {
char printableB = (char) b;
if (b < 32 || b > 126) {
printableB = ' ';
}
Log.w("Log_plotData", "'" + Character.toString(printableB)
+ "' (" + Integer.toString(b) + ")");
strData.append(Character.toString(printableB));
if (b == 10)
{
Log.i("End of line: ", "processBlueData()");
Log.i("strData", strData.toString());
splitData(strData);
strData = new StringBuffer("");
}
}
} catch (Exception e) {
Log.e("Log_plotData_exception", "Exception while processing character "
+ Integer.toString(i) + " code "
+ Integer.toString(b), e);
}
}
Log.i("Leaving: ", "plotData()");
}
private static void splitData(StringBuffer strBuf) {
String strDash = strBuf.toString().trim();
String[] strDashSplit = strDash.split("-");
for (int ndx = 0; ndx < strDashSplit.length; ndx++)
{
if (strDashSplit[ndx].length() > 0)
Log.i("strDashSplit", ndx + ":" + strDashSplit[ndx]);
String strComma = strDashSplit[ndx].trim();
String[] strCommaSplit = strComma.split(",");
for (int mdx = 0; mdx < strCommaSplit.length; mdx++)
{
if (strCommaSplit[mdx].length() > 0)
Log.i("strCommaSplit", mdx + ":" + strCommaSplit[mdx]);
if (mdx == 1)
{
int raw = Integer.parseInt(strCommaSplit[1],16);
Log.i("raw", Integer.toString(raw));
float rawFloat = raw;
Log.i("rawFloat", Float.toString(rawFloat));
float ratio = (float) (rawFloat/65535.0);
Log.i("ratio", Float.toString(ratio));
float voltage = (float) (5.0*ratio);
Log.i("voltage", Float.toString(voltage));
nowPlotData(voltage);
}
}
}
}
public static void nowPlotData(float data) {
// get rid the oldest sample in history:
if (plotHistory.size() > HISTORY_SIZE) {
plotHistory.removeFirst();
}
// add the latest history sample:
plotHistory.addLast(data);
// update the plot with the updated history Lists:
plotHistorySeries.setModel(plotHistory, SimpleXYSeries.ArrayFormat.Y_VALS_ONLY);
//VTR null pointer exception?
if (plotHistoryPlot == null)
Log.i("aprHistoryPlot", "null pointer exception");
// redraw the Plots:
plotHistoryPlot.redraw();
}
Time for a summary: I basically found the update() method in the background thread that was created by the Blueterm activity. The update() method essentially appends newly received bluetooth data to the screen buffer using the append() method. So, the background thread's update() method looked like a good place to call plotPlot(). So I designed plotData() to plot the data being passed to append(). This works as long plotData() is a static method. I would appreciate an explanation as to why plotData() seemingly must be static in order to work.
And again my overall question/concerns: Is my current solution an OK one? Is it going to hold up as I add new features?
I found the method in the background thread that writes BluetoothData to the Logcat. So I am leveraging this method to call a static method, plotData(BluetoothData), in the Plotting Activity. It works nicely plotting the incoming BluetoothData in real-time.
This story does not add up, or BluetoothData is misnamed.
In Android, to plot to the screen, you need an Activity instance, with whatever widget(s) you are plotting on. A plotData() method that does the plotting can be static, but somehow it needs the Activity instance. So, one of the following must be true:
BluetoothData contains an Activity instance (and hence is misnamed), or
plotData() takes more than the one parameter you have indicated, or
you are holding onto an Activity instance in a static data member (BAD BAD BAD BAD BAD), or
plotData() is not a static method, so you are actually calling it on an Activity instance
But, since you repeatedly decline to provide the source code, despite having asked several questions about the code, it is impossible for us to say which of these is really your problem.
Is it not thread safe? Do I have a deadlock issue? Is my solution a fragile one? Am I circumventing the Android OS? Am I lucky that it's working at all? Or is there a proper way to extend the existing design?
The first five of these have different answers depending upon which of the four bullets above reflects reality, and I really do not feel like writing out a 20-cell grid of answers. Your last question assumes that you have actually explained your "design", which you have not.
UPDATE
Some comments based upon the substantial revision to the question:
I would appreciate an explanation as to why plotData() seemingly must be static in order to work.
It probably doesn't have to be static.
Is my current solution an OK one?
Probably not.
Static methods are rarely a problem on their own. Static data is frequently a problem, due to lack of thread safety, memory leaks, and the like. Hence, your objective is to minimize or eliminate all static data members.
There are at least four static data members at play here, perhaps more. One is strData. This isn't too bad, in that you reset the static data member to a fresh StringBuffer on each plotData() call, so your memory leak is modest. However, if plotData() somehow were to be called on multiple threads simultaneously -- and, since I don't know your threading model, that's at least possible -- you will have problems, since you have no synchronization.
However, the far bigger problem is represented by the plotHistory, plotHistorySeries, and plotHistoryPlot static data members are. I have no idea what these objects are. However, by their name and your overall objective, it would appear that redraw() actually draws to the screen, which means that plotHistoryPlot is or holds some subclass of View that is the thing being plotted upon. This means you violated a cardinal rule of Android development:
Never put something that references a transient Context in a static data member
Here, an Activity represents a transient Context, "transient" because activities do go away, Context because it inherits from Context. Your statically-referenced View holds a reference back to its Activity. Hence, this Activity can never be garbage collected, which is bad for business, let alone any possible thread-safety issues.
Again, this is an educated guess, since I don't know what those static data members really are.

Categories

Resources