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!!
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();
}
}
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);
}
}
Related
I want every 1 second registerReceiver.
I try
registerReceiver(receiver, new IntentFilter(Intent.ACTION_TIME_TICK));
but this code every 1 minute
I want every 1 second.
Perhaps, android have a form ?
thanks
What are you trying to accomplish? If you just want to have some code executed every 1s, don't user a BroadcastReceiver. Receivers result in inter-process communication every time they are triggered which is (relatively) expensive.
Best way would be to use a handler,
private static final long TICK_INTERVAL = TimeUnit.SECONDS.toMillis(1);
private static final Handler tickHandler = new Handler(Looper.getMainLooper());
public void onResume() {
super.onResume();
tick(TICK_INTERVAL);
}
private void tick(final long interval) {
tickHandler.postDelayed(
new Runnable() {
public void run() {
tick(interval);
onTick();
}
},
);
}
protected void onTick() {
// do something
}
Ensure you stop the ticking when your activity pauses,
public void onPause() {
super.onPause();
tickHandler.removeCallbacksAndMessages(null);
}
My goal is to have a thread running that plays a sound then chooses a random animation and a random image and displays them.
It is currently working, but I was wondering if there is a better way. I have a Hacker's understanding of threading (as in, I only know that this works), so I'd appreciate some feedback. Also, I've been having issues with memory overflow in my app, is there a better way to manage this Activity memory-wise? Thank you so much!
public int[] images = {R.drawable.splat0,R.drawable.splat1,R.drawable.splat2,R.drawable.splat3,
R.drawable.splat4,R.drawable.splat5,R.drawable.splat6,R.drawable.splat7,R.drawable.splat8,
R.drawable.splat9};
public int[] anims= {R.anim.splat0,R.anim.splat1,R.anim.splat2,
R.anim.splat3,R.anim.splat4,R.anim.splat5,R.anim.splat6};
MediaManager mp;
Handler tick_Handler = new Handler();
MyThread tick_thread = new MyThread();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
MainActivity.this.setContentView(R.layout.activity_main);
mp = new MediaManager();
image = (ImageView)this.findViewById(R.id.mainActivitySplat);
tick_Handler.post(tick_thread);
}
#Override
public void onStop(){
tick_Handler.removeCallbacks(tick_thread);
super.onStop();
}
#Override
public void onResume(){
tick_Handler.post(tick_thread);
super.onResume();
}
private class MyThread implements Runnable {
#Override
public void run() {
mp.playSoundClip(MainActivity.this,R.raw.swoosh);
image.setBackgroundResource(images[(int)(Math.random()*splats.length)]);
Animation myAnim=AnimationUtils.loadAnimation(MainActivity.this,splatAnim[(int)(Math.random()*splatAnim.length)]);
splat.startAnimation(myAnim);
tick_Handler.postDelayed(tick_thread, 3500);
}
}
Edit:
I have discovered this is a BAD way of using the Thread. MyThread holds an implicit reference to the Activity, and causes a massive memory leak. By changing the class to private static MyThread I solve the leak, but I have not yet figured out how to get the desired behavior this way. Will update later.
use a flag like
boolean isActibityKilled=true //when in onstop
use it in the runnable to check if activity is running or not if activity is not running , or it is stopped then kill your thread
In my activity oncreate method, i have called a service using OnStartCommand(). My requirement is when the user is on the same Activity (when the Activity is visible), a set of code should run repeatedly. (Example .. I should make a web service call and get the response and do some action based on it after regular intervals).
I have put this set of code in this method.
#Override
public int onStartCommand(Intent i, int flags , int startId){
// Code to be repeated
return Service.START_STICKY;
}
But, this is getting executed only once. How to make it run repeatedly from the time the user came to this page till he leaves this page ??
CountDownTimer.cancel() method seems to be not working.
I would recommend you to use Timer instead. It's much more flexible and can be cancelled at any time. It may be something like that:
public class MainActivity extends Activity {
TextView mTextField;
long elapsed;
final static long INTERVAL=1000;
final static long TIMEOUT=5000;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mTextField=(TextView)findViewById(R.id.textview1);
TimerTask task=new TimerTask(){
#Override
public void run() {
elapsed+=INTERVAL;
if(elapsed>=TIMEOUT){
this.cancel();
displayText("finished");
return;
}
//if(some other conditions)
// this.cancel();
displayText("seconds elapsed: " + elapsed / 1000);
}
};
Timer timer = new Timer();
timer.scheduleAtFixedRate(task, INTERVAL, INTERVAL);
}
private void displayText(final String text){
this.runOnUiThread(new Runnable(){
#Override
public void run() {
mTextField.setText(text);
}});
}
}
You can use Timer for the fixed-period execution of a method.
See here is a tutorial on this:
http://steve.odyfamily.com/?p=12
I am calling from a method:
myHandler.postDelayed(mMyRunnableHide, 6000);
which calls:
public Runnable mMyRunnableHide = new Runnable()
{
public void run()
{
mTextDisplay.setText("");
DisplayX();
}
};
if a button on screen is clicked I want to stop the runnable:
Button next = (Button) findViewById(R.id.Breaction);
next.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
myHandler.removeCallbacks(mMyRunnableHide);
mTextDisplay.setText("");
DisplayX();
}
});
}
the removecallbacks is not stopping the runnable. What am I doing wrong? Am I using the correct method? I just want the runnable to "Not Run" when the user clicks the button.
Thanks for any help.
It appears to me that removeCallbacks(..) only stops pending messages (Runnables). If your runnable has already started, then there's no stopping it (at least not this way).
Alternatively, you can extend the Runnable class and give it some kind of kill switch like this:
public class MyRunnable implements Runnable
{
private boolean killMe = false;
private void run()
{
if(killMe)
return;
/* do your work */
}
private void killRunnable()
{
killMe = true;
}
}
This will only prevent it from starting, but you could occasionally check killMe and bail out. If you are looping the runnable (like some kind of background thread) you can say:
while(!killMe) {
/* do work */
}
Hope this helps
EDIT I just wanted to post an update on this. Since this original post, Google has come up with a great class called AsyncTask that handles all of this stuff for you. Anyone reading this really should look into it because it is the correct way of doing things.
You can read about it here
Handler.removeCallback is synchronous and will work nicely provided:
You call postDelayed always in the main thread.
You call removeCallback always in the main thread
You don't call postDelayed again after having removed callbacks.
So in your case removeCallbacks is called from a button handler, which runs in the main thread. But you didn't show in your code the point from where you call postDelayed. If you call it from a background thread thats where your problem is.
If you are sure you don't call any of these methods from background threads, and the order of the calls is correct, then you might be leaving uncancelled tasks unadvertedly alive due to activity recreation on config changes (screen rotation, etc). Always make sure to call removeCallbacks again in the onDestroy method to prevent this kind of problems.
Here is another way to accomplish what mtmurdock is describing. This class will allow editing of instance variables in any class that your Runnable is defined as an anonymous inner class.
package support;
/**
* Runnable that can be stopped from executing
*/
public abstract class KillableRunnable implements Runnable{
private boolean isKilled=false;
/**
* Instead of Overriding run(), override this method to perform a Runnable operation.
* This will allow editing instance variables in the class that this Runnable is defined
*/
public abstract void doWork();
//The handler that posts this Runnable will call this method.
//By default, check if it has been killed. doWork() will now be the method
//override to implement this Runnable
#Override
final public void run(){
if(!isKilled){
doWork();
}
}
final public void kill(){
isKilled=true;
}
}
I don't think that removeCallbacks(..) only stops pending messages (Runnables) ,I think removeCallbacks(..) not working have other cause,but i don‘t know. because postDelayed(..) and removeCallbacks(..) is in the same thread
the following has worked for me. Place it in onResume.
mService= null;
public void onServiceConnected(ComponentName name, IBinder service) {
Log.i(TAG, "OnServiceConnected");
ContadorFG.LocalBinder binder = (ContadorFG.LocalBinder) service;
mService = binder.getService();
connected = true;
synchronized (lock){
lock.notifyAll();
}
}
public void onResume() {
super.onResume();
loopDelayed();
}
private void loopDelayed(){
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
if (mService != null) {
----
----
----
return;
}else{
//auto call
loopDelayed();
}
}
}, 10);
}
I'm new to writing app for android. I've got program writing experience in non object oriented program language but would like to learn this way of programming too.
I would like to start simple and do the following:
Press a button and a the vibrator of the phone will be triggered in a certain pattern until the button is pressed again
I know that if you say: vibrator.vibrate(pattern , 0); that it will repeat the pattern. But I would like to turn on and off the screen in that pattern too for example.
What would be the right way to do this?
Thank you very much for your help.
package com.trick-design.simple_prog;
import android.app.Activity;
import android.os.Bundle;
import android.content.Context;
import android.os.Vibrator;
import android.view.View;
public class simple_prog extends Activity {
private long[] pattern = {100, 100, 300, 100, 900, 1050};
boolean vibrator_on = true;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
#Override
protected void onStart() {
super.onStart();
findViewById(R.id.vibrate_button).setOnClickListener(vibrate_click_listener);
}
View.OnClickListener vibrate_click_listener = new View.OnClickListener() {
public void onClick(View v) {
Vibrator vibrator = (Vibrator)getSystemService(Context.VIBRATOR_SERVICE);
vibrator.vibrate(pattern , -1);
}
};
#Override
protected void onStop() {
super.onStop();
// The activity is no longer visible (it is now "stopped")
}
#Override
protected void onDestroy() {
super.onDestroy();
// The activity is about to be destroyed.
}
}
As an extension of TVK-'s answer:
You would need to create a class that either implements Runnable or extends Thread to do this work in another thread to prevent the hang. You can do this with a second class in the same *.java file -- allowing it to access the variables from the other class in the file:
private class VibrateRunner implements Runnable
{
public void run()
{
while(vibrate_on)
{
executeVibrate();
}
}
private void executeVibrate()
{
vibrator.vibrate(pattern , -1);
}
}
Your on click listener would need to be smart enough to start/stop the thread - ex:
// Make a Thread class variable
Thread bgThread = null;
onClick(View v)
{
// You already started the thread - stop it
if(bgThread != null)
{
vibrate_on = false;
bgThread.join();
return;
}
// Need to turn on the thread
vibrate_on = true;
Runnable runner = new VibrateRunner();
bgThread = new BackgroundThread(runner);
bgThread.setDaemon(true); // Run it in the background
bgThread.start();
}
That should get you off in the right direction. Note: I cut out a lot of exception handling in here -- just read up some on Java threads to get the feel for it. Eclipse will also help you generate the right try/catch blocks.
-- Dan
Try this:
public void vibrate() {
vibrator.vibrate(pattern , -1);
if(vibrator_on) {
vibrate();
}
}
This method will repeat itself until vibrate_on is false. (Make sure it runs in its own thread, otherwise it will freeze up whatever thread it's running it for as long as vibrate_on is true.
Update:
As discussed in the comments, this is no good.
This should do better:
public void doVibrate() {
while(vibrate_on) {
executeVibrate();
}
}
public void executeVibrate() {
vibrator.vibrate(pattern , -1);
}