Issue
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(
Solution
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;
}
}
Answered By - Son Truong
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.