I just wanted to test Log.i() and look at the console in android studio. In the code below onResume should start the thread and run() should write an endless stream of "dings" with the tag "run" in the monitor. But the run method apparently only gets called once. Why?
public class MainActivity extends Activity implements Runnable {
Thread gameThread = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.i("onCreate","getting started");
}
public void run() {
Log.i("run","ding");
}
#Override
public void onResume() {
super.onResume();
gameThread = new Thread(this);
gameThread.start();
}
}
You're missing the notion of what threading really does. It allows you to run a unit of work asynchronously. So, all the same normal rules apply. The reason it only runs once, is because the thread exits after run() returns. So just like any other method, you should put something like
while(true)
{
Log.i("run","ding");
}
inside of run(). Ideally you would actually check some condition so that you can exit the thread as needed.
Finally, it is probably a bad idea to have your MainActivity implement Runnable. Typically it is good style to have a thread implemented by its own class, for example DingThread implements Runnable.
You're missing while loop that why its run only once. Use below code. This is the better approach to use thread concept.
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.i("onCreate","getting started");
}
#Override
public void onResume() {
super.onResume();
startThread();// create thread obj and start
}
private GameThread mGameThread = null;
private void startThread() {
stopThread();// if thread already running stop it then create new thread and start (for avoiding multi-threading).
mGameThread = new GameThread();
mGameThread.start();//start the thread.
}
//To stop the thread simply call this method.
private void stopThread() {
if(mGameThread != null) {
mGameThread.setStop();
mGameThread = null;
}
}
private class GameThread extends Thread {
private boolean mIsStop;// mIsStop is default false
#Override
public void run() {
while (!mIsStop) { // if mIsStop is false then only come inside loop.
Log.i("run","ding"); //log will print
}
}
public void setStop() {
mIsStop = true;// set mIStop variable to true.
}
}
}
Related
I'm sure this error is because I don't fully understand threads, but here it goes...
I have a runnable that is started when onCreate() is called within a method:
#Override
protected void onCreate(Bundle savedInstanceState) {
//Set all app specific starting points here
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_avatar);
...
soundMeterLoop();
}
public void soundMeterLoop() {
Log.d("SpeechKit", "Start Sound Meter");
soundMeterHandler = new Handler();
soundMeterRunnable = new Runnable() {
#Override
public void run() {
if(!soundMeter.SoundMeterRunning) {
Log.d("SpeechKit", "Start SoundMeter in the runnable");
startSoundMeter();
}
if (soundMeter.mMediaRecorder != null) {
amplitude = soundMeter.getAmplitude();
decibelLevelOutput.setText("" + amplitude);
if (amplitude > threshold) {
decibelLevelOutput.setTextColor(Color.RED);
Log.d("SpeechKit", "Interrupt and run startNuance()");
startNuance();
} else {
decibelLevelOutput.setTextColor(Color.BLACK);
Log.d("SpeechKit", "Running");
soundMeterHandler.postDelayed(this, 100);
}
}
}
};
soundMeterHandler.postDelayed(soundMeterRunnable, 100);
}
This runs just fine when it's created in the onCreate. As you can see, it kills itself (by not renewing the loop if the statement fails) and runs startNuance().
public void startNuance() {
soundMeterHandler.removeCallbacksAndMessages(soundMeterRunnable);
nuance.toggleReco();
}
I then kill the runnable and start a method in another class. This class runs fine, then when it's finished doing its thing, I call back to this main class with avatar.stopNuance();
This is in the Nuance.java class
#Override
public void onFinishedRecording(Transaction transaction) {
Log.d("SpeechKit", "onFinishedRecording");
//We have finished recording the users voice.
//We should update our state and stop polling their volume.
state = State.PROCESSING;
stopAudioLevelPoll();
avatar.stopNuance(); // <<<<<
}
It then returns back to my main activity (avatar) and runs this stopNuance() method:
public void stopNuance() {
Log.d("SpeechKit", "stopNuance(), start loop again");
soundMeterLoop();
}
Then it tries to run the same loop from before. Only this time, I'm getting a lot of errors that pertain to nullpointerexceptions. specifically starting with decibelLevelOutput.setText("" + amplitude);
I'm not sure why these things are null or how to fix them. Is this because it started a new thread that was not started in the creation of the runnable?
After talking on chat the actual issue was elsewhere in the codebase.
The problem was this:
public class Nuance {
private Activity activity;
private Session session;
public Avatar avatarActivity = new Avatar(); // DONT DO THIS
....
#Override
public void onFinishedRecording(Transaction transaction) {
Log.d("SpeechKit", "onFinishedRecording");
//We have finished recording the users voice.
//We should update our state and stop polling their volume.
state = State.PROCESSING;
stopAudioLevelPoll();
avatarActivity.stopNuance();
}
You should never, ever ever create your own instance of an Activity. They are creted and managed by the system. The system will call the lifecycle methods on the instance (onCreate etc) but if you create an instance these methods are not called - therefore a lot of strange behaviour happens.
The fix here is this:
public class Nuance {
private Avatar activity;
private Session session;
....
#Override
public void onFinishedRecording(Transaction transaction) {
Log.d("SpeechKit", "onFinishedRecording");
//We have finished recording the users voice.
//We should update our state and stop polling their volume.
state = State.PROCESSING;
stopAudioLevelPoll();
activity.stopNuance();
}
you don't want to create a new runnable everytime soundMeterLoop() is called.
Try this:
private final Handler soundMeterHandler = new Handler();
#Override
protected void onCreate(Bundle savedInstanceState) {
//Set all app specific starting points here
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_avatar);
...
soundMeterLoop();
}
public void soundMeterLoop() {
Log.d("SpeechKit", "Start Sound Meter");
soundMeterHandler.postDelayed(soundMeterRunnable, 100);
}
private final Runnable soundMeterRunnable = new Runnable() {
#Override
public void run() {
if(!soundMeter.SoundMeterRunning) {
Log.d("SpeechKit", "Start SoundMeter in the runnable");
startSoundMeter();
}
if (soundMeter.mMediaRecorder != null) {
amplitude = soundMeter.getAmplitude();
decibelLevelOutput.setText("" + amplitude);
if (amplitude > threshold) {
decibelLevelOutput.setTextColor(Color.RED);
Log.d("SpeechKit", "Interrupt and run startNuance()");
startNuance();
} else {
decibelLevelOutput.setTextColor(Color.BLACK);
Log.d("SpeechKit", "Running");
soundMeterHandler.postDelayed(this, 100);
}
}
}
};
I'm writing an app that will display the current download speed which will be updated every second. My Runnable class is able to update the UI with the value, but when I try to place it inside a loop so that it will continuously run and update the UI TextView every second, the app now hangs.
This is my MainActivity.java:
public class MainActivity extends Activity implements SpeedMeter.TaskRunnableSpeedMeterMethods{
private Thread mSpeedMeterThread;
private Handler mHandler;
private TextView downloadSpeedOutput;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
downloadSpeedOutput = (TextView) findViewById(R.id.speed);
mHandler = new Handler(Looper.getMainLooper()) {
#Override
public void handleMessage(Message inputMessage) {
SpeedMeter speedMeter = (SpeedMeter) inputMessage.obj;
downloadSpeedOutput.setText(Long.toString(speedMeter.getmDownloadSpeedKB()));
}
};
SpeedMeter speedMeter = new SpeedMeter(this);
speedMeter.run();
// Creates an explicit intent for an Activity in your app
Intent resultIntent = new Intent(this, MainActivity.class);
}
#Override
public void setSpeedMeterThread(Thread currentThread) {
mSpeedMeterThread = currentThread;
}
#Override
public void setInternetSpeed(SpeedMeter speedMeter) {
Message completeMessage = mHandler.obtainMessage(1, speedMeter);
completeMessage.sendToTarget();
}
}
And here's the other SpeedMeter.java:
public class SpeedMeter implements Runnable {
final TaskRunnableSpeedMeterMethods mMainActivity;
private long mDownloadSpeedKB;
public SpeedMeter(TaskRunnableSpeedMeterMethods mainActivity) {
mMainActivity = mainActivity;
}
#Override
public void run() {
mMainActivity.setSpeedMeterThread(Thread.currentThread());
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND);
// while(true) {
long rxBytesPrevious = TrafficStats.getTotalRxBytes();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
long rxBytesCurrent = TrafficStats.getTotalRxBytes();
long downloadSpeed = rxBytesCurrent - rxBytesPrevious;
setmDownloadSpeedKB(downloadSpeed/1000);
mMainActivity.setInternetSpeed(this);
// }
}
public long getmDownloadSpeedKB() {
return mDownloadSpeedKB;
}
public void setmDownloadSpeedKB(long mDownloadSpeedKB) {
this.mDownloadSpeedKB = mDownloadSpeedKB;
}
interface TaskRunnableSpeedMeterMethods {
void setSpeedMeterThread(Thread currentThread);
void setInternetSpeed(SpeedMeter speedMeter);
}
}
Any help will be appreciated!
You didnt start your runnable as a new thread instead you called the run function like a normal function (so u do the while loop on ur UI thread which blocks it)
replace
speedMeter.run();<br />
SpeedMeter speedMeter = new SpeedMeter(this);
with
new Thread(new SpeedMeter(this)).start();
see https://docs.oracle.com/javase/tutorial/essential/concurrency/runthread.html for more infos on how to start a Runnable :)
The ideal way to do this would be to create an AsyncTask that would post a message to your UI thread, after it complete the task in the doInBackground() call.
Standards
also the interface structure you are following does not make sense and does not follow good standards. Usually an interface is used as a callback, which is basically what you are doing. But the standard is to say onSomethingChangedA() or onSomethingChangedB() from OnSomethingChangedListener interface.
I think your loop is always true so app hangs its better to create a boolean and use while(mboolean) and put this in your loop
if(something){
mboolean=false;
}
you can also use CountDownTimer.
for example:
new CountDownTimer(miliseconds,1000)
//if you have download speed and download size you can find miliseconds
#Override
public void onTick(long l) {
//something you want to do every seconds
}
#Override
public void onFinish() {
//something you want to do on finish
}
EDIT: I've found that what I'm describing below only occurs on my emulated device (Nexus 5, target api 19, 4.4.2 with Intel Atom (x86) cpu), but NOT on my physical device (HTC One)....
EDIT2: Edit1 was due to an IllegalStateException that I didnt catch. Added some code to check if the thread was already running before trying to start it. This combined with the accepted answer resolved my issue.
I have implemented an activty that starts a new thread in the activity's onCreate method, like this:
...
private boolean running;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
running = true;
new Thread(null, work, "myThread").start();
}
Runnable work = new Runnable() {
#Override
public void run() {
while (running) {
//Doing work
}
}
};
I'm "pausing" my thread with my activity's onPause method, like this:
#Override
protected void onPause() {
running = false;
super.onPause();
}
So I thought that resuming it would be just as easy...ยจ
#Override
protected void onResume(){
running = true;
super.onResume();
}
but my thread isn't resuming. Any ideas why? Thankful for any help.
Marcus
All of the answers i think have some issues about your running variable because you can not write and read a variable from two different Threads without synchronized block so i post my own answer:
package com.example.threadandtoast;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.widget.Toast;
public class MainActivity extends ActionBarActivity {
public class MonitorObject{
public boolean running = true;
public String message = "";
public boolean mustBePost = true;
}
Thread t;
int threadNameCounter = 0; // i use this variable to make sure that old thread is deleted
// when i pause, you can see it and track it in DDMS
Runnable work = new Runnable() {
boolean myRunning;
#Override
public void run() {
synchronized(mSync) {
myRunning = mSync.running;
}
while (myRunning) {
runOnUiThread(new Runnable() { // in order to update the UI (create Toast)
#Override // we must switch to main thread
public void run() {
// i want to read the message so i must use synchronized block
synchronized(mSync) {
// i use this variable to post a message just for one time because i am in an infinite loop
// if i do not set a limit on the toast i create it infinite times
if(mSync.mustBePost){
Toast.makeText(MainActivity.this, mSync.message, Toast.LENGTH_SHORT).show();
// the message post so i must set it to false
mSync.mustBePost = false;
// if i am going to pause set mSync.running to false so at the end of infinite loop
//of thread he reads it and leaves the loop
if(mSync.message.equals("Main Activity is going to pause")){
mSync.running=false;
}
}
}
}
});
synchronized(mSync) {
myRunning = mSync.running;
}
}
}
};
final MonitorObject mSync = new MonitorObject();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
protected void onPause() {
super.onPause();
synchronized(mSync) {
// mSync.running = false; you can not set it here because
// it is possible for the thread to read it and exit the loop before he posts your message
mSync.mustBePost=true;
mSync.message = "Main Activity is going to pause";
}
}
#Override
protected void onResume(){
super.onResume();
threadNameCounter++;
synchronized(mSync) {
mSync.running = true;
mSync.mustBePost=true;
mSync.message = "Main Activity is going to resume";
}
t = new Thread(work,"My Name is " + String.valueOf(threadNameCounter));
t.start();
}
}
Or you can use this code:
public class MainActivity extends ActionBarActivity {
Thread t;
int threadNameCounter = 0; // i use this variable to make sure that old thread is deleted
// when i pause, you can see it in DDMS
String message = "";
boolean isPost = false;
Runnable work = new Runnable() {
#Override
public void run() {
while (true) {
runOnUiThread(new Runnable() {
#Override
public void run() {
if(!isPost){
Toast.makeText(MainActivity.this, message, Toast.LENGTH_SHORT).show();
isPost = true;
if( message.equals("Main Activity is going to pause")){
t.interrupt();
}
}
}
});
if(Thread.currentThread().isInterrupted()){
break;
}
}
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
protected void onPause() {
super.onPause();
message = "Main Activity is going to pause";
isPost = false;
}
#Override
protected void onResume(){
super.onResume();
message = "Main Activity is going to resume";
isPost = false;
threadNameCounter++;
t = new Thread(work,"My Name is " + String.valueOf(threadNameCounter));
t.start();
}
}
you can also use semaphore or wait-notify approach.
i put public String message = ""; and public boolean mustBePost = true; in to mSync object but it is
not necessary because only main thread have an access to them.
if you have any problem please ask.
The statement running = false; will stop execution of the Thread, instead of pausing it. Use two variables: One for stopping current Thread, and another for pausing and resuming the Thread, as follow:
boolean isThreadPause=false;
Runnable work = new Runnable() {
#Override
public void run() {
while (running) {
if (!isThreadPause) {
// Doing work
}
}
}
};
In the onPause event of the Activity, set isThreadPause to true, and in the onResume event, set isThreadPause to false.
This is because your Runnable object stops when the while loop stops. You could try this:
Runnable work = new Runnable() {
#Override
public void run() {
while () {
if(running){
//Doing work
}
}
}
};
I've got an activity with the following code:
Handler dataLoaderHandler = new Handler();
int mInterval = 3000;
public MyActivity myself;
Thread dataLoader = new Thread( new Runnable() {
#Override
public void run() {
Log.e("MyActivity","ReloadData");
new DataLoader(new JSONData(myself)).execute(Configuration.dataURL);
dataLoaderHandler.postDelayed(dataLoader, mInterval);
}
});
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
myself = this;
... some other stuff...
dataLoader.start();
}
... other code ...
#Override
protected void onDestroy() {
super.onDestroy();
Log.e("MyActivity","onDestroy ending thread");
try { dataLoader.join(); } catch(Exception e) { e.printStackTrace(); }
Log.e("MyActivity","onDestroy called");
}
When I hit the back button my activity gets destroyed but the thread still continues to run every 3 seconds. Why is the thread not stopped or better said deleted?
Because you haven't stopped it. Since calling stop() on a thread is already deprecated, you should stop the thread within the thread itself. This can be easily done by calling return on it.
However, don't expect your Thread to finish immediately you hit your back button. The Thread will probably run up until Android OS determines it has to (basically, it may take a while to stop even you call it).
You may want to check that question I made a time ago, it's well answered.
try:
boolean finished=false;
Handler dataLoaderHandler = new Handler();
int mInterval = 3000;
public MyActivity myself;
Thread dataLoader = new Thread( new Runnable() {
#Override
public void run() {
if(!finished){
Log.e("MyActivity","ReloadData");
new DataLoader(new JSONData(myself)).execute(Configuration.dataURL);
dataLoaderHandler.postDelayed(dataLoader, mInterval);}
}
});
#Override
protected void onStop() {
super.onStop();
finished=true;
}
I am learning how to use Looper and Handler class in android development
http://developer.android.com/reference/android/os/Looper.html
The example given in the android development is not clear to understand what is the usage and the how to use it.I dont know how to add Handler inside the Looper and how I can call the Looper to loop.
If it is available, can anyone give me a simple example to use it.
public class LooperTest extends Activity{
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
private class LooperTesting extends Thread
{
public Handler handler;
public void run()
{
Looper.prepare();
handler = new Handler()
{
public void handlerMessage(Message msg)
{
// do something
}
};
Looper.loop();
}
}
}
Hope this Links are helps to u
Link1
Link2
Link3
In your example you have only defined a Thread with a Looper. You need to start the Thread with the associated Looper before you can post any messages to it. I've added some code to your example to illustrate what has to be done:
public class LooperTest extends Activity{
LooperTesting mBgThread;
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mBgThread = new mBgThread();
// Start the thread. When started, it will wait for incoming messages.
// Use the post* or send* methods of your handler-reference.
mBgThread.start();
}
public void onDestroy() {
// Don't forget to quit the Looper, so that the
// thread can finish.
mBgThread.handler.getLooper().quit();
}
private class LooperTesting extends Thread
{
public Handler handler;
public void run()
{
Looper.prepare();
handler = new Handler()
{
public void handlerMessage(Message msg)
{
// do something
}
};
Looper.loop();
}
}
}