I'm developing an application where am trying to get the current location. But am getting an error "getLocation() method cannot be called from event thread" even though I have created a new thread. Can anyone help me out?
Here is my code
Thread t = new MyThread();
t.run();
and here is the run function of MyThread class:
try
{
Criteria cr = new Criteria();
cr.setHorizontalAccuracy(500);
LocationProvider lp = LocationProvider.getInstance(cr);
Location l = lp.getLocation(60);
Coordinates c = l.getQualifiedCoordinates();
if (c != null)
{
double longitude = c.getLongitude();
double latitude = c.getLatitude();
Dialog.alert("latitude:"+latitude);
Dialog.alert("longitude:"+longitude);
}
}catch(Exception e){
Dialog.alert(e.getMessage());
}
You don't start a thread by calling the run() method. You start a thread by calling the start() method.
Related
Attempting to use GetAddressesForPositionAsync() in order to find the list of possible addresses that my coordinates can point too. I call the map using esri ArcGIS mapping and add it to a mapview
Esri.ArcGISRuntime.Mapping.Map myMap = new Esri.ArcGISRuntime.Mapping.Map(SpatialReference.Create(4326));
myMapView = new Esri.ArcGISRuntime.Xamarin.Forms.MapView();
geoCoder = new Geocoder();
//assign the map to a mapview
myMapView.Map = myMap;
then use an event handler to wait for the user to double tap the screen.
//event handler
myMapView.GeoViewDoubleTapped += new EventHandler<Esri.ArcGISRuntime.Xamarin.Forms.GeoViewInputEventArgs>(geotappedMap);
//the location for the pin and sent to geothings()
double Lat = ((((Esri.ArcGISRuntime.Xamarin.Forms.GeoViewInputEventArgs)e).Location.X));
double Lng = ((((Esri.ArcGISRuntime.Xamarin.Forms.GeoViewInputEventArgs)e).Location.Y));
myLocation = new MapPoint(Lat, Lng);
The program places a marker there and then uses the coordinates of where the tap was to call get position.
public async void geothings(Object sender, EventArgs e, Position p)
{
try
{
var possibleAddresses = await geoCoder.GetAddressesForPositionAsync(p);
foreach (var address in possibleAddresses)
_label2.Text += "\n\n" + address;
}
catch (Exception ex)
{
await MainPage.DisplayAlert("Error Getting Address", ex.Message, "Close");
}
}
the function runs without a problem except the label is updated with the value 'Antarctica' after being called. Does anyone know why or how this is happening?
I am working on a BlackBerry Application that is supposed to update the location at fixed intervals. The interval value can be selected/changed from a slider. It varies between 1 minute, 2 minutes, 5 minutes, 30 minutes etc. On the very first load (Start App), location interval is 30 seconds. After this, I store the slider value in a persistent store and location is updated accordingly with the set interval. Background thread running to update location is as follows:
private boolean startLocationUpdate()
{
boolean retval = false;
try
{
LocationProvider locationProvider = LocationProvider.getInstance(null);
if ( locationProvider == null )
{
Runnable showGpsUnsupportedDialog = new Runnable()
{
public void run()
{
Dialog.alert("GPS is not supported on this platform, exiting...");
//System.exit( 1 );
}
};
UiApplication.getUiApplication().invokeAndWait( showGpsUnsupportedDialog ); // Ask event-dispatcher thread to display dialog ASAP.
}
else
{
locationProvider.setLocationListener(new LocationListenerImpl(), interval, -1, -1);
retval = true;
}
}
catch (LocationException le)
{
System.err.println("Failed to instantiate the LocationProvider object, exiting...");
System.err.println(le);
System.exit(0);
}
return retval;
}
private class LocationListenerImpl implements LocationListener
{
public void locationUpdated(LocationProvider provider, Location location)
{
if(location.isValid())
{
double longitude = location.getQualifiedCoordinates().getLongitude();
double latitude = location.getQualifiedCoordinates().getLatitude();
updateLocationScreen(latitude, longitude);
}
}
public void providerStateChanged(LocationProvider provider, int newState)
{
}
}
private void updateLocationScreen(final double latitude, final double longitude)
{
UiApplication.getUiApplication().invokeAndWait(new Runnable()
{
public void run()
{
double lat = latitude;
double longi = longitude;
lblLatitude.setText(Double.toString(lat));
spacing.setText(", ");
lblLongitude.setText(Double.toString(longi));
}
});
}
Along with this, there is a "Refresh" button available that will start acquiring a location update immediately once clicked. This button calls a method is another class to acquire the location. The method is as follows:
try {
Criteria myCriteria = new Criteria();
myCriteria.setCostAllowed(false);
LocationProvider myLocationProvider = LocationProvider.getInstance(myCriteria);
double heading = 0;
double velocity = 0;
try {
Location myLocation = myLocationProvider.getLocation(6000);
if(myLocation.isValid())
{
double longitude = myLocation.getQualifiedCoordinates().getLongitude();
double latitude = myLocation.getQualifiedCoordinates().getLatitude();
}
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
//Dialog.alert("Location Updated");
}
});
setLocation(myLocation.getQualifiedCoordinates(),velocity,heading);
} catch ( InterruptedException iex ) {
System.out.println(iex.getMessage());
} catch ( LocationException lex ) {
System.out.println(lex.getMessage());
}
} catch ( LocationException lex ) {
System.out.println(lex.getMessage());
}
Problems I am facing:
1) Interval value not changing. I am implementing the change by picking the value from the persistent store as:
if (PersistentStoreHelper.persistentHashtable.containsKey("gpsInterval"))
{
String intervalValue=((String) PersistentStoreHelper.persistentHashtable.get("gpsInterval"));
MyScreen.interval=Integer.parseInt(intervalValue);
}
This is never empty as navigation to this page inserts a value of 30 minutes to it.
2) Once the "Refresh" button is clicked, the background thread seems to be cancelled. It no longer runs at any interval value.
I read that there is only one instance of the location provider created and with "Refresh" it is cancelled after acquiring the location and thus the background thread stops. Is this true? If yes, how can I achieve my desired result.
EDIT: The gpsInterval value is read as follows:
if (PersistentStoreHelper.persistentHashtable.containsKey("gpsInterval"))
{
String intervalValue=((String)PersistentStoreHelper.persistentHashtable.get("gpsInterval"));
interval=Integer.parseInt(intervalValue);
}
else
{
interval=10;
}
Saving the Interval
So, first of all, make sure that when you let the user change the update interval, via the slider, you properly save it to the PersistentStore. The code should look something like this:
// NOTE: I would recommend persisting the slider value as an Integer, not a String,
// but, the original code used String, so that's what this uses
hashtable.put("gpsInterval", (new Integer(intervalSlider.getValue())).toString());
PersistentObject po = PersistentStore.getPersistentObject(APP_BUNDLE_ID);
po.setContents(hashtable);
po.commit();
Since you didn't post that code, I just wanted to be sure that it was being saved to the persistent store correctly.
Updating the Location Provider / Listener
The other issue, that is a problem, is that you kick off the location updates in startLocationUpdate() with this code:
locationProvider.setLocationListener(new LocationListenerImpl(), interval, -1, -1);
That uses the value of the interval variable at the instant that setLocationListener() is called. If you later update the interval variable,
String intervalValue=((String) PersistentStoreHelper.persistentHashtable.get("gpsInterval"));
MyScreen.interval=Integer.parseInt(intervalValue);
this will have no effect on the location listener. It will keep updating with the original interval value, not the new one. You would have to call setLocationListener() again, with the new value of interval. With your code, you should probably just call startLocationUpdate() again:
String intervalValue=((String) PersistentStoreHelper.persistentHashtable.get("gpsInterval"));
MyScreen.interval=Integer.parseInt(intervalValue);
startLocationUpdate();
Refresh Problem
I'm not 100% sure, but my guess would be that in your existing code that's used when the Refresh button is pressed, you are changing to a different LocationProvider with different criteria. That's probably why the first one is cancelled.
Try changing your startLocationUpdate() method to save the provider as a member variable:
/** this is the one location provider used by this class! */
private LocationProvider _locationProvider;
private boolean startLocationUpdate()
{
boolean retval = false;
try
{
_locationProvider = LocationProvider.getInstance(null);
then, in your refresh code, use the same location provider to get the current location:
double heading = 0;
double velocity = 0;
try {
Location myLocation = _locationProvider.getLocation(6000);
if(myLocation.isValid())
Note: if you really do want to setCostAllowed(false), that's fine. Do that the first time that you assign the _locationProvider member variable. And use that provider/criteria both for normal periodic location updates, and your Refresh button handler. I think the key is to use the same provider, not create a new one with different criteria.
What I want to do is have the a map open with the click of a buttonfield with two Criterias, the user must either specify the location and then the image must be added to the location otherwise the Image must be added to the users current location.
The problem I am having is adding both criterias into an if statement in a thread / new thread or even in a FieldChangeListener.
The error I keep getting is:
Error in location :javax.microedition.location.LocationException:
getLocation() method cannot be cal[0.0] led from event thread [0.0]
Error in location :getLocation() method cannot be called from event
thread
My code:
FieldChangeListener Listener = new FieldChangeListener() {
public void fieldChanged(Field field, int context) {
ButtonField buttonClicked = (ButtonField) field;
if ((buttonClicked.getLabel()).equals("Push")) {
CustomMapField mMapField;
Coordinates mCoordinates;
BlackBerryCriteria blackBerryCriteria = null;
BlackBerryLocation blackBerryLocation = null;
BlackBerryLocationProvider blackBerryLocationProvider = null;
double Doublelat = 0.0;
double Doublelng = 0.0;
blackBerryCriteria = new BlackBerryCriteria();
if(GPSInfo.isGPSModeAvailable(GPSInfo.GPS_MODE_CELLSITE)){
blackBerryCriteria.setMode(GPSInfo.GPS_MODE_CELLSITE);
}else if(GPSInfo.isGPSModeAvailable(GPSInfo.GPS_MODE_ASSIST)){
blackBerryCriteria.setMode(GPSInfo.GPS_MODE_ASSIST);
}else if(GPSInfo.isGPSModeAvailable(GPSInfo.GPS_MODE_AUTONOMOUS)){
blackBerryCriteria.setMode(GPSInfo.GPS_MODE_AUTONOMOUS);
}else{
blackBerryCriteria.setCostAllowed(true);
blackBerryCriteria.setPreferredPowerConsumption(Criteria.POWER_USAGE_LOW);
} try {
blackBerryLocationProvider = (BlackBerryLocationProvider) BlackBerryLocationProvider.getInstance(blackBerryCriteria);
blackBerryLocation = (BlackBerryLocation) blackBerryLocationProvider.getLocation(60);
QualifiedCoordinates qualifiedCoordinates = blackBerryLocation.getQualifiedCoordinates();
Doublelat = qualifiedCoordinates.getLatitude();
Doublelng = qualifiedCoordinates.getLongitude();
mCoordinates = new Coordinates(Doublelat, Doublelng, 0);
mMapField = new CustomMapField();
mMapField.mIcon = Bitmap.getBitmapResource("coin_silver.png");
mMapField.moveTo(mCoordinates);
mMapField.setZoom(1);
add(mMapField);
}catch(Exception e){
System.out.println("Debug 5");
System.out.println("Error in location :"+e.toString());
System.out.println("Error in location :"+e.getMessage());
}
}
}
};
public class CustomMapField extends MapField {
Bitmap mIcon;
XYRect mDest;
public void moveTo(Coordinates coordinates) {
super.moveTo(coordinates);
mDest = null;
}
protected void paint(Graphics graphics) {
super.paint(graphics);
if (null != mIcon) {
if (null == mDest) {
XYPoint fieldOut = new XYPoint();
convertWorldToField(getCoordinates(), fieldOut);
int imgW = mIcon.getWidth();
int imgH = mIcon.getHeight();
mDest = new XYRect(fieldOut.x - imgW / 2,
fieldOut.y - imgH, imgW, imgH);
}
graphics.drawBitmap(mDest, mIcon, 0, 0);
}
}
}
The error Is with the following line add(mMapField);
Doublelat = qualifiedCoordinates.getLatitude();
Doublelng = qualifiedCoordinates.getLongitude();
mCoordinates = new Coordinates(Doublelat, Doublelng, 0);
mMapField = new CustomMapField();
mMapField.mIcon=Bitmap.getBitmapResource("coin_silver.png");
mMapField.moveTo(mCoordinates);
mMapField.setZoom(1);
add(mMapField);
/*MapView mapView = new MapView();
mapView.setLatitude(finalintlat);
mapView.setLongitude(finalintlng);
mapView.setZoom(10);
MapsArguments mapsArgs = new MapsArguments(mapView);
Invoke.invokeApplication(Invoke.APP_TYPE_MAPS, mapsArgs);
Please advise me more in detail how to do so, and please give an example; I can't understand how as "mMapField" is a custom MapField and "mapView" is a class Mapview (please see my code snippet above).
Obtaining a location is a time-consuming task, it can take as much as 1 minute even with good satellite visibility, although newer berries have improved a lot the time-to-first-fix (TTFF).
Time consuming tasks, like opening connections, or obtaining a fix, should not be performed in the event thread, because this thread has to respond to user events, and if you hog it, then the GUI frozens. And everything running inside fieldChanged runs in the event thread. So it is a good thing RIM implemented a thread detection in its new BlackBerryLocationProvider and throws exceptions, now you are aware of the bad design and can take corrective measures.
You have several options to get a fix asynchronously:
Use LocationListener.
Spawn a new thread.
Preemtively obtain a fix long before you need it (or at regular intervals), then you'll have it quickly available when the button is pressed, (either retrieving it from somewhere you previously saved it or calling LocationProvider.getLastKnownLocation).
You should use Invoke.invokeApplication(Invoke.APP_TYPE_MAPS, new MapsArguments(mMapField)); in place of add(mMapField);
I am working on an application for blackberry that will be downloading a file. I would like to show a progress bar during this download. I have tried every combination of GaugeField and ProgressIndicatorView that I can come up with but I am getting nothing. My download is going but the progress bar is never getting updated on the screen. In fact before I started trying for the progress bar I was trying to simply use a TextField and update it with the current percentage completed of the download, and I had no luck that way either.
This is inside my Screen:
view = new ProgressIndicatorView(0);
model = new ProgressIndicatorModel(0, 100, 0);
controller = new ProgressIndicatorController();
model.setController(controller);
view.setModel(model);
view.setController(controller);
controller.setModel(model);
controller.setView(view);
view.setLabel("Percent completion");
view.createProgressBar(Field.FIELD_HCENTER);
add(view);
UiApplication.getUiApplication().invokeLater(new Runnable()
{
public void run()
{
downloadVideo();
initializeMedia();
// If initialization was successful...
if(_videoField != null)
{
createUI();
updateVideoSize();
try {
_player.start();
} catch (MediaException e) {
// TODO Auto-generated catch block
e.printStackTrace();
ScreenSaverActivity.errorDialog("MEDIA EXCEPTION: "+e.toString());
}
}
else
{
_statusField.setText("Error: Could not load media");
}
}
});
downloadVideo() method:
private void downloadVideo(){
DownloadThread _dlThread = new DownloadThread();
_dlThread.start();
}
DownloadThread class:
class DownloadThread extends Thread {
public void run(){
try {
HttpConnection httpConn;
httpConn = (HttpConnection)Connector.open("http://url/to/myMovie.mp4");
InputStream is = httpConn.openInputStream();
String fName = "file:///store/BlackBerry/videos/myMovie.mp4";
FileConnection fconn = (FileConnection) Connector.open(fName, Connector.READ_WRITE);
if (!fconn.exists())
fconn.create();
OutputStream os = fconn.openOutputStream();
long lengthOfFile = httpConn.getLength();
int total = 0;
int count;
byte data[] = new byte[1024];
while ((count = is.read(data)) != -1) {
//I have tried this line outside of synchronized too, with no luck
synchronized(Application.getEventLock()) {
EmbeddedMediaScreen.this.model.setValue((int)(total*100/lengthOfFile));
}
total += count;
//write this chunk
os.write(data, 0, count);
}
os.flush();
os.close();
} catch (IOException e) {
ScreenSaverActivity.errorDialog(e.toString());
e.printStackTrace();
}
}
}
While my application is doing the download the device becomes unresponsive, so I am guess that my trouble is the download work is happing on the main(UI) thread for some reason and that this has something to do with why I can't get the screen to update with the progress. Any help would be greatly appreciated.
P.S. Android is generally my home, not blackberry, sorry if I have overlooked something that should be obvious =/
I see a few issues. It looks like the code after 'downloadVideo()' assumes the download happened synchronously. Since you want the download to happen in the background, you need to setup a separate method to handle the completion of the download. Right now your video playback code executes just after the download thread is started.
Also, you are acquiring the UI event lock every 1k of download. Since this is a video, I'm assuming a multi-megabyte download, meaning you are acquiring and releasing that lock thousands of times. This doesn't make much sense, in part because the download meter only has 100 different values, so you are mostly updating it with the same value. The easiest fix is to keep track of the last value sent to the model. if it is unchanged, then don't acquire or update the value.
In general, I don't like to acquire the UI lock directly, because there is no queue management. So I use invokeLater for all code that needs to update the UI. This has the nice property that the invokeLater code happens sequentially. You can overrun the queue, as this code would do if you directly change it to invokeLater. To avoid that problem, you have to setup another variable to track whether you've got a progress meter update queued. If so, don't queue any more work.
`hi
I am doing a simple synchronous socket programming,in which i employed twothreads
one for accepting the client and put the socket object into a collection,other thread will
loop through the collection and send message to each client through the socket object.
the problem is
1.i connect to clients to the server and start send messages
2.now i want to connect a new client,while doing this i cant update the collection and add
a new client to my hashtable.it raises an exception "collection modified .Enumeration operation may not execute"
how to add a NEW value without having problems in a hashtable.
private void Listen()
{
try
{
//lblStatus.Text = "Server Started Listening";
while (true)
{
Socket ReceiveSock = ServerSock.Accept();
//keys.Clear();
ConnectedClients = new ListViewItem();
ConnectedClients.Text = ReceiveSock.RemoteEndPoint.ToString();
ConnectedClients.SubItems.Add("Connected");
ConnectedList.Items.Add(ConnectedClients);
ClientTable.Add(ReceiveSock.RemoteEndPoint.ToString(), ReceiveSock);
//foreach (System.Collections.DictionaryEntry de in ClientTable)
//{
// keys.Add(de.Key.ToString());
//}
//ClientTab.Add(
//keys.Add(
}
//lblStatus.Text = "Client Connected Successfully.";
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void btn_receive_Click(object sender, EventArgs e)
{
Thread receiveThread = new Thread(new ThreadStart(Receive));
receiveThread.IsBackground = true;
receiveThread.Start();
}
private void Receive()
{
while (true)
{
//lblMsg.Text = "";
byte[] Byt = new byte[2048];
//ReceiveSock.Receive(Byt);
lblMsg.Text = Encoding.ASCII.GetString(Byt);
}
}
private void btn_Send_Click(object sender, EventArgs e)
{
Thread SendThread = new Thread(new ThreadStart(SendMsg));
SendThread.IsBackground = true;
SendThread.Start();
}
private void btnlist_Click(object sender, EventArgs e)
{
//Thread ListThread = new Thread(new ThreadStart(Configure));
//ListThread.IsBackground = true;
//ListThread.Start();
}
private void SendMsg()
{
while (true)
{
try
{
foreach (object SockObj in ClientTable.Keys)
{
byte[] Tosend = new byte[2048];
Socket s = (Socket)ClientTable[SockObj];
Tosend = Encoding.ASCII.GetBytes("FirstValue&" + GenerateRandom.Next(6, 10).ToString());
s.Send(Tosend);
//ReceiveSock.Send(Tosend);
Thread.Sleep(300);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
You simply can't modify a Hashtable, Dictionary, List or anything similar while you're iterating over it - whether in the same thread or a different one. There are concurrent collections in .NET 4 which allow this, but I'm assuming you're not using .NET 4. (Out of interest, why are you still using Hashtable rather than a generic Dictionary?)
You also shouldn't be modifying a Hashtable from one thread while reading from it in another thread without any synchronization.
The simplest way to fix this is:
Create a new readonly variable used for locking
Obtain the lock before you add to the Hashtable:
lock (tableLock)
{
ClientTable.Add(ReceiveSock.RemoteEndPoint.ToString(), ReceiveSock);
}
When you want to iterate, create a new copy of the data in the Hashtable within a lock
Iterate over the copy instead of the original table
Do you definitely even need a Hashtable here? It looks to me like a simple List<T> or ArrayList would be okay, where each entry was either the socket or possibly a custom type containing the socket and whatever other information you need. You don't appear to be doing arbitrary lookups on the table.
Yes. Don't do that.
The bigger problem here is unsafe multi-threading.
The most basic "answer" is just to say: use a synchronization lock on the shared object. However this hides a number of important aspects (like understanding what is happening) and isn't a real solution to this problem in my mind.