Лучший способ выполнить несколько HTTP-подключений с помощью HttpClient на Android

#android #multithreading #android-asynctask #httpclient

#Android #многопоточность #android-asynctask #httpclient httpclient

Вопрос:

Я ищу наилучший способ одновременного выполнения нескольких HTTP-подключений. Мое приложение извлекает RSS-каналы из 25 различных источников, и на данный момент я делаю только одно HTTP-соединение одновременно, что, очевидно, занимает немного времени для просмотра всех URL-адресов. Я использую AsyncTask для задания, и каждый раз, когда RSS-документ загружается и анализируется, я обновляю свою базу данных SQLite, добавляя в нее каналы.

 private class FeedFetcherTask extends AsyncTask<String, Void, Integer> {

    private Context context;
    private String[] pcFeedUrls;
    private String[] xboxFeedUrls;
    private String[] playstationFeedUrls;
    private String[] nintendoFeedUrls;
    private String[] mobileFeedUrls;

    private ArrayList<NewsFeed> newFeeds;

    public FeedFetcherTask(Context c) {

        context = c;
        pcFeedUrls = c.getResources().getStringArray(R.array.pc_feeds);
        xboxFeedUrls = c.getResources().getStringArray(R.array.xbox_feeds);
        playstationFeedUrls = c.getResources().getStringArray(R.array.playstation_feeds);
        nintendoFeedUrls = c.getResources().getStringArray(R.array.nintendo_feeds);
        mobileFeedUrls = c.getResources().getStringArray(R.array.mobile_feeds);

        newFeeds = new ArrayList<NewsFeed>();
    }

    @Override
    protected Integer doInBackground(String... params) {

        long time = System.currentTimeMillis();

        //getActivity().setProgressBarIndeterminateVisibility(true);

        getNewsForPc();
        getNewsForXbox();
        getNewsForPlaystation();
        getNewsForNintendo();
        getNewsForMobile();

        newRowsInserted = storeNewsInDatabase();

        long finish = System.currentTimeMillis() - time;
        Log.i("time elapsed", finish / 1000.0   "");

        return null;
    }

    @Override
    protected void onPostExecute(Integer count) {
        super.onPostExecute(count);

        if (getActivity() != null) {
            getActivity().setProgressBarIndeterminateVisibility(false);

            if (newRowsInserted == 0) {
                Toast.makeText(getActivity().getApplicationContext(), getResources().getString(R.string.no_new_tweets), Toast.LENGTH_SHORT).show();
            } else {
                newFeedsButton.setVisibility(Button.VISIBLE);
                buttonVisible = Button.VISIBLE;
                newFeedsButton.setText(getResources().getQuantityString(R.plurals.new_feeds_plurals, newRowsInserted, newRowsInserted));

                //TODO Create animation
            }
        }

        else{
            buttonVisible = Button.VISIBLE;
        }

    }

    @Override
    protected void onCancelled() {
        super.onCancelled();
    }

    private void retainListViewPosition() {

        int index = listView.getFirstVisiblePosition()   newFeeds.size();
        View v = listView.getChildAt(0);
        int top = (v == null) ? 0 : v.getTop();
        listView.setSelectionFromTop(index, top);
    }

    private void getNewsForPc() {

        int platform = NewsFeed.PLATFORM_PC;

        for (String url : pcFeedUrls) {
            parseRssFeed(url, platform);
        }
    }

    private void getNewsForXbox() {

        int platform = NewsFeed.PLATFORM_XBOX;

        for (String url : xboxFeedUrls) {
            parseRssFeed(url, platform);
        }
    }

    private void getNewsForPlaystation() {

        int platform = NewsFeed.PLATFORM_PLAYSTATION;

        for (String url : playstationFeedUrls) {

            parseRssFeed(url, platform);
        }
    }

    private void getNewsForNintendo() {

        int platform = NewsFeed.PLATFORM_NINTENDO;

        for (String url : nintendoFeedUrls) {
            parseRssFeed(url, platform);
        }
    }

    private void getNewsForMobile() {

        int platform = NewsFeed.PLATFORM_MOBILE;

        for (String url : mobileFeedUrls) {
            parseRssFeed(url, platform);
        }
    }

    private void parseRssFeed(String urlIn, int platformIn) {

        //TODO Too many GC, also need to close streams

        try {
            HttpClient apacheClient = new DefaultHttpClient();
            HttpResponse response = apacheClient.execute(new HttpGet(urlIn));

            BufferedReader br = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
            InputSource is = new InputSource(br);

            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document document = builder.parse(is);

            NodeList nodeList = document.getElementsByTagName("item");

            String provider = document.getElementsByTagName("title").item(0).getTextContent();
            int platform = platformIn;

            Node node;
            NewsFeed feed = null;
            String title;
            String link;
            String description;
            NodeList guidNodeList;
            String guid;
            String pubDate;
            NodeList creatorNodeList;
            String creator;

            Element element;

            for (int i = 0; i < nodeList.getLength(); i  ) {

                node = nodeList.item(i);

                if (node.getNodeType() == Node.ELEMENT_NODE) {

                    element = (Element) node;
                    feed = new NewsFeed();

                    title = element.getElementsByTagName("title").item(0).getTextContent();
                    feed.setTitle(title);

                    link = element.getElementsByTagName("link").item(0).getTextContent();
                    feed.setLink(link);

                    description = element.getElementsByTagName("description").item(0).getTextContent();
                    feed.setDescription(description);

                    guidNodeList = element.getElementsByTagName("guid");

                    if (guidNodeList == null | guidNodeList.getLength() < 1) {
                        feed.setGuid(link);
                    } else {
                        guid = guidNodeList.item(0).getTextContent();
                        feed.setGuid(guid);
                    }

                    pubDate = element.getElementsByTagName("pubDate").item(0).getTextContent();
                    feed.setDate(pubDate);

                    creatorNodeList = element.getElementsByTagName("dc:creator");

                    if (creatorNodeList == null | creatorNodeList.getLength() < 1) {
                        feed.setCreator(provider);
                    } else {
                        creator = creatorNodeList.item(0).getTextContent();
                        feed.setCreator(creator);
                    }

                    feed.setProvider(provider);
                    feed.setPlatform(platform);

                    newFeeds.add(feed);
                }
            }
        } catch (ConnectException ce) {
            Toast.makeText(getActivity(), getActivity().getResources().getString(R.string.connection_exception_error), Toast.LENGTH_SHORT).show();
        } catch (UnknownHostException uhe) {
            uhe.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private int storeNewsInDatabase() {

        return dao.insertAllFeeds(newFeeds);
    }
}
 

Я считаю, что это, вероятно, наименее эффективный способ сделать это. Я был бы признателен за любые предложения о том, как подойти к этой задаче. Я уже посмотрел на ExecutorService, но не уверен, что это правильный путь.

Ответ №1:

Я предлагаю использовать loopj, асинхронную клиентскую библиотеку HTTP. Вам нужно будет провести рефакторинг вашего кода, чтобы сделать его управляемым событиями, но это должно стоить затраченных усилий.