Android GPS Callback off UI Thread - java

I'm having trouble getting the GPS's onLocationChanged to run on a different thread. I understand how to manage UI thread when I'm calling a function but with the GPS, I don't actively call the function.
My intent is to have a light flash every time the GPS receives a reading. I have put this function in a Runnable. I passed this function to a class that implements LocationListener. Then in the main class, I started a new thread that calls requestLocationUpdates. I was hoping that onLocationChanged of the LocationListener would run in a different thread, post to the callback and make the necessary UI effects in the UI thread. Unfortunately, the program crashes every time it tries to call requestLocationUpdates. What's the proper way of doing it?
Right now it looks something like this
Main class:
final Runnable changeLight = new Runnable(){
public void run(){
// do stuff
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.capture);
status = (ImageView) findViewById(R.id.status);
database = new DatabaseManager(this);
new Thread(){
public void run(){
location = (LocationManager) getSystemService(LOCATION_SERVICE);
listener = new GPSManager(database, changeLight, light, status);
location.requestLocationUpdates("gps", 10000L, 0, listener);
}
}.start();
}
LocationListener class:
public void onLocationChanged(Location location) {
if (location.getAccuracy() <= 32.0){
light = lightColors.Green;
status.post(callback);
database.insertData(location);
}
else{
light = lightColors.Yellow;
status.post(callback);
}
}
The exception says Can't create handler inside thread that has not called Looper.prepare()

The thread where you call location.requestLocationUpdates must have a Looper ( see here ).
You can use HandlerThread instead of Thread.

I was running in to the same problem. The solution turns out to be that the new thread where you want to receive Location updates from the Location Manager has to be a Looper thread. To do so, all you should do is add following lines in the run() function of your thread.
Looper.prepare();
Looper.loop();

Could your problems be arising from improper synchronization of the main thread? Do you get any exceptions before the app crashes? Can you post an sscce.org compliant example?
In general when you're processing asynchronous events on the GUI you should have proper synchronization: http://developer.android.com/resources/articles/painless-threading.html

Related

Calling a method in Custom View from a HandlerThread

Hi all I am a beginner in android programming and I am having a little trouble trying to understand how HandlerThread works. Specifically, I am not sure if the method in the custom view class is executed in the background thread (or non-UI thread) whenever I call the method in a runnable that is added to the thread's message queue.
I have a custom view and HandlerThread initialized in mainactivity:
HandlerThread mainHandlerThread = new HandlerThread("mainhandler");
mainHandlerThread.start();
Handler mainHandler = new Handler(mainHandlerThread.getLooper());
myCustomView mcv = (MyCustomView) findViewById(R.id.customView);
And in MyCustomView class (which extends view), i have a method called update():
public void update(int number, String txt) {
//perform some calculations
invalidate(); //redraw the view
}
Everytime MainActivity detects a change, it will use mainHandler.post() to call MyCustomView's update method:
mainHandler.post(new Runnable() {
#Override
public void run () {
mcv.update(123,"test")
}
});
Does the above code cause the custom view to be redrawn from the HandlerThread(which is a non-UI thread)? I was able to draw the view using both invalidate() and postInvalidate(), hence I am confused on whether the update() method is running on the UI-thread or on the HandlerThread I created.

Update ProgressDialog with ProgressBar after rotation

My application processes some images in a thread and meanwhile it shows a dialog with a progressbar.
progressDialog = new ProgressDialog(Activity.this);
progressDialog.setProgressStyle(size);
progressDialog.show();
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
for(int i = 0; i < size; i++{
//process images
runOnUiThread(new Runnable() {
#Override
public void run() {
progressDialog.setProgress(i);
}
});
}
}
});
thread.start();
Now, if a rotation occurs I do
#Override
protected void onDestroy() {
if (progressDialog != null && progressDialog.isShowing()) {
progressDialog.dismiss();
}
super.onDestroy();
}
and in onCreate I recreate the dialog. But the thread don't changes the progressbar of this new dialog.
After rotation the Activity (or the Fragment) in which this dialog is shown is discarded and a new one is created.
You re-create the dialog in the new Activity, but the thread you started updates the dialog in the old one (the fact that this thread has a reference to the old dialog also constitutes a memory leak).
I'd suggest that you do the following:
Implement some ImageProcessor class and put an instance of it in Application object.
Make ImageProcessor class observable and notify the listeners about the progress.
Get a reference to this object in Activity and subscribe to notifications in onStart() (and unsubscribe in onStop()).
When status update notifications arrive - update the progress indication
Using this approach you'll have the processing logic encapsulate in a special object that survives rotation, and different components can invoke its methods and subscribe to notifications from it.

Activity doesn't load layout

I setted up simple application with 2 activities. I had problem in first activity. It is used just for displaing logo for 3 seconds and then launching second activity. Problem is that it doesn't load the layout, it waits 3 seconds and then load second activity. The code is here:
public class StartActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.start_screen);
final Intent myAct = new Intent(this, MyActivity.class);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
startActivity(myAct);
finish();
}
}
I was able to fix this problem by creating another thread and executing waiting there. Fixed code is here:
new Thread(){
public void run(){
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
startActivity(myAct);
finish();
}
}.start();
So i actually fixed the problem. I just want to know why it works the second way but it doesnt work by first way becouse i don't understand it.
In the first case, you are telling the UI Thread to sleep, preventing it to draw the layout. When it finishes the sleep process, it immediately starts the next Activity. That is why you do not see the first Activity layout being shown. This approach is not recommended at all as during the sleep time, your application is not responsive to any user actions. You are recommended to use an AsyncTask or a new Thread or a Handler, using postDelayed() method but never cause the UIThread to stop doing its job (drawing and handling UI events).
In the second case, you are making the sleep() in a new Thread, not the UIThread. So, what happens is that the UI Thread is never interrupted and is allowed to draw the entire layout. At the same time, the sleep time is respected as well. Once the sleep() ends, your new Activity starts.
First method makes the UI thread sleep .This result in stopping of all the functioning and UI interaction of the activity.
The second method uses another thread .Since it's the other thread which sleeps all the UI parts of the main UI thread works fine and the code works as intended.
Actually your approach is also wrong / bad practice.
Starting new activity and finishing current should be done on main thread.
Also try to avoid final objects initialization, they may stack in memory.
Here is one of correct approaches (postDelayed() will be perform on MainThread):
public class StartActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.start_screen);
View view = findViewById(R.id.any_view_from_start_screen_layout);
view.postDelayed(new Runnable(){
public void run(){
startNewActivityAndCloseCurrent();
},3000);
}
private void startNewActivityAndCloseCurrent(){
Intent myAct = new Intent(this, MyActivity.class);
startActivity(myAct);
finish();
}
}

How can I show a Dialog box from a thread other than the UI thread

My app requires gps reading, so on the main thread I start a Thread that reads the GPS but I'm having trouble showing a dialog that says "Please wait". I tied also using a Handler, but that didn't work either. What's the best to control the "Please wait" dialog from the 2nd Thread? Thanks!
public void showWaitDialog() {
prgDialog = new ProgressDialog(context);
prgDialog.setTitle("Please wait.");
prgDialog.setMessage("Please wait.");
prgDialog.setCancelable(false);
prgDialog.show();
}
You can:
Define an Handler in your UI Thread (e.g. in the Activity) and then pass it to your thread. Now from the thread you call handler.post(runnable) to enqueue code to be executed on the UIThread.
Define a BroadcastReceiver in your Activity and from you thread send an Intent with the necessary information in the Bundle
Use an AsyncTask and the methods publishProgress(), onProgressUpdate() or onPostExecute() to inform the Activity of the progress or when the taask has finished
Use runOnUiThread.
It depends on your needs. For short-running asynchronous operations, AsyncTask is a good choice.
Why not use an AsyncTask. You can tell the Task on onPreExecute() to show the Please wait dialog, and then onPostExecute(Result result) you can remove the dialog. Those two methods are working on the UI thread while doInBackground(Params... params) is occurring in a background thread.
Example:
private class GetGPSTask extends AsyncTask<null, null, null>{
#Override
protected void onPreExecute() {
// TODO Auto-generated method stub
super.onPreExecute();
showWaitDialog(); <-Show your dialog
}
#Override
protected void doInBackground(null) {
//your code to get your GPS Data
}
#Override
protected void onPostExecute(String result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
HideDialogbox(); <-Code to hide the dialog box
}
}
Just remember to change the template types if you need to. Where it says AsynTask , the first value is passed to doInBackground, 2nd value is for progress value, 3rd value is a return value from doInBackground to onPostExecute.
As other answers have rightly suggested, you can preferably use AsyncTask. Here is an exampleof how to use it for your purpose: AsyncTask Android example. Otherwise you may use runOnUiThread method as well. From inside your second thread to make the changes on UI thread ( eg: Dialogs and Toasts). According to its documentation, it says:
It runs the specified action on the UI thread. If the current thread is the UI thread, then the action is executed immediately. If the current thread is not the UI thread, the action is posted to the event queue of the UI thread.
For eg;
Your_Activity_Name.this.runOnUiThread(new Runnable() {
#Override
public void run() {
// your stuff to update the UI
showWaitDialog();
}
});
See display progressdialog in non-activity class and Loading Dialog with runOnUiThread for update view on Android.
hope this helps.

Null pointer error due to thread not stopping before orientation change

I have a fragment that displays weather data that runs a background thread that essentially just calls a function in my main UI to check whether my forecast is still valid. This function updates the UI so I am using a Handler and posting a Runnable to the main thread, like so:
public class WaveFxListFragment extends Fragment implements
LoaderManager.LoaderCallbacks<Cursor>, View.OnClickListener {
// .....
// handler for dealing with synchronising update thread with UI
private Handler mHandler;
private UpdateThread mUpdateThread;
private class UpdateThread extends Thread {
volatile boolean running = false;
#Override
public void run() {
running = true;
while (running) {
// get main UI thread to perform update check:
Log.d(TAG, "Handler is " + mHandler);
mHandler.post(new Runnable() { // getting null pointer error here!
#Override
public void run() {
checkValidTime();
}
});
try {
Thread.sleep(1000); // sleep 1 second
} catch (InterruptedException e) {
Log.d(TAG, "Thread was interrupted!");
e.printStackTrace();
}
}
}
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Reuse existing handler:
mHandler = getActivity().getWindow().getDecorView().getHandler();
}
#Override
public void onResume() {
super.onResume();
// start update checker:
mUpdateThread = new UpdateThread();
mUpdateThread.start();
}
#Override
public void onPause() {
// stop update thread
Log.d(TAG, "Asking thread to stop");
mUpdateThread.running = false;
super.onPause();
}
}
This works fine; the problem is when I change my screen orientation. The current activity gets destroyed and if the thread is running, it tries to post a Runnable to a UI thread that no longer exists. So, I put a running member variable in the UpdateThread class and set if to false when my activity goes calls onPause. However, even though I have set the UpdateThread.running variable to false, my thread still tries to post a Runnable, but the Handler is now null! It shouldn't get that far, but it is!
Am I doing this wrong? My log message "Asking thread to stop" gets printed out, so I know it is getting as far as setting running to false.
Can anyone offer an insight?
Thanks
A few things that you can do to resolve this.
Interrupt the thread after you set running to false. This should cause the thread to exit earlier, or at the very least for your error to appear earlier.
Check that your handler is not null in your update thread, and set it to null in onPause.
Move the assignment of mHandler to onResume to make sure that mHandler is valid when the update thread is called.

Categories

Resources