我正在编写一个应用程序,每5秒获取位置。我收到这个错误。java.lang.RuntimeException:无法在未调用loop .prepare()的线程中创建处理程序。我已经搜索了这个错误代码。许多人认为有必要创建runOnUiThread()。然而,即使我注释掉了editTextShowLocation,意思是输出到ui,也会发生同样的错误。
非常感谢你的帮助。谢谢你。
下面是我的代码: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
}
}
}
首先,您错误地考虑了位置api -您不需要每5秒轮询一次更新。你告诉LocationManager你想要监听更新,它会在每次更新发生时告诉你。如果你告诉它将你的更新限制为每5秒一次,它会为了满足你的要求而忽略或延迟更新,但如果没有更新,要求它每5秒更新一次并不能让它更频繁地给你更新。
第二,你写的代码实际上不会每五秒钟做任何事情。您启动的Thread
只是注册更新,等待5秒钟,然后退出—没有发生循环。幸运的是,除了不必要的额外线程和5秒延迟之外,实际上是使用Location api的正确方法。
最后,你的实际问题:你得到你的RuntimeException的原因是,你给LocationManager一个LocationListener,并告诉它(无意中)在你的rcvLocationThread
上执行它的回调方法。Looper
是用作消息泵的类。Looper
s运行在Thread
s之上,执行Handler
s交给它们的工作。Handler
运行在Looper
之上,接收消息和/或Runnable
, Looper
泵入Handler
的队列,并执行每个传入的项目。
如果你在调用requestLocationUpdates
时没有指定Looper
,那么LocationManager假定你正在从一个Looper
线程中调用,并尝试将LocationListener
方法调用排队回同一个线程。因为你的线程没有一个Looper
运行在它上面(在这种情况下它不应该),它实际上不能排队回调,所以抛出一个异常。
那么——你是怎么做的?去掉所有的Thread
废话,把你在RcvLocation.run()
方法中得到的所有东西放在onCreate
末尾的new Thread(...).start()
方法中。此外,您的MyLocationListener
类不需要扩展Handler
-只需让它实现LocationListener
,您就可以了。回调将在主线程上执行(主线程已经有自己的Looper
)。这很好,因为您正在修改视图(必须从主线程完成),并且由于您没有执行任何长时间运行的处理操作,因此看起来很安全。
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
}
}
错误实际上说该怎么做:Looper.prepare()
没有在您的线程内调用。参考这个SO答案:https://stackoverflow.com/a/16886486/798406
它基本上说你必须把Looper.prepare()
放在Runnable
的run()方法中。但是,您应该避免使用Thread.sleep。相反,您可以使用另一个处理程序,并在5秒后发布延迟消息:
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);