从 Activity 调用 Location 时获取 NullPointerException

Posted

技术标签:

【中文标题】从 Activity 调用 Location 时获取 NullPointerException【英文标题】:Getting NullPointerException when call Location from Activity 【发布时间】:2015-08-13 12:28:03 【问题描述】:

这是我的活动,我想进入它的位置。我已经解耦了Activity和geolocation函数

package com.salvo.weather.android.activity;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;


import com.google.android.gms.common.api.GoogleApiClient;
import com.salvo.weather.android.R;
import com.salvo.weather.android.geolocation.CurrentGeolocation;


public class TodayActivity extends Activity 

    public static final String TAG = TodayActivity.class.getSimpleName();

    protected GoogleApiClient mGoogleApiClient;

    protected CurrentGeolocation mCurrentGeolocation;

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_today);

        mCurrentGeolocation = new CurrentGeolocation(this);
        mGoogleApiClient = mCurrentGeolocation.getmGoogleApiClient();
    

    @Override
    protected void onStart() 
        super.onStart();
        mGoogleApiClient.connect();

        Log.i(TAG, String.valueOf(mCurrentGeolocation.getmCurrentGeolocationEntity().getmLastLocation().getLatitude()));
    

    @Override
    protected void onStop() 
        super.onStop();
        if (mGoogleApiClient.isConnected()) 
            mGoogleApiClient.disconnect();
        
    

    @Override
    public boolean onCreateOptionsMenu(Menu menu) 
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_today, menu);
        return true;
    

    @Override
    public boolean onOptionsItemSelected(MenuItem item) 
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) 
            return true;
        

        return super.onOptionsItemSelected(item);
    

这是地理定位功能:

包 com.salvo.weather.android.geolocation;

import android.content.Context;
import android.location.Location;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;


import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks;
import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener;
import com.google.android.gms.location.LocationServices;
import com.salvo.weather.android.entity.CurrentGeolocationEntity;

/**
 * Created by mazzy on 30/05/15.
 */
public class CurrentGeolocation
        implements ConnectionCallbacks, OnConnectionFailedListener 

    private Context mcontext;

    private GoogleApiClient mGoogleApiClient;
    private CurrentGeolocationEntity mCurrentGeolocationEntity;

    public CurrentGeolocation(Context context) 
        mcontext = context;
        mCurrentGeolocationEntity = new CurrentGeolocationEntity();
        buildGoogleApiClient();
    

    protected synchronized void buildGoogleApiClient() 
        this.mGoogleApiClient = new GoogleApiClient.Builder(this.mcontext)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API)
                .build();
    

    @Override
    public void onConnected(Bundle bundle) 

        Location mLastLocation = LocationServices
                .FusedLocationApi
                .getLastLocation(this.mGoogleApiClient);

        if (mLastLocation != null) 
            mCurrentGeolocationEntity.setmLastLocation(mLastLocation);
         else 
            Toast.makeText(this.mcontext, "No location detected", Toast.LENGTH_LONG).show();
        
    

    @Override
    public void onConnectionSuspended(int i) 
        // The connection to Google Play services was lost for some reason. We call connect() to
        // attempt to re-establish the connection.
        // TODO: add some verbose message
    

    @Override
    public void onConnectionFailed(ConnectionResult connectionResult) 
        // Refer to the javadoc for ConnectionResult to see what error codes might be returned in
        // onConnectionFailed.
    

    public GoogleApiClient getmGoogleApiClient() 
        return mGoogleApiClient;
    

    public CurrentGeolocationEntity getmCurrentGeolocationEntity() 
        return mCurrentGeolocationEntity;
    

CurrentGeolocationEntity 类是一个简单的 POJO,我在其中存储表示最后一个已知位置的 Location。

不幸的是我得到了错误:

Attempt to invoke virtual method 'double android.location.Location.getLatitude()' on a null object reference

我不知道如何弄清楚。你对这方面有什么建议吗?

【问题讨论】:

【参考方案1】:

您似乎有竞争条件,并且您在 SDK 有时间连接之前从您的 CurrentGeolocation 类请求位置。

您可以向您的CurrentGeolocation 类添加一个isConnected() 方法,并在onConnected() 回调中设置一个布尔值。

那么,在拨打mCurrentGeolocation.getmCurrentGeolocationEntity().getmLastLocation()之前,请始终拨打isConnected()

另外要提到的是getLastLocation() 很有可能返回 null,因为它没有明确地发出位置请求。您通过此方法获得的位置也可能很旧,并且可能根本无法反映当前位置。

您应该做的第一个修复是,在使用 Location 对象之前对其进行空检查:

@Override
protected void onStart() 
    super.onStart();
    mGoogleApiClient.connect();

    //use a temp Location object:
    Location loc = mCurrentGeolocation.getmCurrentGeolocationEntity().getmLastLocation();
    //null check:
    if (loc != null)

       Log.i(TAG, String.valueOf(loc.getLatitude()));
    

您应该进行的下一个更改是在您的 CurrentGeolocation 类中注册一个位置侦听器,这将明确地从系统请求一个新位置。

    mLocationRequest = new LocationRequest();
    mLocationRequest.setInterval(10);
    mLocationRequest.setFastestInterval(10);
    mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);

    mLocationRequest.setSmallestDisplacement(0.1F);

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

然后添加onLocationChanged()回调:

@Override
public void onLocationChanged(Location mLastLocation) 

    mCurrentGeolocationEntity.setmLastLocation(mLastLocation);


更多信息,请查看this answer中的代码。

请注意,您应尽快取消注册位置侦听器,以尽量减少应用的电池消耗:

 LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);

【讨论】:

谢谢丹尼尔的回答。我的问题是我不知道何时取消注册回调。在我的应用程序中,我没有任何按钮来控制更新位置。这是一个天气应用程序【参考方案2】:

您的问题在于您的活动中的onStart(),在这一行:

Log.i(TAG, String.valueOf(mCurrentGeolocation.getmCurrentGeolocationEntity().getmLastLocation().getLatitude()));

问题在于以下行:

mCurrentGeolocation.getmCurrentGeolocationEntity().getmLastLocation()

返回空值。我现在只能猜测,但我认为当您的活动第一次开始时,您没有最后一个位置,因此该方法返回 null。试试下面的代码:

@Override
protected void onStart() 
    super.onStart();
    mGoogleApiClient.connect();
    if (mCurrentGeolocation.getmCurrentGeolocationEntity().getmLastLocation() != null)
        Log.i(TAG, String.valueOf(mCurrentGeolocation.getmCurrentGeolocationEntity().getmLastLocation().getLatitude()));

【讨论】:

以上是关于从 Activity 调用 Location 时获取 NullPointerException的主要内容,如果未能解决你的问题,请参考以下文章

从适配器调用 Activity 方法

如何从 HomeScreen Widget 调用 Activity 中的函数

如何从 Activity 调用一个类

从 asyncTask doInBackground 调用 Activity

从 Activity 外部调用 startActivity()?

Android 如何从 Main Activity 中的另一个类调用 Activity 数据类型?