Страницы

Поиск по вопросам

Показаны сообщения с ярлыком asynctask. Показать все сообщения
Показаны сообщения с ярлыком asynctask. Показать все сообщения

среда, 17 апреля 2019 г.

Передача ссылки на Activity в AsyncTask, почему плохо и как можно было иначе

Всё что внизу описано, это то как я использовал интерфейс для получения результата из AsynckTack в главный поток, используя CallBack. Почему мне сказали, что плохо , что я передал ссылку на активити в AsyncTask? И как лучше надо было это исполнить?
Вот в классе MainActivity вызываю такой метод и как видите передаю ссылку на активити в AsyncTack
private void UploadData() { String url = "https://qwerty.ru" ; AsyncUploadingData asyncUploadingData = new AsyncUploadingData(this); asyncUploadingData.execute(url); } }
В AsyncTack вложен интерфейс
interface AsyncResult { void getResult(String answer); }
Объявлен private AsyncResult mCallback;
И в конструкторе принимаю ссылку на активити. И неявно привожу к интерфейсу(правильно так выразиться?)
AsyncUploadingData(AsyncResult mCallback) { this.mCallback = mCallback; }
И в опеределенный момент вызываю метод интерфейса, а интерфейс подключен в MainActivity
mCallback.getResult(answer);


Ответ

Почему мне сказали, что плохо , что я передал ссылку на активити в AsyncTask?
Хранение ссылки на активити в AsyncTask чревато тем, что в случае уничтожения переданной активити в момент работы асинстаска:
а) Произойдет утечка памяти, так как GC не сможет убрать из памяти активити, так как на нее будет хранится ссылка (в случае с strong reference);
б) Вызов методов активити, в лучшем случае, ни к чему не приведет, а в худшем – будут возникать ошибки.
Первая проблема решается хранением активити в виде weak reference, однако вторая проблема остается.
И как лучше надо было это исполнить?
Существует большое количество способов решения данной проблемы.
Например, можно воспользоваться лоадерами или retain-фрагментами (лоадеры сохраняют состояние как раз с помощью retain-фрагментов).
Можно использовать различные библиотеки, например Chronos
Если используете MVP, то можно воспользоваться библиотекой Moxy, которая, в частности, решает возникшую у Вас проблему.

вторник, 16 апреля 2019 г.

Как правильно вынести блок кода в поток

В моей программе есть блок кода, отвечающий за открытие новостной статьи и извлечение из неё адреса картинки. Этот блок кода находится в потоке, отвечающем за работу с сетью и получение списка новостей. Когда они работают вместе, та программа сперва загружает всё, и отображает содержимое лишь секунд через 30.
Как правильно реализовать функцию получения адреса картинки используя многопоточность?
Код MainActivity прикреплён ниже:
public class MainActivity extends AppCompatActivity implements MyRecyclerViewAdapter.ItemClickListener {
MyRecyclerViewAdapter adapter;
public Elements content; public ArrayList newsList = new ArrayList<>();

@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);
new NewThread().execute(); RecyclerView recyclerView = findViewById(R.id.rv_list); LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this); recyclerView.setLayoutManager(linearLayoutManager); adapter = new MyRecyclerViewAdapter(this, newsList); adapter.setClickListener(this); recyclerView.setAdapter(adapter); }
@Override public void onItemClick(View view, int position) { Toast.makeText(this, "You clicked " + adapter.getItem(position) + " on row number " + position, Toast.LENGTH_SHORT).show(); }
public class NewThread extends AsyncTask {
String cleaner(String a){ String source = a; String result = ""; ArrayList arr = new ArrayList<>(); arr.add(source);
for(String retrival: source.split(" ")){ arr.add(retrival);
} arr.remove(0); Iterator iterator = arr.iterator(); while (iterator.hasNext()) { String string = iterator.next(); if (string.equals("Россия") || string.equals("Екатеринбург")) { iterator.remove(); }else{ result += string+" "; } } return result; }
String url_interpretator(String src){ String result=""; String interval = "";
char[] morph = src.toCharArray(); morph[0]=' '; morph[1]=' ';
for (char a : morph){ if(a==' '){ continue; }else{ interval=interval+a; } }
result="https://"+interval;
return result; } @Override protected String doInBackground(String... arg) { Document doc; String newsText, newsDate, newsTime, newsPicURL, newsLink;
try{ doc= Jsoup.connect("https://www.znak.com/?&%D0%B5%D0%BA%D0%B0%D1%82%D0%B5%D1%80%D0%B8%D0%BD%D0%B1%D1%83%D1%80%D0%B3%20%D0%BC%D1%83%D0%B7%D0%B5").get(); content = doc.select(".pub");
int link_counter = 0; newsList.clear(); for(Element contents: content){



String linkID = doc.getElementsByClass("pub").get(link_counter).attr("href"); String region = doc.getElementsByClass("region").get(link_counter).text(); String time = doc.getElementsByTag("time").get(link_counter).attr("datetime");


// Document doc2 = Jsoup.connect("https://www.znak.com"+href).get(); // String pic_url=url_interpretator(doc2.getElementsByTag("img").get(1).attr("src"));
DateAndTime dat = new DateAndTime(time); newsText=" "+cleaner(contents.text()); newsDate=" "+dat.getYear()+" "; newsTime=dat.getTime();
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// Document doc2 = null; //вынести в поток try { doc2 = Jsoup.connect("https://www.znak.com" + linkID).get(); } catch (IOException e) { e.printStackTrace(); } String pic_url = url_interpretator(doc2.getElementsByTag("img").get(1).attr("src")); String readyURL = pic_url; Log.d("picture", contents.text() + " " + pic_url); newsPicURL=readyURL; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
newsLink="https://znak.com"+linkID; NewsElement newsElement = new NewsElement( newsText, newsDate, newsTime, newsPicURL, newsLink); newsList.add(newsElement);

link_counter++; }
}catch (IOException e){
e.printStackTrace(); }
return null; }
@Override protected void onPostExecute(String result) { adapter.notifyDataSetChanged(); }
}
private class DateAndTime{
private String date; private String year; private String time=""; private char[] timeArr;
DateAndTime(String date){ this.date=date; }
String getYear(){
String[] dateAndTime = date.split("T"); String[] dateArr = dateAndTime[0].split("-");
timeArr = dateAndTime[1].toCharArray(); year = dateArr[2]+"."+dateArr[1]+"."+dateArr[0];
return year;
}
String getTime(){
for(int i = 0; i String intTime = String.valueOf(timeArr[0])+String.valueOf(timeArr[1]); int timeValue = Integer.valueOf(intTime)+5;
if(timeValue>23){ timeValue=timeValue-24; String time = "0"+String.valueOf(timeValue)+timeArr[2]+timeArr[3]+timeArr[4]; return time; }else{ String time = String.valueOf(timeValue)+timeArr[2]+timeArr[3]+timeArr[4]; return time; }
} }
}


Ответ

Попробуйте уведомлять адаптер не по окончании разбора всех новостей, а после добавления в список каждой:
public class NewThread extends AsyncTask { // ---------------------
protected String doInBackground(String... arg) { // ----------------------
for(Element contents: content){ // ----------------------
newsList.add(newsElement); publishProgress();
// --------------------- }
@Override protected void onProgressUpdate(Void... values) { adapter.notifyDataSetChanged(); }

пятница, 11 января 2019 г.

Ожидание завершения AsyncTask без блокировки основного UI потока

Нужно дождаться завершения AsyncTask без блокировки основного потока для того, чтобы невозможно было выйти из Activity, нажав Back.
Как этого добиться?


Ответ

Заведите себе флаг типа boolean, который ставьте поумолчанию в false, а в onPostExecute() AsyncTask ставльте его в true, и переопределите onBackPressed у Activity, чтобы пока флаг != true, ничего не делать.

понедельник, 8 октября 2018 г.

Выполнение параллельных потоков в AsyncTask

Пользуюсь AsyncTask для создания отдельного потока для постоянного вызова нативной функции. Все замечательно работало до тех пор, пока не понадобилось создать второй поток. Программа тупо зависает на execute() и ждет, пока первый поток завершится.
Чем лечить? Как создавать несколько потоков не блокирующих друг друга?


Ответ

До версии Android 1.6 при запуске задач AsyncTask использовалось только два потока:основной - UI и пользовательский - User. При этом если в пользовательском потоке запускалось более одной задачи, то остальные дожидались своей очереди на выполнение, пока все, что запущенны ранее не отработают свои задачи.То есть мы имели ни что иное, как двухзадачность. И увидели в Google, что это плохо.
с Android 1.6 ввели следующие изменения: Первые пять потоков запускались параллельно, а шестой и последующие дожидались, когда освободится какой то из первых пяти, но при этом, если очередь становилась более 10 потоков, то 16 и последующие вновь запускались параллельно (всего до 128), а те несчастливые с пятого до пятнадцатого так и ждали, когда освободится им место. Кроме того было отведено определенное время (10 секунд, а с Android 2.3 - 1 секунда)- если в течении этого времени появлялись еще новые потоки, то при завершении первых пяти они запускались вперед все тех же несчастных с пятого до пятнадцатого. Как видите в такой схеме разобраться очень не просто, она не очевидна и крайне запутана - программисты допускали множество ошибок и гугл засыпали жалобами.
С android 3.0 опять были введены изменения. В AsyncTask появился новый метод executeOnExecutor() , который имел два ключа:
AsyncTask.THREAD_POOL_EXECUTOR - параллельное выполнение потоков по страшной схеме, применяемой в Android 2.3 AsyncTask.SERIAL_EXECUTOR - последовательное выполнение потоков по схеме до android 1.6
метод execute() так же был оставлен для обратной совместимости, теперь он позволяет запустить последовательно 128 потоков. ( Работает по схеме применяемой до Android 1.6)
На сегодняшний день, если вы желаете, чтобы ваши потоки выполнялись параллельно, их следует запускать следующим образом (работает с API 11):
asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
Если минимальная версия вашего приложения меньше API 11 (Android 3.0), то вам придется придумывать какие то костыли, чтобы совместить желаемое поведение при запуске потоков, учитывая информацию выше, например:
public static void execute(AsyncTask as) { if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.HONEYCOMB_MR1) { as.execute(); } else { as.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } }