2017-06-14 54 views
2

我正在開發一個步數計數器應用程序,我計算步數,並在午夜更新到服務器。我有一個服務不斷運行來完成所有這些。服務在某段時間後停止工作。需要繼續工作

這裏是我的服務:

public class StepCounterService extends Service implements SensorEventListener, StepListener, WebServiceInterface, GoogleApiClient.ConnectionCallbacks, 
     GoogleApiClient.OnConnectionFailedListener, LocationListener { 

    private static final int SERVICE_ID = 27; 
    private static final int SEND_SESSION_REQUEST_CODE = 1; 
    private static final int SEND_ACTIVITY_REQUEST_CODE = 2; 
    private static final int MAIN_NOTIFICATION_ID = 3; 
    private static final int SECONDARY_NOTIFICATION_ID = 4; 
    private LocalBroadcastManager broadcaster; 

    static final public String STEP_INCREMENT = "com.app.STEP_INCREMENTED"; 
    static final public String SESSION_COMPLETE = "com.app.SESSION_COMPLETE"; 
    static final public String ACTIVITY_COMPLETE = "com.app.ACTIVITY_COMPLETE"; 
    static final public String STEP_INCREMENT_KEY = "step_count"; 

    Session session; 

    private StepDetector stepDetector; 
    private SensorManager sensorManager; 
    private Sensor sensor; 
    private int numberOfSteps = 0; 

    private GoogleApiClient googleApiClient; 
    private LocationRequest mLocationRequest; 
    double currentLatitude; 
    double currentLongitude; 
    ArrayList<String> arrayListLocations; 
    private AlarmManager sendActivityAlarmManager; 
    private PendingIntent activityAlarmIntent; 

    private NotificationManager notificationManager; 
    RemoteViews contentView; 
    PowerManager.WakeLock wl; 
    private String TAG = "Wake Lock Tag"; 
    private int SET_LAST_LOCATION_REQUEST_CODE = 5; 


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

    @Override 
    public int onStartCommand(Intent intent, int flags, int startId) { 
     // TODO Auto-generated method stub 
     return START_STICKY; 
    } 

    @Override 
    public void onTaskRemoved(Intent rootIntent) { 
     // TODO Auto-generated method stub 
     System.out.println("---- In onTaskRemoved Function"); 
     restartKilledService(); 
    } 

    @Override 
    public void onDestroy() { 
     System.out.println("---- In onDestroy Function"); 
     if (wl != null) { 
      wl.release(); 
     } 
     super.onDestroy(); 
     restartKilledService(); 
    } 

    void restartKilledService() { 

     System.out.println("---- In restartKilledService Function"); 

     Intent restartService = new Intent(getApplicationContext(), StepCounterService.class); 
     restartService.setPackage(getPackageName()); 
     PendingIntent restartServicePI = PendingIntent.getService(getApplicationContext(), StepCounterService.SERVICE_ID, restartService, PendingIntent.FLAG_CANCEL_CURRENT); 

     AlarmManager alarmService = (AlarmManager) getApplicationContext().getSystemService(Context.ALARM_SERVICE); 
     alarmService.set(AlarmManager.RTC_WAKEUP, SystemClock.elapsedRealtime() + 100, restartServicePI); 
    } 

    @Override 
    public void onCreate() { 
     // TODO Auto-generated method stub 

     PowerManager pm = (PowerManager) getApplicationContext().getSystemService(getApplicationContext().POWER_SERVICE); 
     wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG); 
     wl.acquire(); 

     super.onCreate(); 

     session = new Session(this); 
     buildGoogleApiClient(); 

     broadcaster = LocalBroadcastManager.getInstance(this); 

     sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE); 
     sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); 
     stepDetector = new StepDetector(); 
     stepDetector.registerListener(StepCounterService.this); 

     Context ctx = getApplicationContext(); 
     Calendar cal = Calendar.getInstance(); 
     AlarmManager am = (AlarmManager) ctx.getSystemService(Context.ALARM_SERVICE); 
     long interval = 1000 * 60 * 5; // 5 minutes in milliseconds 
     Intent serviceIntent = new Intent(ctx, StepCounterService.class); 

     PendingIntent servicePendingIntent = PendingIntent.getService(ctx, StepCounterService.SERVICE_ID, serviceIntent, PendingIntent.FLAG_CANCEL_CURRENT); 
     am.setRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), interval, servicePendingIntent); 

     sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_FASTEST); 

     notificationManager = (NotificationManager) getSystemService(Activity.NOTIFICATION_SERVICE); 
     contentView = new RemoteViews(getPackageName(), R.layout.notification_layout); 
     contentView.setImageViewResource(R.id.image, R.drawable.notif_icon); 

     if (session.getUser() != null && !(session.getUser().getName().equals(""))) 
      updateMainNotification(session.getTodaySteps() + ""); 

     startAlarm(); 
    } 


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

     mLocationRequest = LocationRequest.create() 
       .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY) 
       .setInterval(10 * 1000)  // 10 seconds, in milliseconds 
       .setFastestInterval(1 * 1000); 
    } 

    @Override 
    public void onSensorChanged(SensorEvent event) { 
     if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) { 
      stepDetector.updateAccel(event.timestamp, event.values[0], event.values[1], event.values[2]); 
     } 
    } 

    @Override 
    public void step(long timeNs) { 
     numberOfSteps = session.getTodaySteps(); 
     numberOfSteps++; 

     sendStepIncrementBroadcast(numberOfSteps); 

     session.setTodaySteps(numberOfSteps); 

     if (session.getUser() != null && !(session.getUser().getName().equals(""))) 
      updateMainNotification(numberOfSteps + ""); 
     else { 
      try { 
       notificationManager.cancel(MAIN_NOTIFICATION_ID); 
      } catch (Exception e) { 

      } 
     } 
    } 

    public void sendStepIncrementBroadcast(int numberOfSteps) { 
     Intent intent = new Intent(STEP_INCREMENT); 
     intent.putExtra(STEP_INCREMENT_KEY, numberOfSteps); 
     broadcaster.sendBroadcast(intent); 
    } 

    @Override 
    public void onAccuracyChanged(Sensor sensor, int i) { 

    } 

    public float[] getDataFromSteps(int stepsCount) { 
     float caloriesCount = 0; 
     float creditsCount = 0; 
     try { 
      double adjustedWeight = Double.parseDouble(session.getUser().getWeight())/LinksAndKeys.weightAdjuster; 
      caloriesCount = Math.round(((adjustedWeight * LinksAndKeys.metValue)/LinksAndKeys.setPace) * (stepsCount/LinksAndKeys.stepsPerMile)); 
      caloriesCount = caloriesCount * 1.2f; 
      creditsCount = caloriesCount/25.4f; 
     } catch (Exception e) { 
      caloriesCount = 0; 
      creditsCount = 0; 
     } 

     float[] resultantFloat = {caloriesCount, creditsCount}; 
     return resultantFloat; 
    } 

    private void startAlarm() { 

     sendActivityAlarmManager = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE); 

     Intent intent = new Intent(this, ActivityCompleteReceiver.class); 
     activityAlarmIntent = PendingIntent.getBroadcast(this, 0, intent, 0); 

     Calendar calendar = Calendar.getInstance(); 
     calendar.setTimeInMillis(System.currentTimeMillis()); 
     calendar.set(Calendar.HOUR_OF_DAY, 23); 
     calendar.set(Calendar.MINUTE, 58); 
//  calendar.set(Calendar.HOUR_OF_DAY, generateRandomTime()[0]); 
//  calendar.set(Calendar.MINUTE, generateRandomTime()[1]); 

     if (System.currentTimeMillis() > calendar.getTimeInMillis()) { 
      calendar.add(Calendar.DAY_OF_YEAR, 1); 
     } 

     sendActivityAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), 
       AlarmManager.INTERVAL_DAY, activityAlarmIntent); 
    } 

    private void updateMainNotification(String stepsValue) { 

     String title = "Today: " + stepsValue + " steps - " + LinksAndKeys.decimalFormat.format(getDataFromSteps(session.getTodaySteps())[1]) + " FIMOs"; 
     String message = "Keep Walking and Keep Earning"; 

     contentView.setTextViewText(R.id.textViewTitle, title); 
     contentView.setTextViewText(R.id.textViewMessage, message); 

     Intent notificationIntent = new Intent(StepCounterService.this, SplashActivity.class); 
     notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP); 
     PendingIntent intent = PendingIntent.getActivity(StepCounterService.this, 0, notificationIntent, 0); 


     NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this) 
       .setSmallIcon(R.drawable.running_icon) 
       .setContent(contentView).setContentIntent(intent); 

     Notification notification = mBuilder.build(); 
     notification.flags |= Notification.FLAG_ONGOING_EVENT; 
//  notificationManager.notify(MAIN_NOTIFICATION_ID, notification); 
     startForeground(MAIN_NOTIFICATION_ID, notification); 
    } 

    private void updateReminderNotification() { 

     String title = "A gentle reminder"; 
     String message = "Its time to get on track"; 

     contentView.setTextViewText(R.id.textViewTitle, title); 
     contentView.setTextViewText(R.id.textViewMessage, message); 

     Intent notificationIntent = new Intent(StepCounterService.this, SplashActivity.class); 
     notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP); 
     PendingIntent intent = PendingIntent.getActivity(StepCounterService.this, 0, notificationIntent, 0); 


     NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this) 
       .setSmallIcon(R.drawable.running_icon) 
       .setContent(contentView).setContentIntent(intent); 

     Notification notification = mBuilder.build(); 
     notificationManager.notify(SECONDARY_NOTIFICATION_ID, notification); 
    } 

    private int[] generateRandomTime() { 
     int[] timeIntegers = new int[2]; 

     final Random r = new Random(); 
     timeIntegers[0] = r.nextInt(58 - 56) + 56; 
     timeIntegers[1] = r.nextInt(59 - 1) + 1; 

     return timeIntegers; 
    } 
} 

這裏是活動的完整接收器:

public class ActivityCompleteReceiver extends BroadcastReceiver implements WebServiceInterface { 

    Session session; 
    private LocalBroadcastManager broadcaster; 
    static final public String ACTIVITY_COMPLETE = "com.fimo.ACTIVITY_COMPLETE"; 
    private static final int SEND_ACTIVITY_REQUEST_CODE = 1; 
    Gson gson; 
    MyDatabase myDatabase; 
    UserActivity currentUserActivity; 

    @Override 
    public void onReceive(Context context, Intent intent) { 

     session = new Session(context); 
     broadcaster = LocalBroadcastManager.getInstance(context); 
     myDatabase = new MyDatabase(context); 
     gson = new Gson(); 

     sendActivityToServer(context, session.getUser().getId(), session.getTodaySteps()); 
    } 

    private void sendActivityToServer(Context context, String id, int steps) { 

     Calendar c = Calendar.getInstance(); 
     SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); 
     String date = dateFormat.format(c.getTime()); 

     UserActivity userActivity = new UserActivity(); 
     userActivity.setDate(date); 
     userActivity.setSteps(steps); 
     userActivity.setCalories(getDataFromSteps(steps)[0]); 
     userActivity.setCredits(getDataFromSteps(steps)[1]); 

     currentUserActivity = userActivity; 

     if (isNetworkAvailable(context)) { 
      HashMap<String, String> paramsList = new HashMap<>(); 
      ArrayList<UserActivity> arrayListUserActivity = myDatabase.getAllUserActivities(); 
      arrayListUserActivity.add(userActivity); 

      paramsList.put(LinksAndKeys.ID_KEY, id); 
      paramsList.put(LinksAndKeys.DATA_KEY, gson.toJson(arrayListUserActivity)); 

      Log.d("Receiver Request ----", id + " - " + gson.toJson(arrayListUserActivity)); 

      WebServiceController webServiceController = new WebServiceController(
        context, ActivityCompleteReceiver.this); 
      String hitURL = LinksAndKeys.SEND_ACTIVITY_URL; 
      webServiceController.sendSilentRequest(false, hitURL, paramsList, SEND_ACTIVITY_REQUEST_CODE); 
     } else { 
      myDatabase.addUserActivity(currentUserActivity); 
      currentUserActivity = null; 

      Intent in = new Intent(ACTIVITY_COMPLETE); 
      broadcaster.sendBroadcast(in); 

      session.setTodaySteps(0); 
     } 

    } 

    private boolean isNetworkAvailable(Context context) { 
     ConnectivityManager connectivityManager 
       = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); 
     NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo(); 
     return activeNetworkInfo != null && activeNetworkInfo.isConnected(); 
    } 

    @Override 
    public void getResponse(int responseCode, String responseString, String requestType, int requestCode) { 
     if (requestCode == SEND_ACTIVITY_REQUEST_CODE && responseCode == 200) { 
      try { 
       JSONObject responseObject = new JSONObject(responseString); 
       String message = responseObject.getString("message"); 
       if (message.equals("Success")) { 

        myDatabase.deleteAllUserActivities(); 

        JSONObject jsonObject = responseObject.getJSONObject("data"); 
        session.setServerCredits(Float.parseFloat(jsonObject.getString("credits"))); 

        Intent in = new Intent(ACTIVITY_COMPLETE); 
        broadcaster.sendBroadcast(in); 

        session.setTodaySteps(0); 
       } else { 
        myDatabase.addUserActivity(currentUserActivity); 
        currentUserActivity = null; 

        Intent in = new Intent(ACTIVITY_COMPLETE); 
        broadcaster.sendBroadcast(in); 

        session.setTodaySteps(0); 
       } 

      } catch (Exception e) { 
       myDatabase.addUserActivity(currentUserActivity); 
       currentUserActivity = null; 

       Intent in = new Intent(ACTIVITY_COMPLETE); 
       broadcaster.sendBroadcast(in); 

       session.setTodaySteps(0); 
      } 
     } else { 
      if (currentUserActivity != null) { 
       myDatabase.addUserActivity(currentUserActivity); 
       currentUserActivity = null; 

       Intent in = new Intent(ACTIVITY_COMPLETE); 
       broadcaster.sendBroadcast(in); 

       session.setTodaySteps(0); 
      } 
     } 
    } 

    public float[] getDataFromSteps(int stepsCount) { 
     float caloriesCount = 0; 
     float creditsCount = 0; 
     try { 
      double adjustedWeight = Double.parseDouble(session.getUser().getWeight())/LinksAndKeys.weightAdjuster; 
      caloriesCount = Math.round(((adjustedWeight * LinksAndKeys.metValue)/LinksAndKeys.setPace) * (stepsCount/LinksAndKeys.stepsPerMile)); 
      caloriesCount = caloriesCount * 1.2f; 
      creditsCount = caloriesCount/25.4f; 
     } catch (Exception e) { 
      caloriesCount = 0; 
      creditsCount = 0; 
     } 

     float[] resultantFloat = {caloriesCount, creditsCount}; 
     return resultantFloat; 
    } 
} 

編寫的代碼應該表現在這條路上:

每天都會計算用戶採取的步驟數。 - >在11.56-11.59之間的特定時間的午夜時間,將數據發送到活動完成的接收方 - >如果互聯網可用,則接收方接收數據並嘗試將其發送到服務器。如果沒有,則將其保存到本地數據庫 - >保存或發送後,將步驟重置爲0,服務將從第二天的0重新開始計數。

問題是服務實際上並沒有在給定的時間工作,接收者也沒有收到活動的完整意圖。如果我打開手機並通過設定一個距離當前時間幾分鐘的時間進行測試,則服務正常。但是如果手機長時間保持不動,那麼我認爲這種情況會在服務停止工作時發生。這是我的猜測,實際的問題可能是別的。任何建議或解決方案,高度讚賞。

+0

請將服務切換到清單中的進程並嘗試。或嘗試前臺服務。服務可能會在一段時間後停止 – Godwin

+0

當設備存在內存不足問題時,Android會關閉服務,您可以在服務的onTerminate()上啓動服務,或者您可以在服務的onDestroy()上發送廣播並使用接收器再次啓動服務。 –

+0

[此問題](https://stackoverflow.com/q/43230330/5241933)有幫助嗎? –

回答

2

從API級別19開始,使用AlarmManager.setRepeating()您正在調用您的ActivityCompleteReceiver,並不可靠。如果需要,Android可以延遲發送此警報。如果您確實希望在每天的確切時間觸發此功能,您應該執行以下操作:

使用AlarmManager.setExact()併爲下一個觸發時間設置一個警報(不是重複警報)。當此警報發送時,發送您的統計數據或任何您想要的,然後致電AlarmManager.setExact()設置下一個警報(第二天)。除非您絕對需要使用它,否則應該避免使用setRepeating()

你需要看你用喚醒鎖定在做什麼,你目前的代碼保持部分喚醒鎖定所有的時間,這將阻止設備進入休眠狀態,這將耗盡電池。閱讀如何優化電池使用。

0

•啓動的服務可以使用startForeground(int,Notification)API將服務置於前臺狀態,系統認爲該服務是用戶主動注意的內容,因此不適合在低時在記憶中。 (它仍然是理論上可能的服務下,從目前的前臺應用程序極端的內存壓力被殺死,但在實踐中,這不應該是一個問題

1:啓動通知使用服務使用startForeground

2。 :從OnCommandStart返回START_STICKY

private void showLocationNotification() 
    { 
     Notification notification = new Notification.Builder(this) 
       .setContentTitle("Service") 
       .setContentText("Service Started Successfully") 
       .setSmallIcon(R.mipmap.ic_launcher) 
       .build(); 

     startForeground(1, notification); 
    } 

上創建致電上述方法

3。在您的服務標籤清單文件把這個android:stopWithTask="false"

0

我認爲你的主要問題是,Android是不是可以持續運行,事情的系統。

即使使用一個服務,即使使用startForeground也不能保證你的進程不會被系統隨時被殺。

Android不是服務器的操作系統。服務期間的最初幾個小時內被殺害的可能性非常小,但後2〜3天變得非常高

Android上的一個主要任務是,以節省電池在保持一些服務運行。

此外,您的設計很明顯,您的應用程序在電池耗電應用程序中得分非常高,因爲您打算不允許設備進入深度睡眠狀態,並且肯定用戶必須不止一次爲電池充電天。

我認爲你應該改變你的設計,並限制你的跑步時間,像典型的運動應用那樣限制時間。

1

你似乎運行成打盹模式的限制,在Android 6.0和分裂引入光與深打盹在Android 7.0

當設備是電池供電,並在屏幕已經被關閉了一定時間,設備進入打盹並應用第一部分限制:它關閉應用網絡訪問,並延遲作業和同步。如果設備在進入打盹後靜止一段時間,則系統將其餘的打盹限制應用於PowerManager.WakeLock,AlarmManager警報,GPS和Wi-Fi掃描。無論是否應用部分或全部打盹限制,系統都會將設備喚醒以進行簡短維護時段,在此期間允許應用程序訪問網絡並可執行任何延期作業/同步。

所以,即使你設法讓報警火,例如與setAndAllowWhileIdle(),你仍然無法訪問網絡。通常的建議是使用JobScheduler或類似的東西,例如Firebase JobDispatcherEvernote Android-Job。這些框架允許配置在網絡可用時運行的作業,例如,與JobInfo.Builder.setRequiredNetworkType()。當Doze進入其「閒置維護」窗口時,時間將受制於擺佈,但至少您知道您將在網絡訪問發生時進行訪問。

如果需要在定時更多的控制,有可能設置一個報警,這將在空閒時醒來,然後使用GCM/FCM高優先級消息,迫使網絡訪問。我不確定您是如何在Firebase端挑選這些的 - 這取決於您的服務器端代碼。

0

使您的服務Foreground service並顯示持續通知。它幾乎不會被殺死。所有可靠的步計數器應用程序都以相同的原因顯示持續通知。音樂播放器也是這樣做的。

0

在Android 6.0休眠模式開始已經引入通過推遲背景CPU和網絡活動應用程式時,該設備不使用很長一段時間,以減少電池消耗。

要確認打盹模式是不運行的服務的原因,把你的手機是打盹模式強制使用下面的命令,並且服務運行檢查(通過設置時間幾分鐘後)

$ adb shell dumpsys deviceidle force-idle 

我們也可以通過打盹模式將應用程序列入白名單進行測試。在設置>電池>電池優化中手動配置白名單,然後選擇您的應用。 https://developer.android.com/training/monitoring-device-state/doze-standby.html

+0

如果問題是打盹模式,則過程將在幾分鐘後或一小時內停止。問題在於Android不是服務器操作系統,並且沒有100%保證服務不會被殺死。服務在頭幾個小時內被殺的概率很低,但在2或3天后變得非常高。 –

相關問題