Right way to use Android BroadcastReceiver to trigger Room DB Operation - android

My app has a Foreground Service and I prefer that all my network calls happen in that, to keep things sane. I also implement a Room database in the Service.
My plan is to have all database operations take place based on triggers, at the Service end. The broadcast has the data passed to the service in the form of a Bundle in the Intent.
However, my problem is:
BroadcastListeners operate on the main thread.
Room DB operations need to be off the main thread so the app is not ANR'd
When I implement a HandlerThread or a Thread with Runnable in the BroadcastListener, I am unable to pass the data as the intent variable is 'accessed from an inner class and needs to be declared final'
My code (as it stands) is as below.
BroadcastReceiver houseCallCancelReceiver = new BroadcastReceiver () {
#Override
public void onReceive(Context context, Intent intent) {
new Thread(new Runnable() {
#Override
public void run() {
Bundle bundle = intent.getParcelableExtra ("housecall");
String email = bundle.getString("email");
String housecallid = bundle.getString("housecallid");
.
.
.
}
}
}).start();
}
};
Atm I can think of several options, including:
Writing the DB logic (and thread) in a function and passing the
intent as a parameter to the thread. - this leads to code that is
not all together and makes it harder to read as I will have one
function per operation
Making the call in an AsyncTask - same as above plus a lot of one-off AsyncTask calls. Plus can I call an AsyncTask in a ForegroundService?
Upgrading to Java 1.8 and using Lambdas - I'm worried this may break compatibility with older phones.
Any help is much appreciated!

I found an answer (of sorts) in this thread: Runnable with a Parameter
By moving the Room DB logic to a separate function, and declaring a class in the function itself and invoking the function in the constructor, I can pass the Intent to the Class as a parameter.
BroadcastReceiver refreshHousecallsReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "onReceive: In refreshHousecallsReceiver");
class OneShotTask implements Runnable {
// idea from https://stackoverflow.com/questions/5853167/runnable-with-a-parameter
Intent intent;
OneShotTask(Intent intent1) { intent= intent1; }
public void run() {
refreshHouseCalls(intent);
}
}
Thread t = new Thread(new OneShotTask(intent));
t.start();
}
};
I tried this and it works. However, this is not a reusable way to do things, and I am on the lookout for other ways to do the same thing - please share your examples or ideas as well!

Related

Can push notification execute code in parallel with UI thread or not?

AsFirebaseMessagingService does not use the Main Thread, I am just wondering as all my code in all of my activities or fragments run in UI thread(Main Thread). Now suppose my activity's onCreate method is executing and then I receive the push notification. Will these two blocks of code run in parallel, or will the push notification code wait in the queue until onCreate() method OR Activity's last life cycle method gets executed?
Edit- As you are saying code will run parallelly then suppose I have a variable in App.java
public class App extends Application {
int ctr = 100;
}
StatusActivity.java
public class StatusActivity extends BaseActivity {
public void onCreate() {
fun();
}
public void fun() {
int d = App.ctr - 1;//Step 1 Here d = 99
int m = App.ctr - 1; // Step 3 Here m = 98
}
}
FcmListener.java
public class FcmListener extends FirebaseMessagingService {
Override
public void onMessageReceived(RemoteMessage mssg) {
App.ctr = App.ctr - 1;//STEP 2 // Now App.ctr = 99
}
}
Now as you can see in the above code there will be problems if push notif code executes in parallel with fun(). I want push_notif and fun() to run serially, where order doesn't matter but not in parallel.
As already pointed out in a parallel answer, the overriden methods of FirebaseMessagingService run in a background thread, so you should use synchronization strategies in order to access/use mutable object from different thread.
But the question I want to answer is a bit different. Let's for a moment assume, that overriden methods run on a main thread. So is there a possibility, that the order of execution will be STEP 1 then STEP 2 and then STEP 3?
Android works with a technique called MessageQueue, basically there are Messages posted on that queue, on which Looper loops and "parses/executes" them.
Now if we assume, that you are currently located on STEP 1, it means, that there was a particular Message which is currently being executed (hypothetically, let's assume that action is - perform onCreate() of this activity).
Until this message is fully executed there cannot exist another Message which might get have a chance to be executed. So if we assume, that Firebase dispatches an event on background thread but the actual overriden method is being run on main thread, then this overriden method would have chance to be executed only after current Message (activity's onCreate()) has finished. In other words, there would be posted another Message on the MessageQueue, which would perform onMessageReceived() when the Looper will give chance for this message to be executed.
So, theoretically, there is no chance that the ordering would be STEP 1 -> STEP 2 -> STEP 3.
If STEP 1 is already executed, then it will continue with STEP 3 and the STEP 2 (at some point in future, because you can't know what other Messages are already posted on MessageQueue).
See this article for more details about MessageQueue and related classes.
How about it?
class Sample {
private String message = null;
private final Object lock = new Object();
public void newMessage(String x) {
synchronized (lock) {
message = x;
}
}
public String getMessage() {
synchronized (lock) {
String temp = message;
message = null;
return temp;
}
}
}
Here is my 2 cents. You say,
Suppose my activity's onCreate method is executing and then I receive the push notification. Will these two blocks of code run parallelly or will the push notification code wait in the queue until onCreate method OR Activity's last life cycle method gets executed?
From the official documentation of FirebaseMessagingService:
Extending this class is required to be able to handle downstream messages. It also provides functionality to automatically display notifications, and has methods that are invoked to give the status of upstream messages. Override base class methods to handle any events required by the application. Methods are invoked on a background thread.
So its possible both methods execute at the same time. If you want to do the operations on a shared variable in your Application class, you can do thread safe operations using synchronize. See How to synchronize or lock upon variables in Java?. That will make sure only one thread is making changes at a time on that variable. If a new thread comes in, it waits for the lock to get free and then makes the changes on that variable. However this doesn't guarantee the order. It just means that one thread operates on it at time and is in FIFO order.
I suggest you a different approach, because using those global variables can lead to unexpected behavior.
If your ctr var is related to your activity, then keep it inside. If you need it on other activities consider passing it via the Intent as an extra.
Use LocalBroadcastManager to inform your activity that you received the push message
public class FcmListener extends FirebaseMessagingService {
public static final String ACTION_MESSAGE_RECEIVED = "ACTION_MESSAGE_RECEIVED"
#Override
public void onMessageReceived(RemoteMessage mssg) {
Intent intent = new Intent(ACTION_MESSAGE_RECEIVED) // put extra vars as needed
boolean delivered = LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
// 'delivered' is true if there is at least someone listening to the broadcast, eg. your activity
// If your activity is not running, then 'delivered' is false so you can act accordingly
}
}
Then inside your activity
public class StatusActivity extends BaseActivity {
private BroadcastReceiver messageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (TextUtils.equals(FcmListener.ACTION_MESSAGE_RECEIVED, action)) {
// do stuff with 'ctr'
}
}
};
#Override
protected void onStart() {
super.onStart();
IntentFilter filter = new IntentFilter(FcmListener.ACTION_MESSAGE_RECEIVED);
LocalBroadcastManager.getInstance(this).registerReceiver(messageReceiver, filter);
}
#Override
protected void onStop() {
super.onStop();
LocalBroadcastManager.getInstance(this).unregisterReceiver(messageReceiver);
}
}

Refreshing fragments in FragmentActivity after sync service runs

does anybody have any elegant solution for refreshing the Views in Fragments in a FragmentActivity's ViewPager after a sync Service from a SyncAdapter runs?
I've tried calling notifyDataSetChanged() and notifyDataSetInvalidated() on my adapter, as well as refreshDrawableState() on my views (GridViews), but to no avail. Perhaps I've been calling them from the wrong places -- I've tried doing it at setUserVisibleHint where isVisible=true, hoping to trigger it whenever the fragment comes into view, but it doesn't work.
I've also been using ASync calls to the SQLite database for my data needs, rather than a Content Provider, which I think would have made this a bit easier. I can think of a couple of ways to do it without a Content Provider, but neither are very nice.
Any ideas? I can provide code if wished. Thanks.
I'll assume that you're using an AsyncTask for loading the cursor just for the sake of the explanation, but it would work the same if you're using a Loader, an ThreadPool or whatever.
From the service, as soon as new data was changed I would send a LocalBroadcast. The activity might be there or not, so a broadcast is a good way to let it know there's new data. So from the service you would do:
// that's an example, let's say your SyncAdapter updated the album with this ID
// but you could create a simply "mybroadcast", up to you.
Intent i = new Intent("albumId_" + albumId);
LocalBroadcastManager.getInstance(this).sendBroadcast(i);
and then from the activity/fragment that have the Cursor, you'll be listening to this broadcast like this:
public void onResume(){
// the filter matches the broadcast
IntentFilter filter = new IntentFilter("albumId_" + albumId);
LocalBroadcastManager.getInstance(this).registerReceiver(myReceiver, filter);
}
public void onPause(){
LocalBroadcastManager.getInstance(this).unregisterReceiver(myReceiver);
}
// and of course you have to create a BroadcastReceiver
private BroadcastReceiver myReceiver = new BroadcastReceiver(){
#Override
public void onReceive(Context context, Intent intent){
// here you know that your data have changed, so it's time to reload it
reloadData = new ReloadData().execute(); // you should cancel this task onPause()
}
};
as I said, this next part varies depending on what threading method you're using to load the Cursor, for this example I'll show in a AsyncTask because it's very popular (but I really believe you and every developer in the world should use the Loaders pattern).
private class ReloadData extends AsyncTask<Void, Void, Cursor> {
protected Cursor doInBackground(Void... void) {
// here you query your data base and return the new cursor
... query ...
return cursor;
}
protected void onPostExecute(Cursor result) {
// you said you're using a subclass of CursorAdater
// so you have the method changeCursor, that changes the cursor and closes the old one
myAdapter.changeCursor(result);
}
}
The above approach I tested and used before and I know it works. There's a way of making it work with the flag FLAG_REGISTER_CONTENT_OBSERVER and override onContentChanged() to re-execute the query and swap the cursor, but I've never tested it. It will be something like that:
init your adapter with the constructor CursorAdapter(Context context, Cursor c, int flags) passing the flag FLAG_REGISTER_CONTENT_OBSERVER and override onContentChanged(). Inside onContentChanged you will execute the AsyncTask just like above. This way you don't have to use the LocalBroadcastManager as the database will alert. The reason that method is not my main answer, it's because I've never tested it.
Note that autoRequery have been deprecated and it's discouraged as it performs data loading in the UI thread.
edit:
I just noticed that the content observer is an API 11 thing. You have two options: 1 use the support library instead: https://developer.android.com/reference/android/support/v4/widget/CursorAdapter.html or the broadcast option.
Register a BroadcastReceiver in the fragments you have and in its onReceive call refresh - this method is supposed to update the UI depending what you have inside. For making your code easy to use, have a base Fragment class and do the registering/unregistering there along with an abstract refresh method that will be implemented by children fragments. Something like:
public abstract class BaseRefreshableFragment extends Fragment {
private BroadcastReceiver refreshReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if ("package_name.REFRESH_ACTION".equals(intent)) {
refresh();
}
}
};
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
IntentFilter filter = new IntentFilter("package_name.REFRESH_ACTION");
LocalBroadcastManager.getInstance(getActivity()).registerReceiver(refreshReceiver, filter);
}
#Override
public void onDestroyView() {
LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(refreshReceiver);
super.onDestroyView();
}
protected abstract void refresh();
}
In your Service, when your work is done, broadcast an intent with above action. So if there are fragments to show updated data, their receiver will be notified and that will call refresh ON EACH FRAGMENT. Something like from your service:
Intent intent = new Intent("package_name.REFRESH_ACTION");
LocalBroadcastManager.getInstance(MySyncService.this).sendBroadcast(intent);
The advantage is that you don't need to care when the fragment is shown or not since the receiver is there for the life-time of your fragment's view.
Recreate your adapter from scratch with the new data, and reassign it to the ListView (or whatever Views you have).

How to access SQLite database from UI thread if I'm inserting data in AsyncTask thread?

I have a class in my Android app, called Main.java, to validate a user login (user name + password) against the data in my server. At first, I succeeded; I used an AsyncTask thread to do it plus a library which handles the Http connection, call HttpPostAux.java (in fact, I found the library's code here in this forum). In the onPostExecute method of AsyncTask, I was creating and starting a new activity instead of modifying the current one and it worked.
But now I want to do things different. I want to save the validated data (user name + password) into a SQLite table in the AsyncTask thread and then in the UI thread, recover that data and use it to open the mentioned activity. The insertion occurs but when I'm trying to access the database from UI thread: it says that the table is empty. So I looked in the logcat and I found that UI thread executes before AsyncTask thread.
So my question is how to insert data in the AsyncTask thread and then recover it inside UI thread? Can anybody help here? I'm kind of lost!
I will appreciate a code example! Thanks in advance!
Greetings from Venezuela!
UI thread is your applications main thread. When you create an AsyncTask, your long time-taking task will be executed(inside doInBackground function) on a separate thread. When doInBackground completes, onPostExecute() will be called from the UI thread. So you simply need to execute your UI thread task(="recover that data and use it to open the mentioned activity") from inside onPostExecute().
I think the way you did it before is correct. I would recommend to make a sort of SplashScreen activity which checks if the user has been logged in before, i.e. if there is a username/password in the database. If this is the case, use this data to login the user and then proceed to your main activity. If the user hasn't been logged in before, promp them with a login screen, store this data for future use and continue to your main activity.
You can implement a BroadcastReciever in your UIThread that listens to Intents sent from your AsyncTask when the insert to the database is done.
fire from AsyncTask:
Intent intent = new Intent("DATABASE_INSERTION_DONE");
context.sendBroadcast(intent );
register in UiThread:
IntentFilter intentFilter = new IntentFilter("DATABASE_INSERTION_DONE");
registerReceiver(myIntentsReceiver, intentFilter);
class:
class MyIntentReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if ("DATABASE_INSERTION_DONE".equals(intent.getAction())) {
//do something
}
}
}
You have to keep in mind that once you start an AsyncTask, it will run on another thread and your UI thread will keep moving forward. For example:
x="foo";
new SampleAsyncTask().execute();
txtView.setText(x);
Class SampleAsyncTask extends AsyncTask<...>{
.....
public Void doInBackground(...){
//LOTS OF CALCULATIONS
x="bar";
}
}
Your txtView will most definitely show "foo" rather than "bar". To make sure that your AsyncTask is finished there are lots of options. You can call a method to start the rest of your operations in PostExecute of your AsyncTask. You can also use intents and a broadcastreceiver like Jelgh said. But my favorite is using a simple callback:
public class LoginActivity extends Activity implement SampleAsyncTask.asyncCallback{
#Override
protected void onCreate(Bundle savedInstanceState){
x="foo";
new SampleAsyncTask().execute();
Class SampleAsyncTask extends AsyncTask<...>{
asyncCallback mCallback;
public SampleAsyncTask(Context c){
mCallback; = (asyncCallback) c
}
public interface asyncCallback{
void servedMyPurposeYouCanGoOn();
}
public Void doInBackground(...){
//LOTS OF CALCULATIONS
x="bar";
mCallback.servedMyPurposeYouCanGoOn;
//REST OF THE WORK
}
}
}
#Override
public void servedMyPurposeYouCanGoOn(){
runOnUIThread(
//rest of the work
)
}
}

Pass data from broadcast receiver to activity

I have a broadcast receiver which is listening to the WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.
In that receiver I filter all the available networks and return a list of networks with a specific SSID. I nned to pass that list back to the MainActivity of my application.
I know I can create an inner class for my Broadcast receiver but I prefer to create a separate class for better organization.
I am thinking in creating a static variable in my mainActivity class and then set that value.
Is this a good practice?
A good way of sharing and access information a cross of Activites and other classes is by using the application object. You can access the application object from all your classes as long as you have the application context.
See this tutorial about application object: How to use application object
Usage from activities:
MyApplicationObject app = (MyApplicationOjbject)getApplicationContext();
app.setMyVariable(variable);
From other classes outside activity:
MyApplicationObject app = (MyApplicationOjbject)context.getApplicationContext();
app.setMyVariable(variable);
Stefan is right, this static link is not pretty. You can sometimes have multiple instance of the same activity (when recreated, until Garbage collector collect it). Or multiple broadcast happening, overwriting your static variable value.
If you don't want to use an anonymous inner class, you can override the constructor and pass a reference to your current activity that you will be able to use to send the results when processing onReceive(). Just clean up this reference when you are done to avoid leaking your activity.
I've used the same technique with success. The one time this bit me was when I did not consider that the user could tilt the screen and the activity would be recreated. I failed to check if the static variable was already set and replaced it repeatedly. Watch out for that.
One more technique I can think of is to share a callback between the activity and the broadcast receiver. The receiver makes a call to the callback which stores a reference to the right activity and calls runOnUiThread(action) to make UI updates. References should be updated onStart() and onStop(). I've never really used this pattern. Thought about it in the shower :)
I recommend to not use a static variable to deliver the information. If your main activity is the only object receiving the information from the receiver make the BroadcastReceiver local to the main activity. Doing so groups those elements which share a responsibility.
This is how I get data from broadcasts, little bit of more code but its way simpler to read
for future, in case of complex stuff get going.
Intent intent = new Intent(context, BroadcastReciever.class);
SimpleResultReciever resultReciever = new SimpleResultReciever(new Handler())
.setCallback(new OnLocationCallback() {
#Override
public void onRecieverLocation(Location location) {
if(location != null) {
MainActivity.this.location = location;
}
}
});
intent.putExtra(SimpleResultReciever.KEY_RESULT_RECIEVER, resultReciever);
//call intent or create pending intent you will use broadcast stuff.
public class SimpleResultReciever extends ResultReceiver {
public final static String KEY_RESULT_RECIEVER = "bundle.broadcast.reciever.callback";
private OnLocationCallback callback;
public LocationResultReciever setCallback(OnLocationCallback callback) {
this.callback = callback;
return this;
}
/**
* Create a new ResultReceive to receive results. Your
* {#link #onReceiveResult} method will be called from the thread running
* <var>handler</var> if given, or from an arbitrary thread if null.
*
* #param handler
*/
public LocationResultReciever(Handler handler) {
super(handler);
}
#Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
super.onReceiveResult(resultCode, resultData);
Location location = resultData.getParcelable(LocationManager.KEY_LOCATION_CHANGED);
if(callback != null) {
callback.onRecieverLocation(location);
}
}
}
public class LocationBroadcastReciever extends BroadcastReceiver {
public LocationBroadcastReciever() {
super();
}
#Override
public void onReceive(Context context, Intent intent) {
Bundle extra = intent.getExtras();
Location location = extra.getParcelable(LocationManager.KEY_LOCATION_CHANGED);
ResultReceiver res = extra.getParcelable(LocationResultReciever.KEY_RESULT_RECIEVER);
if(res != null) {
Bundle data = new Bundle();
data.putParcelable(LocationManager.KEY_LOCATION_CHANGED, location);
res.send(Activity.RESULT_OK, data);
}
}
}
if u r launching an Main activity form the receiver and then u can pass the list in by using putextra(), and then you can get that in the main activity.
some thing like this.
Intent intent = new Intent(ctx.getApplicationContext(), targetActivity);
intent.putCharSequenceArrayListExtra(name, value);

sendBroadcast(intent) gives a null pointer exception

This is my second post today as fixing the 1st post lead to another problem which I am really struggling on. I want to use Broadcasts and intent to send data packets back from a service to the UI. I managed to bind and start the service successfully
see my other post if you want history and code
The null pointer exception comes on the sendBroadcast() inside the service. The service class does have its constructor re-called after binding the UI to the service. This happens from another class so Context can not be easily used. So I guess the sendBroadcast has no-where to go :(
I suspect this is my problem...the re-calling of the Service constructor after the initial binding. I have onDestroy, onPause and onResume covered with binding an unbinding.
Any ideas or suggestion would be great or maybe I am just going about this wrong is there another way?
EDIT
The previous problem of binding is now solved and due to me being new to the forums a slight delay in accepting the answer...sorry.
The class diagram is like this (it is ported C#code)
Activity doBind (on Curve.class)---> Activity starts a worker class (not treated as a service) Comm.class with a Handler for some comms--> the Comm starts another worker class --> previous worker class finally calls new Curve.class.
It is this last stage Curve.class where the sendBroadcastReceiver() then throws a nullpointer ref becasue the binder is lost.
I tested the broadcastreceiver just with a simple timer cutting out the worker classes in between and it works fine. Problems start when the Curve.class is recalled later further down the hierarchy and the binder gets nulled or "lost".
I removed all references of the binder from Curve except in onBind(). This might not be a good idea. Again the code below does work if used with a simple timer started directly from the UI (no other worker classes).
Some more code here:
The service
public class Curve extends Service
{
private NewCurvePointEventArgs newpoint = null;
private static final String TAG = "Curve";
Intent intent;
int counter = 0;
#Override
public void onCreate() {
super.onCreate();
}
#Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
IBinder mBinder = new LocalBinder<Curve>(this);
return mBinder;
}
#Override
public void onStart(Intent intent, int startId) {
}
#Override
public void onDestroy() {
}
public Curve(){
}
private void refreshintent(NewCurvePointEventArgs tmp)
{
ArrayList<String> thepoint = new ArrayList<String>();
thepoint.add()//various things added here
Bundle bundle = new Bundle();
// add data to bundle
bundle.putStringArrayList("New_Point", thepoint);
intent = new Intent();
intent.setAction(NEWCURVE_POINT);
intent.putExtra("NEW_POINT", bundle
sendBroadcast(intent);
}
The activity has this code now. the doBind() is called after the onCreate of the activity.
private BroadcastReceiver CurveReceiver = new BroadcastReceiver(){
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Curve.NEWCURVE_POINT)) {
displaynewpoint(intent);
}
}
};
private ServiceConnection CurveServiceConncetion = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
// TODO Auto-generated method stub
CurveService = ((LocalBinder<Curve>) service).getService();
}
#Override
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub
CurveService = null;
}
};
#Override
public synchronized void onResume() {
super.onResume();
if(D) Log.e(TAG, "+ ON RESUME +");
}
#Override
public synchronized void onPause() {
super.onPause();
if(D) Log.e(TAG, "- ON PAUSE -");
}
#Override
public void onStop() {
super.onStop();
if(D) Log.e(TAG, "-- ON STOP --");
}
#Override
public void onDestroy() {
super.onDestroy();
if(D) Log.e(TAG, "--- ON DESTROY ---");
unregisterReceiver(CurveReceiver);
unbindService(CurveServiceConncetion);
}
public void doBind(){
Boolean tmp;
tmp = bindService(new Intent(this, Curve.class), CurveServiceConncetion, Context.BIND_AUTO_CREATE);//Context.BIND_AUTO_CREATE
IntentFilter filter = new IntentFilter(Curve.NEWCURVE_POINT);
registerReceiver(CurveReceiver, filter);
}
This problem to me is because the Curve.class has its constructor called again after the initial doBind().
Surely there must be a way around this otherwise I have to load my worker classes closer in hierarchy to the UI with the code from Curve.class???
EDIT
Curve is an object that processes data, constants etc sent from an external machine and contains the processed data in arrays.
The logCat did of course exist I just wasn't looking in the right place here it is
ARN/System.err(10505): java.lang.NullPointerException
WARN/System.err(10505): at android.content.ContextWrapper.sendBroadcast(ContextWrapper.java:271)
WARN/System.err(10505): at pi.droid.Core.Curve.refreshintent(Curve.java:206)
WARN/System.err(10505): at pi.droid.Core.Curve.AddPoint(Curve.java:400)
WARN/System.err(10505): at pi.droid.Core.Comm.CommMeasure$CommMeasurement.AddPoint(CommMeasure.java:363)
WARN/System.err(10505): at pi.droid.Core.Comm.CommMeasure$GenericCommMeasurement.TryProcessData(CommMeasure.java:168)
WARN/System.err(10505): at pi.droid.Core.Comm.CommMeasure$CommMeasurement.ProcessData(CommMeasure.java:302)
WARN/System.err(10505):at pi.droid.Core.Comm.ClientConnection$timer_tick.run(ClientConnection.java:164)
WARN/System.err(10505): at java.util.Timer$TimerImpl.run(Timer.java:289)
You can also see the chain of the 2 other worker classes I use. The constructor of Curve is called after the bind from CommMeasure.
So this is my problem.
Do I need to totally change how my program is set up or is there another way around this?
FINAL EDIT
This code is brought from c# and Curve used eventhandlers to pass data around. I got rid of all them(java listeners) and used android Handler and broadcastreceiver.
It has been suggested that I should pass the CurveService around but this will be problematic as Curve has multiple constructors. The no parameter 1 for the service and then 1 like this
public Curve(Unit XUnit, Unit YUnit)
{ this.Title = "Curve";
this.finished = false;
this.XUnit = XUnit;
this.YUnit = YUnit;
this.YDirection = CurveDirection.Unspecified;
}
so surely instantiating that would be a problem with CurveService, which has to be like this: public Curve(){} ??
Anyway many thanks for all your help and advice.
Final Edit +1..lol
The UI creates a new instance of ClientConnection, that in turns creates a new instance of CommMeasure and finally CommMeasure creates a new instance of Curve to access Curve.addpoint.
I think this thread and the other linked 1 goes beyond a simple android problem and highlights the difficulties of code porting. Any .Net developer for example reading this will learn some peculiarities of android a lot quicker than I did. There is also good working code in there.
Thanks to all who helped especially Justin Breitfeller
The best thing for you to do is follow the example from the Android APIDemos.
A service to be used like you want to use it:
http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/app/LocalService.html
Look at the Binding class inside of this file to see a class that does binding like you should:
http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/app/LocalServiceActivities.html
Finally, if your constructor is being called twice on your service, you aren't binding to your service properly, or perhaps you are unbinding from it and binding to it again unexpectedly.
EDIT
From the stack trace, it appears that CommMeasure needs to have a reference to the instance of Curve that you receive in onServiceConnected.
EDIT 2
If you really want to make your life simple, pass getApplicationContext() to your CommMeasure class and just appContext.sendBroadcast() from that class to send out your point. This will prevent you from requiring a reference to the service in your long-running task.

Categories

Resources