当我将 Android Location 对象传递给 asyncTask 时,它返回 null

Posted

技术标签:

【中文标题】当我将 Android Location 对象传递给 asyncTask 时,它返回 null【英文标题】:Android Location object returns null when I pass it to asyncTask 【发布时间】:2015-08-03 18:35:03 【问题描述】:

我将 android 用户位置纬度和经度连同 registration ID 和其他参数作为 Asynctask(活动的嵌套类)中的 json 对象发送到服务器。但是 Location 对象(在应用程序开始时具有值)在活动中实例化,因此

location = LocationServices.FusedLocationApi
                    .getLastLocation(mgoogleapiclient);

在 Asynctask 类中返回 null。有人可以向我解释为什么吗?我是否必须使用单独的类来获取用户位置并将其发送到另一个异步任务或服务中(从架构的角度来看这没有意义)?

Location mLastLocation = LocationServices.FusedLocationApi.getLastLocation(
                mGoogleApiClient);
        if (mLastLocation != null) 
            PostData pdata = new PostData();
            double latitude = mLastLocation.getLatitude();
            double longitude = mLastLocation.getLongitude();
            pdata.execute(String.valueOf(latitude), String.valueOf(longitude));
        

我正在从 AsyncTask 类中检索这个:

json.put("latitude", String.valueOf(args[1]));
                    json.put("longitude", String.valueOf(args[2]));

但是当我调试它时,我得到了我从另一个方法发送到 AsyncTask 类的注册 ID。 我希望我能说清楚。

@Override
        protected String doInBackground(String... args)    
            try 
                try 
                    URL url;
                    HttpURLConnection urlConn;
                    url = new URL ("myphp.php");
                    urlConn = (HttpURLConnection)url.openConnection();
                    urlConn.setDoInput (true);
                    urlConn.setDoOutput (true);
                    urlConn.setUseCaches (false);
                    urlConn.setRequestProperty("Content-Type","application/json");   
                    urlConn.setRequestProperty("Accept", "application/json");
                    urlConn.setChunkedStreamingMode(0);
                    urlConn.setRequestMethod("POST");
                    urlConn.connect();                  
                    //get google account
                    AccountManager am = AccountManager.get(getBaseContext()); // "this" references the current Context
                    Account[] accounts = am.getAccountsByType("com.google");

                    //Create JSONObject here
                    JSONObject json = new JSONObject();
                    json.put("regID", String.valueOf(args[0]));
                    json.put("Google account", accounts[0].name);
                    json.put("name", "Amanda");
                    json.put("tel", "2069994444");
                    json.put("latitude", String.valueOf(args[1]));
                    json.put("longitude", String.valueOf(args[2]));

                    String postData=json.toString();

                    // Send POST output.
                    OutputStreamWriter os = new OutputStreamWriter(urlConn.getOutputStream(), "UTF-8");
                      os.write(postData);
                      Log.i("NOTIFICATION", "Data Sent");
                      os.close();

                    BufferedReader reader = new BufferedReader(new InputStreamReader(urlConn.getInputStream())); 
                    String msg=""; 
                    String line = ""; 
                    while ((line = reader.readLine()) != null) 
                        msg += line;  
                    Log.i("msg=",""+msg);
                 catch (MalformedURLException muex) 
                    // TODO Auto-generated catch block
                    muex.printStackTrace();
                 catch (IOException ioex)
                    ioex.printStackTrace();
                 
             catch (Exception e) 
                e.printStackTrace();
                Log.e("ERROR", "There is error in this code");

            
            return null;

        

以下是我发送注册ID的方式

gcm = GoogleCloudMessaging.getInstance(this);
            regid = getRegistrationId(context);
            if (!regid.isEmpty()) 
                PostData pd = new PostData();               
                pd.execute(regid); 
             else 
                //register
                registerInBackground();
            

【问题讨论】:

getLastLocation() 可能返回 null。它经常这样做。注册一个监听器,并从 onLocationChanged() 回调中调用您的 AsyncTask。 @Daniel 感谢您的回答,但到目前为止我还没有运气。我可以调用 Asynctask 两次以传递 ID 并从两种不同的方法传递纬度和经度吗?如何检索 doInBackground(String ...params) 方法中的值? 您是否在访问 AsycTask 中的 JSONObject 和 Location 对象时遇到问题?请发布您迄今为止尝试过的代码。您还可以在此处查看用于注册位置侦听器的示例代码:***.com/questions/30191047/… 我已经更新了问题。 您是否在代码中的其他地方以不同的方式启动 AsyncTask?您可以添加使用注册 ID 调用它的代码,还可以添加整个 AsyncTask 代码吗? 【参考方案1】:

问题是您在不同时间向 AsyncTask 发送了两组不同的 varargs

当您调用 execute() 时,您应该将所有必要的数据发送到 AsyncTask,以便它拥有所需的数据。

因此,您需要准备好所有数据,并通过一次调用将其发送至execute(),如下所示:

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

gcm = GoogleCloudMessaging.getInstance(this);
regid = getRegistrationId(context);
if (!regid.isEmpty() && mLastLocation != null) 
    double latitude = mLastLocation.getLatitude();
    double longitude = mLastLocation.getLongitude();
    PostData pd = new PostData();
    pd.execute(regid, String.valueOf(latitude), String.valueOf(longitude));
 else 
    //register if regid is empty
    if (regid.isEmpty())
       registerInBackground();
    

此外,无需对传递给doInBackground() 的字符串参数调用String.valueOf(),因此您可以删除这些调用:

@Override
protected String doInBackground(String... args) 
    try 
        try 
            URL url;
            HttpURLConnection urlConn;
            url = new URL ("myphp.php");
            urlConn = (HttpURLConnection)url.openConnection();
            urlConn.setDoInput (true);
            urlConn.setDoOutput (true);
            urlConn.setUseCaches (false);
            urlConn.setRequestProperty("Content-Type","application/json");
            urlConn.setRequestProperty("Accept", "application/json");
            urlConn.setChunkedStreamingMode(0);
            urlConn.setRequestMethod("POST");
            urlConn.connect();
            //get google account
            AccountManager am = AccountManager.get(getBaseContext()); // "this" references the current Context
            Account[] accounts = am.getAccountsByType("com.google");

            //Create JSONObject here
            JSONObject json = new JSONObject();
            json.put("regID", args[0]); //modified
            json.put("Google account", accounts[0].name);
            json.put("name", "Amanda");
            json.put("tel", "2069994444");
            json.put("latitude", args[1]); //modified
            json.put("longitude", args[2]); //modified

            String postData=json.toString();

            // Send POST output.
            OutputStreamWriter os = new OutputStreamWriter(urlConn.getOutputStream(), "UTF-8");
            os.write(postData);
            Log.i("NOTIFICATION", "Data Sent");
            os.close();

            BufferedReader reader = new BufferedReader(new InputStreamReader(urlConn.getInputStream()));
            String msg="";
            String line = "";
            while ((line = reader.readLine()) != null) 
                msg += line; 
            Log.i("msg=",""+msg);
         catch (MalformedURLException muex) 
            // TODO Auto-generated catch block
            muex.printStackTrace();
         catch (IOException ioex)
            ioex.printStackTrace();
        
     catch (Exception e) 
        e.printStackTrace();
        Log.e("ERROR", "There is error in this code");

    
    return null;


编辑:听起来您应该注册一个位置侦听器才能显式请求位置。下面是示例代码,您可以将其用作参考,以便在代码中注册位置侦听器:

public class MainActivity extends Activity implements
        GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener 

    LocationRequest mLocationRequest;
    GoogleApiClient mGoogleApiClient;

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

        buildGoogleApiClient();
        mGoogleApiClient.connect();
    

    @Override
    protected void onPause()
        super.onPause();
        if (mGoogleApiClient != null) 
            LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
        
    

    protected synchronized void buildGoogleApiClient() 
        Toast.makeText(this,"buildGoogleApiClient",Toast.LENGTH_SHORT).show();
        mGoogleApiClient = new GoogleApiClient.Builder(this)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API)
                .build();
    

    @Override
    public void onConnected(Bundle bundle) 
        Toast.makeText(this,"onConnected",Toast.LENGTH_SHORT).show();

        mLocationRequest = new LocationRequest();
        mLocationRequest.setInterval(10);
        mLocationRequest.setFastestInterval(10);
        mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
        //mLocationRequest.setPriority(LocationRequest.PRIORITY_LOW_POWER);
        //mLocationRequest.setSmallestDisplacement(0.1F);

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

    @Override
    public void onConnectionSuspended(int i) 
        Toast.makeText(this,"onConnectionSuspended",Toast.LENGTH_SHORT).show();
    

    @Override
    public void onConnectionFailed(ConnectionResult connectionResult) 
        Toast.makeText(this,"onConnectionFailed",Toast.LENGTH_SHORT).show();
    

    @Override
    public void onLocationChanged(Location location) 

        Log.d("locationtesting", "accuracy: " + location.getAccuracy() + " lat: " + location.getLatitude() + " lon: " + location.getLongitude());

        Toast.makeText(this,"Location Changed",Toast.LENGTH_SHORT).show();


        //unregister here if you only need one location update:
        if (mGoogleApiClient != null) 
            LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
        
    

【讨论】:

我之前尝试过这种方法,现在我得到空指针异常,因为最后一个已知位置在 pd.excecute(regid, String.valueOf(latitude), String.valueOf(longitude)); @Quantumdroid 你只需要确保mLastLocation 不为空,查看if (!regid.isEmpty() && mLastLocation != null) 处的检查,并查看我刚刚在答案中所做的更新。 我这样做了,但 mLastLocation 为空。我试图弄清楚是什么原因造成的。当我这样做时,我会更新你。非常感谢。 @Quantumdroid 正如我在第一条评论中提到的,getLastLocation() 可以并且将返回 null,因为它没有明确请求位置。我刚刚添加了示例代码,您可以将其用作注册位置侦听器的参考,该侦听器将显式请求位置。您在onLocationChanged() 中获得的位置不会为空。 即使在那时我也遇到了空指针异常,但我终于明白了。我将此归咎于 Eclipse,因为它非常胆怯地说明原因:( 我会感谢你指导我并给我明确的答案。你太棒了。我会把你的作为最好的答案。【参考方案2】:
private void startReceivingLocationUpdates() 
        if (locationManager == null) 
            locationManager = (android.location.LocationManager)context.getSystemService(Context.LOCATION_SERVICE);
        

        if (locationManager != null) 
            try locationManager.requestLocationUpdates(android.location.LocationManager.NETWORK_PROVIDER, 1000, 0F, 
                    (android.location.LocationListener) listener);
             
         catch (SecurityException ex) 
             
                Log.i(TAG, "fail to request location update, ignore", ex);
             

           catch (IllegalArgumentException ex)
           
                Log.d(TAG, "provider does not exist " + ex.getMessage());
            

            try 
                locationManager.requestLocationUpdates(android.location.LocationManager.GPS_PROVIDER, 1000, 0F, 
                        (android.location.LocationListener) listener);
                //if (listener != null) listener.showGpsOnScreenIndicator(false);
            
           catch (SecurityException ex) 
                Log.i(TAG, "fail to request location update, ignore", ex); 
                 

            catch (IllegalArgumentException ex) 
                Log.d(TAG, "provider does not exist " + ex.getMessage());  
                

            Log.d(TAG, "startReceivingLocationUpdates");
        
    

这解决了我的 Location 对象上的空指针异常,我从 here 得到它

【讨论】:

以上是关于当我将 Android Location 对象传递给 asyncTask 时,它返回 null的主要内容,如果未能解决你的问题,请参考以下文章

当我将它传递给套接字 io 时,对象失去了它的原型功能

React Typescript - 在路由中传递时如何将类型添加到 location.state

Ionic 2:当我将数组传递给组件时,我在 component.ts 上得到一个字符串

GeoPoint 属性 ("place:location") 未传递给 Open Graph 对象

从 JUnit 单元测试中设置 android.location.Location 对象的参数

Android:将 AppWidgetId 传递给服务