What I'm trying to understand is how to loop a simple beat in a given time frame using a Handler like a metronome. I've read a lot and saw a few things I can do but the best way is make a Handler, is this true? So after reading I tried something but I can't understand it exactly and it's not working right now.
EDIT:
This is what I have now, it's not crashing anymore but it isn't playing the sound like it should. What did I do wrong?
public class MainActivity extends Activity {
private MediaPlayer mpBeat;
private Handler playBeatHandler = new Handler();
private Runnable playBeatTask = new Runnable() {
public void run() {
mpBeat.start();
playBeatHandler.postDelayed(this, 500);
}
};
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mpBeat = MediaPlayer.create(this, R.raw.beat);
playBeatHandler.postDelayed(playBeatTask, 500);
}
}
This is what I came across and what helped me a lot to build a steady beat!
http://code.google.com/p/android-sleep-metronome/
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
playBeatHandler.postDelayed(playBeatTask, 0); // now works
}
Related
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
loadImageFromUrl(url);
}
}, 2000);
addTouchlistener();
addButtonListener();
}
private void loadImageFromUrl(String url) {
Picasso.with(iv.getContext())
.load(url)
.networkPolicy(NetworkPolicy.NO_CACHE, NetworkPolicy.NO_STORE)
.into(iv);
}
the "url" is my http server, i will be receiving the .jpg continuously.
i want repeat the function every 2s to receive the .jpg
In this case, the first time load the image will be delay 2s
but, when i update my photo, it can not show the new photo
i also tried the timer "scheduleAtFixedRate" to repeat but it's not work.
Because a Handler only runs what you post to it once. It doesn't do so repeatedly. To do so repeatedly, the runnable needs to repost itself at the end.
Although I suggest you don't do it this way. How often do you really change the image? Every few minutes? Few hours? Checking every 2 seconds is ridiculous, you're wasting bandwidth 99% of the time. Use a much longer timer. Better yet, use push messaging to tell the client when to reload.
This is a working example I have in a Fragment, but the logic is the same for an Activity.
I included extra code to turn off the downloads when the app goes onPause().
protected Handler programacionTimer;
protected Runnable programacionRunnable;
....
#Override
public void onResume() {
super.onResume();
if (programacionTimer == null) {
programacionTimer = new Handler();
}
if (programacionRunnable == null) {
programacionRunnable = new Runnable() {
#Override
public void run() {
//Here what you to do (download the image)
programacionTimer.postDelayed(this, 2000);
}
};
}
programacionTimer.postDelayed(programacionRunnable, 2000);
}
#Override
public void onPause() {
super.onPause();
programacionTimer.removeCallbacks(programacionRunnable);
programacionRunnable = null;
}
I was testing this code to check if app crashes for changing ui component from background thread. but it didn't.
Here in the code added below. I started a new thread in onCreate() method of MainActivity and It should have crashed as per the android docs which says
In the class, the Runnable.run() method contains the code that's
executed. Usually, anything is allowable in a Runnable. Remember,
though, that the Runnable won't be running on the UI thread, so it
can't directly modify UI objects such as View objects.
So I was expecting it to crash. Which it didn't. See code -
public class MainActivity extends AppCompatActivity {
TextView txt;
Thread thread;
Runnable runnable = new Runnable() {
#Override
public void run() {
txt.setText("bro");
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
txt = (TextView) findViewById(R.id.name_txt);
thread = new Thread(runnable);
thread.start();
}
}
While if i try changing ui while starting thread from onClicklistener() as below it does crash. which is expected.
public class MainActivity extends AppCompatActivity {
TextView txt;
Thread thread;
Runnable runnable = new Runnable() {
#Override
public void run() {
txt.setText("bro");
}
};
View.OnClickListener listener = new View.OnClickListener() {
#Override
public void onClick(View v) {
thread.start();
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
txt = (TextView) findViewById(R.id.name_txt);
thread = new Thread(runnable);
txt.setOnClickListener(listener);
}
}
Now, that the second code snippet crashes, which is expected and the first one doesn't.
Please explain why is this happening, as I'm creating a new worker thread each time but just at different places. Official docs reference will be appreciated.
I found the reason behind this behavior as pointed out by #krish in the comments on my question. The reason is that, the thread was able to make changes in the TextView object only till it was not visible on UI screen i.e not rendered. It is only after the view rendering, that any thread except the Main thread may not make changes to any UI components. I Tried using view observer to see if the view was rendered before the changes or not. which showed that changes were made before the view rendering.
Here is the code that i tried.
public class MainActivity extends AppCompatActivity {
TextView txt;
Thread thread;
Runnable runnable = new Runnable() {
#Override
public void run() {
txt.setText("bro");
Log.d("ThreadTest", "The Text was changed.");
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
txt = (TextView) findViewById(R.id.name_txt);
thread = new Thread(runnable);
txt.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
Log.d("ThreadTest", "The TextView was rendered");
}
});
}
#Override
protected void onResume() {
super.onResume();
thread.start();
}
}
Using the code above. You'll see in the output:
The Text was changed.
The TextView was rendered
Which means text was changed before view rendering. if you try to start thread to makes changes in onGlobalLayout method. App crashes as it should.
The UI is not thread-safe see processes-and-threads, so you were just lucky you did not hit one of the many landmines waiting for you.
If you don't like relying on luck then:
You should be using:
runOnUiThread(runnable);
instead of:
thread = new Thread(runnable);
AsyncTask enables proper and easy use of the UI thread.
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
I'm having an awful time trying to get a simple Android app to work properly. I've very little experience in Java, having come from C, and I'm trying to start small with a simple clock app that will update the display every second. Here's my Java code:
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String currentdatetime =
DateFormat.getDateTimeInstance().format(new Date());
final TextView textview = new TextView(this);
Handler handler = new Handler();
Runnable runnable = new Runnable() {
#Override
public void run() {
String currentdatetime =
DateFormat.getDateTimeInstance().format(new Date());
textview.setText(currentdatetime);
}
};
textview.setGravity(Gravity.CENTER);
textview.setTextSize(20);
textview.setText(currentdatetime);
setContentView(textview);
handler.postDelayed(runnable, 1000);
runnable.run();
}
I'm running this in Debian Linux with the Google-provided ADT bundle, and have followed all the suggestions Eclipse gives for fixing these problems, to no avail. The program compiles fine and displays exactly as I'd expect it to, except that it does not update at all. I've scoured Google as best I can and have followed many different ways of doing this, but I've not been successful.
What I'd expect this program to do is, on creation, set "currentdatetime" to the current date and time, stick it in a TextView, and then change the TextView to the main view. I'd expect it to then hit handler.postDelayed and do this again every thousand milliseconds. Clearly, though, that's not what's happening.
I'm afraid I'm completely lost. Can anyone point me in the right direction?
Well, appears that I was on rather the wrong track. I've got the code working now, rather more neatly, with the following:
protected void onCreate(Bundle savedInstanceState) {
final Handler handler = new Handler();
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final TextView textview = new TextView(this);
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
handler.post(new Runnable() {
public void run() {
String currentDateTimeString =
DateFormat.getDateTimeInstance().format(new Date());
textview.setGravity(Gravity.CENTER);
textview.setTextSize(60);
textview.setText(currentDateTimeString);
setContentView(textview);
}
});
}
},1000,1000);
}
So thanks to anyone who was getting ready to answer.
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);
}
}