Android加速度计日志记录服务停止工作,并且未调用onDestroy

时间:2014-03-28 23:33:21

标签: java android service android-service ondestroy

我正在尝试创建一个应该尽快在磁盘上记录加速度计值和时间戳的服务。

只要Activity存在,它就可以正常工作,但问题是当我退出此服务所附带的Activity时,服务就会停止。我在onCreateonStartCommandonDestroy举杯祝福,对于前两个,它正常工作,但它从未在onDestroy上显示任何内容,所以我很清楚原因是什么。我还在onDestroy的Android Studio中设置了断点,但它也没有触发。

以下是完整的代码,请让我知道您认为可能出现的问题:

package com.embedonix.mobilehealth.services.accelerometerlog;

import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Environment;
import android.os.IBinder;
import android.widget.Toast;

import com.embedonix.mobilehealth.AppConstants;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class AccelerometerLogService extends Service {

    private boolean mIsServiceStarted = false;
    private Context mContext = null;
    private SensorManager mSensorManager = null;
    private Sensor mSensor;
    private File mLogFile = null;
    private FileOutputStream mFileStream = null;
    private AccelerometerLogService mReference = null;
    private Float[] mValues = null;
    private long mTimeStamp = 0;
    private ExecutorService mExecutor = null;

    /**
     * Default empty constructor needed by Android OS
     */
    public AccelerometerLogService() {
        super();
    }

    /**
     * Constructor which takes context as argument
     *
     * @param context
     */
    public AccelerometerLogService(Context context) {
        super();

        if (context != null)
            mContext = context;
        else
            mContext = getBaseContext();
    }

    @Override
    public void onCreate() {
        super.onCreate();

        Toast.makeText(getBaseContext(), "Service onCreate", Toast.LENGTH_SHORT).show();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        if (isServiceStarted() == false) {

            mContext = getBaseContext();
            mReference = this;
            mSensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
            mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
            mValues = new Float[]{0f, 0f, 0f};
            mTimeStamp = 0;
            mExecutor = Executors.newSingleThreadExecutor();

            setupFolderAndFile();
            startLogging();
        }

        //set started to true
        mIsServiceStarted = true;


        Toast.makeText(mContext, "Service onStartCommand", Toast.LENGTH_SHORT).show();
        return Service.START_STICKY;
    }

    private void setupFolderAndFile() {
        mLogFile = new File(Environment.getExternalStorageDirectory().toString()
                + "/" + AppConstants.APP_LOG_FOLDER_NAME + "/test.txt");

        try {
            mFileStream = new FileOutputStream(mLogFile, true);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }

    private void startLogging() {

        mExecutor.execute(new Runnable() {
            @Override
            public void run() {
                mSensorManager.registerListener(
                        new SensorEventListener() {
                            @Override
                            public void onSensorChanged(SensorEvent sensorEvent) {
                                mTimeStamp = System.currentTimeMillis();
                                mValues[0] = sensorEvent.values[0];
                                mValues[1] = sensorEvent.values[1];
                                mValues[2] = sensorEvent.values[2];

                                String formatted = String.valueOf(mTimeStamp)
                                        + "\t" + String.valueOf(mValues[0])
                                        + "\t" + String.valueOf(mValues[1])
                                        + "\t" + String.valueOf(mValues[2])
                                        + "\r\n";

                                try {
                                    mFileStream.write(formatted.getBytes());
                                } catch (IOException e) {
                                    e.printStackTrace();
                                }
                            }

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

                            }
                        }, mSensor, SensorManager.SENSOR_DELAY_FASTEST
                );
            }
        });
    }

    @Override
    public void onDestroy() {
        super.onDestroy();

        //Flush and close file stream
        if (mFileStream != null) {
            try {
                mFileStream.flush();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                mFileStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        Toast.makeText(mContext, "Service onDestroy", Toast.LENGTH_LONG).show();
        mIsServiceStarted = false;
    }

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

    /**
     * Indicates if service is already started or not
     *
     * @return
     */
    public boolean isServiceStarted() {
        return mIsServiceStarted;
    }
}

更新1

我发现这种情况发生在Nexus 7平板电脑上。相同的代码在我的手机MotoG上工作正常。这两款设备都安装了Android 4.4.2 ....可能是什么原因?

3 个答案:

答案 0 :(得分:1)

我认为使用服务记录加速度计值和时间戳的逻辑存在问题。在主UI线程上运行的android服务。如果您的服务需要在后台运行,则需要在单独的线程中启动(例如AsyncTask)。

在主线程上运行会冒中断UI响应的风险,在我看来这是你问题的根源。因此,它可能在某些设备上运行正常,而在其他设备上运行正常。

我的建议是在您的服务中运行AsyncTask作为后台工作人员来记录加速度计值。

在您的课程AccelerometerLogService中,您实施了AsyncTask类似的内容:

public class AccelerometerLogService extends Service {
     ..........................
/**
  * @author 
  * Private class which logs accelerometer values and timestamp.
  */
 private class AsyncTaskRunner extends AsyncTask<String, String, String> {

  private String resp;

  @Override
  protected String doInBackground(String... params) {
   publishProgress("running..."); // Calls onProgressUpdate()

   try {
        setupFolderAndFile();  // Here you are doing the job
        startLogging();

   } catch (Exception e) {
    e.printStackTrace();
    resp = e.getMessage();
   }
   return resp;
  }

  /*
   * @see android.os.AsyncTask#onPostExecute(java.lang.Object)
   */
  @Override
  protected void onPostExecute(String result) {
   // execution of result of Long time consuming operation
   finalResult.setText(result);
  }

  /*
   * @see android.os.AsyncTask#onPreExecute()
   */
  @Override
  protected void onPreExecute() {
   // Things to be done before execution of long running operation. For
   // example showing ProgessDialog
  }

  /*
   * @see android.os.AsyncTask#onProgressUpdate(Progress[])
   */
  @Override
  protected void onProgressUpdate(String... text) {
   // Things to be done while execution of long running operation is in
   // progress. For example updating ProgessDialog
  }
 }}

然后在AsyncTaskRunner内调用onStartCommand,如:

@Override
public int onStartCommand(Intent intent, int flags, int startId) {

    if (isServiceStarted() == false) {

        mContext = getBaseContext();
        mReference = this;
        mSensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
        mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        mValues = new Float[]{0f, 0f, 0f};
        mTimeStamp = 0;
        mExecutor = Executors.newSingleThreadExecutor();

        _runner = new AsyncTaskRunner();  // Start backgroud thread here
    _runner.execute();  
    }

    //set started to true
    mIsServiceStarted = true;


    Toast.makeText(mContext, "Service onStartCommand", Toast.LENGTH_SHORT).show();
    return Service.START_STICKY;
}

runner是一个初始化的私有变量:

public class AccelerometerLogService extends Service {

private boolean mIsServiceStarted = false;
.............

AsyncTaskRunner _runner = null;
.............

这主要描述了我的解决方案,但它需要你做更多的工作。希望现在更清楚了。

答案 1 :(得分:0)

尝试更改START_STICKY - &gt; START_REDELIVER_INTENT。虽然文档声明在销毁服务之前调用方法onDestroy(),但实际上并不总是调用此方法。个人面临这种​​情况,很可能这是由于服务的实施(根据谷歌的建议)

答案 2 :(得分:0)

以上都不是答案。不幸的是,这是Nexus 7和谷歌Nexus 5产品的已知问题。

这是bug报告的链接(虽然我认为它是故意的feautur !!!)

https://code.google.com/p/android/issues/detail?id=63793