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);
}
}
}
};
Related
Here is my JSON Object
{
"services": [
{
"name": "Our Test Service",
"authCode": 981846
},
{
"name": "BuckeyeLink",
"authCode": 272860
}]
}
I have the link to the part of the website I want to continually do GET requests asking for this specific information, but my question is what is the best way to do this?
Shoudl I just make an infinite loop that is constantly querying the server? or is there a better way to do it?
An infinite loop would not be useful (and never is). What you want is use a Handler and a Runnable together in a stepped fashion (say, every few seconds). In your activity you could define a handler as private member, then a couple of methods such as pollForData and cancelPolling (because eventually, you might no longer be interested in polling for changes to the data, and you should cancel that to be kind to the device's battery/performance).
As an example, a simple activity:
public class YourActivity extends AppCompatActivity {
private static final String TAG = "YourActivity";
private long retryInterval = 3000; // 3 seconds
private Handler handler = new Handler(Looper.getMainLooper());
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.your_layout);
}
#Override
protected void onResume() {
// when the activity starts, start/resume polling
super.onResume();
pollForData();
}
#Override
protected void onPause() {
// when the activity is paused, also pause the polling
super.onPause();
cancelPolling();
}
private void pollForData() {
// I'm a bit heavy on the null checks...
if(runnable != null) {
Runnable r = new Runnable() {
#Override
public void run() {
Log.d(TAG, "Runnable executing");
// do the thing that fetches the data
getData();
// repost this runnable on the interval we defined
handler.postDelayed(this, retryInterval);
}
};
// post the first runnable to start the chain
handler.postDelayed(r, retryInterval);
}
}
private void cancelPolling() {
// cancel all runnable callbacks for the handler; we're gtg
if(handler != null) {
handler.removeCallbacksAndMessages(null);
}
}
private void getData() {
// your code to fetch your JSON data from a url
}
}
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.
}
}
}
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 have a Service which is performing a data update. I have an activity which attaches a listener to the service (via a local binding). The listener receives progress updates. Upon receiving a progress update, it schedules a runnable to be run on the UI thread. Here's the code (updated to show the full listing):
public class MyActivity extends Activity {
static final int UPDATE_DIALOG = 0;
ProgressDialog updateDialog;
private TaskService taskService;
private ServiceConnection taskServiceConnection = new ServiceConnection() {
private final TaskServiceObserver taskServiceObserver = new TaskServiceObserver() {
public void updateProgress(final int progress, final int total) {
runOnUiThread(new Runnable() {
public void run() {
if (updateDialog == null || !updateDialog.isShowing()) {
showDialog(UPDATE_DIALOG);
}
updateDialog.setProgress(progress);
}
});
}
public void updateCompleted() {
runOnUiThread(new Runnable() {
public void run() {
dismissDialog(UPDATE_DIALOG);
startNextActivity();
}
});
}
};
public void onServiceConnected(ComponentName name, IBinder binder) {
taskService = ((LocalBinder) binder).getService();
taskService.addObserver(taskServiceObserver);
}
public void onServiceDisconnected(ComponentName name) {
taskService.removeObserver(taskServiceObserver);
taskService = null;
}
};
protected void onStart() {
super.onStart();
Intent intent = new Intent(this, TaskService.class);
startService(intent);
bindService(intent, taskServiceConnection, Context.BIND_AUTO_CREATE);
}
protected void onStop() {
super.onStop();
if (taskService != null) {
unbindService(taskServiceConnection);
}
}
protected Dialog onCreateDialog(int id) {
switch (id) {
case UPDATE_DIALOG:
updateDialog = new ProgressDialog(this);
updateDialog.setTitle("My App");
updateDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
updateDialog.setMessage("Preparing to run for the first time...");
return updateDialog;
default:
return null;
}
}
}
If I tap the home button while the dialog is showing, then return to the app, I get a crash on the showDialog line. With the debugger I was able to determine that the activity is in the finished state.
What would be an appropriate check to put in my runnable which would determine whether it is safe to call showDialog?
I would personnally dismiss the progress dialog when the activity goes to pause (override onPause) and recreate it if necessary when the activity is resumed (override onResume). You could be leaking memory by keeping references to your activity in other separate objects (your dialog)
You should detach the listener in the onPause method so that since your activity is going into the background, the listener won't fire and try to update the UI.
The solution I ended up going with was to create a flag taskServiceBound which was set to true after binding to the service in onStart and set to false before unbinding from the service in onStop. Because the flag is updated on the UI thread, I can use it to gate the Runnables in updateProgress and updateCompleted. For example:
public void updateCompleted() {
runOnUiThread(new Runnable() {
public void run() {
if (taskServiceBound) {
dismissDialog(UPDATE_DIALOG);
startNextActivity();
}
}
});
}