Other thread wont let me change Activity layout!?

2019-09-14 16:14发布

问题:

SOLUTION:

It might not be the best or the prettiest solution, but it works. Start a new thread so the main thread can display a ProgressDialog while the new thread retrieves, assigns and interrupts the main thread with a new runnable to initiate views. For the ProgressDialog to be shown while all the activity in the new thread is going on, the ProgressDialog must, as mentioned, be started in the main thread and dismissed of course at the end of the new thread. The ProgressDialog must be started as the last thing right before the new thread starts, and everything you wanna wait for must go into the new thread. And remember, if you wanna make UI alterations while waiting and showing ProgressDialog you must interrupt the main thread with a new runnable, which makes the alterations in it's run() method, by using runOnUiThread.

public void onCreate(Bundle savedInstanceState) {
                ...

                ((ScrollView) findViewById(R.id.contentContainer)).addView(getLayoutInflater().inflate(R.layout.myCustomContentLayout, null));

                ((TextView) findViewById(R.id.name)).setText(customer.getProperty("name").getValue().toString());

                final ProgressDialog progressDialog = ProgressDialog.show(this, "Loading", "Please wait...");

                new Thread(new Runnable() {
                    protected void run() {
                        ... //get some data, fill some lists and assign some variables...

                        runOnUiThread(new Runnable() {
                                          public void run() {
                                              initiateActivity();
                                          }
                                      });

                        progressDialog.dismiss();

                        return true;
                    }
                }).start();
}

ORIGINAL POST:

Hello all and many thanks for reading. :)

Is it really not possible to change your activity layout from a different thread than the main UI-thread?

I'm trying to fill data into a table from another thread while a ProgressDialog is running. Like this.

public void onCreate(Bundle savedInstanceState) {
                ...

                ((ScrollView) findViewById(R.id.contentContainer)).addView(getLayoutInflater().inflate(R.layout.myCustomContentLayout, null));

                ((TextView)findViewById(R.id.name)).setText(customer.getProperty("name").getValue().toString());

                new Thread(new Runnable() {
                    public void run() {
                            ... //get some data, fill some lists and assign some variables...

                        initiateActivity();
                    }
                }).start();

                progressDialog = ProgressDialog.show(this, "Loading", "Please wait...");
}

private void initiateActivity() {
    fillOrderTable();
    initiateGestureview();
    fillCustServiceForCustomer();

    progressDialog.dismiss();
}

But as soon as I start altering anything or adding data to tables I get the exception "Activity customerapp.customercentral.Overview has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView@46d2fce0 that was originally added here.

And I really don't wanna move the initation methods inside the thread because they are reused from other part of my activity.

Is there a solution to this?

Big thanks! :)

EDIT:

Tried this but then the ProgressDialog doesn't show...

public void onCreate(Bundle savedInstanceState) {
                ...

                ((ScrollView) findViewById(R.id.contentContainer)).addView(getLayoutInflater().inflate(R.layout.myCustomContentLayout, null));

                ((TextView)findViewById(R.id.name)).setText(customer.getProperty("name").getValue().toString());

                new Thread(new Runnable() {
                    public void run() {
                        runOnUiThread(new Runnable() {
                            public void run() {
                                ... //get some data, fill some lists and assign some variables...

                                initiateActivity();
                            }
                        });
                    }
                }).start();

                progressDialog = ProgressDialog.show(this, "Loading", "Please wait...");
}

The same goes for this...

public void onCreate(Bundle savedInstanceState) {
                ...

                ((ScrollView) findViewById(R.id.contentContainer)).addView(getLayoutInflater().inflate(R.layout.myCustomContentLayout, null));

                ((TextView)findViewById(R.id.name)).setText(customer.getProperty("name").getValue().toString());

                new AsyncTask<Boolean, Boolean, Boolean>() {
                    protected void onPreExecute() {
                        progressDialog = ProgressDialog.show(this, "Loading", "Please wait...");
                    }

                    protected Boolean doInBackground(Boolean... unused) {
                        runOnUiThread(new Runnable() {
                            public void run() {
                                ... //get some data, fill some lists and assign some variables...

                                initiateActivity();
                            }
                        });

                        return true;
                    }

                    protected void onPostExecute(Boolean unused) {
                        progressDialog.dismiss();
                    }
                }).execute(true);
}

回答1:

No you can't change the UI from another thread. If you have task that do not touch the UI you can use AsynchTask to run them in the background and be notified in the main thread when the task is completed. But if you need to modify the thread you need to do it in the main thread. If your code is somewhere else and it is used by other threads you can use runOnUIThread to execute the code that changes the UI

public void onCreate(Bundle savedInstanceState) {
  ...
progressDialog = ProgressDialog.show(this, "Loading", "Please wait...");
initiateActivity();
progressDialog.dismiss();

}

private void initiateActivity() {
    fillOrderTable();
    initiateGestureview();
    fillCustServiceForCustomer();
}

EDIT

Thinking about it: the difference is that you need to exit the onCreate that is what happens if you run a thread that attaches itself to the main thread. So the solution above is not equivalent to your solution for that reason.



回答2:

It is possible to initiate changing layout from another thread, but all the job has to be done in the UI thread. To do that you can use Activity.runOnUiThread(), View.post(), View.postDelayed() methods, or you can use a Handler.

The article Painless Threading contains more details regarding this task.



回答3:

You can not change UI from other threads... TO change UI you have to use "runOnUiThread" or use some handler to make changes.. try this... make your activity implement Runnable

public class myclass extends Activity implements Runnable{
public void onCreate(Bundle savedInstanceState) {
 ......
progressDialog = ProgressDialog.show(this, "Loading", "Please wait...");
 Thread thread = new Thread(this);
            thread.start();
}
 public void run() {
            fillOrderTable();
            initiateGestureview();
            fillCustServiceForCustomer();
            handler.sendEmptyMessage(0);
    }
 private Handler handler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                    progressDialog.dismiss();


            }
    };
}


回答4:

SOLUTION:

It might not be the best or the prettiest solution, but it works. Start a new thread so the main thread can display a ProgressDialog while the new thread retrieves, assigns and interrupts the main thread with a new runnable to initiate views. For the ProgressDialog to be shown while all the activity in the new thread is going on, the ProgressDialog must, as mentioned, be started in the main thread and dismissed of course at the end of the new thread. The ProgressDialog must be started as the last thing right before the new thread starts, and everything you wanna wait for must go into the new thread. And remember, if you wanna make UI alterations while waiting and showing ProgressDialog you must interrupt the main thread with a new runnable, which makes the alterations in it's run() method, by using runOnUiThread.

public void onCreate(Bundle savedInstanceState) {
                ...

                ((ScrollView) findViewById(R.id.contentContainer)).addView(getLayoutInflater().inflate(R.layout.myCustomContentLayout, null));

                ((TextView) findViewById(R.id.name)).setText(customer.getProperty("name").getValue().toString());

                final ProgressDialog progressDialog = ProgressDialog.show(this, "Loading", "Please wait...");

                new Thread(new Runnable() {
                    protected void run() {
                        ... //get some data, fill some lists and assign some variables...

                        runOnUiThread(new Runnable() {
                                          public void run() {
                                              initiateActivity();
                                          }
                                      });

                        progressDialog.dismiss();

                        return true;
                    }
                }).start();
}