Android:当我使用AsyncTask时,“调用线程必须是准备好的Looper线程”错误

时间:2017-02-27 10:17:53

标签: android multithreading

我创建了一个AsyncTask来检索精度为20米的GPS位置。 我想执行一个while循环,直到准确性可以加入。

问题是当我请求更新职位时我遇到了异常。

java.lang.NullPointerException: Calling thread must be a prepared Looper thread.

这是错误的代码

@Override
protected String doInBackground(Void... params) {
if (ActivityCompat.checkSelfPermission(activity, android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(activity, android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                return null;
            }
            mLocationRequest.setInterval(100);
            mLocationRequest.setPriority(100);
            mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
            while (mLastLocation == null || (mLastLocation.getAccuracy()>Float.parseFloat("20.0") && mLastLocation.hasAccuracy())){
                try {
                    LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient,mLocationRequest,activity);
                    mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
                } catch (SecurityException secex) {

                }
            }

            return null;
        }

4 个答案:

答案 0 :(得分:9)

我整整一天都遇到了麻烦。我在服务中的后台运行FusedLocationApi。

我做了什么 - 我在其他地方读过它 - 将Looper.getMainLooper()添加为requestLocationUpdates()的最后一个参数。

所以,而不是这个

LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient,mLocationRequest,activity);

你会有

LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this, Looper.getMainLooper());

如果您阅读整个FusedLocationProviderApi页面,您会看到该方法会根据参数执行不同的操作。

答案 1 :(得分:2)

在doInBackground中运行时使用Looper.prepare() or runOnUiThread()函数在UI中进行更改,或者在doInBackground以外的方法中进行更改

示例:

 runOnUiThread(new Runnable() {
            @Override
            public void run() {
                progressBar = new ProgressDialog(MainActivity.this);
                progressBar.setCancelable(false);
                progressBar.setTitle("Downloading Files...");
                progressBar.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
                progressBar.setProgress(0);
                progressBar.setMax(100);
                progressBar.show();
            }
        });

答案 2 :(得分:0)

我使用了guava中的SettableFuture,这样当调用返回时我就不会在主循环器上运行不必要的代码。 (如果你的回调开启了一些繁重的工作量并导致UI崩溃,这可能是一个问题。)

  protected String doInBackground(Void... params) {
      SettableFuture<Location> future = SettableFuture.create();
       Log.w(TAG, "before fused call isMainLooper= %b", Looper.getMainLooper().isCurrentThread());
      // future.set runs on the main thread.  
      //future.get stays on the background thread.
      mFusedLocationApi.requestLocationUpdates(
          client,
          LOCATION_REQUEST,
          location1 -> {
             future.set(location1);
      Log.w(TAG, "callback: isMainLooper= %b", Looper.getMainLooper().isCurrentThread());

          }
          Looper.getMainLooper());

      location = future.get(10, SECONDS);
      Log.w(TAG, "after fused call isMainLooper= %b", Looper.getMainLooper().isCurrentThread());

记录输出

W TAG: before fused call isMainLooper = false
W TAG: callback: isMainLooper = true
W TAG: after fused call isMainLooper = false

答案 3 :(得分:0)

我在后台获取位置时遇到了问题。当我发现异常时我添加了Looper.getMainLooper ..然后我决定再次阅读文档,我完全改变了我在后台获取位置的方式;区别在于使用FusedLocationProviderClient和这样的回调。

首先定义您的提供者客户端。

private FusedLocationProviderClient mFusedLocationClient;

定义位置回调,确保使用com.google.android.gms.location.LocationCallback

/**
 * Fuse location api callback
 */
private LocationCallback mLocationCallback = new LocationCallback() {
    @Override
    public void onLocationResult(LocationResult locationResult) {

        super.onLocationResult(locationResult);

        for (Location location : locationResult.getLocations()) {

            mLastLocation = location;                

            //Send to your server or update your UI

        }
    }

    @Override
    public void onLocationAvailability(LocationAvailability locationAvailability) {

        super.onLocationAvailability(locationAvailability);
        //Send to your server or update your UI

    }
};

定义请求位置更新的方法

public void startLocationUpdates() {

    boolean hasLocationPermission = ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED &&
            ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED;

    if (!hasLocationPermission) {
        gettingLocationUpdates = false;
        return;
    }

    mFusedLocationClient.requestLocationUpdates(mLocationRequest, mLocationCallback, /*Looper*/ null);

    com.google.android.gms.tasks.Task<Location> task = mFusedLocationClient.getLastLocation();

    task.addOnCompleteListener(task1 -> {

        mLastLocation = task1.getResult();

        gettingLocationUpdates = true;

    });
}

在服务的onCreate中初始化它并开始请求更新

@Override
public void onCreate() {
    mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
    startLocationUpdates();
}

不要忘记删除位置更新,否则它将永远不会停止

@Override
public void onDestroy() {

if (mFusedLocationClient != null) {
     mFusedLocationClient.flushLocations();                         
   mFusedLocationClient.removeLocationUpdates(mLocationCallback);
}

尝试一下,让我知道。