通过Android中的服务获取GPS位置


70

我需要使用后台服务监视用户的位置,然后加载它们并向用户显示路径。

使用活动,获取GPS位置非常容易,但是当我必须通过服务来获取GPS位置时,我遇到了一个问题,因为它似乎仅适用于弯针线程(或类似的线程)。

当我在互联网上寻找解决方案时,我发现很多人遇到了同样的问题,但是我找不到可行的解决方案。有人说您需要使用prepare-> loop-> quit,而有人说您必须使用handlerThread,但是,我仍然无法找到如何以正确的方式来做这些事情。


2
循环线程:D您的意思是弯针线程。Looper线程是具有请求队列机制的特殊线程。主线程或UI线程是Looper线程。至于服务的问题,您是否在主线程上调用requestLocationUpdates?还是您正在其他线程中执行此操作?由于活动和服务是通过主线程(即循环线程)运行的,因此调用requestLocationUpdates必须相同。您能否指出提及您正在谈论的这个问题的其他帖子?
Vikram Bodicherla 2012年

Answers:


58

我不了解在Service中实现位置侦听功能到底有什么问题。它看起来与您在“活动”中所做的非常相似。只需定义位置侦听器并注册位置更新即可。您可以参考以下代码作为示例:

清单文件:

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<application
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name" >
    <activity android:label="@string/app_name" android:name=".LocationCheckerActivity" >
        <intent-filter >
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    <service android:name=".MyService" android:process=":my_service" />
</application>

服务文件:

import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.location.Location;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;

public class MyService extends Service {

    private static final String TAG = "BOOMBOOMTESTGPS";
    private LocationManager mLocationManager = null;
    private static final int LOCATION_INTERVAL = 1000;
    private static final float LOCATION_DISTANCE = 10f;

    private class LocationListener implements android.location.LocationListener {
        Location mLastLocation;

        public LocationListener(String provider) {
            Log.e(TAG, "LocationListener " + provider);
            mLastLocation = new Location(provider);
        }

        @Override
        public void onLocationChanged(Location location) {
            Log.e(TAG, "onLocationChanged: " + location);
            mLastLocation.set(location);
        }

        @Override
        public void onProviderDisabled(String provider) {
            Log.e(TAG, "onProviderDisabled: " + provider);
        }

        @Override
        public void onProviderEnabled(String provider) {
            Log.e(TAG, "onProviderEnabled: " + provider);
        }

        @Override
        public void onStatusChanged(String provider, int status, Bundle extras) {
            Log.e(TAG, "onStatusChanged: " + provider);
        }
    }

    LocationListener[] mLocationListeners = new LocationListener[]{
            new LocationListener(LocationManager.GPS_PROVIDER),
            new LocationListener(LocationManager.NETWORK_PROVIDER)
    };

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

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.e(TAG, "onStartCommand");
        super.onStartCommand(intent, flags, startId);
        return START_STICKY;
    }

    @Override
    public void onCreate() {
        Log.e(TAG, "onCreate");
        initializeLocationManager();
        try {
            mLocationManager.requestLocationUpdates(
                    LocationManager.NETWORK_PROVIDER, LOCATION_INTERVAL, LOCATION_DISTANCE,
                    mLocationListeners[1]);
        } catch (java.lang.SecurityException ex) {
            Log.i(TAG, "fail to request location update, ignore", ex);
        } catch (IllegalArgumentException ex) {
            Log.d(TAG, "network provider does not exist, " + ex.getMessage());
        }
        try {
            mLocationManager.requestLocationUpdates(
                    LocationManager.GPS_PROVIDER, LOCATION_INTERVAL, LOCATION_DISTANCE,
                    mLocationListeners[0]);
        } catch (java.lang.SecurityException ex) {
            Log.i(TAG, "fail to request location update, ignore", ex);
        } catch (IllegalArgumentException ex) {
            Log.d(TAG, "gps provider does not exist " + ex.getMessage());
        }
    }

    @Override
    public void onDestroy() {
        Log.e(TAG, "onDestroy");
        super.onDestroy();
        if (mLocationManager != null) {
            for (int i = 0; i < mLocationListeners.length; i++) {
                try {
                    mLocationManager.removeUpdates(mLocationListeners[i]);
                } catch (Exception ex) {
                    Log.i(TAG, "fail to remove location listners, ignore", ex);
                }
            }
        }
    }

    private void initializeLocationManager() {
        Log.e(TAG, "initializeLocationManager");
        if (mLocationManager == null) {
            mLocationManager = (LocationManager) getApplicationContext().getSystemService(Context.LOCATION_SERVICE);
        }
    }
}

我知道,它在辅助项目上运行良好,但是由于某些原因,我与某些人一起工作,所以总是遇到异常:java.lang.RuntimeException:无法在未调用Looper.prepare()的线程内创建处理程序我看过代码,但看不到它在特殊线程上工作,就像在您的代码上一样,该服务在具有指定名称的进程上运行。
Android开发人员

也许是因为该服务具有自己的进程,并且当您以后打开应用程序时,它使用了不同的线程,这不是原始线程吗?
android开发人员

1
顺便说一句,是否必须为“ onDestroy”方法调用“ removeUpdates”?我认为删除它不会导致内存泄漏,不是吗?
Android开发人员

afaik,如果不从locationManager中删除侦听器,则位置管理器将继续使用对侦听器对象的引用,因此GC将无法释放它们(您的侦听器)。
alex.veprik 2012年

1
@Hissain正确。我是很久以前写的,实际上我不知道我在这里想什么。但是,您应该避免在服务/活动中创建匿名处理程序,因为这可能会导致内存泄漏(当然,除非您小心谨慎)。这也是Lint会警告您的原因。
Android开发人员

23
public class GPSService extends Service implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, com.google.android.gms.location.LocationListener {
    private LocationRequest mLocationRequest;
    private GoogleApiClient mGoogleApiClient;
    private static final String LOGSERVICE = "#######";

    @Override
    public void onCreate() {
        super.onCreate();
        buildGoogleApiClient();
        Log.i(LOGSERVICE, "onCreate");

    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i(LOGSERVICE, "onStartCommand");

        if (!mGoogleApiClient.isConnected())
            mGoogleApiClient.connect();
        return START_STICKY;
    }


    @Override
    public void onConnected(Bundle bundle) {
        Log.i(LOGSERVICE, "onConnected" + bundle);

        Location l = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
        if (l != null) {
            Log.i(LOGSERVICE, "lat " + l.getLatitude());
            Log.i(LOGSERVICE, "lng " + l.getLongitude());

        }

        startLocationUpdate();
    }

    @Override
    public void onConnectionSuspended(int i) {
        Log.i(LOGSERVICE, "onConnectionSuspended " + i);

    }

    @Override
    public void onLocationChanged(Location location) {
        Log.i(LOGSERVICE, "lat " + location.getLatitude());
        Log.i(LOGSERVICE, "lng " + location.getLongitude());
        LatLng mLocation = (new LatLng(location.getLatitude(), location.getLongitude()));
        EventBus.getDefault().post(mLocation);

    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.i(LOGSERVICE, "onDestroy - Estou sendo destruido ");

    }

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

    @Override
    public void onConnectionFailed(ConnectionResult connectionResult) {
        Log.i(LOGSERVICE, "onConnectionFailed ");

    }

    private void initLocationRequest() {
        mLocationRequest = new LocationRequest();
        mLocationRequest.setInterval(5000);
        mLocationRequest.setFastestInterval(2000);
        mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);

    }

    private void startLocationUpdate() {
        initLocationRequest();

        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            // TODO: Consider calling
            //    ActivityCompat#requestPermissions
            // here to request the missing permissions, and then overriding
            //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
            //                                          int[] grantResults)
            // to handle the case where the user grants the permission. See the documentation
            // for ActivityCompat#requestPermissions for more details.
            return;
        }
        LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
    }

    private void stopLocationUpdate() {
        LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);

    }

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

}

我正在使用lib EventBus发送该位置以进行查看。-> EventBus.getDefault()。post(mLocation)
vrbsm

它不是在后台运行。我正在使用Red MI note3。当应用程序打开时,它可以正常工作,但是当我终止应用程序时,它不能作为服务工作。
Darpan Pathak

1
如何在这里处理权限和位置设置?
humble_wolf

@DarpanPathak这是与小米有关的问题。您必须在应用程序的手机设置中添加权限
ViT-Vetal-'Nov

你好。我遇到的问题是找不到LocationRequest。请帮助
-kemdo

13

从M-到Android“ O”-8,所有这些答案均不起作用。由于推土机模式限制了服务,因此任何服务或任何需要在后台进行离散操作的后台操作都将无法运行。

因此,方法是通过BroadCastReciever监听系统FusedLocationApiClient,该系统始终监听位置并即使在打ze模式下也可以工作。

发布链接将毫无意义,请使用广播接收器搜索FusedLocation。

谢谢


使用Services或IntentServices在后台询问位置将导致每小时进行一次新更新。适用于Android 8+
UserK

前台服务与后台进程没有相同的问题。我使用前台服务,它处理gps更新。
路加·杜平

2

只是作为补充,我以这种方式实施,通常在我的Service班上工作

为我服务

@Override
public void onCreate()
{
    mHandler = new Handler(Looper.getMainLooper());
    mHandler.post(this);
    super.onCreate();
}

@Override
public void onDestroy() 
{
    mHandler.removeCallbacks(this);     
    super.onDestroy();
}

@Override
public void run()
{
    InciarGPSTracker();
}

0

这是我的解决方案

步骤1在清单中注册服务

<receiver
    android:name=".MySMSBroadcastReceiver"
    android:exported="true">
    <intent-filter>
        <action android:name="com.google.android.gms.auth.api.phone.SMS_RETRIEVED" />
    </intent-filter>
</receiver>

Step2服务代码

public class FusedLocationService extends Service {

    private String mLastUpdateTime = null;

    // bunch of location related apis
    private FusedLocationProviderClient mFusedLocationClient;
    private SettingsClient mSettingsClient;
    private LocationRequest mLocationRequest;
    private LocationSettingsRequest mLocationSettingsRequest;
    private LocationCallback mLocationCallback;
    private Location lastLocation;

    // location updates interval - 10sec
    private static final long UPDATE_INTERVAL_IN_MILLISECONDS = 5000;

    // fastest updates interval - 5 sec
    // location updates will be received if another app is requesting the locations
    // than your app can handle
    private static final long FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS = 500;
    private DatabaseReference locationRef;
    private int notificationBuilder = 0;
    private boolean isInitRef;

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.log("LOCATION GET DURATION", "start in service");
        init();
        return START_STICKY;
    }

    /**
     * Initilize Location Apis
     * Create Builder if Share location true
     */
    private void init() {
        mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
        mSettingsClient = LocationServices.getSettingsClient(this);
        mLocationCallback = new LocationCallback() {
            @Override
            public void onLocationResult(LocationResult locationResult) {
                super.onLocationResult(locationResult);
                receiveLocation(locationResult);
            }
        };

        mLocationRequest = new LocationRequest();
        mLocationRequest.setInterval(UPDATE_INTERVAL_IN_MILLISECONDS);
        mLocationRequest.setFastestInterval(FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS);
        mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);

        LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder();
        builder.addLocationRequest(mLocationRequest);
        mLocationSettingsRequest = builder.build();
        startLocationUpdates();
    }

    /**
     * Request Location Update
     */
    @SuppressLint("MissingPermission")
    private void startLocationUpdates() {
        mSettingsClient
                .checkLocationSettings(mLocationSettingsRequest)
                .addOnSuccessListener(locationSettingsResponse -> {
                    Log.log(TAG, "All location settings are satisfied. No MissingPermission");

                    //noinspection MissingPermission
                    mFusedLocationClient.requestLocationUpdates(mLocationRequest, mLocationCallback, Looper.myLooper());
                })
                .addOnFailureListener(e -> {
                    int statusCode = ((ApiException) e).getStatusCode();
                    switch (statusCode) {
                        case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
                            Log.loge("Location settings are not satisfied. Attempting to upgrade " + "location settings ");
                            break;
                        case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE:
                            Log.loge("Location settings are inadequate, and cannot be " + "fixed here. Fix in Settings.");
                    }
                });
    }

    /**
     * onLocationResult
     * on Receive Location  share to other activity and save if save true
     *
     * @param locationResult
     */
    private void receiveLocation(LocationResult locationResult) {
        lastLocation = locationResult.getLastLocation();

        LocationInstance.getInstance().changeState(lastLocation);

        saveLocation();
    }

    private void saveLocation() {
        String saveLocation = getsaveLocationStatus(this);

        if (saveLocation.equalsIgnoreCase("true") && notificationBuilder == 0) {
            notificationBuilder();
            notificationBuilder = 1;
        } else if (saveLocation.equalsIgnoreCase("false") && notificationBuilder == 1) {
            ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).cancel(1);
            notificationBuilder = 0;
        }

        Log.logd("receiveLocation : Share :- " + saveLocation + ", [Lat " + lastLocation.getLatitude() + ", Lng" + lastLocation.getLongitude() + "], Time :- " + mLastUpdateTime);

        if (saveLocation.equalsIgnoreCase("true") || getPreviousMin() < getCurrentMin()) {
            setLatLng(this, lastLocation);

            mLastUpdateTime = DateFormat.getTimeInstance().format(new Date());

            if (isOnline(this) && !getUserId(this).equalsIgnoreCase("")) {
                if (!isInitRef) {
                    locationRef = getFirebaseInstance().child(getUserId(this)).child("location");
                    isInitRef = true;
                }
                if (isInitRef) {
                    locationRef.setValue(new LocationModel(lastLocation.getLatitude(), lastLocation.getLongitude(), mLastUpdateTime));
                }
            }
        }
    }

    private int getPreviousMin() {
        int previous_min = 0;
        if (mLastUpdateTime != null) {
            String[] pretime = mLastUpdateTime.split(":");
            previous_min = Integer.parseInt(pretime[1].trim()) + 1;

            if (previous_min > 59) {
                previous_min = 0;
            }
        }
        return previous_min;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        stopLocationUpdates();
    }

    /**
     * Remove Location Update
     */
    public void stopLocationUpdates() {
        mFusedLocationClient
                .removeLocationUpdates(mLocationCallback)
                .addOnCompleteListener(task -> Log.logd("stopLocationUpdates : "));
    }

    private void notificationBuilder() {
        if (Build.VERSION.SDK_INT >= 26) {
            String CHANNEL_ID = "my_channel_01";
            NotificationChannel channel = new NotificationChannel(CHANNEL_ID, "Channel human readable title",
                    NotificationManager.IMPORTANCE_DEFAULT);

            ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel);

            Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
                    .setContentTitle("")
                    .setContentText("").build();

            startForeground(1, notification);
        }
    }

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

第三步

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

步骤4

implementation 'com.google.android.gms:play-services-location:16.0.0'


-23

好的,我通过在服务的onCreate上创建一个处理程序并通过那里调用gps函数解决了它。

代码很简单:

final handler=new Handler(Looper.getMainLooper()); 

然后,我要求post在UI上强制运行。


80
请发布代码。它确实可以帮助在寻找答案时偶然发现此页面的其他人。
Razgriz

1
这已经在很久以前得到了回答,但是我将尝试解释:由于GPS处理需要在UI线程上运行,因此请在UI线程上运行的服务上的任何方法上创建Handler对象(例如onCreate),然后将其设置为服务中的字段。然后,只要您需要使用GPS操作,只需在此Handler实例上调用post(Runnable)。
Android开发者

@delive对不起。我不能。很久以前,我没有代码。只要有一个处理程序并使用它。
Android开发人员

@Razgriz我输入了我记得写过的代码。但是我不记得所有的细节... :(
android开发人员
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.