Dịch vụ nền với trình nghe vị trí trong Android


76

Tôi đang tạo một dịch vụ nền sẽ chạy trong quy trình của riêng nó. Nó sẽ cho phép tôi nghe nếu vị trí thiết bị đã thay đổi. Tôi có thể thay đổi các tiêu chí như khoảng cách đã di chuyển trước khi thông báo cho UI.

Làm thế nào tôi có thể làm như vậy? Tôi có một chút kiến ​​thức về dịch vụ và cách LocationListenertriển khai. Bất kỳ hướng dẫn xung quanh mạng sẽ được đánh giá cao.

Tôi nhận được một liên kết ngược do tràn ngăn xếp, nhưng tôi không hiểu nhiều về nó.


2
Kiểm tra bài đăng trên blog này: android-developers.blogspot.ro/2011/06/…
Felix

Xem phương pháp GoogleApiClient mới nhất stackoverflow.com/a/41981246/3496570
AndroidGeek

Câu trả lời:


116

Đầu tiên bạn cần tạo một Service. Trong đó Service, tạo một lớp mở rộng LocationListener. Đối với điều này, hãy sử dụng đoạn mã Servicesau:

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();
    }
}

Thêm điều này vào Servicebất kỳ vị trí nào trong dự án của bạn, theo cách bạn muốn! :)


5
Hãy lưu ý: Đó là mã trông giống như nó có thể được bắt nguồn từ một dự án GPL (hoặc, nó có thể là từ này dự án được cấp phép MIT Bất cứ ai cũng biết tình trạng giấy phép của các mã trên.?
Inactivist

@Nilanchala nếu công trình này cho bạn hơn mark nó đúng để tôi có thể giúp đỡ người khác có vấn đề như vậy
Usman Kurd

11
chức năng "performanceOnBackgroundThread" để làm gì? nó được sử dụng ở đâu đó?
Martin Mlostek

2
Vì Dịch vụ của Google Play bao gồm các dịch vụ định vị, bạn nên sử dụng nó thay thế: github.com/nickfox/GpsTracker/blob/master/phoneClients/android/…
Nirmal

3
Sử dụng onStartCommand () thay vì onStart () và trả về START_STICKY. Đoạn mã trên cũng sẽ không chạy trong quy trình riêng biệt (đối với những giải pháp này có các giải pháp khác nhau), vì vậy bất cứ khi nào ứng dụng bị giết (chủ yếu là sau khi chạy ở chế độ nền), Dịch vụ sẽ dừng - mặc dù nó sẽ bắt đầu lại nếu có sẵn tài nguyên cảm ơn START_STICKY. Ngoài ra còn có các giải pháp khác nhau để giữ cho Dịch vụ tồn tại khi ứng dụng ở chế độ nền.
Darek Deoniziak

13

Tôi biết tôi đăng câu trả lời này hơi muộn, nhưng tôi cảm thấy đáng sử dụng dịch vụ nhà cung cấp vị trí cầu chì của Google để có được vị trí hiện tại.

Các tính năng chính của api này là:

1. API đơn giản: Cho phép bạn chọn mức độ chính xác cũng như mức tiêu thụ điện năng.

2.Sẵn có ngay lập tức : Cấp cho ứng dụng của bạn quyền truy cập ngay lập tức vào vị trí tốt nhất, gần đây nhất.

3. Hiệu quả năng lượng: Nó chọn cách hiệu quả nhất để có được vị trí với mức tiêu thụ điện năng ít hơn

4. Tính linh hoạt: Đáp ứng nhiều nhu cầu, từ sử dụng tiền cảnh cần vị trí chính xác cao đến sử dụng hậu cảnh cần cập nhật vị trí định kỳ với tác động điện năng không đáng kể.

Nó cũng linh hoạt trong khi cập nhật ở vị trí. Nếu bạn chỉ muốn vị trí hiện tại khi ứng dụng của bạn khởi động thì bạn có thể sử dụng getLastLocation(GoogleApiClient)phương pháp này.

Nếu bạn muốn cập nhật vị trí của mình liên tục thì bạn có thể sử dụng requestLocationUpdates(GoogleApiClient,LocationRequest, LocationListener)

Bạn có thể tìm thấy một blog rất hay về vị trí cầu chì ở đây và tài liệu google về vị trí cầu chì cũng có thể được tìm thấy ở đây .

Cập nhật

Theo tài liệu dành cho nhà phát triển bắt đầu từ Android O, họ đã thêm các giới hạn mới về vị trí nền.

Nếu ứng dụng của bạn đang chạy trong nền, dịch vụ hệ thống định vị chỉ tính toán một vị trí mới cho ứng dụng của bạn một vài lần mỗi giờ. Trường hợp này xảy ra ngay cả khi ứng dụng của bạn yêu cầu cập nhật vị trí thường xuyên hơn. Tuy nhiên, nếu ứng dụng của bạn đang chạy ở nền trước, thì không có thay đổi về tỷ lệ lấy mẫu vị trí so với Android 7.1.1 (API cấp 25).


1
Tôi nghĩ bạn nói đúng, nhưng một số Custom Rom đã loại bỏ dịch vụ google play nên bạn không thể sử dụng nhà cung cấp vị trí cầu chì.
Chen

2
Nếu bạn chỉ muốn biết gần nơi bạn đang ở trong một thời gian nhất định hoặc để theo dõi ít chính xác thì FusedLocationProviderlà ok nhưng đối với các ứng dụng đòi hỏi phải theo dõi nó chính xác cao chỉ là không đủ tốt
StuStirling

2
Một giải pháp cho những bản rom tùy chỉnh đó là sử dụng LOST từ Mapzen. Mời cùng chức năng với FusedLocationProviderAPI từ Google, mà không cần phải đăng ký với dịch vụ Google Play
Adrian C.

1
Làm cách nào tôi có thể theo dõi lộ trình của người dùng trong một ứng dụng đang chạy trên Android Oreo nếu nó chỉ có thể cập nhật vị trí vài lần mỗi giờ?
Makalele

8

Dịch vụ định vị nền. Nó sẽ được khởi động lại ngay cả sau khi tắt ứng dụng.

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) {nó có thể if (!isNetworkEnable) {bị thiếu KHÔNG trong trường hợp khác. ;)
Shailendra Madda 20/02/18

1
Ngoài ra, cần kiểm tra tính khả dụng của quyền. 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

Tôi đã có thể lấy vị trí khi ứng dụng đang chạy sử dụng mã này, nhưng khi ứng dụng đã bị giết chết nó đã không làm việc, tôi đang cố gắng để cập nhật vị trí của người dùng trong cơ sở dữ liệu ngay cả khi ứng dụng bị giết
Mandeep Kumar

1
Trong các phiên bản Android mới, cần có rất nhiều quyền để dịch vụ vị trí tiếp tục chạy. Công việc dịch vụ sẽ không đơn giản này, im khá chắc chắn
M. Usman Khan

1
Đồng ý với @ M.UsmanKhan vì trong các phiên bản Android mới nhất startService sẽ không bao giờ giữ ứng dụng ở trạng thái nền. Tất cả Nội dung cần thiết để sử dụng dịch vụ nền trước và giữ cho ứng dụng chạy qua điều này. Để biết thêm chi tiết - developer.android.com/about/versions/oreo/background#services
Anuj Raghuvanshi

2

Rất dễ dàng không cần tạo lớp mở rộng LocationListener 1- Biến

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

2- onStartService ()

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

3- Phương thức 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);
}

Đối với một số lý do cập nhật vị trí chỉ xảy ra khi ứng dụng là theo quan điểm ...
David Callanan
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.