可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I am currently developing an app in Android which will record sensor data for a fixed length of time for several cycles. For example, I plan to record the data for 10 seconds, and then stop, let the phone rest for 10 seconds, and start record again, ... working in this pattern for 1 hour. My question is, how to let the phone automatically execute this plan? I am currently using code below ( from Android: How to collect sensor values for a fixed period of time?) , but it only works for one cycle, I have to manually start new cycles after I am sure the previous cycle has finished.
public void onResume() { mSensorManager.registerListener(mListener, mSensorAcceleration, SensorManager.SENSOR_DELAY_GAME); mSensorManager.registerListener(mListener, mSensorMagnetic, SensorManager.SENSOR_DELAY_GAME); Handler h = new Handler(); h.postDelayed(new Runnable() { @Override public void run() { // do stuff with sensor values mSensorManager.unregisterListener(mListener); } }, 10000);
...
Any help will be appreciated!!
回答1:
Step #1: Have your activity implement Runnable
, rather than use an anonymous inner class, moving your run()
method to be implemented on the activity.
Step #2: In your run()
method, schedule yourself (the activity) to run again after a delay using postDelayed()
. This, plus your existing call to postDelayed()
, will effectively set up a periodic call to run()
.
Step #3: Keep track of whether you are in "sensors on" or "sensors off" mode, and, in run()
, either register or unregister the listeners as appropriate.
Step #4: In onPause()
, call removeCallbacks()
on your Handler
to stop the periodic calls to run()
.
You will see an example of this sort of schedule-yourself-to-run-again logic in this sample project. Here is the activity:
package com.commonsware.android.post; import android.app.Activity; import android.os.Bundle; import android.view.View; import android.widget.Toast; public class PostDelayedDemo extends Activity implements Runnable { private static final int PERIOD=5000; private View root=null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); root=findViewById(android.R.id.content); } @Override public void onResume() { super.onResume(); run(); } @Override public void onPause() { root.removeCallbacks(this); super.onPause(); } @Override public void run() { Toast.makeText(PostDelayedDemo.this, "Who-hoo!", Toast.LENGTH_SHORT) .show(); root.postDelayed(this, PERIOD); } }
回答2:
I think there's a better and more correct way to implement this. Specifically, I think it's wrong to let the Activity
implement Runnable
. It leaks logic in its public interface that should be kept private (and hidden). I.e. no one is ever supposed to invoke run()
outside the activity. I would suggest implementing it as follows instead:
public class PostDelayedDemo extends Activity { // Declaration of sensor-related fields. private static final int PERIOD = 10000; private Handler handler; private final Runnable processSensors = new Runnable() { @Override public void run() { mSensorManager.registerListener(mListener, mSensorAcceleration, SensorManager.SENSOR_DELAY_GAME); mSensorManager.registerListener(mListener, mSensorMagnetic, SensorManager.SENSOR_DELAY_GAME); // Do work with the sensor values. mSensorManager.unregisterListener(mListener); // The Runnable is posted to run again here: handler.postDelayed(this, PERIOD); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); handler = new Handler(); } @Override public void onResume() { super.onResume(); handler.post(processSensors); } @Override public void onPause() { handler.removeCallbacks(processSensors); super.onPause(); } }