Update INotifyPropertyChanged-property from ViewModel in another thread WinRT - multithreading

I have a property that RaisePropertyChanged(PropName, oldValue, true, true) when I don't have any connection to the internet no more but it throws the exception that I'm on the wrong thread.
So I want to update the property form my ViewModel but how do I get the current thread in my ViewModel or what is you proposal for a solution?
My ViewModel-ctor
public MyViewModel()
{
// START LISTENING TO NETWORKSTATUS
NetworkInformation.NetworkStatusChanged += OnNetworkStatusChangedHandler;
}
NetworkChanged-callback method
private async void OnNetworkStatusChangedHandler(object sender)
{
ConnectionProfile profile = NetworkInformation.GetInternetConnectionProfile();
if (profile == null)
{
IsRefreshEnabled = false;
}
else
{
IsRefreshEnabled = true;
}
}
My Property
public const string IsRefreshEnabledPropertyName = "IsRefreshEnabled";
private bool _isRefreshEnabled = true;
public bool IsRefreshEnabled
{
get { return _isRefreshEnabled; }
set
{
if (_isRefreshEnabled == value) { return; }
var oldValue = _isRefreshEnabled;
_isRefreshEnabled = value;
RaisePropertyChanged(IsRefreshEnabledPropertyName, oldValue, value, true);
}
}
Thanks in advance!

You need to replace the RaisePropertyChanged call to:
await Dispatcher.RunAsync(DispatcherPriority.Normal, (){RaisePropertyChanged(IsRefreshEnabledPropertyName, oldValue, value, true)});
That will cause the call to RaisePropertyChanged to run on the UI thread.
I'm assuming that your class derives from a Xaml control (to access the Dispatcher property on the control).

Related

Pubnub hereNow: Issues with variable scope & threads

Here is my code.
I am trying to check if a subscriber with a specific ID is subscribed to my channel.
public class HubActions {
private boolean reply;
public boolean isConnected(String id, String chan) {
String subKey = "sub-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
String pubKey = "pub-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
Pubnub pubnub = new Pubnub(pubKey, subKey);
Callback callback = new Callback() {
public void successCallback(String channel, Object response) {
System.out.println(response.toString());
try {
JSONObject json = new JSONObject(response.toString());
JSONArray uuids = new JSONArray();
uuids = json.getJSONArray("uuids");
for(int i=0;i<json.getInt("occupancy");i++) {
if(uuids.get(i).equals(id)) {
System.out.println("Yup!");
reply = true;
break;
}
}
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void errorCallback(String channel, PubnubError error) {
System.out.println(error.toString());
}
};
pubnub.hereNow(chan, callback);
return reply;
}
public static void main(String[] args) {
boolean b = new HubActions().isConnected("2", "2_sub");
System.out.println(b);
}
}
Here is my output
false
{"occupancy":2,"service":"Presence","message":"OK","status":200,"uuids":["2","3bbe065c-d84a-4529-a641-26d05439e71d"]}
Yup!
I dont understand why false is returned even though the function clearly assigns true to value of boolean variable reply
I understand that when we create an instance of pubnub 2 threads are initialized. I think it might be that the threads are still processing while the function returns the default value of variable reply
But how do I resolve this?
This is an async issue. You are calling hereNow, and it is returning immediately and then you are outputting the value of reply while the successCallback is getting called asynchronously and running the loop.
That is why you see the value of reply get displayed before the Yup is displayed. Do this instead:
public class HubActions {
private boolean reply;
public boolean isConnected(String id, String chan) {
String subKey = "sub-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
String pubKey = "pub-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
Pubnub pubnub = new Pubnub(pubKey, subKey);
Callback callback = new Callback() {
public void successCallback(String channel, Object response) {
System.out.println(response.toString());
try {
JSONObject json = new JSONObject(response.toString());
JSONArray uuids = new JSONArray();
uuids = json.getJSONArray("uuids");
for(int i=0;i<json.getInt("occupancy");i++) {
if(uuids.get(i).equals(id)) {
System.out.println("Yup!");
reply = true;
break;
}
}
// output value of reply here
// or implement your own listener for the
// caller to receive the update
System.out.println(reply);
}
catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void errorCallback(String channel, PubnubError error) {
System.out.println(error.toString());
}
};
pubnub.hereNow(chan, callback);
return reply;
}
public static void main(String[] args) {
boolean b = new HubActions().isConnected("2", "2_sub");
// b will always be false due to async nature of code
// must implement a listener to get the result from this scope
// System.out.println(b);
}
}
In the above code, I am punting on the real solution, which is to implement a listener pattern the successCallback code and call to notify the orginal caller in main.
All I did was comment out your System.out.println(b) as that will always be false and doing System.out.println(reply) in the successCallback after the for loop has terminated.

Entity Framework The context cannot be used while the model is being created

My unit of work class is mentioned below and I am using Ninject and I have tried injecting IUnitOfWork per request per thread scope, transient etc. but I am still getting error which is:
"Message":"An error has occurred.","ExceptionMessage":"The context cannot be used while the model is being created. This exception may be thrown if the context is used inside the OnModelCreating method or if the same context instance is accessed by multiple threads concurrently. Note that instance members of DbContext and related classes are not guaranteed to be thread safe.","ExceptionType":"System.InvalidOperationException
I get this error when i make two web API (get) calls at the same time using angularJS and it shows error at the point _context.Set<TEntity>().FirstOrDefault(match);
public class UnitOfWork : IUnitOfWork, IDisposable
{
private My_PromotoolEntities _uowDbContext = new My_PromotoolEntities();
private Dictionary<string, object> _repositories;
// Do it like this if no specific class file
private GenericRepository<MysPerson> _personRepository;
//private GenericRepository<MysDataSource> dataSourcesRepository;
//private GenericRepository<MysCountry> countryMasterRepository;
// Or like this if with specific class file.
private DataSourceRepository _dataSourcesRepository;
private CustomerRepository _customerRepository;
private DeviceRepository _deviceRepository;
private DeviceRegistrationRepository _deviceRegistrationRepository;
private EmailQueueRepository _emailQueueRepository;
public void SetContext(My_PromotoolEntities context)
{
_uowDbContext = context;
}
public void CacheThis(object cacheThis, string keyName, TimeSpan howLong)
{
Cacheing.StaticData.CacheStaticData(cacheThis, keyName, howLong);
}
public object GetFromCache(string keyName)
{
return Cacheing.StaticData.GetFromCache(keyName);
}
public GenericRepository<T> GenericRepository<T>() where T : BaseEntity
{
if (_repositories == null)
{
_repositories = new Dictionary<string, object>();
}
var type = typeof(T).Name;
if (!_repositories.ContainsKey(type))
{
var repositoryType = typeof(GenericRepository<>);
var repositoryInstance = Activator.CreateInstance(repositoryType.MakeGenericType(typeof(T)), _uowDbContext);
_repositories.Add(type, repositoryInstance);
}
return (GenericRepository<T>)_repositories[type];
}
public GenericRepository<MysPerson> PersonRepository
{
get
{
if (this._personRepository == null)
{
this._personRepository = new GenericRepository<MysPerson>(_uowDbContext);
}
return _personRepository;
}
}
public DataSourceRepository DataSourcesRepository
{
get
{
if (this._dataSourcesRepository == null)
{
this._dataSourcesRepository = new DataSourceRepository(_uowDbContext);
}
return _dataSourcesRepository;
}
}
public CustomerRepository CustomerRepository
{
get
{
if (this._customerRepository == null)
{
this._customerRepository = new CustomerRepository(_uowDbContext);
}
return _customerRepository;
}
}
public DeviceRepository DeviceRepository
{
get
{
if (this._deviceRepository == null)
{
this._deviceRepository = new DeviceRepository(_uowDbContext);
}
return _deviceRepository;
}
}
public DeviceRegistrationRepository DeviceRegistrationRepository
{
get
{
if (this._deviceRegistrationRepository == null)
{
this._deviceRegistrationRepository = new DeviceRegistrationRepository(_uowDbContext);
}
return _deviceRegistrationRepository;
}
}
public EmailQueueRepository emailQueueRepository
{
get
{
if (this._emailQueueRepository == null)
{
this._emailQueueRepository = new EmailQueueRepository(_uowDbContext);
}
return _emailQueueRepository;
}
}
/// <summary>
/// Commits all changes to the db. Throws exception if fails. Call should be in a try..catch.
/// </summary>
public void Save()
{
try
{
_uowDbContext.SaveChanges();
}
catch (DbEntityValidationException dbevex)
{
// Entity Framework specific errors:
StringBuilder sb = new StringBuilder();
var eve = GetValidationErrors();
if (eve.Count() > 0)
{
eve.ForEach(error => sb.AppendLine(error));
}
ClearContext();
// Throw a new exception with original as inner.
var ex = new Exception(sb.ToString(), dbevex);
ex.Source = "DbEntityValidationException";
throw ex;
}
catch (Exception)
{
ClearContext();
throw;
}
}
private void ClearContext()
{
DetachAll();
}
private void DetachAll()
{
foreach (DbEntityEntry dbEntityEntry in _uowDbContext.ChangeTracker.Entries())
{
if (dbEntityEntry.Entity != null)
{
dbEntityEntry.State = EntityState.Detached;
}
}
}
/// <summary>
/// Checks for EF DbEntityValidationException(s).
/// </summary>
/// <returns>Returns a List of string containing the EF DbEntityValidationException(s).</returns>
public List<string> GetValidationErrors()
{
if (_uowDbContext.GetValidationErrors().Count() != 0)
{
return _uowDbContext.GetValidationErrors().Select(e => string.Join(Environment.NewLine, e.ValidationErrors.Select(v => string.Format("{0} - {1}", v.PropertyName, v.ErrorMessage)))).ToList();
}
return null;
}
private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
_uowDbContext.Dispose();
}
}
this.disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
You should never use a context in 2 places at the same time, that's exactly why you are getting this error. From the MSDN documentation:
Thread Safety: Any public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.
It is a little hard to make suggestions without a repro but there is a brute force approach that should resolve the issue. If you have an interception point before/during DI setup then you can cause all the context initialization etc to happen by creating an instance of your context and calling ctx.Database.Initialize(force: false); Passing 'force: false' will ensure that the initialization still only happens once per AppDomain

Async issue - not updating UI

I am using async method in my Windows 8 development. but it is not updating the UI until it finishes executing full method.
here is my code.
below method calls when user presses refresh button on screen.
public async override void ExceuteRefresh()
{
IsBusy = true; //This is bounded to my UI to show progress bar, but its not updating to UI
foreach (var category in CategoryObservableColl)
{
CheckforOnlineUpdatesAsync(category, true);
}
}
private async void CheckforOnlineUpdatesAsync(Category category, bool isReFresh)
{
var feed = await _dataService.GetOnlineRssFeedAsync(category.RssFeedLink.URL);
var newsCategory = _dataService.GetCategoryFromFeed(feed, true, category.RssFeedLink);
if (newsCategory != null)
{
isRefreshCount +=1;
}
if(isRefreshCount ==9)
{
IsBusy = false;
}
}
IsBusy has implemented InotifyPropertyChanged.
Any help would be greatly appreciated. tried following approach.
public override async void ExceuteRefresh()
{
await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => IsBusy = true);
_reloadCategories = false;
_refreshLinkCount = 0;
try
{
foreach (var category in CategoryObservableColl)
{
_refreshLinkCount += 1;
await CheckforOnlineUpdatesAsync(category, true);
}
}
catch (Exception ex)
{
MainPage.ShowMessage("Something Went wrong!. " + ex.Message);
}
}
Also made following method return type as task.
private async Task CheckforOnlineUpdatesAsync(Category category, bool isReFresh)
{
}
Thanks in advance
Try this
Change async void CheckforOnlineUpdatesAsync(...) to async Task CheckforOnlineUpdatesAsync(...)
Change method call to await CheckforOnlineUpdatesAsync(category, true);
Change public async override void ExceuteRefresh() to public async override Task ExceuteRefresh()
Replace IsBusy = false; to below given code. You need to use UI thread to update the UI.
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => IsBusy = false);

How to reflect changes in viewmodel to tableviewcell view with binding in MVVMcross

Am a little stuck with getting changes reflected from the ViewModel to the View when used in a MvxBindableTableViewCell. I am using the vNext branch of MvvmCross on iOS.
Everything is set up properly and the initial values are visible when loading/showing the list for the first time. The list is a ObservableCollection<T> and the ViewModels inherit from MvxViewModel (thus implements INotifyPropertyChanged).
The main ViewModel looks like this:
public abstract class BaseViewModel : MvxViewModel, IMvxServiceConsumer
{
//... just regular implementation
}
public class UploadListViewModel: BaseViewModel
{
private readonly IUploadItemTasks uploadItemTasks;
private readonly IPhotoPickerService photoPickerService;
public IObservableCollection<UploadItemViewModel> Uploads { get { return this.LoadUploadItems(); } }
public UploadListViewModel()
{
this.uploadItemTasks = this.GetService<IUploadItemTasks>();
this.photoPickerService = this.GetService<IPhotoPickerService>();
}
private IObservableCollection<UploadItemViewModel> LoadUploadItems()
{
using (var unitOfWork = UnitOfWork.Start ())
{
return new SimpleObservableCollection<UploadItemViewModel>(uploadItemTasks.GetAll());
}
}
public void StartUpload ()
{
if (this.Uploads == null || this.Uploads.Count == 0) {
ReportError("Error", "No images to upload");
return;
}
this.Uploads.ForEach (uploadItem => PostCallback (uploadItem));
}
private void PostCallback (UploadItemViewModel uploadAsset)
{
IProgressReporter progressReporter = uploadAsset;
this.photoPickerService.GetAssetFullImage(uploadAsset.ImageUrl,
(image) => {
UIImage fullImage = image;
NSData jpeg = fullImage.AsJPEG();
byte[] jpegBytes = new byte[jpeg.Length];
System.Runtime.InteropServices.Marshal.Copy(jpeg.Bytes, jpegBytes, 0, Convert.ToInt32(jpeg.Length));
MemoryStream stream = new MemoryStream(jpegBytes);
Uri destinationUrl = new Uri(uploadAsset.DestinationUrl + "&name=" + uploadAsset.Name + "&contentType=image%2FJPEG");
//TO DO: Move this to plugin
var uploader = new Uploader().UploadPicture (destinationUrl, stream, UploadComplete, progressReporter);
uploader.Host = uploadAsset.Host;
ThreadPool.QueueUserWorkItem (delegate {
uploader.Upload ();
jpeg = null;
});
});
}
private void UploadComplete (string name)
{
if (name == null){
ReportError("Error","There was an error uploading the media.");
} else
{
//ReportError("Succes", name);
}
}
The item ViewModel looks like:
public interface IProgressReporter
{
float Progress { get; set;}
}
public abstract class BaseAssetViewModel: BaseViewModel, IBaseAssetViewModel
{
//... just regular properties
}
public class UploadItemViewModel: BaseAssetViewModel, IProgressReporter
{
public UploadItemViewModel(): base()
{
}
private float progress;
public float Progress {
get {
return this.progress;
}
set {
this.progress = value;
this.RaisePropertyChanged(() => Progress);
}
}
}
The View for the items inherits from MvxBindableTableViewCell and has the property:
private float progress;
public float ProgressMarker {
get {
return progress;
}
set {
progress = value;
// change progressbar or textfield here
}
}
The tableviewcell is bounded to the UploadItemViewModel via the BindingText:
public const string BindingText = #"ProgressMarker Progress, Converter=Float;";
The Uploader class mentioned in the snippet of UploadListViewModel implements a private method which tries to set the progress on the IProgressReporter.
float progressValue;
void SetProgress (float newvalue)
{
progressValue = newvalue;
this.dispatcher.InvokeOnMainThread (delegate {
if (ProgressReporter != null)
ProgressReporter.Progress = progressValue;
});
}
During the first viewing of the list I can see that the properties in both the ViewModel and View are being hit but when I update the ViewModel via the interface IProgressReporter with a new value in Progress the View in the tableviewcell is not updated nor the property is being called.
What am I doing wrong or what am I missing here?
UPDATE: Check the answer to this question.
I found why the binding didn't work. I was replacing the ObservableCollection over and over again.. I changed that piece of code as stated below and now it reflects the changes made to the UploadItemViewModel in the View of the cell.
private IObservableCollection<UploadItemViewModel> uploads;
private IObservableCollection<UploadItemViewModel> LoadUploadItems()
{
if (uploads == null)
{
using (var unitOfWork = UnitOfWork.Start ())
{
uploads = new SimpleObservableCollection<UploadItemViewModel>(uploadItemTasks.FindAll());
}
}
return uploads;
}

BlackBerry - cancel a thread executed in another class to refresh location

How can I cancel a thread from another class fetching/refreshing location. I am able to cancel a thread from within the same class. But I am unable to do this across classes. Declaring the GPSThread static did not help. Can anyone please guide?
Class1:
public class GPSListener {
/* Other instantiation code */
Dialog busyDialog1 = new Dialog("Refreshing Location...",
new String [] { "Cancel" },
new int [] { Dialog.CANCEL},
Dialog.CANCEL,
Bitmap.getPredefinedBitmap(Bitmap.HOURGLASS))
{
public void fieldChanged(Field field1, int context1)
{
GPSHandler.requestStop();
busyDialog1.cancel();
}
};
public String refreshCoordinates() {
String test = "nothing";
if (GPSHandler.isStopRequested())
{
GPSHandler.stopRequested = false;
return null;
}
GPSHandler.getInstance().setListener(this);
GPSHandler.getInstance().requestLocationUpdates();
if (GPSHandler.isStopRequested())
{
GPSHandler.stopRequested = false;
return null;
}
busyDialog1.setEscapeEnabled(false);
busyDialog1.show();
return test;
}
public void onLocationReceived(Coordinates location) {
lblLatitude.setText(Double.toString(location.getLatitude()));
lblLongitude.setText(Double.toString(location.getLongitude()));
busyDialog1.cancel();
}
}
Class 2:
public class GPSHandler {
private GPSThread _gpsThread;
private Coordinates _location;
private boolean _gotLocation;
private GPSListener _listener;
/** this class will be a Singleton, as the device only has one GPS system */
private static GPSHandler _instance;
/** #return the Singleton instance of the GPSHandler */
public static GPSHandler getInstance() {
if (_instance == null) {
_instance = new GPSHandler();
}
return _instance;
}
public static boolean stopRequested = false;
public synchronized static void requestStop() {
stopRequested = true;
}
public synchronized static boolean isStopRequested() {
return stopRequested;
}
/** not publicly accessible ... use getInstance() */
private GPSHandler() {
}
/** call this to trigger a new location fix */
public void requestLocationUpdates() {
if (_gpsThread == null || !_gpsThread.isAlive()) {
_gpsThread = new GPSThread();
_gpsThread.start();
}
}
public void setListener(GPSListener listener) {
// only supports one listener this way
_listener = listener;
}
private void setLocation(final Coordinates value) {
_location = value;
if (value.getLatitude() != 0.0 || value.getLongitude() != 0.0) {
_gotLocation = true;
if (_listener != null) {
// this assumes listeners are UI listeners, and want callbacks on the UI thread:
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
_listener.onLocationReceived(value);
}
});
}
}
}
private class GPSThread extends Thread {
private void getLocationFromGoogle() {
try {
int cellID = GPRSInfo.getCellInfo().getCellId();
int lac = GPRSInfo.getCellInfo().getLAC();
String urlString2 = "http://www.google.com/glm/mmap";
// Open a connection to Google Maps API
ConnectionFactory connFact = new ConnectionFactory();
ConnectionDescriptor connDesc;
connDesc = connFact.getConnection(urlString2);
HttpConnection httpConn2;
httpConn2 = (HttpConnection)connDesc.getConnection();
httpConn2.setRequestMethod("POST");
// Write some custom data to Google Maps API
OutputStream outputStream2 = httpConn2.openOutputStream();//getOutputStream();
writeDataGoogleMaps(outputStream2, cellID, lac);
// Get the response
InputStream inputStream2 = httpConn2.openInputStream();//getInputStream();
DataInputStream dataInputStream2 = new DataInputStream(inputStream2);
// Interpret the response obtained
dataInputStream2.readShort();
dataInputStream2.readByte();
final int code = dataInputStream2.readInt();
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
Dialog.alert(code + "");
}
});
if (code == 0) {
final double latitude = dataInputStream2.readInt() / 1000000D;
final double longitude = dataInputStream2.readInt() / 1000000D;
setLocation(new Coordinates(latitude, longitude, 0.0f));
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
Dialog.alert(latitude+"-----"+longitude);
}
});
dataInputStream2.readInt();
dataInputStream2.readInt();
dataInputStream2.readUTF();
} else {
System.out.println("Error obtaining Cell Id ");
}
outputStream2.close();
inputStream2.close();
} catch (Exception e) {
System.out.println("Error: " + e.getMessage());
}
}
private void tryGetLocationFromDevice() {
_gotLocation = false;
try {
Criteria myCriteria = new Criteria();
myCriteria.setCostAllowed(false);
LocationProvider myLocationProvider = LocationProvider.getInstance(myCriteria);
try {
Location myLocation = myLocationProvider.getLocation(300);
setLocation(myLocation.getQualifiedCoordinates());
} catch ( InterruptedException iex ) {
System.out.println(iex.getMessage());
} catch ( LocationException lex ) {
System.out.println(lex.getMessage());
}
} catch ( LocationException lex ) {
System.out.println(lex.getMessage());
}
if (!_gotLocation) {
getLocationFromGoogle();
}
}
public void run() {
int bbMapsHandle = CodeModuleManager.getModuleHandle("net_rim_bb_lbs"); // OS 4.5 - 6.0
int bbMapsHandle60 = CodeModuleManager.getModuleHandle("net_rim_bb_maps"); // OS 6.0
if (bbMapsHandle > 0 || bbMapsHandle60 > 0) {
tryGetLocationFromDevice();
} else {
getLocationFromGoogle();
}
}
}
private void writeDataGoogleMaps(OutputStream out, int cellID, int lac) throws IOException {
DataOutputStream dataOutputStream = new DataOutputStream(out);
dataOutputStream.writeShort(21);
dataOutputStream.writeLong(0);
dataOutputStream.writeUTF("en");
dataOutputStream.writeUTF("Android");
dataOutputStream.writeUTF("1.0");
dataOutputStream.writeUTF("Web");
dataOutputStream.writeByte(27);
dataOutputStream.writeInt(0);
dataOutputStream.writeInt(0);
dataOutputStream.writeInt(3);
dataOutputStream.writeUTF("");
dataOutputStream.writeInt(cellID);
dataOutputStream.writeInt(lac);
dataOutputStream.writeInt(0);
dataOutputStream.writeInt(0);
dataOutputStream.writeInt(0);
dataOutputStream.writeInt(0);
dataOutputStream.flush();
}
}
Your GPSThread object is currently declared as a private inner class within GPSHandler. If you want to stop execution (or indeed do anything with it) from outside the scope of GPSHandler you will need to mark it as public. You will also need to provide some public mechanism (e.g. a stop() method) to cancel the thread execution.
The most common way of doing this is to have a boolean flag inside your thread (e.g shouldStop) which is checked within your main execution loop inside run() to see if it should stop. When the stop() method is called shouldStop is set to true and your Thread will stop.
Here's a good example: How to stop threads in Java?
There's two groups of changes you should make.
Change the Stop Requested Flag
First, remember that encapsulation is a good thing in Object-Oriented languages. The isStopRequested() method, or stopRequested variable of the GPSHandler should not be used outside of that class. Your UI's GPSListener should not attempt to use either of those. I would change your GPSHandler to use this:
private static boolean stopRequested = false;
public synchronized static void requestStop() {
stopRequested = true;
}
private synchronized static boolean isStopRequested() {
return stopRequested;
}
Only requestStop() should be public. It looks like you made stopRequested public to allow the GPSListener to reset it. If it needs resetting, let the class that owns that variable do the resetting. For example, in GPSHandler:
/** call this to trigger a new location fix */
public void requestLocationUpdates() {
if (_gpsThread == null || !_gpsThread.isAlive()) {
// reset this stop flag:
stopRequested = false;
_gpsThread = new GPSThread();
_gpsThread.start();
}
}
requestLocationUpdates() is really the method that starts the thread, so it should be where stopRequested gets reset to false.
Also, another reason that you should not make stopRequested public and allow other classes to use it is that this is not generally thread-safe. One of the reasons to wrap stopRequested with the requestStop() and isStopRequested() methods is to add thread-safety. There's many ways to do that, but those two methods achieve thread-safety by being marked with the synchronized keyword.
Change How/Where You Check the Flag
After you make these fixes, you need to change where you check if a stop has been requested. You don't really want to check isStopRequested() in the refreshCoordinates() method. That method involves almost no work. Even though it starts the process of getting a location fix, that only starts a thread, but the actual work of getting the location is done on a background thread (your GPSThread). If requestStop() is called, it's very unlikely that it will be called in the middle of refreshCoordinates(), so that's not where you should check it.
Check isStopRequested() multiple times within the GPSHandler class's methods tryGetLocationFromDevice() and getLocationFromGoogle(). Those are the methods that perform slow processing. Those are the ones you might want to interrupt in the middle. So, something like this:
private void getLocationFromGoogle() {
try {
int cellID = GPRSInfo.getCellInfo().getCellId();
int lac = GPRSInfo.getCellInfo().getLAC();
String urlString2 = "http://www.google.com/glm/mmap";
if (isStopRequested()) return;
// Open a connection to Google Maps API
ConnectionFactory connFact = new ConnectionFactory();
ConnectionDescriptor connDesc;
connDesc = connFact.getConnection(urlString2);
HttpConnection httpConn2;
httpConn2 = (HttpConnection)connDesc.getConnection();
httpConn2.setRequestMethod("POST");
// Write some custom data to Google Maps API
OutputStream outputStream2 = httpConn2.openOutputStream();//getOutputStream();
writeDataGoogleMaps(outputStream2, cellID, lac);
if (isStopRequested()) return;
// Get the response
InputStream inputStream2 = httpConn2.openInputStream();//getInputStream();
DataInputStream dataInputStream2 = new DataInputStream(inputStream2);
// Interpret the response obtained
dataInputStream2.readShort();
dataInputStream2.readByte();
if (isStopRequested()) return;
final int code = dataInputStream2.readInt();
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
Dialog.alert(code + "");
}
});
And in tryGetLocationFromDevice(), you could do this (make sure to add the member variable and new method below):
private LocationProvider _locationProvider; // must be a member variable!
public void requestStop() {
if (_locationProvider != null) {
// this will interrupt the _locationProvider.getLocation(300) call
_locationProvider.reset();
}
}
private void tryGetLocationFromDevice() {
_gotLocation = false;
try {
Criteria myCriteria = new Criteria();
myCriteria.setCostAllowed(false);
_locationProvider = LocationProvider.getInstance(myCriteria);
try {
Location myLocation = _locationProvider.getLocation(300);
setLocation(myLocation.getQualifiedCoordinates());
} catch ( InterruptedException iex ) {
// this may be caught if stop requested!!!!
System.out.println(iex.getMessage());
} catch ( LocationException lex ) {
System.out.println(lex.getMessage());
}
} catch ( LocationException lex ) {
System.out.println(lex.getMessage());
}
if (!_gotLocation && !isStopRequested()) {
getLocationFromGoogle();
}
}
Then, call the GPSThread.requestStop() method from the outer GPSHandler.requestStop() method:
public synchronized static void requestStop() {
stopRequested = true;
if (_gpsThread != null) {
_gpsThread.requestStop();
}
}

Resources