【问题标题】:Keep location service alive when the app is closed关闭应用程序时保持位置服务处于活动状态
【发布时间】:2014-01-29 19:53:59
【问题描述】:

我有一项服务,可在用户更改其位置时发送通知。该服务运行良好,但是当服务也关闭时用户关闭应用程序就会出现问题。

即使应用程序关闭,我如何才能使服务仍然有效?

我的服务是:

public class LocationService extends Service implements LocationListener {
    public final static int MINUTE = 1000 * 60;


    boolean isGPSEnabled = false;
    boolean isNetworkEnabled = false;
    boolean canGetLocation = false;

    Location location; // location
    double latitude = 0; // latitude
    double longitude = 0; // longitude
    String provider;

    // The minimum distance to change Updates in meters
    private static final long MIN_DISTANCE_CHANGE_FOR_UPDATES = 10;

    // The minimum time between updates in milliseconds
    private static final long MIN_TIME_BW_UPDATES = 1 * MINUTE;

    // Declaring a Location Manager
    protected LocationManager locationManager;



    // Binder given to clients
    private final IBinder mBinder = new LocalBinder();

    /**
     * Class used for the client Binder. Because we know this service always
     * runs in the same process as its clients, we don't need to deal with IPC.
     */
    public class LocalBinder extends Binder {
        public LocationService getService() {
            // Return this instance of LocalService so clients can call public
            // methods
            return LocationService.this;
        }
    }

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

    public Location getLocation() {
        try {
            locationManager = (LocationManager) getBaseContext().getSystemService(LOCATION_SERVICE);

            // getting GPS status
            isGPSEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);

            // getting network status
            isNetworkEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);

            if (!isGPSEnabled && !isNetworkEnabled) {
                // no network provider is enabled. DEFAULT COORDINATES


            } else {
                this.canGetLocation = true;
                if (isNetworkEnabled) {
                    locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, MIN_TIME_BW_UPDATES, MIN_DISTANCE_CHANGE_FOR_UPDATES,
                            this);
                    Log.d("Network", "Network Enabled");
                    if (locationManager != null) {
                        location = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
                        if (location != null) {
                            latitude = location.getLatitude();
                            longitude = location.getLongitude();
                        }
                    }
                }
                // if GPS Enabled get lat/long using GPS Services
                if (isGPSEnabled) {
                    if (location == null) {
                        locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, MIN_TIME_BW_UPDATES, MIN_DISTANCE_CHANGE_FOR_UPDATES,
                                this);
                        Log.d("GPS", "GPS Enabled");
                        if (locationManager != null) {
                            location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
                            if (location != null) {
                                latitude = location.getLatitude();
                                longitude = location.getLongitude();
                            }
                        }
                    }
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        Log.i("LOCATION", "Latitude: " + latitude + "- Longitude: " + longitude);


        return location;
    }

    @Override
    public void onLocationChanged(Location arg0) {

        NotificationManager mNotificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
        Intent intent = null;

        intent = new Intent(this, CompleteSurveyActivity.class);

        PendingIntent contentIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);

        NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this).setSmallIcon(R.drawable.ic_launcher).setAutoCancel(true)
                .setContentIntent(contentIntent).setContentTitle(this.getString(R.string.app_name)).setContentText("text");

        // mBuilder.setContentIntent(contentIntent);
        mNotificationManager.notify((int) System.currentTimeMillis() % Integer.MAX_VALUE, mBuilder.build());

        double longitude = location.getLongitude();
        double latitude = location.getLatitude();

        Log.i("LOCATION", "Latitude: " + latitude + "- Longitude: " + longitude);

    }

    @Override
    public void onProviderDisabled(String arg0) {
    }

    @Override
    public void onProviderEnabled(String arg0) {
    }

    @Override
    public void onStatusChanged(String arg0, int arg1, Bundle arg2) {
    }

}

我从这里打过电话:

public class MyActivity extends Activity {

    LocationService mService;
    boolean mBound = false;


    private ServiceConnection mConnection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName className, IBinder service) {
            // We've bound to LocalService, cast the IBinder and get
            // LocalService instance
            LocalBinder binder = (LocalBinder) service;
            mService = binder.getService();
            mBound = true;

        }

        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            mBound = false;
        }
    };

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

        exampleButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                initService();
            }
        });

    }

    public void initService() {
        if (mBound)
            mService.getLocation();
    }

    @Override
    protected void onStart() {
        super.onStart();
        // Bind to LocalService
        Intent intent = new Intent(this, LocationService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        // Unbind from the service
        if (mBound) {
            unbindService(mConnection);
            mBound = false;
        }
    }
}

Manifest.xml

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

【问题讨论】:

  • 可以给我们提供无限服务的新代码吗? @sany

标签: android location android-service


【解决方案1】:

与@sven-menschner 所说的相反,我认为未绑定的Service 正是您所需要的,因为绑定的服务受制于绑定/取消绑定机制,这会终止您的服务。这就是我会做的:

在您的清单文件中,定义您的服务:

<service
  android:name=".YourService"
  android:enabled="true"
  android:exported="true"
  android:description="@string/my_service_desc"
  android:label="@string/my_infinite_service">
  <intent-filter>
    <action android:name="com.yourproject.name.LONGRUNSERVICE" />
  </intent-filter>
</service>

注意:有一个已经实现的操作列表,但您可以定义自己的操作以启动服务。只需创建一个单例类并定义为它们分配一个应该是唯一的String 的字符串。 “启用”设置为 true 只是为了实例化服务,而导出设置为 true 只是在您需要其他应用程序向您的Service 发送意图的情况下。如果没有,您可以放心地将 last 设置为 false。

以下步骤将从您的活动开始您的服务。这可以通过以下方式轻松完成:

public class MainActivity extends Activity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    Intent servIntent = new Intent("com.yourproject.name.LONGRUNSERVICE");
    startService(servIntent);

    ...
  }
}

最后一步是定义Service 初始化。密切关注onBind() 方法。由于您不希望它被绑定,因此只需返回null。应该是这样的:

public class MyService extends Service {
  @Override
  public IBinder onBind(Intent intent) {
    // This won't be a bound service, so simply return null
    return null;
  }

  @Override
  public void onCreate() {
    // This will be called when your Service is created for the first time
    // Just do any operations you need in this method.
  }

  @Override
  public int onStartCommand(Intent intent, int flags, int startId) {
    return super.onStartCommand(intent, flags, startId);
  }
}

现在,即使您关闭主 Activity,您的服务也会运行。只剩下一步:为了帮助您的Service 未完成,请将其作为前台服务运行(在您的服务中执行此操作)。这基本上会在状态栏中创建一个通知图标。这并不意味着您的主要活动也在运行(这就是您不想要绑定服务的原因),因为活动和服务具有不同的生命周期。为了帮助该服务运行这么长时间,请尽量保持您的堆尽可能低,以免 Android SO 杀死它。

再一次声明:您无法测试服务是否仍在运行以杀死 DVM。如果你杀死 DVM,你会杀死一切,因此也会杀死服务。

【讨论】:

  • 哦不!我的意思是正确的,但写错了。当然,启动(或未绑定)的服务是正确的。只是在第一句话中弄乱了顺序......但是可以从我的其余答案中弄清楚我的意图;)
  • 我使用前台服务和警报管理器来获取LastLocation 并设置间隔。问题:当我关闭或终止应用程序时,定位服务已关闭。 2天后终于发现问题是因为我的应用程序目标是SDK 30或Android 11。解决方案:需要在AndroidManifest文件中添加ACCESS_BACKGROUND_LOCATION权限,并且需要去应用程序设置->权限->位置->“一直允许”。希望可以帮助面临同样问题的人。 ^^
【解决方案2】:

有两种 Android 服务:启动和绑定。您需要使用第一个(启动服务)。 The documentation 展示了如何使用它,下面有一个很好的生命周期图。

您需要先调用startService(),而不是使用bindService() 一步启动和绑定服务。但是startService() 不会帮助您从奥利奥开始。您需要从那里使用startForegroundService()。然后它会一直运行,直到您停止它,即使应用程序已关闭。

如果您希望 Android OS 在杀死它时再次选择您的服务,请启动 Sticky Service

【讨论】:

    猜你喜欢
    • 2014-11-14
    • 1970-01-01
    • 2015-11-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多