2015-06-29 5 views
3

Насколько я знаю, мы не можем изменять элементы пользовательского интерфейса из doInBackground метода AsyncTask, но он не дает никаких исключений при выполнении этого же и изменении текста TextView.Умение обновлять интерфейс в doInBackground

public class MainActivity extends Activity { 

    TextView tv; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_main); 
    tv = (TextView)findViewById(R.id.tv); 
    Toast.makeText(MainActivity.this, "Main : " + Thread.currentThread().getName(), Toast.LENGTH_SHORT).show(); 
    new DemoAsync().execute(); 
    } 

    class DemoAsync extends AsyncTask<Void, Void, String> { 

    @Override 
    protected String doInBackground(Void... params) { 
     String threadName = "Async : " + Thread.currentThread().getName(); 
     for(int i=0;i<1000;i++) { 
      tv.setText("Changed " + i); 
     } 
     return threadName; 
    } 

    @Override 
    protected void onPostExecute(String threadName) { 
     Toast.makeText(MainActivity.this, threadName, Toast.LENGTH_SHORT).show(); 
    } 
    } 
} 

Я попытался проверить, работает ли makeInBackground в главной теме, но это тоже не так. Тем не менее, при показе Toast в doInbackground, сбои приложений, которые он должен. Модификация TextView также должна была повредить приложение. Может кто-нибудь объяснить это поведение?

PS - Что касается возможного дубликата, я запускаю код на pre lollipop, поэтому вопрос, а также выбранный ответ не относятся к этому. Также ответ предполагает, что изменение TextView приведет к сбою при последующих вызовах (18-й точнее, в случае выбранного ответа). Я обновил код, чтобы изменить TextView в цикле из 1000 итераций, и он все еще работает нормально.

+1

Это странно, но переменная tv даже не является окончательной, как вы можете получить к ней доступ из внутреннего класса. – dsharew

+0

Это действительно странно, если это происходит на самом деле! – Skynet

+0

Каков ваш выход журнала при сбое приложения? – barq

ответ

0

Вы не должны получить доступ к UI из doInBackground, как указано в Android "Processes and Threads" documentation:

Не доступ инструментарий Android UI из вне потока пользовательского интерфейса

+4

Источник? Процитировать это? – Skynet

+0

Источник добавлен в мой ответ – barq

+0

@barq - Но что произойдет, если я это сделаю? Должен ли он потерпеть крах или нет? Почему это происходит только в случае Toast, а не TextView? –

-2

чек здесь AsyncTask

AsyncTask обеспечивает правильное и простое использование потока пользовательского интерфейса. Этот класс позволяет выполнять фоновые операции и публиковать результаты в потоке пользовательского интерфейса без необходимости манипулировать потоками и/или обработчиками.

Редактировать: В ООП вы можете получить доступ к глобальной переменной из внутреннего класса. поэтому, с этой точки зрения, в этом примере класс, расширяющий AsyncTask, является внутренним классом, вы можете получить доступ к TextView tv; поскольку execute вызывается в основном потоке пользовательского интерфейса, тем самым вы можете изменять, но если он не был внутренним классом, вы не можете получить доступ к переменной, определенной в соответствии с абстракцией, потому что doInBackground работает в другом потоке (а не в интерфейсе пользователя). Короче говоря, вы не можете изменить элемент пользовательского интерфейса в doInBackground(), но внутренний класс может получить доступ к объекту виджета, объявленному глобально, для изменения значений в контейнере, как это сделано в этом вопросе.

+0

Это не ответ, а голосующий голос. – Skynet

+0

Позвольте мне уточнить, вы можете изменять элементы пользовательского интерфейса в потоке пользовательского интерфейса не в фоновом потоке. да, вы правы, вы не можете изменять элементы пользовательского интерфейса в doInBaclground(), поскольку он работает в фоновом режиме. здесь вы объявили _TextView tv; _ глобально, и поскольку глобальные объекты могут быть доступны для внутренних классов, это может изменить содержимое. а также вы вызываете execute() в потоке пользовательского интерфейса, это возможно, чтобы понять это полностью, проверить официальную документацию. – ThatsME

+0

Просьба указать источник со ссылкой. – Skynet

0

Это всего лишь временное недоразумение: на первый взгляд кажется, что tv.setText("Changed " + i); в doInBackground() изменяет вид в пользовательском интерфейсе,

но

в реальности, когда последний SetText выполняется пользовательский интерфейс еще не готов и связан с деятельностью, при этом последний SetText в цикле изменяет Просмотр, которого еще нет в пользовательском интерфейсе.

Если мы задерживаем цикл, например. на 10000 итераций пользовательский интерфейс запустится, и мы получим всегда исключение «Только исходный поток, создавший представление иерархии, может коснуться его представления».

Смежные вопросы