I really need help for a problem I don´t know how to solve.
I´m trying to make an application for my father, and I will work with Map.
The problem is specific in a mapFrag.getMapAsync() inside a while loop.
#Override
public void onResume(){
super.onResume();
new Thread(){
public void run(){
while ( mapFrag.getMapAsync() == null){
try {
Thread.sleep(1000);
} catch (InterruptedException e){
e.printStackTrace();
}
}
runOnUiThread(new Runnable() {
#Override
public void run() {
configMap();
}
});
}
}.start();
}
while ( mapFrag.getMapAsync(), this line gets an error that says'
getMapAsync(OnMapReadyCallback) in SupportMapFragment cannot be applied to ()
The OnMapReadyCallback was implemented on a MainActivity.
According documentation for MapFragment.getMapAsync():
public void getMapAsync (OnMapReadyCallback callback)
Sets a callback object which will be triggered when the GoogleMap
instance is ready to be used.
Note that:
This method must be called from the main thread.
The callback will be executed in the main thread.
In the case where Google Play services is not installed on the user's device, the callback will not be triggered until the user
installs it.
In the rare case where the GoogleMap is destroyed immediately after creation, the callback is not triggered.
The GoogleMap object provided by the callback is non-null.
Especially this:
Parameters callback The callback object that will be triggered when
the map is ready to be used.
In your call mapFrag.getMapAsync() there is no OnMapReadyCallback parameter. So, you should add it:
...
mapFrag.getMapAsync(new OnMapReadyCallback() {
#Override
public void onMapReady(GoogleMap googleMap) {
googleMap.moveCamera(cameraUpdate);
}
})
...
And you shouldn't call getMapAsync() in separate thread, because of (see above link to documentation):
This method must be called from the main thread.
The callback will be executed in the main thread.
So, please read carefully official Get Started page.
Related
I defined this method, in MyActivity class that allow me to download in memory some data from a Firebase storage.
public void submitDownload() {
Task<byte[]> downloadTask=FirebaseStorage.getInstance.
getReference(DATA_PATH_TO_DOWNLOAD).getBytes(MAX_BYTES);
isTaskActive=true;
//remove eventually a previous callback from the handler
timeoutHandler.removeCallbacks(timeoutCallback);
downloadTask.addOnSuccessListener(MyActivity.this, onSuccessListener);
downloadTask.addOnFailureListener(MyActivity.this, onFailureListener);
timeoutHandler.postDelayed(timeoutCallback, 5000);
}
This is, instead, the onCreate() method:
protected void onCreate() {
super.onCreate();
onSuccessListener=new OnSuccessListener<byte[]>() {
public void onSuccess(byte[] bytes) {
if(isTaskActive) {
isTaskActive=false;
Log.d("DOWNLOAD_TASK", "SUCCESS");
}
}
};
onFailureListener=new OnFailureListener() {
public void onFailure(Exception e) {
if(isTaskActive) {
isTaskActive=false;
Log.d("DOWNLOAD_TASK", "FAILURE");
}
}
};
timeoutHandler=new Handler();
timeoutCallback=new Runnable() {
public voi run() {
if(isTaskActive) {
isTaskActive=false;
Log.d("DOWNLOAD_TASK", "TIMEOUT");
submitDownload(); //retry download
}
}
};
submitDownload();
}
Obviously, onSuccessListener, onFailureListener, timeoutHandler, timeoutCallback and isTaskActive are instance variable.
As you can see in the run() method defined in timeoutCallback, in addition to a log message, is also called the sumbitDownload(). Pratically, if a timeout occurs and the task is still active, a new download is started.
Now, imagine this scenario.
When Activity is created, a download task is started. Suppose that downloadTask doesn't complete, and neither onSuccessListener nor onFailureListener are called, but timeout occurs. So, from the run() method of timeoutCallback a new download is started.
Now, what happens to the previous downloadTask? Is it canceled? Is it replaced by the current task? or does it continue to be active and potentially could trigger its attached listeners?
If the latter question is true, how to remove the listeners from a Task<T> object?
Does the getResult() method, however, complete (i.e finish) the task?
My scenario is an onCreate() activity method which executes the following code (simplified):
dialog.show(); //loading wheel
try {
remote.sendRequest(myData, new MyHandler());
}
catch (Exception e) {
dialog.dismiss();
//log and react
}
class MyHandler extends SDKSpecificCompiledHandler {
#Override
public void failure() {
dialog.dismiss();
//do stuff
}
#override
public void success() {
dialog.dismiss();
//do stuff
}
}
//I have read-only access to this handler!
public abstract class SDKSpecificCompiledHandler {
public abstract void success(JSONObject successData);
public abstract void failure(JSONObject errorData);
}
Explanation: A remote service is called passing an handler that gets called when he's done. A loading wheel (dialog) is shown to the user until a success, failure or exception happens.
The problem is when the service gets successfully called but no response ever comes. In that case dialog.dismiss() doesn't get called and the loading wheel keeps spinning for ever.
What I need is a sort of timeout which dismisses the dialog (and possibly takes other actions) after some seconds if the server doesn't get back.
My first though would be to create a new thread for the service call, and right after the launch set a timer which dismisses the dialog.
Would it be a good idea?
Thank you,
EDIT:
The service is third-party/not editable. I'm using a pre-compiled artifact.
Still not really sure what you're trying to achieve but if you want to run some code after some time on main thread (i.e. your code will do stuff to the UI), you can use a android.os.Handler
mHandler = new Handler(getMainLooper());
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
// do stuff on UI thread
}
},10000);
When your call returned from the server, simply cancel the messages on the queue:
mHandler.removeCallbacksAndMessages(null);
It is better to use time out in service call itself, You can set the time out with service , If you need know how to set the time out then I should know what kind of service you are using ?
One more thing is that if you are using a loader you should make that loader in such a way that it can be cancel by the client.
I have a service that constantly polls a webservice for some options. These options basically restart a LocationManager. This restart is done using a custom event which gets fired from a method used inside a thread.
Here is my custom event
public class OptionsChangedEvent extends EventObject {
public OptionsChangedEvent(Object o){
super(o);
}
}
and the custom listener
public interface OptionsChangedListener extends EventListener {
public void optionsChanged(OptionsChangedEvent evt);
}
the thread that runs in the service and polls for new options is the following
private Thread optionsThread = new Thread() {
public void run() {
//Looper.prepare();
while(true) {
String opts = getOptsFromServer();
if(!opts.equals(currentOpts)) updateOpts(opts); //the prob is here
//Looper.loop();
Thread.sleep(10 * 1000) // sleep 10sec
}
}
}
Finally here is how I implement the listener inside my tracker.
locOpts.addOptionsChangedListener(new OptionsChangedListener() {
#Override
public void optionsChanged(OptionsChangedEvent event) {
Log.d("LOCATION_OPTIONS_CHANGED", "SUCCESS");
mLocationManager.removeUpdates(mLocationListener);
mLocationManager.requestLocationUpdates(
provider,
update,
0, mLocationListener
);
}
});
I had the following error which basically says that I need to use Looper.loop and Looper.prepare inside my thread.
12-03 11:31:39.544 26751-26843/com.test.location E/AndroidRuntime﹕ FATAL EXCEPTION: Thread-10370
Process: com.test.location, PID: 26751
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
at android.os.Handler.<init>(Handler.java:200)
at android.os.Handler.<init>(Handler.java:114)
at android.location.LocationManager$ListenerTransport$1.<init>(LocationManager.java:221)
at android.location.LocationManager$ListenerTransport.<init>(LocationManager.java:221)
at android.location.LocationManager.wrapListener(LocationManager.java:844)
at android.location.LocationManager.requestLocationUpdates(LocationManager.java:857)
at android.location.LocationManager.requestLocationUpdates(LocationManager.java:454)
at com.test.location.trackers.LocationTracker$2.optionsChanged(LocationTracker.java:93)
at com.test.location.options.LocationTrackerOptions.notifyListeners(LocationTrackerOptions.java:22)
at com.test.location.options.LocationTrackerOptions.fromJSON(LocationTrackerOptions.java:34)
at com.test.location.MainService$5.run(MainService.java:219)
If I uncomment the Looper parts the thread works only once and I cant figure out why this happens.
update
After some investigation I found that code that needs the message queue and creates this problem. Does anyone needed to do something like this? I would like to not change my design here if it possible.
private synchronized void notifyListeners(Object obj) {
for (OptionsChangedListener listener : listeners) {
listener.optionsChanged(new OptionsChangedEvent(obj));
}
}
Do you try to update the UI in your callback method updateOpts ?
Looper prepare & loop using in other way, in case when you want to send tasks from UI thread to the backround thread, so you send message to the thread, and inside the thread process that message in handlemessage event of Handler.
try to avoid UI calls directly from your thread, maybe also try using runOnUIThread(...)
or AsyncTasks onPost to make the proper callback to the UI thread.
The solution was actually very simple. I was missing the callback function.
locOpts.addOptionsChangedListener(new OptionsChangedListener() {
#Override
public void optionsChanged(OptionsChangedEvent event) {
Log.d("LOCATION_OPTIONS_CHANGED", "SUCCESS");
mLocationManager.removeUpdates(mLocationListener);
mLocationManager.requestLocationUpdates(
provider,
update,
0, mLocationListener,
Looper.getMainLooper() // this was the problem
);
}
});
I want to delay my application for a while while a melody is playing, and when it's finished change the image on an imageview.
public void addListenerOnButtons() {
harmonicaTecknad= (ImageView)this.findViewById(R.id.harmonicatecknadspelautblas);
harmonicaTecknad.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
harmonicaTecknad.setImageResource(R.drawable.harmonicatecknadtryckrood);
RunAnimations();
utblas=MediaPlayer.create(SpelaTonerActivity.this, R.raw.utblas4);
utblas.start();
Thread timer = new Thread(){
public void run() {
try { // The delay should occur here
sleep(utblas.getDuration());
} catch(InterruptedException e) {
e.printStackTrace();
} finally {
harmonicaTecknad.setImageResource(R.drawable.harmonicatecknad1);
}
}
};
timer.start();
}
}
I get an exception error, obviously I cannot set the image inside the thread, so where should I set it?
This is all explained in details, with examples, in the android documentation:
Android offers several ways to access the UI thread from other
threads. You may already be familiar with some of them but here is a
comprehensive list:
Activity.runOnUiThread(Runnable)
View.post(Runnable)
View.postDelayed(Runnable, long)
Handler
[...]
Unfortunately, these classes and methods could also tend to make your
code more complicated and more difficult to read. It becomes even
worse when your implement complex operations that require frequent UI
updates.
To remedy this problem, Android 1.5 and later platforms offer a
utility class called AsyncTask, that simplifies the creation of
long-running tasks that need to communicate with the user interface.
Write harmonicaTecknad.setImageResource(R.drawable.harmonicatecknad1); code on UI thread because you can not write UI code to non UI thread.
So simply replace above line of code with
runOnUiThread(new Runnable()
{
#Override
public void run()
{
harmonicaTecknad.setImageResource(R.drawable.harmonicatecknad1);
}
});
If you are still getting error then for testing purpose just change sleep(utblas.getDuration()); with sleep(1000);
As the other answers say, you need to set the image from the UI thread. However, while you can use Thread.sleep(), you should in general avoid using sleeps for logic. Most classes have some way to get callbacks when things happen - for example in your case you can use MediaPlayer.OnCompletionListener. You register for a callback from the media player, and that callback will always be on the UI thread. This is what it looks like:
public void addListenerOnButtons() {
harmonicaTecknad= (ImageView)this.findViewById(R.id.harmonicatecknadspelautblas);
harmonicaTecknad.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
harmonicaTecknad.setImageResource(R.drawable.harmonicatecknadtryckrood);
RunAnimations();
utblas=MediaPlayer.create(SpelaTonerActivity.this, R.raw.utblas4);
utblas.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
harmonicaTecknad.setImageResource(R.drawable.harmonicatecknad1);
}
};
utblas.start();
}
}
i bascially have an activity
that calls an async task to set up the twitter classes, provided via twitter4j.
But i recieve an error regarding "cant create handler inside thread that has not called looper.prepare "
which is orginating from the TwitterApp class where there is a handler...
how can i get around this? successfully setting up the class no on the main UI thread as i used to have before (which worked perfectly but slowed down the app);
im basically doing:
new SetUpAsyncTaskt().execute();
within the asynctask all im doing is:
TwitterApp mTwitter;
postToTwitter = true;
String twitter_consumer_key="bllaalaa";
String twitter_secret_key="blaa"
private class SetUpAsyncTask extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... params) {
mTwitter = new TwitterApp(context, twitter_consumer_key,twitter_secret_key);
mTwitter.setListener(mTwLoginDialogListener);
return null;
}
#Override
protected void onPostExecute(Void result) {
if(!mTwitter.hasAccessToken()){
postToTwitter=false;
}
}
}
Thanks for any help!
UPDATE: After doing more testing, it seems the problem is due to the context, as if i remove all context based data within the class it works, but what i dont get is if i pass in the context from the UI thread, it still occurs ?? :S
UPDATE2: Found another way around it, thanks to all who replied.
Look here for the documentation: http://developer.android.com/reference/android/os/AsyncTask.html
some rules:
The task instance must be created on the UI thread.
execute(Params...) must be invoked on the UI thread.
My guess is you are executing the task on some different thread. To Execute it on UI thread create a Handler in onCreate and:
mHandler.post(new Runnable(){
//insert task creation & execution here
});
In this way the result that are in onPostExecute will be returned on the UI Thread too.
You can use runOnUiThread() to make the non-UI task run on the UI,
Try this,
#Override
protected Void doInBackground(Void... params) {
mTwitter = new TwitterApp(context, twitter_consumer_key,twitter_secret_key);
Activity_name.this.runOnUiThread(new Runnable() {
#Override
public void run() {
mTwitter.setListener(mTwLoginDialogListener);
}
});
return null;
}