【问题标题】:Get GPS location via a service in Android通过 Android 中的服务获取 GPS 位置
【发布时间】:2012-02-08 08:41:07
【问题描述】:

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

使用活动,很容易获得 GPS 位置,但是当我通过服务进行时,我遇到了一个问题,因为它似乎只适用于 looper 线程(或类似的东西)。

当我在互联网上搜索解决方案时,我发现很多人都遇到了同样的问题,但我找不到有效的解决方案。有人说你需要使用 prepare->loop->quit ,有人说你必须使用 handlerThread ,但我仍然不知道如何以正确的方式做这些事情。

【问题讨论】:

  • loopy 线程:D 你的意思是 looper 线程。 Looper 线程是具有请求队列机制的特殊线程。主线程或 UI 线程是 Looper 线程。至于服务的问题,你是在主线程上调用 requestLocationUpdates 吗?或者你是在不同的线程中做的吗?因为活动和服务是通过主线程运行的,主线程是一个looper线程,所以调用requestLocationUpdates必须是一样的。您能否指出其他提到您正在谈论的这个问题的帖子?

标签: android service gps location


【解决方案1】:

我不明白在服务中实现位置监听功能到底有什么问题。它看起来与您在 Activity 中所做的非常相似。 只需定义一个位置监听器并注册位置更新。 您可以参考以下代码作为示例:

清单文件:

<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: Can't create handler inside thread that没有调用 Looper.prepare() 我查看了代码,但看不到它在特殊线程上工作,就像在您的代码上一样,服务在具有指定名称的进程上运行。
  • 可能是因为服务有自己的进程,后来打开应用程序时使用了不同的线程,不是原来的那个?
  • 顺便说一句,是否必须为“onDestroy”方法调用“removeUpdates”?我认为删除它不会导致内存泄漏,不是吗?
  • afaik,如果你不从 locationManager 中移除监听器,你的监听器对象的引用将仍然被 LocationManager 使用,因此 GC 将无法释放它们(你的监听器)。跨度>
  • @Hissain 正确。我很久以前写的,实际上我不知道我在想什么。但是,您应该避免在服务/活动中创建匿名处理程序,因为它可能会导致内存泄漏(当然,除非您很小心)。这也是 Lint 会警告您的原因。
【解决方案2】:
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)
  • 它没有在后台运行。我正在使用 Red MI note 3。当应用程序打开时它工作正常,但当我终止应用程序时它不能作为服务工作。
  • 这里如何处理权限和位置设置??
  • @DarpanPathak 这是与小米相关的问题。您必须在应用的手机设置中添加权限
  • 你好。我遇到的问题是找不到 LocationRequest。请帮助
【解决方案3】:

所有这些答案在 M 到 Android“O” - 8 中都不起作用,由于推土机模式限制了服务 - 任何需要在后台离散事物的服务或任何后台操作都将不再能够运行.

因此,该方法将通过 BroadCastReciever 监听系统 FusedLocationApiClient,该系统始终监听位置,即使在打盹模式下也能正常工作。

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

谢谢

【讨论】:

  • 使用服务或 IntentServices 在后台请求位置将导致每小时更新一次。这适用于 Android 8+
  • 前台服务与后台进程没有相同的问题。我使用前台服务,它处理 gps 更新。
【解决方案4】:

只是作为补充,我以这种方式实现并且通常在我的服务类中工作

为我服务

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

【讨论】:

    【解决方案5】:

    查看 Google Play 位置示例GitHub repository

    Location Updates using a Foreground Service:使用绑定并启动的前台服务获取有关设备位置的更新。

    Location Updates using a PendingIntent:使用PendingIntent 获取有关设备位置的更新。示例显示了使用 IntentServiceBroadcastReceiver 的实现。

    【讨论】:

    • 链接被交换了。
    【解决方案6】:

    这是我的解决方案:

    第一步:在清单中注册服务

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

    第 2 步: 服务代码

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

    第 3 步:清单

    <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 步: Gradle

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

    【讨论】:

      【解决方案7】:

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

      代码就这么简单:

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

      然后为了强制在 UI 上运行,我调用了post

      【讨论】:

      • 请张贴代码。它真的会帮助其他在寻找答案时偶然发现此页面的人。
      • 这是很久以前回答的,但我将尝试解释:由于 GPS 处理需要在 UI 线程上运行,因此在运行的服务上的任何方法上创建一个 Handler 对象UI 线程(如 onCreate)并将其设置为服务中的字段。比,任何时候你需要使用 GPS 操作,只需在这个 Handler 实例上调用 post(Runnable) 。
      • @delive 对不起。我不能。那是很久以前的事了,我没有代码。只要有一个处理程序并使用它。
      • @Razgriz 我已经把我记得我写过的代码放在了上面。但我不记得所有的细节...... :(
      猜你喜欢
      • 2013-08-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多