使用Android中的Network_Provider获取用户当前位置名称,而不使用GPS或互联网。

13
此问题与同一篇流行的stackoverflow问题直接相关,在 "Android: get current location of user without using gps or internet" 这里,被接受的答案实际上没有回答这个问题。
我应该能够通过网络提供商获取设备的当前位置名称(例如城市名称、村庄名称),而不是使用GPS或互联网。以下是该问题中被接受的答案。(下面的代码部分应包含在onCreate()方法中)
// Acquire a reference to the system Location Manager
LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);

// Define a listener that responds to location updates
LocationListener locationListener = new LocationListener() {
    public void onLocationChanged(Location location) {
      // Called when a new location is found by the network location provider.
      makeUseOfNewLocation(location);
    }

    public void onStatusChanged(String provider, int status, Bundle extras) {}

    public void onProviderEnabled(String provider) {}

    public void onProviderDisabled(String provider) {}
  };

// Register the listener with the Location Manager to receive location updates
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListener);
我按照链接答案中给出的代码改变了上面的代码,但没有成功。
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    final TextView txtView = (TextView) findViewById(R.id.tv1);
    txtView.setText("ayyo samitha");
    ////

    // Acquire a reference to the system Location Manager
    LocationManager locationManager;
   locationManager= (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);

    // Define a listener that responds to location updates
    LocationListener locationListener = new LocationListener() {
        public void onLocationChanged(Location location) {
            // Called when a new location is found by the network location provider.
            makeUseOfNewLocation(location);

        }

        private void makeUseOfNewLocation(Location location) {
            txtView.setText("sam came in");
            txtView.append(location.toString());
        }

        public void onStatusChanged(String provider, int status, Bundle extras) {}

        public void onProviderEnabled(String provider) {
           // makeUseOfNewLocation(location);
        }

        public void onProviderDisabled(String provider) {}
    };

    // Register the listener with the Location Manager to receive location updates
    if (locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
        locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListener);
    }

}

如何通过更正上面的代码或任何其他方法来实现我想要的目标?请注意,我想获取位置名称,而不是经纬度。有人能帮帮我吗。


我不太清楚你在问什么,请描述清楚你要完成什么目标,你想要得到什么样的行为,期望位置更新之间的时间等。 - Kai
我只想获取用户当前位置,而不使用GPS或互联网。 - Samitha Chathuranga
1
问题在于你尝试的代码确实可以工作,但可能不如你所愿。例如,这种方法在三星Galaxy S3上提供的精度为2000米,意味着我的实际位置可能在一个半径为2公里的圆内的任何地方。此外,由于误差范围很大,您的应用程序可能需要相当大的位置变化才能被通知到位置更改。Ruben提供的答案会多次给出更好的结果,但您必须将优先级更改为“LocationRequest.PRIORITY_LOW_ENERGY”以满足您的无GPS要求。 - Kai
@Kai 我想在手机没有开启GPS或互联网的情况下获取位置。我不关心精度,只想通过信号塔获取位置。我不清楚在程序中何处使用LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY。你认为我应该在Ruben的程序中使用它来替换LocationRequest.PRIORITY_HIGH_ACCURACY吗?不管怎样,这是否可以使得在没有开启互联网的情况下获取位置? - Samitha Chathuranga
@Kai,Ruben的代码中mLocationProviderApi是什么?我看到LocationProviderApi变量没有被初始化。 - Samitha Chathuranga
显示剩余3条评论
6个回答

4
你在这里提到的(在旧手机上显示位置名称)是使用“Cell Broadcast”(或“CB”)完成的。这与位置API或其任何变体完全无关。
基站可以发送广播信息,可以被设备接收(类似于“一对多短信”)。一些运营商已经使用Cell Broadcast来广播所在基站的位置名称。一些运营商已经使用Cell Broadcast来广播基站的位置(纬度/经度)。一些运营商已经使用Cell Broadcast来发送广告滚动消息。没有标准规定CB广播消息中包含的信息,每个移动运营商可以选择使用它或不使用它。
由于大多数运营商不发送这些消息,因此尝试接收和解码它们可能没有意义。但是如果您想尝试,可以注册一个BroadcastReceiver来侦听此Intent操作:android.provider.Telephony.SMS_CB_RECEIVED。有关Intent中包含的数据的更多详细信息,请参见文档

非常感谢...这正是我苦苦寻找的答案...100%正确...很抱歉我无法在赏金期结束前接受答案,因为我无法在最后一刻检查。 - Samitha Chathuranga
我尝试获取蜂窝广播短信。但是找不到正确的方法。如果您知道,请告诉我一种方法。 - Samitha Chathuranga
你知道你的运营商是否正在发送小区广播吗?你已经确认过了吗? - David Wasser
是的,我确认了。当我在手机上打开蜂窝广播消息时,这些消息会带有位置名称。 - Samitha Chathuranga
是的,这两个要求都没问题。实际上我已经尝试了这段代码:https://dev59.com/1VrUa4cB1Zd3GeqPjmyI 但是我卡在了某个点上... 这个问题在那个问题的被接受答案的评论中有提到。 - Samitha Chathuranga
显示剩余2条评论

1
你可以尝试使用Locale对象或Telephony服务来获取国家级别的精度,无需互联网或GPS。
从Locale中获取国家代码:
String locale = context.getResources().getConfiguration().locale.getCountry();

从Android的Telephony服务获取国家代码:
TelephonyManager tm = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
// Will work on all networks. Only provide the SIM card's country
String countryCode = tm.getSimCountryIso();

// Might not work well on CDMA networks. Will provide the country code
// for the country the device is currently in.
String currentCountryCode = tm.getNetworkCountryIso();

更好的代码示例和讨论在这里

1
但我需要城市级别的准确性。 - Samitha Chathuranga

1
根据安卓文档,使用 LocationManager 不是当前推荐的 API(请参阅 reference):
The Google Play services location APIs are preferred over the 
Android framework location APIs (android.location) as a way of
adding location awareness to your app.

要了解如何设置Google服务客户端库,请参阅Google Play服务指南中的Setup
一旦您将Google服务客户端库链接到您的应用程序,您可以使用FusedLocationProviderApi来实现用户位置:
    import android.location.Location;
    import android.os.Bundle;
    import android.support.v7.app.ActionBarActivity;
    import android.util.Log;
    import android.view.Menu;
    import android.view.MenuItem;
    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.common.api.PendingResult;
    import com.google.android.gms.common.api.ResultCallback;
    import com.google.android.gms.common.api.Status;
    import com.google.android.gms.location.FusedLocationProviderApi;
    import com.google.android.gms.location.LocationListener;
    import com.google.android.gms.location.LocationRequest;
    import com.google.android.gms.location.LocationServices;

    public class MainActivity extends ActionBarActivity
            implements ConnectionCallbacks, OnConnectionFailedListener {

        // ..

        private GoogleApiClient mGoogleAPIClient;

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

            // create google api client object
            mGoogleAPIClient = new GoogleApiClient.Builder(this)
                .addApi(LocationServices.API)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .build();
        }

        @Override
        protected void onStart() {
            super.onStart();

            mGoogleAPIClient.connect();
        }

        @Override
        protected void onStop() {
            super.onStop();

            mGoogleAPIClient.disconnect();
        }

        @Override
        public void onConnectionFailed(ConnectionResult connectionResult) {
            Toast.makeText(this,
                "Could not connect to Google Play Services",
                Toast.LENGTH_SHORT).show();

            finish();
        }

        @Override
        public void onConnected(Bundle bundle) {
            Log.i(TAG,
                "Successfuly connect to Google Play Services");

            // retrieve last location once connected
            Location lastLocation = LocationServices.FusedLocationApi
                .getLastLocation(mGoogleAPIClient);

            if (lastLocation == null) {
                // should request new one
                // location should be enabled
                Log.i(TAG,
                    "No location data previously acquired.. should request!");

                Toast.makeText(this,
                    "Requesting location data ..",
                    Toast.LENGTH_SHORT).show();

                LocationRequest locationRequest = LocationRequest.create();
                locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
                locationRequest.setInterval(5000);

                PendingResult<Status> result = LocationServices.FusedLocationApi
                    .requestLocationUpdates(mGoogleAPIClient,
                        locationRequest,
                        new LocationListener() {

                    @Override
                    public void onLocationChanged(Location location) {
                        makeUseOfNewLocation(location);
                    }
                });

                // TODO: use result to retrieve more info

            } else {
                makeUseOfNewLocation(lastLocation);
            }
        }

        @Override
        public void onConnectionSuspended(int i) {
        }

        private void makeUseOfNewLocation(Location location) {
             // do your stuff here
        }

我已经测试了上述代码,它可以在没有互联网连接的情况下工作,但需要用户启用设备的位置功能。此外,它还要求用户已经将位置历史记录功能启用到位置功能中。
希望这能帮助您。

抱歉,我的错误。我编辑了代码,请看一下。当我第一次实现代码时,我创建了一个名为mLocationProviderApiFusedLocationProviderApi属性实例,但我发现我可以直接从LocationServices.FusedLocationApi访问FusedLocationProviderApi的引用。 - Ruben O. Chiavone
谢谢,"在设备上启用位置功能"是什么意思?我猜这并不意味着我应该打开GPS。 - Samitha Chathuranga
好的,这个程序没有错误,但是它无法给我位置名称(例如伦敦)。它只提供纬度和经度... 天哪,!!! - Samitha Chathuranga
我想你可能见过最原始的手机,比如“诺基亚1110”,它可以通过信号塔显示位置名称(无需互联网或GPS)。我想获取这些数据。但是,我发现这种方法无法实现,因为Location类没有获取位置名称的方法。 - Samitha Chathuranga
我的意思是启用位置服务,可能会使用GPS,但仅在您发出请求时才会使用。问题是,如果您使用Google Play服务,则可以使用位置历史记录来检索用户的最后位置,并且我检查了它不需要互联网连接。我没有看到您需要位置名称,但是通过纬度和经度,您可以解决它。 - Ruben O. Chiavone
显示剩余3条评论

1
问题在于你尝试的代码确实可以工作,但可能不如你所希望的那样好。例如,这种方法在三星Galaxy S3上提供的精度为2000m,意味着实际位置可能在半径为2公里的圆形范围内的任何地方。此外,由于误差范围很大,除非位置发生了相当大的变化,否则您的应用程序可能需要相当长的时间才能被通知位置发生了变化。
要获得合理的位置信息,需要使用GPS或LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY(如果使用Google Play服务)。但是,这确实需要android.permission.ACCESS_FINE_LOCATION权限。除非您只需要公里级别的精度,否则这个权限是必需的。
最后,请注意,使用Google Play服务和LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY,即使没有打开GPS,我也可以获得10m的精确位置数据,因此这仍然可以满足您的要求。
以下是一个完整的示例:

AndroidManifest.xml

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

MainActivity.java

import android.app.Activity;
import android.location.Location;
import android.os.Bundle;
import android.widget.TextView;

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.FusedLocationProviderApi;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;

public class MainActivity extends Activity implements
        com.google.android.gms.location.LocationListener, ConnectionCallbacks,
        OnConnectionFailedListener {
    private final FusedLocationProviderApi fusedLocationProviderApi = LocationServices.FusedLocationApi;
    private GoogleApiClient mGoogleAPIClient;

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

        mGoogleAPIClient = new GoogleApiClient.Builder(this)
                .addApi(LocationServices.API).addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this).build();
    }

    @Override
    protected void onResume() {
        super.onResume();

        mGoogleAPIClient.connect();
    }

    @Override
    protected void onPause() {
        super.onPause();

        if (mGoogleAPIClient != null) {
        mGoogleAPIClient.disconnect();
        }
    }

    @Override
    public void onConnected(Bundle arg0) {
        final LocationRequest locationRequest = LocationRequest.create();
        locationRequest
                .setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
        locationRequest.setInterval(30 * 1000);
        locationRequest.setFastestInterval(5 * 1000);
        fusedLocationProviderApi.requestLocationUpdates(mGoogleAPIClient,
                locationRequest, this);
    }

    @Override
    public void onConnectionSuspended(int arg0) {
        // TODO Auto-generated method stub

    }

    @Override
    public void onConnectionFailed(ConnectionResult arg0) {
        // TODO Auto-generated method stub

    }

    @Override
    public void onLocationChanged(Location location) {
        // the location is no more than 10 min old, and with reasonable
        // accurarcy (50m), done
        if (System.currentTimeMillis() < location.getTime() + 10 * 60 * 1000
                && location.getAccuracy() < 50) {
            mGoogleAPIClient.disconnect();
            mGoogleAPIClient = null;
            ((TextView) findViewById(R.id.test)).setText(location.toString());
        }
    }
}

是的,您可以关闭GPS和任何互联网连接并自行尝试。 - Kai
但这不能给我位置名称(例如伦敦)。这只提供纬度和经度。我也尝试使用地理编码器,但它需要互联网。 - Samitha Chathuranga
我想你肯定见过最原始的手机,比如“诺基亚1110”,它能够通过信号塔显示地名(无需联网或GPS)。我想获取这些数据,但是就我所知,这种方法并不能实现,因为Location类没有获取地名的方法。 - Samitha Chathuranga
你提到了获取位置信息本身完全不同于获取位置名称的事情,但你并没有提及任何关于获取位置名称的内容。这是需要互联网连接才能完成的。 - Kai
抱歉,我没有想到我应该提到那个。[我会编辑问题的。] 但是我认为这并不是不可能的,因为大多数原始手机(如“诺基亚1110”)默认从信号塔显示位置名称。(无需互联网或GPS)。 因此,如果这样一个简单的手机可以做到这一点,我想安卓也可以做到。 - Samitha Chathuranga

0
尝试这段代码...
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.IBinder;

public class AppLocationService extends Service implements LocationListener         {

protected LocationManager locationManager;
Location location;

private static final long MIN_DISTANCE_FOR_UPDATE = 10;
private static final long MIN_TIME_FOR_UPDATE = 1000 * 60 * 2;

public AppLocationService(Context context) {
    locationManager = (LocationManager) context
            .getSystemService(LOCATION_SERVICE);
}

public Location getLocation(String provider) {
    if (locationManager.isProviderEnabled(provider)) {
        locationManager.requestLocationUpdates(provider,
                MIN_TIME_FOR_UPDATE, MIN_DISTANCE_FOR_UPDATE, this);
        if (locationManager != null) {
            location = locationManager.getLastKnownLocation(provider);
            return location;
        }
    }
    return null;
}

@Override
public void onLocationChanged(Location location) {
}

@Override
public void onProviderDisabled(String provider) {
}

@Override
public void onProviderEnabled(String provider) {
}

@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}

@Override
public IBinder onBind(Intent arg0) {
    return null;
}

}

下一个课程是

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.location.Location;
import android.location.LocationManager;
import android.os.Bundle;
import android.provider.Settings;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

public class AndroidLocationActivity extends Activity {

Button btnGPSShowLocation;
Button btnNWShowLocation;

AppLocationService appLocationService;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    appLocationService = new AppLocationService(
            AndroidLocationActivity.this);

    btnGPSShowLocation = (Button) findViewById(R.id.btnGPSShowLocation);
    btnGPSShowLocation.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View arg0) {

            Location gpsLocation = appLocationService
                    .getLocation(LocationManager.GPS_PROVIDER);

            if (gpsLocation != null) {
                double latitude = gpsLocation.getLatitude();
                double longitude = gpsLocation.getLongitude();
                Toast.makeText(
                        getApplicationContext(),
                        "Mobile Location (GPS): \nLatitude: " + latitude
                                + "\nLongitude: " + longitude,
                        Toast.LENGTH_LONG).show();
            } else {
                showSettingsAlert("GPS");
            }

        }
    });

    btnNWShowLocation = (Button) findViewById(R.id.btnNWShowLocation);
    btnNWShowLocation.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View arg0) {

            Location nwLocation = appLocationService
                    .getLocation(LocationManager.NETWORK_PROVIDER);

            if (nwLocation != null) {
                double latitude = nwLocation.getLatitude();
                double longitude = nwLocation.getLongitude();
                Toast.makeText(
                        getApplicationContext(),
                        "Mobile Location (NW): \nLatitude: " + latitude
                                + "\nLongitude: " + longitude,
                        Toast.LENGTH_LONG).show();
            } else {
                showSettingsAlert("NETWORK");
            }

        }
    });

}

public void showSettingsAlert(String provider) {
    AlertDialog.Builder alertDialog = new AlertDialog.Builder(
            AndroidLocationActivity.this);

    alertDialog.setTitle(provider + " SETTINGS");

    alertDialog
            .setMessage(provider + " is not enabled! Want to go to settings menu?");

    alertDialog.setPositiveButton("Settings",
            new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                    Intent intent = new Intent(
                            Settings.ACTION_LOCATION_SOURCE_SETTINGS);
                    AndroidLocationActivity.this.startActivity(intent);
                }
            });

    alertDialog.setNegativeButton("Cancel",
            new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                    dialog.cancel();
                }
            });

    alertDialog.show();
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.main, menu);
    return true;
}

    }

并且在清单文件中授予了此用户权限

<!-- to get location using GPS -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

<!-- to get location using NetworkProvider -->
<uses-permission android:name="android.permission.INTERNET" />

当需要时,您也可以更改您的XML文件。谢谢和问候。 - Hardik Parmar
是的,您可以获取这个经纬度。使用这个经纬度来获取地址。 - Hardik Parmar
你从哪里获取地址的纬度和经度? - Hardik Parmar
我可以通过经纬度获取地址,但这需要互联网.. 我不能在整个过程中使用互联网。 - Samitha Chathuranga

0

祝你好运。它被称为地理编码器。更具体地说,是将坐标转换为人类可读的输出的反向地理编码。我相当确定谷歌提供的是付费服务,但你可以获得一堆免费的服务。所以计划缓存结果,并尽可能使用缓存的结果。

List<Address> list = geoCoder.getFromLocation(location
            .getLatitude(), location.getLongitude(), 1);
    if (list != null & list.size() > 0) {
        Address address = list.get(0);
        result = address.getLocality();
        return result;

https://developer.android.com/training/location/display-address.html

如何从Google地图的纬度和经度坐标中获取城市名称?


连接到哪个塔设备? - danny117
是的。无论这座塔所在地区的名称如何..!! - Samitha Chathuranga
看起来塔所在的区域是专有信息。我找不到任何东西。我认为你最好将已知位置缓存到设备上。你的应用程序必须具有一点数据连接... - danny117

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接