Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/android/221.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Android 即使我的应用程序被关闭,我如何保持我的位置服务运行_Android_Android Service - Fatal编程技术网

Android 即使我的应用程序被关闭,我如何保持我的位置服务运行

Android 即使我的应用程序被关闭,我如何保持我的位置服务运行,android,android-service,Android,Android Service,我有一个应用程序,它在用户登录时将用户的位置发送到服务器。我希望即使应用程序被终止,也能让服务继续运行 我尝试了stack overflow中给出的一些方法,该服务似乎在android marshmallow下面运行,但在android上面的版本中似乎不起作用 这是我的服务 public class MyLocationService extends Service { public static final int TWO_MINUTES = 120000; // 120 secon

我有一个应用程序,它在用户登录时将用户的位置发送到服务器。我希望即使应用程序被终止,也能让服务继续运行

我尝试了stack overflow中给出的一些方法,该服务似乎在android marshmallow下面运行,但在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;
    }
}

与@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>
最后一步是定义服务初始化。注意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);
  }
}
现在,即使您关闭了主要活动,您的服务也将运行。只剩下一步:为了帮助您的服务不被完成,请将其作为前台服务运行(在您的服务中这样做)。这将基本上在状态栏中创建一个通知图标。这并不意味着您的主要活动也在运行(这就是为什么您不希望绑定服务),因为活动和服务具有不同的生命周期。为了帮助该服务运行这么长时间,请尝试将堆保持在尽可能低的水平,以避免Android将其扼杀


还有一点值得称赞:您无法测试该服务是否仍在运行,从而终止DVM。如果您杀死DVM,您将杀死一切,因此也将杀死服务。

您可以使用前台服务而不是服务

或者您可以使用WorkManager而不是服务


只是反复检查,不是解决办法。你的应用程序管理器允许你的应用程序在后台运行吗?即使我有同样的问题,我的问题是应用程序管理器。
public class MainActivity extends Activity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

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

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