Can someone tell me why this doesn't work? I am trying to figure out how to use thread/runnable. Thread doesnt do much but just to loop and let the main thread know to update the text. I dont know what I missed, the centertext doesnt update. Thanks so much.
public class MainActivity extends AppCompatActivity {
TextView centerText;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
final SysTimeObj sysTimeObj = new SysTimeObj();
centerText = findViewById(R.id.centerText);
Handler stHandler = new Handler(getMainLooper()){
#Override
public void handleMessage(#NonNull Message msg) {
super.handleMessage(msg);
centerText.setText("thread updated");
}
};
startThread(sysTimeObj, stHandler);
}
public void startThread(SysTimeObj sysTimeObj, Handler handler){
clockThread rc = new clockThread(sysTimeObj, handler);
Thread t1 = new Thread(rc);
t1.start();
}
}
public class clockThread implements Runnable {
//private String sysTime;
private Handler handler;
SysTimeObj sysTimeObj;
public clockThread(SysTimeObj sysTimeObj, Handler mHandler){
//sysTime = GregorianCalendar.getInstance().getTime().toString();
this.sysTimeObj = sysTimeObj;
handler = mHandler;
}
#Override
public void run() {
sysTimeObj.setTime();
handler.postDelayed(this, 100);
}
}
You want to do something on the Main/UI Thread after a certain amount of time ? On Android, you don't need a new thread for that.
The Main Thread has a message queue that you can Post to. That message queue is emptied on a regular basis. Posted messages can be configured to be executed at a later time (which is what you seem to want).
To post messages, you need to create a Handler for the target thread. This Handler will let you send messages to that thread. Then, Post a Runnable to that thread using one of the posting methods availlable (here, postDelayed).
You'll end with something like this :
public class MainActivity extends AppCompatActivity {
private TextView yourTextView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
yourTextView = findViewById(R.id.yourTextView);
Handler handler = new Handler(getMainLooper());
handler.postDelayed(new Runnable() {
#Override
public void run() {
yourTextView.setText("Updated after 100 ms");
}
}, 100);
}
}
If threads is really what you want, I suggest you look at AsyncTasks. You might also want to look at the official documentation about Process and Threads on Android Developpers.
Related
I have a .lrc file and I need to go over every line with a CountDownTimer. I have tried using AsyncTask to do so but I get the error:
Caused by: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
On line new CountDownTimer... I tried doing it with runnable but I still get the same error. My goal is to get it to go over every line in .lrc file which looks like this:
[00:04.15]Help me, it's like the walls are caving in
[00:10.46]Sometimes I feel like giving up
[00:13.63]But I just can't
...
I am not sure how efficient it is to do it the way I am trying to do. I am thinking of going through every line in the doInBackground(). If there is a better way to do it then let me know. But to begin with, why am I getting the EXCEPTION ?
Just to note.. I have simplified the code as much as I could so it would be easier to understand what I am trying to do.
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MyView myView = new
myView.play();
}
}
public class MyView{
public void play() {
new CustomAsync().execute();
}
}
class CustomAsync extends AsyncTask<Lyric, Void, Void> {
protected Void doInBackground(Lyric... param) {
startLyricCountDownTimer(param);
return null;
}
protected void onPostExecute(Void param) {
//Print Toast or open dialog
}
private void startLyricCountDownTimer(Lyric lyric){
new CountDownTimer(30000, 10) { //This is where it throws the error
public void onTick(long millisUntilFinished) {
//Do the thing
}
public void onFinish() {
}
}.start();
}
}
EDIT
Is it better to go with the AsyncTask and do like Son Truong suggested or to use the following code for each and every lrc line?
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
new CountDownTimer(millisInFuture,countDownInterval) {
#Override
public void onTick(
CountDownTimer uses a Handler to post messages to a Message Queue of a Thread which has a Looper. onTick and onFinish will be called on which thread based on where you create CountDownTimer instance.
In your case because you create CountDownTimer instance in doInBackground method of AsyncTask so these two methods will be call on AsyncTask thread.
In constructor of CountDownTimer, it will create Handler instance as well. The Handler will check whether or not current thread has a Looper, if not it will throw a RuntimeException with message.
Can't create handler inside thread that has not called
Looper.prepare()
Because AsyncTask uses a thread which has no Looper, that why your app crashes.
My suggestion is in doInBackground method you open a connection to .lrc file and read each line, for each line read, use runOnUIThread to send the line to UI thread (then you can process the line read there by display a Toast on screen, etc).
Update: I will demo how to read line by line from a file then display it on a text view each 3 seconds.
First write a class which read from an inputstream line by line
static class ReadLyricTask extends AsyncTask<InputStream, String, Void> {
WeakReference<MainActivity> mMainActivity;
ReadLyricTask(MainActivity activity) {
mMainActivity = new WeakReference<>(activity);
}
#Override
protected Void doInBackground(InputStream... inputStreams) {
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStreams[0]));
String line;
try {
while ((line = reader.readLine()) != null) {
publishProgress(line);
}
} catch (IOException e) {
// Do nothing.
} finally {
try {
inputStreams[0].close();
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
#Override
protected void onProgressUpdate(String... values) {
MainActivity activity = mMainActivity.get();
if (activity != null) {
activity.displayLyricLineOnTextView(values[0]);
}
}
}
Then just use it in MainActivity
public class MainActivity extends AppCompatActivity {
private static final int UPDATE_LYRIC_TEXT_INTERVAL = 3000; // Change lyric text each 3 seconds.
private int mCurrentInterval = 0;
private TextView mLyricTextView;
private Handler mHandler = new Handler();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mLyricTextView = findViewById(R.id.lyricText);
// I put a file named lyric.lrc in raw folder, for your case just open an input stream from a file.
InputStream inputStream = getResources().openRawResource(R.raw.lyric);
new ReadLyricTask(this).execute(inputStream);
}
private void displayLyricLineOnTextView(final String lyricLine) {
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
mLyricTextView.setText(lyricLine);
}
}, mCurrentInterval);
mCurrentInterval += UPDATE_LYRIC_TEXT_INTERVAL;
}
}
CountDownTimer runs in separate thread and no need of asynctask to run a timer.Best solution would be create a service and make service to trigger a timer.
As timer run on non ui thread, while updating ui make sure you update from UI thread.You could use UI handler or runOnUithread method to update view.
I want to change dynamically the text of a textview, but I will need the same logic if I want to make a game thread, so I need to make the communication between the main one and the second one.
I have the files :
MainActivity
public class MainActivity extends ActionBarActivity {
public static Handler mHandler;
Runnable thread = new SampleThread();
TextView txt1 = (TextView) findViewById(R.id.txt1);
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActionBar actionBar = getSupportActionBar();
actionBar.hide();
//hiding status bar
if (Build.VERSION.SDK_INT < 16) {
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
} else {
View decorView = getWindow().getDecorView();
int uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN;
decorView.setSystemUiVisibility(uiOptions);
}
setContentView(R.layout.activity_main);
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
// i want to change the text of txt1 here
}
};
new Thread(thread).start();
}
}
SampleThread
package com.example.katsar0v.myapplication;
import android.util.Log;
/**
* Created by Katsar0v on 1/21/2015.
*/
public class SampleThread implements Runnable {
#Override
public void run() {
int two = 0;
while(two<10) {
two++;
try {
Thread.sleep(1000);
//instead of logging, i want to send the text to main UI
Log.d("MSG", String.valueOf(two + "sec"));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
The problem I see is, how do I change the text with the handler, when my thread is in another file? Or should I make the second class static within the first one (and what should I do when the code gets really long, it can't be all in one file)?
You could implement a custom Interface in order to handle it from your main activity.
On your SampleThread:
public interface TextViewChangeListener
{
public void onTextViewChanged(String newName);
}
TextViewChangeListener mListener;
Then call mListener.onTextViewChanged(String newName) wherever you want to have the new name in your TextView. Remember to initialize mListener with an instance of your MainActivity first, otherwise you will get a null pointer exception. You can do that either in the constructor of SampleThread or by creating a method for the purpose.
In your activity you should implement SampleThread.TextViewChangeListener and override the onTextViewChanged.
#Override
public void onTextViewChanged(String newName)
{
//MyTextView.setText(newName);
}
Edit: untested code:
MainActivity:
public class MainActivity extends ActionBarActivity implements SampleThread.TextViewChangeListener {
#Override
public void onTextViewChanged(Message msg)
{
// process incoming messages here
// i want to change the text of txt1 here
}
public static Handler mHandler;
Runnable thread = new SampleThread(this);
TextView txt1 = (TextView) findViewById(R.id.txt1);
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActionBar actionBar = getSupportActionBar();
actionBar.hide();
//hiding status bar
if (Build.VERSION.SDK_INT < 16) {
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
} else {
View decorView = getWindow().getDecorView();
int uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN;
decorView.setSystemUiVisibility(uiOptions);
}
setContentView(R.layout.activity_main);
new Thread(thread).start();
}
}
SampleThread:
package com.example.katsar0v.myapplication;
import android.util.Log;
/**
* Created by Katsar0v on 1/21/2015.
*/
public class SampleThread implements Runnable
{
public interface TextViewChangeListener
{
public void onTextViewChanged(Message msg);
}
public SampleThread(TextViewChangeListener mListener)
{
this.mListener = mListener;
}
TextViewChangeListener mListener;
#Override
public void run() {
int two = 0;
while(two<10) {
two++;
try {
Thread.sleep(1000);
mListener.onTextViewChanged(String.valueOf(two + "sec"));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Let me know if that helped.
You can find some examples in Grafika, which does a lot of work off the UI thread. For example, TextureFromCameraActivity has a pair of handlers, one for the UI thread, one for the renderer thread. In onResume() you can see the main thread passing its handler to the renderer through a constructor, then retrieving the renderer thread's handler with a method call.
ContinuousCaptureActivity has a slightly different approach, using a Handler that also implements a callback interface. The handler object is passed to the CircularEncoder constructor as an interface instance. The public callback methods use the Handler internally.
The only tricky bit is if you're passing a Handler out of the non-UI thread. You either need to do it before the thread starts, or use appropriate thread synchronization operations to avoid data races.
You don't need to have your classes in the same file (and you really shouldn't unless one is nested inside the other). If they're in the same package then the default (package) scope will let them see each other. The first example from Grafika uses nested / private classes, the second example is more spread out.
Of course, if all you're trying to do is submit UI events from a non-UI thread, you can just use Activity.runOnUiThread().
I have a very simple test application I'm making, and to set the seekBar's position I'm using a runnable. Although I have very little experience with actually working with a runnable.
public class MySpotify extends Activity implements Runnable {
private SeekBar progress;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.spotify_app);
myProgress = (SeekBar) findViewById(R.id.myBar);
}
#Override
public void run() {
myProgress.setProgress(25);
}
}
If I move myProgress.setProgress(25); into the onCreate then it works. But I want it to be set off in the runnable. Any ideas?
You need to post() a Runnable to a Thread for it to execute. Try calling post(this); inside onCreate().
Try
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.spotify_app);
myProgress = (SeekBar) findViewById(R.id.myBar);
myProgress.post(new Runnable()
{
public void run()
{
myProgress.setProgress(25);
}
});
}
You need something to run the post() method on
You can start the run method by just calling run();
Be aware that it will execute on the main thread.
Also be aware that it will run only once since there is no loop.
if you want to update while doing something else you sjould create a new thread.
example:
public class MySpotify extends Activity{
private SeekBar myProgress; //I asume it is call "myProgress" instead of "progress"
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.spotify_app);
myProgress = (SeekBar) findViewById(R.id.myBar);
ThreadExample example = new ThreadExample();
example.start();
/* Start a new thread that executes the code in the thread by creating a new thread.
* If ou call example.run() it will execute on the mainthread so don't do that.
*/
}
private class ThreadExample extends Thread{
public void run() {
myProgress.setProgress(25);
}
}
}
So I wanted to implement Timer in my Anroid program and I found out the better way to do that is using Handler class.
First I decided to write the simplest program using Handler - the text is set after 1 second. I'm totall beginner in Android, so I went through some tutorials on web especially that one http://developer.android.com/resources/articles/timed-ui-updates.html , but still my application shows error ("application mTimer stopped").
So could anyone point out where exactly am I making mistake? I would be gratefull, here's the code:
public class mTimer extends Activity {
TextView tv;
Button button1,button2;
Handler mHandler;
private Runnable myTask = new Runnable() {
public void run() {
tv.setText("text");
}
};
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
button1=(Button)findViewById(R.id.button1);
tv=(TextView)findViewById(R.id.textView1);
button1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mHandler.postDelayed(myTask, 100);
}
});
}
}
You should initialize your Handler in your onCreate method with at least a code like mHandler = new Handler();.
Note, that the myTask task will be run on the UI thread, since your handler is declared there
API Docs for Handler.postDelayed:
The runnable will be run on the thread
to which this handler is attached.
How to call Main thread from secondary thread in Android?
The simplest way is to call runOnUiThread(...) from your thread
activity.runOnUiThread(new Runnable() {
public void run() {
... do your GUI stuff
}
});
My recommendation to communicate threads in the same process is sending messages between those threads. It is very easy to manage this situation using Handlers:
http://developer.android.com/reference/android/os/Handler.html
Example of use, from Android documentation, to handling expensive work out of the ui thread:
public class MyActivity extends Activity {
[ . . . ]
// Need handler for callbacks to the UI thread
final Handler mHandler = new Handler();
// Create runnable for posting
final Runnable mUpdateResults = new Runnable() {
public void run() {
updateResultsInUi();
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
[ . . . ]
}
protected void startLongRunningOperation() {
// Fire off a thread to do some work that we shouldn't do directly in the UI thread
Thread t = new Thread() {
public void run() {
mResults = doSomethingExpensive();
mHandler.post(mUpdateResults);
}
};
t.start();
}
private void updateResultsInUi() {
// Back in the UI thread -- update our UI elements based on the data in mResults
[ . . . ]
}
}
You'll need a Handler that passes the information back to the main thread.
Also, it's good to remember that if you get your secondary thread through an AsyncTask, you have the option to call onProgressUpdate(), onPostExecute(), etc., to do work on the main thread.
Sample code using HandlerThread
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final Handler responseHandler = new Handler(Looper.getMainLooper()){
#Override
public void handleMessage(Message msg) {
//txtView.setText((String) msg.obj);
Toast.makeText(MainActivity.this,
"Result from UIHandlerThread:"+(int)msg.obj,
Toast.LENGTH_LONG)
.show();
}
};
HandlerThread handlerThread = new HandlerThread("UIHandlerThread"){
public void run(){
/* Add your business logic to pupulate attributes in Message
in place of sending Integer 5 as in example code */
Integer a = 5;
Message msg = new Message();
msg.obj = a;
responseHandler.sendMessage(msg);
System.out.println(a);
}
};
handlerThread.start();
}
}
Explanation:
In above example, HandlerThread post a Message on Handler of UI Thread, which has been initialized with Looper of UI Thread.
final Handler responseHandler = new Handler(Looper.getMainLooper())
responseHandler.sendMessage(msg); sends Message from HandlerThread to UI Thread Handler.
handleMessage processes Message received on MessageQueue and shows a Toast on UI Thread.