简体   繁体   中英

receiving location update periodically

I am writing an app to get location every 5s. I received this error. java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare(). I have searched for this error code. Many says that there is a need to create runOnUiThread(). However, even when i comment away the editTextShowLocation, meaning on output to ui, the same error happens.

Would appreciate the much needed help. Thank you.

Below is my code:

package com.example.celine.telemetryloop;

import android.app.Activity;
import android.content.Context;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.widget.Button;
import android.widget.EditText;

public class MainActivity extends Activity {

    private EditText editTextShowLocation;
    private Button buttonGetLocation;

    private LocationManager locManager;
    private LocationListener locListener = new MyLocationListener();

    private boolean gps_enabled = false;
    private boolean network_enabled = false;
    String TAG = "App";

    /**
     * Called when the activity is first created.
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        editTextShowLocation = (EditText) findViewById(R.id.editTextShowLocation);

        locManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);

        Thread rcvLocationThread = new Thread(new RcvLocation());
        rcvLocationThread.start();
    }


    class RcvLocation implements Runnable {
        public void run() {
            try {
                gps_enabled = locManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
            } catch (Exception ex) {
            }
            try {
                network_enabled = locManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
            } catch (Exception ex) {
            }

            // don't start listeners if no provider is enabled
            if (!gps_enabled && !network_enabled) {
                Log.d(TAG, "Not Enabled!"); //to enable it for user.
            }

            if (gps_enabled) {
                locManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locListener);
            }
            if (network_enabled) {
                locManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locListener);
            }
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    class MyLocationListener extends Handler implements LocationListener {
        @Override
        public void onLocationChanged(Location location) {
            Log.d(TAG, "2");
            if (location != null) {
                // This needs to stop getting the location data and save the battery power.
                //locManager.removeUpdates(locListener);

                String londitude = "Londitude: " + location.getLongitude();
                String latitude = "Latitude: " + location.getLatitude();
                String altitiude = "Altitiude: " + location.getAltitude();
                String accuracy = "Accuracy: " + location.getAccuracy();
                String time = "Time: " + location.getTime();

                editTextShowLocation.setText(londitude + "\n" + latitude + "\n" + altitiude + "\n" + accuracy + "\n" + time);
            }
        }

        @Override
        public void onProviderDisabled(String provider) {
            // TODO Auto-generated method stub

        }

        @Override
        public void onProviderEnabled(String provider) {
            // TODO Auto-generated method stub

        }

        @Override
        public void onStatusChanged(String provider, int status, Bundle extras) {
            // TODO Auto-generated method stub

        }
    }
}

The error actually say what to do: Looper.prepare() hasn't been called inside your thread. Refer to this SO answer: https://stackoverflow.com/a/16886486/798406

It basically says that you have to put Looper.prepare() inside the run() method of your Runnable . However, you should avoid using Thread.sleep. Instead, you could use another handler and post a delayed message after the 5 seconds:

private class Poller implements Runnable {

    /**
     * {@inheritDoc}
     * 
     * @see java.lang.Runnable#run()
     */
    @Override
    public void run() {
        // Do something

        long pollingDelay = 5000;
        mHandler.postDelayed(this, pollingDelay);
    }
}

// Start the handler
mPollingRunnable = new Poller();
mHandler.post(mPollingRunnable);

First, you're thinking about the Location APIs incorrectly - you don't need to poll for updates every 5 seconds. You tell the LocationManager that you want to listen for updates, and it will tell you about an update every time one happens. If you tell it to limit your updates to once every 5 seconds, it will ignore or delay updates in order to satisfy your requirement, but asking it every five seconds won't get it to give you updates more often if there are none to give.

Second, the code you've written wouldn't actually do anything every five seconds anyway. The Thread you start simply registers for updates, waits five seconds, and then exits - there is no looping happening. Fortunately, aside from the unnecessary extra thread and five-second delay, that actually is the correct way to use the Location APIs.

Finally, to your actual question: The reason you're getting your RuntimeException is that you're giving the LocationManager a LocationListener and telling it (inadvertently) to execute its callback methods on your rcvLocationThread . Looper is a class that is used as a message pump. Looper s run on top of Thread s and execute work given to them by Handler s. Handler s run on top of Looper s and receive messages and/or Runnable s, and the Looper pumps through the Handler 's queue and executes each of the incoming items.

If you don't specify a Looper when calling requestLocationUpdates , then the LocationManager assumes that you're making the call from a Looper thread and tries to enqueue the LocationListener method calls back onto the same thread. Since your thread doesn't have a Looper running on it (and it shouldn't in this case), it can't actually enqueue the callbacks and so throws an exception.

So - what do you do? Get rid of all the Thread nonsense, and put everything you've got in your RcvLocation.run() method in place of your new Thread(...).start() at the end of onCreate . Also, your MyLocationListener class doesn't need to extend Handler - just have it implement LocationListener and you'll be fine. The callbacks will execute on the main thread (which already has its own Looper ). That's good because you're modifying views (something that must be done from the main thread) and appears safe since you're not doing any long-running processing operations.

public class MainActivity extends Activity {

    ... // variables and such

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        editTextShowLocation = (EditText) findViewById(R.id.editTextShowLocation);

        locManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);

        setupLocationUpdates();
    }


    private void setupLocationUpdates() {
        try {
            gps_enabled = locManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
        } catch (Exception ex) { /* tsk tsk, empty catch block */ }
        try {
            network_enabled = locManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
        } catch (Exception ex) { /* tsk tsk, empty catch block */ }

        // don't start listeners if no provider is enabled
        if (!gps_enabled && !network_enabled) {
            Log.d(TAG, "Not Enabled!"); //to enable it for user.
        }

        if (gps_enabled) {
            locManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locListener);
        }
        if (network_enabled) {
            locManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locListener);
        }
    }

    class MyLocationListener implements LocationListener {
        @Override
        public void onLocationChanged(Location location) {
            // do your stuff
        }

        // all the other required callback methods
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM