3

I have the class ScheduleTimer that works on the array of dates. Here it is:

class ScheduleTimer {

    public TextView textView;

    private Timer dateTimer;

    private Timer remainderTimer;

    private Date formatDate = new Date();

    private Date nextDate;

    private boolean remainderTimerStarted;

    private static final long REMINDER_UPDATE_INTERVAL = 1000;

    private static String[] DATES;

    private int currentIndex;

    public ScheduleTimer(final TextView t) {
        textView = t;
        dateTimer = new Timer();
    }

    public void main(String[] dates) throws ParseException {
        checkDates(dates);
        run();
    }

    private void checkDates(String[] dates) throws ParseException {
        List<String> list = new ArrayList<>();
        DateFormat format = new SimpleDateFormat("dd.MM.yyyy HH:mm", Locale.ENGLISH);
        for(String date : dates) {
            long current = System.currentTimeMillis() + 1000;
            if(format.parse(date).getTime() - current > 0) {
                list.add(date);
            }
        }
        DATES = new String[list.size()];
        list.toArray(DATES);
    }

    private void run() {
        nextDate = parseDate(DATES[currentIndex]);
        schedule();
    }

    public void schedule() {
        runSecondsCounter();
        dateTimer.schedule(new TimerTask() {

            @Override
            public void run() {

                System.out.println("Current date is:" + new Date());
                currentIndex++;
                if (currentIndex < DATES.length) {
                    nextDate = parseDate(DATES[currentIndex]);
                    System.out.println("Next date is:" + nextDate);
                    schedule();
                } else {
                    remainderTimer.cancel();
                }
            }
        }, nextDate);

    }

    private Date parseDate(String nextDate) {
        Date date = null;
        DateFormat format = new SimpleDateFormat("dd.MM.yyyy HH:mm",
                Locale.ENGLISH);
        try {
            date = format.parse(nextDate);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return date;
    }

    private void runSecondsCounter() {
        if (remainderTimerStarted) {
            remainderTimer.cancel();
        }

        remainderTimer = new Timer();
        remainderTimer.scheduleAtFixedRate(new TimerTask() {

            @Override
            public void run() {
                remainderTimerStarted = true;
                long remains = nextDate.getTime() - new Date().getTime();
                System.out.println("Remains: " + (remains / 1000) + " seconds");
                formatDate.setTime(remains);
                textView.setText(formatDate.toString());
            }
        }, REMINDER_UPDATE_INTERVAL, REMINDER_UPDATE_INTERVAL);
    }
}

It works fine if I run it just like a Java application, not android, and it prints out every counted second in the console. But when it comes to run it in the android environment, it either says that the UI thread cannot be touched from any other thread, or it gives me NullPointerException in the method run() of the class ScheduleTimer.

I'm using it like this: new ScheduleTimer(textView).main(new String[] {"13.04.2015 13:59", "13.04.2015 14:14", "13.04.2015 14:15"});

I tried using AsyncTask or Handler, but probably, I didn't do it right. Anyway, I need to find the way to update my TextView somehow using this class.

Could anybody help me with that? How can I run it normally in my onCreateView method and pass the needed TextView correctly?

Denis Yakovenko
  • 3,241
  • 6
  • 48
  • 82
  • 5
    `runOnUiThread(new Runnable(){...});` – Alex Salauyou Apr 13 '15 at 10:57
  • Could you please be more specific? what should I write in the method run() of the Runnable class? – Denis Yakovenko Apr 13 '15 at 10:59
  • 1
    An Async Task would be easier for you to implement, it has exact methods you require. You can do your processing in the doInBackground() method, pass your end result to the onPostExecute() method and for intermediate updates to your views you can use onProgressUpdate() method. – Skynet Apr 13 '15 at 11:00
  • @Skynet, Well, I've actually tried doing that, but I guess I did it wrong. What I did was extend my current class from AsyncTask and implement the method doInBackground there. In the method doInBackground I started the method run() of the ScheduleTimer class. But it didn't work:( – Denis Yakovenko Apr 13 '15 at 11:03
  • 1
    doInBackground is a separate thread alltogether you dont need to create a separate thread there. Also it is better to write your AsyncTask separately as a standalone unit - pass some arguments - process in doInBackground get the final result in onPostExecute() and update intermediate values using onProgressUpdate() – Skynet Apr 13 '15 at 11:04

3 Answers3

3

runOnUiThread() method will send your Runnable to execute onto the main thread. In run(), you can manipulate UI controls:

@Override
public void run() {
     remainderTimerStarted = true;
     long remains = nextDate.getTime() - new Date().getTime();
     formatDate.setTime(remains);
     runOnUiThread(new Runnable() {    // <= here!
          @Override
          public void run() {
              textView.setText(formatDate.toString());
          }
     });
 }

Check this for more explanation.

Community
  • 1
  • 1
Alex Salauyou
  • 14,185
  • 5
  • 45
  • 67
2

A skeleton of your AsyncTask would be:

public class ListLoader extends AsyncTask<Void, Void, String> {

        ProgressDialog Asycdialog = new ProgressDialog(CreateGroup.this);
        @Override
        protected void onPreExecute() {
            // TODO Auto-generated method stub
            System.out.println("Pre Execute");
            Asycdialog.setMessage("Working");
            Asycdialog.getWindow().setGravity(Gravity.CENTER_VERTICAL);
            Asycdialog.getWindow().setGravity(Gravity.CENTER_HORIZONTAL);
            Asycdialog.setCancelable(false);
            Asycdialog.show();
            super.onPreExecute();
        }

        protected void onPostExecute(String result) {
            Asycdialog.cancel();

            //Play with result here - Update UI
        }

        @Override
        protected String doInBackground(Context... params) {

            //Memory intense or long running operation here
            publishProgress(progress); //Publish your progress - update a textView

            return "result will be sent to onPostExecute()";

        }

        protected void onProgressUpdate(String... values) {

            super.onProgressUpdate(values);
            Asycdialog.setMessage("" + values[0]);
        }


    }
Skynet
  • 7,820
  • 5
  • 44
  • 80
2

The complete answer is: Your fragment:

public class PlaceholderFragment extends Fragment {

        public PlaceholderFragment() {
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState) {
            View rootView = inflater.inflate(R.layout.fragment_main, container, false);

            TextView textView = (TextView) rootView.findViewById(R.id.tv);

            try {
                new ScheduleTimer(textView, getActivity())
                        .main(new String[] {"13.04.2015 13:59", "13.04.2015 14:14", "13.04.2015 14:15"});
            } catch (ParseException e) {
                e.printStackTrace();
            }

            return rootView;
        }
    }

Your ScheduleTimer class:

class ScheduleTimer {

    public TextView textView;

    private Timer dateTimer;

    private Timer remainderTimer;

    private Date formatDate = new Date();

    private Date nextDate;

    private boolean remainderTimerStarted;

    private static final long REMINDER_UPDATE_INTERVAL = 1000;

    private static String[] DATES;

    private int currentIndex;

    private Activity activity;

    public ScheduleTimer(final TextView t, Activity a) {
        textView = t;
        activity = a;
        dateTimer = new Timer();
    }

    public void main(String[] dates) throws ParseException {
        checkDates(dates);
        run();
    }

    private void checkDates(String[] dates) throws ParseException {
        List<String> list = new ArrayList<>();
        DateFormat format = new SimpleDateFormat("dd.MM.yyyy HH:mm", Locale.ENGLISH);
        for(String date : dates) {
            long current = System.currentTimeMillis() + 1000;
            if(format.parse(date).getTime() - current > 0) {
                list.add(date);
            }
        }
        DATES = new String[list.size()];
        list.toArray(DATES);
    }

    private void run() {
        nextDate = parseDate(DATES[currentIndex]);
        schedule();
    }

    public void schedule() {
        runSecondsCounter();
        dateTimer.schedule(new TimerTask() {

            @Override
            public void run() {

                System.out.println("Current date is:" + new Date());
                currentIndex++;
                if (currentIndex < DATES.length) {
                    nextDate = parseDate(DATES[currentIndex]);
                    System.out.println("Next date is:" + nextDate);
                    schedule();
                } else {
                    remainderTimer.cancel();
                }
            }
        }, nextDate);

    }

    private Date parseDate(String nextDate) {
        Date date = null;
        DateFormat format = new SimpleDateFormat("dd.MM.yyyy HH:mm",
                Locale.ENGLISH);
        try {
            date = format.parse(nextDate);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return date;
    }

    private void runSecondsCounter() {
        if (remainderTimerStarted) {
            remainderTimer.cancel();
        }

        remainderTimer = new Timer();
        remainderTimer.scheduleAtFixedRate(new TimerTask() {

            @Override
            public void run() {
                remainderTimerStarted = true;
                long remains = nextDate.getTime() - new Date().getTime();
                System.out.println("Remains: " + (remains / 1000) + " seconds");
                formatDate.setTime(remains);

                activity.runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        textView.setText(formatDate.toString());
                    }
                });

            }
        }, REMINDER_UPDATE_INTERVAL, REMINDER_UPDATE_INTERVAL);
    }
}
Ibrahim Disouki
  • 2,642
  • 4
  • 21
  • 52