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.
Related
I'm trying to start a thread to perform a piece of code inside a while loop. The problem is that the UI hangs and the progress dialog stops immediately after being shown. when removing the handler object it runs fine, but no changes happen to the UI of course. What is the problem?
MainActivity class
public class MainActivity extends AppCompatActivity {
...
Handler mHandler = new Handler(Looper.getMainLooper()) {
#Override
public void handleMessage(Message msg) {
mProgressDialog.dismiss();
mTextOutput.setText(mRepeatedText);
}
};
...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
...
mProgressDialog = new ProgressDialog(MainActivity.this);
mProgressDialog.setTitle("Text Repeater");
mProgressDialog.setMessage("We're generating your text...");
mProgressDialog.show();
Runnable mRunnable = new Runnable() {
#Override
public void run() {
mRepeatedText = StringUtils.repeat(mText, mNumberOfIterations);
mHandler.sendEmptyMessage(0);
}
};
mThread = new Thread(mRunnable);
mThread.start();
StringUtils class
public class StringUtils {
public static String repeat(String text, int numberOfIterations){
StringBuilder buffer = new StringBuilder(text.length()*numberOfIterations);
while(numberOfIterations-- > 0){
buffer.append(text);
}
return buffer.toString();
}
}
It looks like the return statement in the method repeat in the StringUtils class is not in the right place. This is probably what you are looking for:
public class StringUtils {
public static String repeat(String text, int numberOfIterations) {
StringBuilder buffer = new StringBuilder(text.length()*numberOfIterations);
while(numberOfIterations-- > 0){
buffer.append(text);
}
return buffer.toString();
}
}
The problem was in this line of code:
mTextOutput.setText(mRepeatedText);
When using a huge number of iterations, for example a 100,000 times. This makes a 100,000 copy of my entered word, sentence or whatever. And it takes time to load all that in the textview.
I'm currently working on my first Android application.
The application accesses a database to get some informations that I want to print on the screen. To send requests and get answers on the network, I need to use a new thread (I'll name it "N thread"), different from the UI Thread. This part is ok.
Now, I want to modify the variable eventList to get the values stored in a collection, in the N thread.
public class MainActivity extends AppCompatActivity {
public List<Event> eventList = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
/* I fill the list in an other thread */
new Thread(new Runnable() {
public void run(){
eventList = new WebService().getEvents(); //returns a list
}
// if I check here, eventList contains elements
}).start();
/* I check the result */
TextView respView = (TextView) findViewById(R.id.responseView);
if(eventList != null)
{
respView.setText("Ok");
} else {
respView.setText("Not ok");
}
...
}
The problem is : eventList is not modified. How can modify this variable and print it from the UI thread ?
Thank you for your help.
You can use runOnUiThread function or Handler to update UI from other thread. I suggest you reading the below tutorial first: AndroidBackgroundProcessing
Try this
new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... params)
{
eventList = new WebService().getEvents();
runOnUiThread(new Runnable() {
#Override
public void run() {
TextView respView = (TextView) findViewById(R.id.responseView);
if(eventList != null)
{
respView.setText("Ok");
} else {
respView.setText("Not ok");
}
}
});
}
}.execute();
private class EventsDownloader extends AsyncTask<Void, Void, Void> {
protected Long doInBackground(Void... params) {
eventList = new WebService().getEvents()
}
protected void onPostExecute(Void result) {
TextView respView = (TextView) findViewById(R.id.responseView);
if(eventList != null)
{
respView.setText("Ok");
} else {
respView.setText("Not ok");
}
}
}
This AsyncTask does what you want, the doInBackground runs on a thread and the 'onPostExecute' runs on the UI thread, and it's only called after the doInBackground finishes. This class is "managed" by the OS. To run it you just need to instantiate it and call 'execute'. I recommend doing something like this
The thing with your code is that the thread runs at the same time as the rest of your code (the calls to the setText), this means when it runs the setText the Thread is still getting the events.
Im using a webservice that get a data and stores in a String. I need to use this String but I cant take it. Global variables don't work in Threads. I'm using a traditional Thread new Thread() { public void run() {.
Example of AsyncTask:
public class Task extends AsyncTask<Params, Progress, String> {
// are you know how to use generic types?
protected String doInBackground(Params[] params){
// this code will run in seperate thread
String resultString;
return resultString;
}
protected void onPostExecute(String resultString){
// this code will call on main thread (UI Thread) in this thread you can update UI e.g. textView.setText(resultString);
}
}
Use LocalBroadcastManager to send and receive data.
This way you can avoid memory leak issues.
Here is code for activity
public class YourActivity extends Activity {
private void signal(){
LocalBroadcastManager.getInstance(YourActivity.this).registerReceiver(receiver, new IntentFilter("Your action name"));
Intent yourAction = new Intent(YourActivity.this, YourIntentService.class);
String string = "someData";
yourAction .putExtra("KEY_WITH_URL", string);
startService(yourAction);
}
private BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String string = intent.getStringExtra("KEY_WITH_ANSWER");
//Do your code
}
};
}
Here code for thread which download String or whatever
public class YourIntentService extends IntentService {
#Override
protected void onHandleIntent(Intent intent) {
// Download here
Intent complete = new Intent ("Your action name");
complete.putExtra("KEY_WITH_ANSWER", stringToReturn);
LocalBroadcastManager.getInstance(YourIntentService.this).sendBroadcast(complete);
}
}
You can use Thread instead of IntentService.
use a Handler created from the main thread. Then pass your data throuh it
use a weak reference of your activity in your thread; this way you can call directly the main thread - Activity.runOnUiThread(Runnable)
...
Activity activity = activityWeakReference.get();
if (activity != null && !activity.isFinishing() && !activity.isDestroyed()) {
activity.runOnUiThread(new Runnable() {
#Override
public void run()
{
// you are in main thread, pass your data
}
});
}
You can use Async task:
private class Whatever extends AsyncTask<Void, Void, String> {
protected String doInBackground(Void... void) {
// do your webservice processing
return your_string;
}
protected void onPostExecute(String result) {
// Retrieves the string in the UI thread
}
}
I have an app that has a Fragment with a ListView. I ping IP addresses on the network and when I get a response I add the IP address to a list. Once I've finished pinging the IP addresses, I put this list of IP addresses that replied to my ping into a ListView.
What I want to do is update this ListView as I'm pinging rather than doing after I've pinged all the IP addresses. To ping the IP addresses I'm using an AsyncTask which then calls a Runnable Thread. How do I tell the Fragment to update the UI from that Runnable class when I find an IP address?
The rough layout of my classes is below.
public class FinderFragment extends ListFragment {
private void CallFind(){
new Find().execute();
}
private class Find extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... voids) {
SearchNetwork searchNetwork = new SearchNetwork(ipAddress);
try {
searchNetwork.StartFind();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
UpdateDeviceList();
}
}
}
public class SearchNetwork {
public void StartFind() throws InterruptedException {
Thread t[] = new Thread[20];
for(int i=0; i<02; i++){
t[i] = new Thread(new FindDevices(i*5));
t[i].start();
}
for(Thread thread : t){
thread.join();
}
}
class FindDevices implements Runnable{
#Override
public void run() {
//Ping the IP addresses here
//I want to update UI from here
}
}
Try this....
getActivity().runOnUiThread(new Runnable(){
#Override
public void run(){
// Do whatever you want
}
});
use Handler, runOnUiThread or View.post
android.os.Handler handler = new android.os.Handler();
handler.post(new Runnable() {
#Override
public void run() {
//update here
}
});
You should use AsyncTask instead of raw threads! There is a method onPostExecute() which runs on the main UI thread and that's the point where you can update your UI ...
But you have to keep in mind, that you have to cancel the AsyncTask (threads as well) if the fragment gets destroyed, otherwise your task will continue to run and will try to change the UI of the fragment, but the fragment doesn't exist anymore which will lead to exceptions like IllegalStateException: Fragment not attached to Activity or View Not Attached Exceptions
We can use Handler, runOnUiThread, postDelayed and AsyncTask.
AsyncTask is good option other than rest as it extends the handler class.
When you have to code in thread use the doInBackground() method like this:
class myAsyncTask extends AsyncTask(Void,Void,Void){
public doInBackground(){
// do your code here
}
}
And call it on the UI thread like this:
new MyAsyncTask().execute();
if you want to auto update every x sec you can use this:
Runnable runnable = new Runnable() {
#Override
public void run() {
while(refreshing){
handler.post(new Runnable() {
#Override
public void run() {
//do your stuff here
}
});
try{
Thread.sleep(30000);
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
};
new Thread(runnable).start();
Try
publishProgress()
and catch it in the overidden method
#Override
protected void onProgressUpdate(Void... values) {
super.onProgressUpdate(values);
}
I have a thread that needs to be receiving data all the time from the network and I want this data to be displayed to an EditText object.
Obviously, I can't access the UI EditText from within my receiving thread; what I read is that I can use AsyncTask but reading the example in Painless Threading it seems to me that I have to be done with receiving the data before I can be able to post the results to the UI component.
I can't use post or postDelayed as both will be run over the UI thread and I can't block the UI to receive the data; I need to keep receiving the data all the time.
What other options do I have?
Use LocalBroadcastManager, your Activity containing TextView will start listening for broadcast:
public class MyActivity extends Activity {
private TextView mTextView;
BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getStringExtra("actionType");
if(action.equals("updateTextView")){
mTextView.setText("whatever you want to set");
}
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Start listening, you can put it on onResume too
LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver, new IntentFilter(MyActivity.class.getSimpleName()));
mTextView = (TextView) findViewById(R.id.something);
}
}
So whenever your Thread receive something that needs to update the screen, call this:
Intent intent = new Intent(MyActivity.class.getSimpleName());
intent.putExtra("actionType", "updateTextView");
// Once this is called, your broadcast receiver in MyActivity should receive it and start processing
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
Also remember to unregister it in onDestroy or onPause.
*side note: you need to import android support v4 library, and you can pass simple String or object over by intent using Intent.putExtra("","") and Inteng.getExtra("");
Another way is to implement a data listener interface.
public interface DataListener{
void onUpdateData(MyData data);
}
You activities that contain the UI components that need to be updated will implement this interface. It will specify what need to do with updated data.
You may want to keep all instances these data listener interface somewhere in your app.
I assume that you have a different thread to handle network sending/receiving actions. On receiving data, you just call:
dataListenerInstance.onUpdateData(data)
Then it will activate the handler that you have implemented in your activity.
In MainActivity call AsyncTask but make #Override method onPostExecute(..)
public class MainActivity extends ActionBarActivity{
...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(Utils.isNetworkAvailable(this)) {
DownloadFileFromURL downloader = new DownloadFileFromURL(){
#Override
protected void onPostExecute(Integer file_content) {
onCompleteLoad();
}
};
downloader.execute(new String[]{file_url, fileName});
...
onCompleteLoad(); - will be call in UI thread of MainActivity. You don't need even implements Interface!
Secon way more suitable for server solutions, but can also be used on the client it is Callable
public class DoGetSize implements Callable<Integer> {
private final String file_url;
private int lenghtOfFile = -1;
public DoGetSize(String file_url) {
this.file_url = file_url;
}
public Integer call() {
try {
URL url = new URL(file_url);
URLConnection connection = url.openConnection();
connection.connect();
lenghtOfFile = connection.getContentLength();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return lenghtOfFile;
}
}
And call this like:
FutureTask<Integer> task = new FutureTask(new DoGetSize(file_url));
ExecutorService es = Executors.newSingleThreadExecutor();
es.submit (task);
try {
Integer result = task.get();
File file = new File(fileName);
if(file.length() != result.intValue()) {
// Do something
...
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
You can send and receive any object in such a way
Full example see on github: https://github.com/app-z/OffLineShop/blob/master/app/src/main/java/net/appz/offlineshop/offlineshop/MainActivity.java
You can do this using a simple delegation.
class NonUIThread {
private NonUIThreadDelegate delegate; //NonUIThreadDelegate can be an interface or an object that has access to your UI thread like an Activity
public void setDelegate(NonUIThreadDelegate delegate) {
this.delegate = delegate;
}
private void doSomthing() {
//do something and at the end:
delegate.someMethodThatUpdatesThatComponent();
}
}
class TheUIThread implements NonUIThreadDelegate /*assuming you've decided to make NonUIThreadDelegate an interface*/ { // the "delegator"
/*
your code
*/
private void initiateNonUIThread() {
NonUIThread nonUIThread;
/*do whatever needed*/
nonUIThread.setDelegate(this);
nonUIThread.start();
}
public void someMethodThatUpdatesThatComponent() { //will be called by the non ui thread
//update the UI
}
}
It's explained a little better (of course using AsincTask) in here: Simple Delegation Pattern in Android