Android中具有位置侦听器的后台服务


76

我正在创建一个将在其自己的进程中运行的后台服务。如果设备位置已更改,它应该允许我收听。我应该能够在通知UI之前更改条件,例如移动的距离。

我该怎么做?我对服务和LocationListener实现有一点了解。围绕网络的任何教程将不胜感激。

我从堆栈溢出中获得了一个反向链接,但是我对它的了解不多。



查看最新的GoogleApiClient方法stackoverflow.com/a/41981246/3496570
AndroidGeek

Answers:


116

首先,您需要创建一个Service。在那Service,创建一个扩展类LocationListener。为此,请使用以下代码段Service

public class LocationService extends Service {
public static final String BROADCAST_ACTION = "Hello World";
private static final int TWO_MINUTES = 1000 * 60 * 2;
public LocationManager locationManager;
public MyLocationListener listener;
public Location previousBestLocation = null;

Intent intent;
int counter = 0;

@Override
public void onCreate() {
    super.onCreate();
    intent = new Intent(BROADCAST_ACTION);
}

@Override
public void onStart(Intent intent, int startId) {
    locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
    listener = new MyLocationListener();
    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
        return;
    }
    locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 4000, 0, (LocationListener) listener);
    locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 4000, 0, listener);
}

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

protected boolean isBetterLocation(Location location, Location currentBestLocation) {
    if (currentBestLocation == null) {
        // A new location is always better than no location
        return true;
    }

    // Check whether the new location fix is newer or older
    long timeDelta = location.getTime() - currentBestLocation.getTime();
    boolean isSignificantlyNewer = timeDelta > TWO_MINUTES;
    boolean isSignificantlyOlder = timeDelta < -TWO_MINUTES;
    boolean isNewer = timeDelta > 0;

    // If it's been more than two minutes since the current location, use the new location
    // because the user has likely moved
    if (isSignificantlyNewer) {
        return true;
        // If the new location is more than two minutes older, it must be worse
    } else if (isSignificantlyOlder) {
        return false;
    }

    // Check whether the new location fix is more or less accurate
    int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation.getAccuracy());
    boolean isLessAccurate = accuracyDelta > 0;
    boolean isMoreAccurate = accuracyDelta < 0;
    boolean isSignificantlyLessAccurate = accuracyDelta > 200;

    // Check if the old and new location are from the same provider
    boolean isFromSameProvider = isSameProvider(location.getProvider(),
            currentBestLocation.getProvider());

    // Determine location quality using a combination of timeliness and accuracy
    if (isMoreAccurate) {
        return true;
    } else if (isNewer && !isLessAccurate) {
        return true;
    } else if (isNewer && !isSignificantlyLessAccurate && isFromSameProvider) {
        return true;
    }
    return false;
}



/** Checks whether two providers are the same */
private boolean isSameProvider(String provider1, String provider2) {
    if (provider1 == null) {
        return provider2 == null;
    }
    return provider1.equals(provider2);
}



@Override
public void onDestroy() {
    // handler.removeCallbacks(sendUpdatesToUI);     
    super.onDestroy();
    Log.v("STOP_SERVICE", "DONE");
    locationManager.removeUpdates(listener);
}

public static Thread performOnBackgroundThread(final Runnable runnable) {
    final Thread t = new Thread() {
        @Override
        public void run() {
            try {
                runnable.run();
            } finally {

            }
        }
    };
    t.start();
    return t;
}
public class MyLocationListener implements LocationListener
{

    public void onLocationChanged(final Location loc)
    {
        Log.i("*****", "Location changed");
        if(isBetterLocation(loc, previousBestLocation)) {
            loc.getLatitude();
            loc.getLongitude();
            intent.putExtra("Latitude", loc.getLatitude());
            intent.putExtra("Longitude", loc.getLongitude());
            intent.putExtra("Provider", loc.getProvider());
            sendBroadcast(intent);

        }
    }

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

    }

    public void onProviderDisabled(String provider)
    {
        Toast.makeText( getApplicationContext(), "Gps Disabled", Toast.LENGTH_SHORT ).show();
    }


    public void onProviderEnabled(String provider)
    {
        Toast.makeText( getApplicationContext(), "Gps Enabled", Toast.LENGTH_SHORT).show();
    }
}

Service以您想要的方式将此添加到项目中的任何位置!:)


5
请注意:代码看起来可能从一个被采购GPL的项目(或者,它可能是从这个MIT许可证的项目任何人都知道上面的代码的许可证状态。?
Inactivist

@Nilanchala如果这对您有用,则将其标记为正确,以便我可以帮助其他有此类问题的人
Usman Kurd 2013年

11
“ performOnBackgroundThread”函数的作用是什么?在某处使用吗?
Martin Mlostek 2014年

2
由于Google Play服务包含定位服务,因此建议改用它:github.com/nickfox/GpsTracker/blob/master/phoneClients/android/…– 2014
Nirmal

3
使用onStartCommand()而不是onStart()并返回START_STICKY。上面的代码也不会在单独的进程中运行(因为这些解决方案有不同的解决方案),因此,每当应用被Android系统杀死(通常是在后台运行之后),服务都会停止-尽管如果有可用资源,它将再次启动多亏了START_STICKY。当应用程序在后台运行时,还有多种解决方案可以使Service保持活动状态。
Darek Deoniziak

13

我知道我会晚些时候发布此答案,但是我觉得值得使用Google的保险丝位置提供程序服务来获取当前位置。

该API的主要功能是:

1.Simple API:让您选择准确性级别以及功耗。

2.立即可用:可让您的应用立即访问最近的最佳位置。

3.Power-efficiency:它选择最有效的方式以更少的功耗获得位置

4.多功能性:满足广泛的需求,从需要高度精确位置的前景使用到需要定期位置更新且功耗影响可忽略的后台使用。

位置更新时也很灵活。如果仅在应用启动时需要当前位置,则可以使用getLastLocation(GoogleApiClient)方法。

如果要连续更新位置,则可以使用 requestLocationUpdates(GoogleApiClient,LocationRequest, LocationListener)

您可以在此处找到有关保险丝位置的非常不错的博客,有关保险丝位置的Google文档也可以在此处找到。

更新资料

根据从Android O开始的开发人员文档,他们在后台位置上增加了新的限制。

如果您的应用程序在后台运行,则位置系统服务每小时仅会几次为您的应用程序计算一个新位置。即使您的应用请求更频繁的位置更新,也是如此。但是,如果您的应用程序在前台运行,则与Android 7.1.1(API级别25)相比,位置采样率没有变化。


1
我认为您是对的,但是某些Custom Rom删除了Google Play服务,因此您无法使用保险丝位置提供程序。

2
如果您只是想大致了解某个时间的位置或不太准确的跟踪,那么FusedLocationProvider可以,但是对于需要高精度跟踪的应用程序来说,这还不够好
StuStirling 2015年

2
这些自定义Roms的解决方案是使用Mapzen的LOST。Google提供了与FusedLocationProviderAPI相同的功能,而无需在Google Play服务中注册
Adrian C.

1
如果每小时仅更新几次位置,我如何在android Oreo中运行的应用程序中跟踪用户路线?
Makalele

8

后台定位服务。即使终止了该应用程序,它也将重新启动。

MainActivity.java

public class MainActivity extends AppCompatActivity {
    AlarmManager alarmManager;
    Button stop;
    PendingIntent pendingIntent;

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

        if (alarmManager == null) {
            alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
            Intent intent = new Intent(this, AlarmReceive.class);
            pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
            alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), 30000,
                    pendingIntent);
        }
    }
}

BookingTrackingService.java

public class BookingTrackingService extends Service implements LocationListener {

    private static final String TAG = "BookingTrackingService";
    private Context context;
    boolean isGPSEnable = false;
    boolean isNetworkEnable = false;
    double latitude, longitude;
    LocationManager locationManager;
    Location location;
    private Handler mHandler = new Handler();
    private Timer mTimer = null;
    long notify_interval = 30000;

    public double track_lat = 0.0;
    public double track_lng = 0.0;
    public static String str_receiver = "servicetutorial.service.receiver";
    Intent intent;

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

    @Override
    public void onCreate() {
        super.onCreate();

        mTimer = new Timer();
        mTimer.schedule(new TimerTaskToGetLocation(), 5, notify_interval);
        intent = new Intent(str_receiver);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        this.context = this;


        return START_NOT_STICKY;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.e(TAG, "onDestroy <<");
        if (mTimer != null) {
            mTimer.cancel();
        }
    }

    private void trackLocation() {
        Log.e(TAG, "trackLocation");
        String TAG_TRACK_LOCATION = "trackLocation";
        Map<String, String> params = new HashMap<>();
        params.put("latitude", "" + track_lat);
        params.put("longitude", "" + track_lng);

        Log.e(TAG, "param_track_location >> " + params.toString());

        stopSelf();
        mTimer.cancel();

    }

    @Override
    public void onLocationChanged(Location location) {

    }

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

    }

    @Override
    public void onProviderEnabled(String provider) {

    }

    @Override
    public void onProviderDisabled(String provider) {

    }

    /******************************/

    private void fn_getlocation() {
        locationManager = (LocationManager) getApplicationContext().getSystemService(LOCATION_SERVICE);
        isGPSEnable = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
        isNetworkEnable = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);

        if (!isGPSEnable && !isNetworkEnable) {
            Log.e(TAG, "CAN'T GET LOCATION");
            stopSelf();
        } else {
            if (isNetworkEnable) {
                location = null;
                locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 1000, 0, this);
                if (locationManager != null) {
                    location = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
                    if (location != null) {

                        Log.e(TAG, "isNetworkEnable latitude" + location.getLatitude() + "\nlongitude" + location.getLongitude() + "");
                        latitude = location.getLatitude();
                        longitude = location.getLongitude();
                        track_lat = latitude;
                        track_lng = longitude;
//                        fn_update(location);
                    }
                }
            }

            if (isGPSEnable) {
                location = null;
                locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 0, this);
                if (locationManager != null) {
                    location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
                    if (location != null) {
                        Log.e(TAG, "isGPSEnable latitude" + location.getLatitude() + "\nlongitude" + location.getLongitude() + "");
                        latitude = location.getLatitude();
                        longitude = location.getLongitude();
                        track_lat = latitude;
                        track_lng = longitude;
//                        fn_update(location);
                    }
                }
            }

            Log.e(TAG, "START SERVICE");
            trackLocation();

        }
    }

    private class TimerTaskToGetLocation extends TimerTask {
        @Override
        public void run() {

            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    fn_getlocation();
                }
            });

        }
    }

//    private void fn_update(Location location) {
//
//        intent.putExtra("latutide", location.getLatitude() + "");
//        intent.putExtra("longitude", location.getLongitude() + "");
//        sendBroadcast(intent);
//    }
}

AlarmReceive.java(BroadcastReceiver)

public class AlarmReceive extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.e("Service_call_"  , "You are in AlarmReceive class.");
        Intent background = new Intent(context, BookingTrackingService.class);
//        Intent background = new Intent(context, GoogleService.class);
        Log.e("AlarmReceive ","testing called broadcast called");
        context.startService(background);
    }
}

AndroidManifest.xml

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

 <service
            android:name=".ServiceAndBroadcast.BookingTrackingService"
            android:enabled="true" />

        <receiver
            android:name=".ServiceAndBroadcast.AlarmReceive"
            android:exported="false">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
        </receiver>

if (isNetworkEnable) {if (!isNetworkEnable) {在其他情况下可能会丢失。;)
Shailendra Madda

1
另外,需要检查权限可用性。if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { return;}
Shailendra Madda '18

我可以使用此代码在应用程序运行时获取位置,但是当应用程序被杀死时,它无法正常工作,即使在应用程序被杀死时,我也试图更新数据库中用户的位置
Mandeep Kumar

1
在新的Android版本中,位置服务需要大量权限才能继续运行。这个简单的服务无法正常工作,Im相当肯定
M.乌斯曼·汗

1
同意@ M.UsmanKhan,因为最新的Android版本startService 永远不会使应用程序处于后台状态。所有东西都需要使用前台服务,并在此期间保持应用程序运行。有关详细信息- developer.android.com/about/versions/oreo/background#services
Anuj Raghuvanshi

2

非常容易,无需创建类扩展LocationListener 1-变量

private LocationManager mLocationManager;
private LocationListener mLocationListener;
private static double currentLat =0;
private static double currentLon =0;

2-onStartService()

@Override public void onStartService() {
    addListenerLocation();
}

3-方法addListenerLocation()

 private void addListenerLocation() {
    mLocationManager = (LocationManager)
            getSystemService(Context.LOCATION_SERVICE);
    mLocationListener = new LocationListener() {
        @Override
        public void onLocationChanged(Location location) {
            currentLat = location.getLatitude();
            currentLon = location.getLongitude();

            Toast.makeText(getBaseContext(),currentLat+"-"+currentLon, Toast.LENGTH_SHORT).show();

        }

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

        @Override
        public void onProviderEnabled(String provider) {
            Location lastKnownLocation = mLocationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
            if(lastKnownLocation!=null){
                currentLat = lastKnownLocation.getLatitude();
                currentLon = lastKnownLocation.getLongitude();
            }

        }

        @Override
        public void onProviderDisabled(String provider) {
        }
    };
    mLocationManager.requestLocationUpdates(
            LocationManager.GPS_PROVIDER, 500, 10, mLocationListener);
}

4- onDestroy()

@Override
public void onDestroy() {
    super.onDestroy();
    mLocationManager.removeUpdates(mLocationListener);
}

由于某些原因,位置更新仅在查看应用程序时发生...
David Callanan
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.