Страницы

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

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

четверг, 9 апреля 2020 г.

Android->Приложение->Потоки->AsyncStack Как сделать заставку для приложения,которая исчезнет через n секунд

#android #многопоточность #android_asynctask

                    
Подскажите,пожалуйста,как реализовать Asyntask для следующей задачи,у меня есть MainActivity,
в котором находится ListView, содержащий 1000 элементов. Мне нужно,чтобы перед MainActivity
запускалось другое Activity, содержащее только картинку, проходило 2 секунды, Activity
исчезает и появляется MainActivity. 
    


Ответы

Ответ 1



Это называется Splash Screen Вот пример реализации: public class SplashScreen extends Activity { private static int SPLASH_TIME_OUT = 2000; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_splash); new Handler().postDelayed(new Runnable() { @Override public void run() { Intent i = new Intent(SplashScreen.this, MainActivity.class); startActivity(i); finish(); } }, SPLASH_TIME_OUT); } } Смысл кода я думаю пояснять нет необходимости.

суббота, 4 апреля 2020 г.

ImageView и ProgressTab в ListView

#java #android #listview #android_asynctask

                    
Есть ListView в которое по нажатию кнопки должен добавлять новый элемент с ProgressTab
и невидимое ImageView. 

ImageView сразу начинает конвертироваться в нужное нам изображение через AsyncTask
и прогресс конвертирования отображается в ProgressTab. Когда конвертирование закончилось,
ProgressTab удаляется, а вImageView сеттится картинка и она становится видимой.



Но после 3 или 4 повторения операции это перестает работать и в ImageView сразу сеттится
рандомная из картинок, которые были обработаны до этого. (как я понял, такое происходит,
когда элементы из listView перестают быть видными на экране).  Как это можно решить?
И вообще правилен ли у меня подход?

Код адаптера:

public class ListAdapter extends BaseAdapter {

Context context;
List dataList;

public ListAdapter(Context context, List dataList) {
    this.context = context;
    this.dataList = dataList;
}

@Override
public int getCount() {
    return dataList.size();
}

@Override
public Object getItem(int i) {
    return null;
}

@Override
public long getItemId(int i) {
    return 0;
}

static class ViewHolder{
    ImageView image;
    ProgressBar progressBar;
}

@Override
public View getView(int position, View convertView, ViewGroup viewGroup) {
    View v = convertView;
    ViewHolder holder;

    if (convertView == null) {
        LayoutInflater inf = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        v = inf.inflate(R.layout.item_progress, null);

        holder = new ViewHolder();
        holder.image = (ImageView) v.findViewById(R.id.itemProgress_previewImage);
        holder.progressBar = (ProgressBar) v.findViewById(R.id.itemProgress_progressBar);
        v.setTag(holder);
    } else {
        holder = (ViewHolder) convertView.getTag();
    }

    if (holder.progressBar.getProgress() == 0 && holder.progressBar.getVisibility()
== View.VISIBLE) {
        ConvertImageTask convertImageTask = new ConvertImageTask(holder.progressBar,
holder.image, dataList.get(position).getType());
        convertImageTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, dataList.get(position).getBitmap());
    }

    return v;
}


}

Код Таска для конвертирования:

public class ConvertImageTask extends AsyncTask {

private static final String TAG = "WHALETAG";
Random random = new Random();
int timeout = random.nextInt(5)+2; // для искусственного замедления конвертации

private static final int TYPE_ROTATE = 0;
private static final int TYPE_INVERT = 1;
private static final int TYPE_MIRROR = 2;

private int type = -1;

ProgressBar bar;
ImageView image;
public ConvertImageTask(ProgressBar bar, ImageView imageView, int type) {
    this.bar = bar;
    this.image = imageView;
    this.type = type;
}

@Override
protected void onPreExecute() {
    super.onPreExecute();
    bar.setMax(timeout);
}

@Override
protected void onProgressUpdate(Integer... values) {
    super.onProgressUpdate(values);
    int i = values[0];
    bar.setProgress(i);
}

@Override
protected Bitmap doInBackground(Bitmap... bitmaps) {

    Bitmap bitmap = bitmaps[0];

    switch(type){
        case TYPE_ROTATE:
            bitmap = rotateBitmap(bitmap);
            break;
        case TYPE_INVERT:
            bitmap = invertBitmap(bitmap);
            break;
        case TYPE_MIRROR:
            bitmap = mirrorBitmap(bitmap);
            break;
    }

    // Искусственно замедляем конвертацию
    for (int i=1; i<=timeout; i++) {
        Log.d(TAG, "doInBackground: осталось " + (timeout-i));
        try {
            Thread.sleep(1000); // спим 1 секунду
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        publishProgress(i);
    }

    return bitmap;
}

@Override
protected void onPostExecute(Bitmap bitmap) {
    super.onPostExecute(bitmap);
    Log.d(TAG, "onPostExecute: закончилась конвертация типа " + type);
    bar.setVisibility(View.GONE);
    image.setImageBitmap(bitmap);
    image.setVisibility(View.VISIBLE);
}




скажите, может правильнее будет поступить так:


Запускать асинкТаск, передавая в него (битмап для обработки, адаптер)


Внутри асинкТаска получать конвертированный bitmap и отправлять его в список битмапов.
По завершении таска вызывать adapter.notifyDataSetChanged();

В адаптере устанавливать готовый полученный bitmap в его holder.imageView?
Вопрос только, как тогда обновлять progressBar по мере конвертирования?

    


Ответы

Ответ 1



Обнулите картинку в ImageView перед началом загрузки: if (holder.progressBar.getProgress() == 0 && holder.progressBar.getVisibility() == View.VISIBLE) { holder.image.setImageDrawable(null); //теперь запускайте скачивание }

понедельник, 9 марта 2020 г.

Как передать результат выполнения асинхронного запроса обратно в вызвавшую его активность?

#java #android #activity #android_asynctask


Есть окно с элементом spinner, который должен заполняться значениями из базы данных
MS SQL. Для связи с БД использую jtds 1.2.8 в асинхронных запросах, но никак не выходит
найти способ чтобы переправить данные результата запроса в активность, которая послала
асинхронный запрос. Как реализовать заполнение spinner после выполнения запроса?

UA.java

public class UA extends AppCompatActivity{

private QueryItem Item = new QueryItem();

@Override
protected void onCreate(Bundle savedInstanceState) {    
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_ua);
    Item.setId(Search_Page.id_intent);
    Search_Page.id_intent = 0;
    String[] query = new String[2];
    query[1] = "get_dish_links";
    query[0] = "EXEC " + query[1] + "" + Item.getId();
    new AsyncRequest().execute(query);
    final SeekBar seekbar = (SeekBar) findViewById(R.id.seekBar);
}

public void onClick(View view) {
    final Spinner spinner = (Spinner)findViewById(R.id.spinner);
    //Item.setUnit(spinner);
}

public void fill_spinner(List links){
    final Spinner spinner = (Spinner)findViewById(R.id.spinner);
    ArrayAdapter possible_units = new ArrayAdapter(this, android.R.layout.simple_list_item_1,
links);
    spinner.setAdapter(possible_units);
    spinner.setSelection(0);
    possible_units.notifyDataSetChanged();
}


AsyncRequest.java

public final class AsyncRequest extends AsyncTask {

    public final static String MSSQL_DB = "jdbc:jtds:sqlserver://localhost:1433;
DatabaseName=Lazycook";
    public final static String MSSQL_LOGIN = "lazyuser";
    public final static String MSSQL_PASS = "12344321";
    public final static JSONConverter conv = new JSONConverter();
    public String action;

    @Override
    protected JSONArray doInBackground(String[] query) {
        JSONArray resultSet = new JSONArray();
        action = query[1];
        try {
            Class.forName("net.sourceforge.jtds.jdbc.Driver");
            Connection con = null;
            Statement st = null;
            ResultSet rs = null;
            try {
                con = DriverManager.getConnection(MSSQL_DB, MSSQL_LOGIN, MSSQL_PASS);
                if (con != null) {
                    st = con.createStatement();
                    rs = st.executeQuery(query[0]);
                    if (rs != null) {
                        int columnCount = rs.getMetaData().getColumnCount();
                        // Сохранение данных в JSONArray
                        while (rs.next()) {
                            JSONObject rowObject = new JSONObject();
                            for (int i = 1; i <= columnCount; i++) {
                                rowObject.put(rs.getMetaData().getColumnName(i),
(rs.getString(i) != null) ? rs.getString(i) : "");
                            }
                            resultSet.put(rowObject);
                        }
                    }
                }
            } catch (SQLException e) {
                e.printStackTrace();
            } catch (JSONException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (rs != null) rs.close();
                    if (st != null) st.close();
                    if (con != null) con.close();
                } catch (SQLException e) {
                    throw new RuntimeException(e.getMessage());
                }
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return resultSet;
    }

    @Override
    protected void onPostExecute(JSONArray result) {
        switch (action){
            case "get_dish_links" : {
                List links = conv.JSONtoLink(result);
                // вот здесь думал использовать вызов fill_spinner
                // нужного объекта, передав ему результат
            }


        }
    }


}
    


Ответы

Ответ 1



Создайте интерфейс public interface MyInterface { void doSmth(JSONArray result); } Реализуйте его в активити public class UA extends AppCompatActivity implements MyInterface{ @Override public void doSmth(JSONArray result) { System.out.println("вызван doSmth в Активити"); //вот и ваш результат из задачи в актвити } ... } Передавайте его в задачу через конструктор и вызывайте его метод, передавая туда результат из onPostExecute: public final class AsyncRequest extends AsyncTask { MyInterface myInterface; public AsyncRequest(MyInterface myInterface){ this.myInterface = myInterface; } @Override protected void onPostExecute(JSONArray result) { System.out.println("вызван onPostExecute"); myInterface.doSmth(result); } } В активити запускайте так: new AsyncRequest(UA.this).execute(query);

среда, 26 февраля 2020 г.

Предотвращение одновременного запуска нескольких AsyncTask

#android #многопоточность #android_asynctask #mutex


Есть некий AsyncTask. Нужно предотвратить одновременное выполнение нескольких его
экземпляров.

Есть идея использовать старый добрый Mutex, но насколько это правильно?
    


Ответы

Ответ 1



У AsyncTask есть метод executeOnExecutor (Executor exec, Params... params), с помощью которого можно организовать поведение Ваших тасков. Так же есть два заготовленных для этого Executor'а: SERIAL_EXECUTOR и THREAD_POOL_EXECUTOR Если я правильно понял вопрос Вам нужен SERIAL_EXECUTOR - все таски запущенные такой командой будут выполняться по очереди task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, params...); Либо Вы можете создать свой, более подходящий экземпляр Executor и использовать его для организации задач.

четверг, 13 февраля 2020 г.

Выполнение нескольких потоков asynctask в цикле

#java #потоки_данных #android #android_asynctask


Разрабатываю приложение в котором необходимо выполнить несколько потоков в цикле.
Имеется 4 Spinner-а.Данные для каждого спиннера берутся из базы данных на сервере в
в каждом отдельном поток AsyncTasc. Мне нужно сделать чтобы в первом спиннере при выборе
пункта "Все" показывались результаты всех спиннеров. Для этого делаю несколько вложенных
в друг друга циклов. Но при вызове в каждом цикле new example().execute() данные не
успевают передаваться. При Debug-е данные показываются но при выполнении программы
в обычном режиме нет. 
Как сделать чтобы выполнение кода выполнялось только после окончания выполнения AsyncTasc?
    


Ответы

Ответ 1



класс AsyncTask так же имеет метод get() , который дожидается выполнения задачи , а затем возвращает ее результат , если преопределение onPostExecute() вам не подходит (пример использования) Так же это не совсем решение вашей проблемы , но если вы выполняете более одного потока параллельно , запускать их на выполнение следует через: asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); Данное решение ускорит обработку фоновых задач , так как указывает запускать поток параллельно с уже запущенными . Дело в том , что с API11 запуск потока через execute() ставит все следующие после первого потоки в очередь и они дожидаются окончания выполнения предыдущих , то есть задачи выполняются последовательно , а не параллельно.

Ответ 2



onPostExecute(). есть такой метод в асинктаске. Он выполняется на UI потоке. В него и положите код, который надо выполнить после операций с AsyncTask

Ответ 3



Воспользуйтесь переопределенным методом onPostExecute, который выполнится после выполнения задачи в потоке doInBackground

суббота, 11 января 2020 г.

ASP.NET MVC и библиотека MediatR, странное поведение

#c_sharp #aspnet_mvc #android_asynctask #mediatr


В одном из проектов подключена библиотека MediatR. 

Используя абстракции из данной библиотеки определил команду:

public class TestCommand: IRequest
{ }


и обработчик данной команды:

public class TestHandler : IAsyncRequestHandler
{
    public Task Handle(TestCommand command)
    {
        return Task.Factory.StartNew(() =>
        {
            // Здесь намеренно бросается исключение.
            throw new ApplicationException();
        });
    }
}


Далее в контроллер инжектится сам IMediator, через который мы можем вызвать нужный
обработчик для конкретной команды.

Контроллер:

public class HomeController : Controller
{
    private readonly IMediator _mediator;

    public HomeController(IMediator mediator)
    {
        _mediator = mediator;
    }

    public  ActionResult Index()
    {
        try
        {
            // Передаем команду, тем самым вызываем обработчик данной команды - TestHandler.
            _mediator.Send(new TestCommand()).Wait();
        }
        catch (Exception e)
        {
            // Сюда никогда не попадаем 
        }

        return View();
    }

}


Суть проблемы:

Как можно заметить, в методе Handle класса TestHandler, который обрабатывает команду
TestCommand создается и возвращается новая задача, в которой просто бросается исключение
ApplicationException. 

Далее в экшене Index контроллера Home, к возвращаемой из TestHandler.Handle задаче
применен метод Wait(), который должен дождаться ее выполнения. Так как в возвращенной
задаче бросается исключение ApplicationException мы по идее должны попасть в блок catch,
но этого не происходит, вместо этого, такое ощущение, что возникает какой-то deadlock. 

Собственно вопрос, это баг в библиотеке или я что-то упустил и не заметил и данное
поведение нормально? 

P.S. Если заменить 

 _mediator.Send(new TestCommand()).Wait();


на 

await _mediator.Send(new TestCommand());


то поведение будет ожидаемым, т.е. мы попадем в блок catch. Но лично мне хотелось
бы понять, почему код перестает работать если используется метод Wait() 
    


Ответы

Ответ 1



Автор библиотеки не удосужился вызвать ConfigureAwait(false) (RequestHandler.cs), так что внутри библиотеки произошёл захват контекста, а вы вызовом Wait() заблокировали поток, в который должно вернуться управление, так что произошёл классический deadlock с async/await, можете почитать детальный разбор тут Еще один аргумент за ConfigureAwait(false) в библиотеках

пятница, 3 января 2020 г.

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

#java #android #android_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);

    


Ответы

Ответ 1



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

Ответ 2



Да особой разницы в целом нет, будь то интерфейс или прямая ссылка на Activity. Не советовали туда передавать Activity так как нарушает инкапсуляцию. Так-то, конечно же, можно передавать Activity, но хотя бы заворачивать её тогда уж в WeakReference. WeakReference mWeakActivity; public AsyncUploadingData(Activity activity) { mWeakActivity = new WeakReference(activity); } Но, независимо от подхода, при вызове из onPostExecute нужно проверять ссылку на null, так как к моменту завершения AsyncTack Activity уже могла быть пересоздана.

четверг, 2 января 2020 г.

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

#java #android #android_asynctask


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

Как этого добиться?
    


Ответы

Ответ 1



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

среда, 1 января 2020 г.

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

#java #android #многопоточность #android_asynctask


В моей программе есть блок кода, отвечающий за открытие новостной статьи и извлечение
из неё адреса картинки. Этот блок кода находится в потоке, отвечающем за работу с сетью
и получение списка новостей. Когда они работают вместе, та программа сперва загружает
всё, и отображает содержимое лишь секунд через 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; i23){
                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;
            }

        }
    }

}

    


Ответы

Ответ 1



Попробуйте уведомлять адаптер не по окончании разбора всех новостей, а после добавления в список каждой: 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(); }

воскресенье, 1 декабря 2019 г.

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

#java #android #android_asynctask


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

Чем лечить? Как создавать несколько потоков не блокирующих друг друга?
    


Ответы

Ответ 1



До версии 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); } }

пятница, 12 июля 2019 г.

Как многократно можно обращаться к выполнению AsyncTask

Суть вопроса в том, что есть AsyncTask в котором я получаю JSON с сайта, и передаю выборку в MainActivity. Первое обращение к AsyncTask создается при запуске программы. Также в форме есть кнопка обновить данные, при нажатии которой я хочу повторно обновить данные с сайта обратившись к AsyncTask, но получаю сообщение что он уже выполняется и может быть выполнен только один раз.
Прошу помощи в решении задачи. Также если есть предложение по изменению AsyncTask на другое решение тоже буду рад. :)
- Публикую код -
MainActivity.java
public class MainActivity extends AppCompatActivity implements AsyncResponse{
Button click; public static TextView data; GetDataFromSite asyncTask = new GetDataFromSite(); String str = "";
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);
click = (Button) findViewById(R.id.button); data = (TextView) findViewById(R.id.fachetdata);
ReadJSONDataFromFile();
asyncTask.delegate = this; asyncTask.execute();
click.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { asyncTask.execute(); } }); }
@Override public void processFinish(String JSON_Write_Data){ WriteJSONDataToFile(JSON_Write_Data); ReadJSONDataFromFile(); }
void WriteJSONDataToFile(String JSONDW) { try { BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(openFileOutput("nano_data.json", MODE_PRIVATE))); bw.write(JSONDW); bw.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }
void ReadJSONDataFromFile() { try { BufferedReader br = new BufferedReader(new InputStreamReader(openFileInput("nano_data.json"))); while ((str = br.readLine()) != null) { data.setText(str); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }
}

GetDataFromSite.java
public class GetDataFromSite extends AsyncTask { ... Тут переменные ... public AsyncResponse delegate = null; @Override protected Void doInBackground(Void... voids) {
try { ... Тут качаем JSON данные с сайта и пишем их в переменную Full_Info которая передается в MainActivity и пишеться в фаил в WriteJSONDataToFile ... } catch (IOException e1) { e1.printStackTrace(); } catch (JSONException e1) { e1.printStackTrace(); }
return null; }
@Override protected void onPostExecute(Void aVoid) { super.onPostExecute(aVoid); MainActivity.data.setText(this.Full_Info); delegate.processFinish(this.Full_Info); } }

Ну и интерфейс AsyncResponse.java
public interface AsyncResponse { void processFinish(String JSON_Write_Data); }


Ответ

Если исправлять в лоб, без рефакторинга, который тут не помешал бы, то вот так:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);
click = (Button) findViewById(R.id.button); data = (TextView) findViewById(R.id.fachetdata);
ReadJSONDataFromFile();
startAsyncTask();
click.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { startAsyncTask(); } }); }
private void startAsyncTask() { GetDataFromSite asyncTask = new GetDataFromSite(); asyncTask.delegate = this; asyncTask.execute(); }
Должно работать при условии, что delegate будет вызываться в UI треде.

вторник, 9 июля 2019 г.

ASP.NET MVC и библиотека MediatR, странное поведение

В одном из проектов подключена библиотека MediatR.
Используя абстракции из данной библиотеки определил команду:
public class TestCommand: IRequest { }
и обработчик данной команды:
public class TestHandler : IAsyncRequestHandler { public Task Handle(TestCommand command) { return Task.Factory.StartNew(() => { // Здесь намеренно бросается исключение. throw new ApplicationException(); }); } }
Далее в контроллер инжектится сам IMediator, через который мы можем вызвать нужный обработчик для конкретной команды.
Контроллер:
public class HomeController : Controller { private readonly IMediator _mediator;
public HomeController(IMediator mediator) { _mediator = mediator; }
public ActionResult Index() { try { // Передаем команду, тем самым вызываем обработчик данной команды - TestHandler. _mediator.Send(new TestCommand()).Wait(); } catch (Exception e) { // Сюда никогда не попадаем }
return View(); }
}
Суть проблемы:
Как можно заметить, в методе Handle класса TestHandler, который обрабатывает команду TestCommand создается и возвращается новая задача, в которой просто бросается исключение ApplicationException.
Далее в экшене Index контроллера Home, к возвращаемой из TestHandler.Handle задаче применен метод Wait(), который должен дождаться ее выполнения. Так как в возвращенной задаче бросается исключение ApplicationException мы по идее должны попасть в блок catch, но этого не происходит, вместо этого, такое ощущение, что возникает какой-то deadlock.
Собственно вопрос, это баг в библиотеке или я что-то упустил и не заметил и данное поведение нормально?
P.S. Если заменить
_mediator.Send(new TestCommand()).Wait();
на
await _mediator.Send(new TestCommand());
то поведение будет ожидаемым, т.е. мы попадем в блок catch. Но лично мне хотелось бы понять, почему код перестает работать если используется метод Wait()


Ответ

Автор библиотеки не удосужился вызвать ConfigureAwait(false) (RequestHandler.cs), так что внутри библиотеки произошёл захват контекста, а вы вызовом Wait() заблокировали поток, в который должно вернуться управление, так что произошёл классический deadlock с async/await, можете почитать детальный разбор тут Еще один аргумент за ConfigureAwait(false) в библиотеках

суббота, 15 июня 2019 г.

AsyncTask vs IntentService vs что-то другое при работе с сетью

Есть некоторое приложение, которое по запросу пользователя загружает некоторые данные из сети и отображает их. Сетевые запросы в этом приложении выполняются в отдельном потоке средствами AsyncTask
Ввиду того, что многие категорически советуют не использовать AsyncTask при работе с интернетом (а выносить сетевые запросы в сервис) и возник данный вопрос.
Чуть подробнее:
Суть приложения - отображение списка (текстовых) новостей из нескольких источников.
Как это реализовано сейчас:
Есть активити с Navigation Drawer, в котором отображается список сайтов, по клику на определенный пункт меню появляется фрагмент, содержащий RecyclerView, куда с помощью AsyncTask загружаются данные с выбранного сайта.
При прокрутке RecyclerView до определенного элемента запускается AsyncTask который подгружает в RecyclerView следующую порцию информации и так далее (эдакий Endless RecyclerView).
При выборе другого пункта меню, появляется другой фрагмент, но с таким же принципом работы (просто данные грузятся из другого источника).
При создании каждого из фрагментов выполняется setRetainInstance(true), то есть при изменении конфигурации устройства данные из AsyncTask благополучно вернутся в нужный фрагмент.
Процесс извлечения нужной информации с сайтов организован с помощью Jsoup
Вопросы:
Чем плоха такая архитектура данного приложения? (в части использования AsyncTask и в общем).
Хорошим ли тоном для данной конкретной задачи является использование AsyncTask? Или же стоит перенести работу с сетью в сервис? (я понимаю, что не все типы сервисов работают не в главном потоке).
А если все же стоит перенести работу с сетью в сервис, то при необходимости загрузки данных запускать сервис, загружать данные, возвращать данные во фрагмент и завершать сервис? И так каждый раз?
Честно говоря, хоть и на каждом углу написано, что AsyncTask - это плохо, я не вижу явных причин для вынесения работы с сетью в сервис в данном конкретном приложении. Но раз многие говорят, значит я чего-то не понимаю. Объясните мне это, пожалуйста.
По сути, AsyncTask же предназначен для кратковременных задач, а у меня загружается очень небольшой объем данных (текст), и в итоге AsyncTask как раз отрабатывает за пару секунд.
А основное назначение сервиса - это, насколько я понял, выполнение длительных фоновых задач.
Еще раз повторюсь - я не утверждаю, что в данном случае лучшим решением будет использование AsyncTask, но и аргументов против его использования я не вижу.
А может есть какие-либо другие более правильные архитектурные решения для данной задачи?
Хоть интернет и читал (очень много читал), но буду рад как ссылкам, где описывается что-то подобное (может я пропустил), так и Вашим ответам по данной теме и Вашим мнениям по данному вопросу.


Ответ

Честно говоря, хоть и на каждом углу написано, что AsyncTask - это плохо
Так говорят только те, кто не умеет их готовить. AsyncTask вполне нормальная практика, если вы умеете справляться с пограничными кейсами: поворот экрана, уничтожени контекста и т.п.
AsyncTask не рекомендуют использовать по большей степени потому что есть уже готовые нормальные библиотеки типо Volley, Retrofit и т.п.
Сервисы используются для тяжёлых задач или же для периодических задач.

четверг, 30 мая 2019 г.

Как передать результат выполнения асинхронного запроса обратно в вызвавшую его активность?

Есть окно с элементом spinner, который должен заполняться значениями из базы данных MS SQL. Для связи с БД использую jtds 1.2.8 в асинхронных запросах, но никак не выходит найти способ чтобы переправить данные результата запроса в активность, которая послала асинхронный запрос. Как реализовать заполнение spinner после выполнения запроса?
UA.java
public class UA extends AppCompatActivity{
private QueryItem Item = new QueryItem();
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_ua); Item.setId(Search_Page.id_intent); Search_Page.id_intent = 0; String[] query = new String[2]; query[1] = "get_dish_links"; query[0] = "EXEC " + query[1] + "" + Item.getId(); new AsyncRequest().execute(query); final SeekBar seekbar = (SeekBar) findViewById(R.id.seekBar); }
public void onClick(View view) { final Spinner spinner = (Spinner)findViewById(R.id.spinner); //Item.setUnit(spinner); }
public void fill_spinner(List links){ final Spinner spinner = (Spinner)findViewById(R.id.spinner); ArrayAdapter possible_units = new ArrayAdapter(this, android.R.layout.simple_list_item_1, links); spinner.setAdapter(possible_units); spinner.setSelection(0); possible_units.notifyDataSetChanged(); }
AsyncRequest.java
public final class AsyncRequest extends AsyncTask {
public final static String MSSQL_DB = "jdbc:jtds:sqlserver://localhost:1433; DatabaseName=Lazycook"; public final static String MSSQL_LOGIN = "lazyuser"; public final static String MSSQL_PASS = "12344321"; public final static JSONConverter conv = new JSONConverter(); public String action;
@Override protected JSONArray doInBackground(String[] query) { JSONArray resultSet = new JSONArray(); action = query[1]; try { Class.forName("net.sourceforge.jtds.jdbc.Driver"); Connection con = null; Statement st = null; ResultSet rs = null; try { con = DriverManager.getConnection(MSSQL_DB, MSSQL_LOGIN, MSSQL_PASS); if (con != null) { st = con.createStatement(); rs = st.executeQuery(query[0]); if (rs != null) { int columnCount = rs.getMetaData().getColumnCount(); // Сохранение данных в JSONArray while (rs.next()) { JSONObject rowObject = new JSONObject(); for (int i = 1; i <= columnCount; i++) { rowObject.put(rs.getMetaData().getColumnName(i), (rs.getString(i) != null) ? rs.getString(i) : ""); } resultSet.put(rowObject); } } } } catch (SQLException e) { e.printStackTrace(); } catch (JSONException e) { e.printStackTrace(); } finally { try { if (rs != null) rs.close(); if (st != null) st.close(); if (con != null) con.close(); } catch (SQLException e) { throw new RuntimeException(e.getMessage()); } } } catch (ClassNotFoundException e) { e.printStackTrace(); } return resultSet; }
@Override protected void onPostExecute(JSONArray result) { switch (action){ case "get_dish_links" : { List links = conv.JSONtoLink(result); // вот здесь думал использовать вызов fill_spinner // нужного объекта, передав ему результат }
} }
}


Ответ

Создайте интерфейс
public interface MyInterface { void doSmth(JSONArray result); } Реализуйте его в активити
public class UA extends AppCompatActivity implements MyInterface{
@Override public void doSmth(JSONArray result) { System.out.println("вызван doSmth в Активити"); //вот и ваш результат из задачи в актвити }
... } Передавайте его в задачу через конструктор и вызывайте его метод, передавая туда результат из onPostExecute
public final class AsyncRequest extends AsyncTask {
MyInterface myInterface;
public AsyncRequest(MyInterface myInterface){ this.myInterface = myInterface; }
@Override protected void onPostExecute(JSONArray result) { System.out.println("вызван onPostExecute"); myInterface.doSmth(result); } } В активити запускайте так:
new AsyncRequest(UA.this).execute(query);