-1

I am trying to implement a thread that changes something on the UI in a Fragment. Therefore I need to refer to the main thread. Based on my research, I've found that the following code should do the trick:

new Handler(Looper.getMainLooper()).post(new Runnable() {
        @Override
        public void run() {
            Toast.makeText(menuActivity, "HELLO", Toast.LENGTH_SHORT).show();
        }
    });

This will execute only once though, even if the Looper should normally keep the thread alive. Trying to invoke Looper.prepare() inside the Handler will cause a RuntimeException as only one Looper is allowed per thread. Edit: My goal is to update a TextView permanently each second.

I have also tried the following:

Thread t = new Thread() {
        @Override
        public void run() {
            menuActivity.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("-----------TEST");
                }

            }); 
        }
    };
t.start();

But this will execute only once too.

I've also read this article, but I guess my first snippet of code is just a shorter version of the code shown in the article.

Where may my mistake be in any of these snippets of code?

This question is not a duplicate, due to the fact that I presented a totally different snippet of code which is the base of the problem I had. Furthermore, the Looper is explained more in depth in this thread.

ProgFroz
  • 197
  • 4
  • 17
  • Possible duplicate of [Update TextView Every Second](https://stackoverflow.com/questions/14814714/update-textview-every-second) – Janus Varmarken Aug 27 '18 at 17:12

2 Answers2

1

I understand that you want to update a text view repeatedly after 1 seconds. Here is a simple demo I just write.

Mockup screen

enter image description here

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:id="@+id/text_view_money"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:gravity="center"
        android:textColor="@android:color/black"
        android:textSize="20sp" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="30dp"
        android:layout_marginTop="30dp"
        android:orientation="horizontal">

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:onClick="startUpdateTextViewMoney"
            android:text="Start" />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:onClick="stopUpdateTextViewMoney"
            android:text="Stop" />

    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:orientation="horizontal">

        <EditText
            android:id="@+id/edit_text_money"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1" />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="setMoney"
            android:text="SET MONEY" />

    </LinearLayout>

</LinearLayout>

MainActivity.java

public class MainActivity extends AppCompatActivity {
    private static final int UPDATE_TEXT_MONEY_INTERVAL = 1000;

    private Handler mMainHandler;

    private TextView mTextViewMoney;
    private TextView mEditTextMoney;

    private String money;

    private Runnable mUpdateTextViewMoneyTask = new Runnable() {
        @Override
        public void run() {
            if (TextUtils.isEmpty(money)) {
                mTextViewMoney.setText(String.valueOf(SystemClock.elapsedRealtime()));
            } else {
                mTextViewMoney.setText(money);
                money = null;
            }
            mMainHandler.postDelayed(this, UPDATE_TEXT_MONEY_INTERVAL);
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mTextViewMoney = findViewById(R.id.text_view_money);
        mEditTextMoney = findViewById(R.id.edit_text_money);

        mMainHandler = new Handler(Looper.getMainLooper());
    }

    @Override
    protected void onStop() {
        super.onStop();
        stopUpdateTextViewMoney(null);
    }

    public void startUpdateTextViewMoney(View view) {
        mMainHandler.post(mUpdateTextViewMoneyTask);
    }

    public void stopUpdateTextViewMoney(View view) {
        mMainHandler.removeCallbacks(mUpdateTextViewMoneyTask);
    }

    public void setMoney(View view) {
        String money = mEditTextMoney.getText().toString();
        this.money = !TextUtils.isEmpty(money) ? money : "";
    }
}
  • When users press Start button, the app will start updating text view each second
  • When users press Stop button, the app will stop updating the text view.
  • If users want to set a new money to display in the next time, just enter in the edit text then press Set Money.
Son Truong
  • 13,661
  • 5
  • 32
  • 58
  • Would you suggest using postDelayed instead of using a Timer as @Coeus.D suggested? – ProgFroz Aug 27 '18 at 08:51
  • My suggestion is using Handler. You can find details here http://androidtrainningcenter.blogspot.com/2013/12/handler-vs-timer-fixed-period-execution.html – Son Truong Aug 27 '18 at 08:54
  • I have implemented your code now, but one clear problem is that whenever you reopen the fragment, you post mutliple runnables again and again. – ProgFroz Aug 27 '18 at 12:12
  • Basically you always create a similar runnable and post it. How can I prevent this without cancelling the runnable? It should continue in background . – ProgFroz Aug 27 '18 at 12:30
  • What do you mean when you say "should continue in the background"? Background mean you press home button or you remove the app from recents screen? – Son Truong Aug 27 '18 at 12:52
  • What do you mean "reopen the fragment", Can you give me your scenario when you want to update a text view in the background? – Son Truong Aug 27 '18 at 12:53
0

This will execute only once though, even if the Looper should normally keep the thread alive.

You seem to be confused about the Looper's purpose/functionality. The Looper is keeping the thread alive. If the main thread was not kept alive, your application would exit. The Looper does not, however, provide repeated execution of the Runnables posted to the Handler/Thread which it is associated with. Each Runnable is executed exactly once. If you want to execute the same Runnable multiple times, you must post it multiple times. For example:

Handler mainThreadHandler = new Handler(Looper.getMainLooper());
Runnable doToast = new Runnable() {
        @Override
        public void run() {
            Toast.makeText(menuActivity, "HELLO", Toast.LENGTH_SHORT).show();
        }
    });
mainThreadHandler.post(doToast); // Queue the first execution of the code in the Runnable's run() method. 
mainThreadHandler.post(doToast); // Queue the second execution of the code in the Runnable's run() method.
Janus Varmarken
  • 2,306
  • 3
  • 20
  • 42
  • Imagine a TextView displaying money, which has to be update infinite amount of times, with a Thread.sleep(1000), how would you achieve this with the given code? I am certain that you cannot simply wrap the .post() in a while-loop. – ProgFroz Aug 27 '18 at 06:28
  • @ProgFroz Well, you didn't really say what you ultimately wanted to do :). I explained why what you had didn't do what you said you _expected_ it to :). – Janus Varmarken Aug 27 '18 at 17:01
  • Yeah thats why I added my goal earlier on after you said that ^^ – ProgFroz Aug 27 '18 at 17:08
  • Based on the edit, this is a duplicate of https://stackoverflow.com/q/14814714/1214974 – Janus Varmarken Aug 27 '18 at 17:11