即使我的应用程序被杀死,如何保持我的定位服务运行

时间:2019-03-26 10:56:11

标签: android android-service

我有这个应用程序,当他(她)登录后,该应用程序会将用户的位置发送到服务器。我希望即使在应用程序被终止的情况下也能保持服务正常运行

我尝试了堆栈溢出中提供的一些方法,该服务似乎在android棉花糖下运行,但似乎在高于它的android版本中不运行

这是我的服务


public class MyLocationService extends Service {
    public static final int TWO_MINUTES = 120000; // 120 seconds
    public static Boolean isRunning = false;
    double sendlat,sendlng;
    public LocationManager mLocationManager;
    public LocationUpdaterListener mLocationListener;
    public Location previousBestLocation = null;

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

    @Override
    public void onCreate() {
        mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
        mLocationListener = new LocationUpdaterListener();
        super.onCreate();
    }

    Handler mHandler = new Handler();
    Runnable mHandlerTask = new Runnable() {
        @Override
        public void run() {
            if (!isRunning) {
                startListening();
            }
            mHandler.postDelayed(mHandlerTask, TWO_MINUTES);
        }
    };

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

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

    private void startListening() {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
                || ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
            if (mLocationManager.getAllProviders().contains(LocationManager.NETWORK_PROVIDER))
                mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, mLocationListener);

            if (mLocationManager.getAllProviders().contains(LocationManager.GPS_PROVIDER))
                mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, mLocationListener);
        }
        isRunning = true;
    }

    private void stopListening() {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
                || ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
            mLocationManager.removeUpdates(mLocationListener);
        }
        isRunning = false;
    }

    public class LocationUpdaterListener implements LocationListener {
        @Override
        public void onLocationChanged(Location location) {
            if (isBetterLocation(location, previousBestLocation)) {
                previousBestLocation = location;
                try {
                    if (previousBestLocation != null) {
                        sendlat = previousBestLocation.getLatitude();
                        sendlng = previousBestLocation.getLongitude();

                    } else {
                        CommonObjects.showToast(getApplicationContext(),"Couldn't get the location. Make sure location is enabled on the device");
                    }
                    String sLat = String.valueOf(sendlat);
                    String sLnt = String.valueOf(sendlng);
                    DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
                    Date date = new Date();
                    showNotificationWithImage(sLat,sLnt);
                    Log.i("lat,long",sLat+","+sLnt+","+dateFormat.format(date));
                    if(CommonObjects.isNetworkAvailable(getApplicationContext())) {
                        CommonObjects.sendTechLoc(getApplicationContext(),sLat,sLnt);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

        @Override
        public void onProviderDisabled(String provider) {
            stopListening();
        }

        @Override
        public void onProviderEnabled(String provider) {
        }

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

    protected boolean isBetterLocation(Location location, Location currentBestLocation) {
        if (currentBestLocation == null) {
            return true;
        }
        long timeDelta = location.getTime() - currentBestLocation.getTime();
        boolean isSignificantlyNewer = timeDelta > TWO_MINUTES;
        boolean isSignificantlyOlder = timeDelta < -TWO_MINUTES;
        boolean isNewer = timeDelta > 0;

        if (isSignificantlyNewer) {
            return true;
        } else if (isSignificantlyOlder) {
            return false;
        }
        int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation.getAccuracy());
        boolean isLessAccurate = accuracyDelta > 0;
        boolean isMoreAccurate = accuracyDelta < 0;
        boolean isSignificantlyLessAccurate = accuracyDelta > 200;

        boolean isFromSameProvider = isSameProvider(location.getProvider(), currentBestLocation.getProvider());

        if (isMoreAccurate) {
            return true;
        } else if (isNewer && !isLessAccurate) {
            return true;
        } else if (isNewer && !isSignificantlyLessAccurate && isFromSameProvider) {
            return true;
        }
        return false;
    }

    private boolean isSameProvider(String provider1, String provider2) {
        if (provider1 == null) {
            return provider2 == null;
        }
        return provider1.equals(provider2);

    }

    private void showNotificationWithImage(String la,String lo) {
        Intent intent = new Intent(this, HomeActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent,
                PendingIntent.FLAG_ONE_SHOT);
        //Bitmap largeIcon = BitmapFactory.decodeResource(getResources(), R.mipmap.mapimg);
        NotificationCompat.InboxStyle style = new NotificationCompat.InboxStyle();
        style.setSummaryText("");
        Uri defaultSound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
        NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(getApplicationContext())
                .setSmallIcon(getNotificationIcon())
                .setColor(getResources().getColor(R.color.app_color))
                .setContentTitle("Mr Fix"+la+lo)
                .setStyle(style)
                .setVisibility(VISIBILITY_PUBLIC);
        // Since android Oreo notification channel is needed.
        String channelId = getString(R.string.default_notification_channel_id);
        NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel(channelId,
                    "Mr Fix",
                    NotificationManager.IMPORTANCE_HIGH);
            manager.createNotificationChannel(channel);
        }
        manager.notify(0 , notificationBuilder.build());
    }

    private void sendNotification(String la,String lo) {
        Intent intent = new Intent(this, HomeActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent,
                PendingIntent.FLAG_ONE_SHOT);

        String channelId = getString(R.string.default_notification_channel_id);
        Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
        NotificationCompat.Builder notificationBuilder =
                new NotificationCompat.Builder(this, channelId)
                        .setSmallIcon(R.drawable.ic_stat_name)
                        .setContentTitle(getString(R.string.app_name))
                        .setCategory(NotificationCompat.CATEGORY_SERVICE)
                        .setContentText(la+","+lo)
                        .setSound(defaultSoundUri)
                        .setContentIntent(pendingIntent);

        NotificationManager notificationManager =
                (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

        // Since android Oreo notification channel is needed.
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel(channelId,
                    "Channel human readable title",
                    NotificationManager.IMPORTANCE_DEFAULT);
            notificationManager.createNotificationChannel(channel);
        }

        notificationManager.notify(0 /* ID of notification */, notificationBuilder.build());
    }

    private int getNotificationIcon() {
        boolean useWhiteIcon = (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP);
        return useWhiteIcon ? R.drawable.ic_stat_name: R.mipmap.ic_launcher_round;
    }
}

这是我的电话

Intent i=new Intent(HomeActivity.this, MyLocationService.class);
        Log.i("Service","Service Started....");
        startService(i);

2 个答案:

答案 0 :(得分:1)

与@ sven-menschner所说的相反,我认为一个未绑定的服务正是您需要的,因为绑定的服务受绑定/取消绑定机制的约束,这会杀死您的服务。那就是我要做的:

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

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

注意:这里有一个已经实施的动作列表,但是您可以为启动服务定义自己的动作。只需创建一个singleton类并定义字符串,即可为其分配一个唯一的String。设置为true的“启用”仅用于实例化服务,而设置为true的导出设置仅在您需要其他将意图发送给您的服务的应用程序的情况下使用。如果没有,您可以安全地将最后一个设置为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);

    ...
  }
}

最后一步是定义服务初始化。注意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也正在运行(这就是为什么您不想要绑定服务的原因),因为Activity和Services具有不同的生命周期。为了帮助该Service运行很长时间,请尝试使堆保持尽可能低,以免Android SO杀死它。

另一个声明:您无法测试服务是否仍在运行以杀死DVM。如果您杀死了DVM,您将杀死所有内容,因此也将杀死服务。

答案 1 :(得分:0)