创建更好的下载服务

时间:2014-08-06 21:52:47

标签: android android-service androidhttpclient

我是新服务(并下载了这个问题)...我有这个下载XML Feed并下载它的下载服务,但需要一段时间。

我从14个不同的URL下载,此服务从URL下载,解析xml,然后将消息发送回活动,告知服务重新开始使用下一个URL。

我确定有更好的方法来实现这一目标,并且正在寻找一些建议。以下是下载服务中的代码,如果您需要其他代码(例如主要活动中的handlemessage),请告诉我,我会把它放完。

因为我被告知我需要一个特定的问题:是否有更有效的方法从多个URL下载,而不是从此下载服务接收响应并在循环中重新启动它直到所有URL都被下载?

    package prs.psesto.rotorss;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;

import android.app.Activity;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;

public class DownloadService extends Service {

    // XML node keys
    private final String KEY_ITEM = "item"; // parent node
    private final String KEY_GUID = "guid";
    private final String KEY_LINK = "link";
    private final String KEY_TITLE = "title";
    private final String KEY_DESCRIPTION = "description";
    private final String KEY_UPDATED = "a10:updated";

    public static final String nameSpace = null;

    private static String[] sportArray = { "nfl", "mlb", "nba", "nhl", "bpl",
            "cfb", "gol", "nas" };

    private final int PLAYER_NEWS = 0;
    private final int ARTICLES = 1;

    private final String THREAD_PLAYER_NEWS = "THREAD_PLAYER_NEWS";
    private final String THREAD_ARTICLES = "THREAD_ARTICLES";

    public static enum DownloadType {
        PLAYER_NEWS, ARTICLES
    };

    /**
     * Looper associated with the HandlerThread.
     */
    private volatile Looper mServiceLooper;

    /**
     * Processes Messages sent to it from onStartCommnand() that
     */
    private volatile ServiceHandler mServiceHandler;

    /**
     * Hook method called when DownloadService is first launched by the Android
     * ActivityManager.
     */
    public void onCreate() {
        super.onCreate();
        Log.d("DownloadService", "onCreate");

        // Create and start a background HandlerThread since by
        // default a Service runs in the UI Thread, which we don't
        // want to block.
        HandlerThread thread = new HandlerThread("DownloadService");
        thread.start();

        // Get the HandlerThread's Looper and use it for our Handler.
        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

    /**
     * Hook method called each time a Started Service is sent an Intent via
     * startService().
     */
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d("DownloadService", "onStartCommand");

        // Create a Message that will be sent to ServiceHandler to
        // retrieve animagebased on the URI in the Intent.
        Message message = mServiceHandler.makeDownloadMessage(intent, startId);

        // Send the Message to ServiceHandler to retrieve an image
        // based on contents of the Intent.
        mServiceHandler.sendMessage(message);

        // Don't restart the DownloadService automatically if its
        // process is killed while it's running.
        return Service.START_NOT_STICKY;
    }

    /**
     * Helper method that returns sport if download succeeded.
     */
    public static int getSportIndex(Message message) {
        Log.d("DownloadService", "getSportIndex");

        // Extract the data from Message, which is in the form
        // of a Bundle that can be passed across processes.
        Bundle data = message.getData();

        // Extract the pathname from the Bundle.
        int sport = data.getInt("SPORTCODE");
        Log.d("DownloadService", "sport = " + String.valueOf(sport));

        // Check to see if the download succeeded.
        if (message.arg1 == 9)
            return 9;
        else
            return sport;
    }

    /**
     * Helper method that returns sport if download succeeded.
     */
    public static int getTypeIndex(Message message) {
        Log.d("DownloadService", "getTypeIndex");

        // Extract the data from Message, which is in the form
        // of a Bundle that can be passed across processes.
        Bundle data = message.getData();

        // Extract the pathname from the Bundle.
        int type = data.getInt("TYPECODE");
        Log.d("DownloadService", "type = " + String.valueOf(type));

        // Check to see if the download succeeded.
        if (message.arg1 == 9)
            return 9;
        else
            return type;
    }

    /**
     * Factory method to make the desired Intent.
     */
    public static Intent makeIntent(Context context, int type, int sport,
            Handler downloadHandler) {
        Log.d("DownloadService", "makeIntent");
        // Create the Intent that's associated to the DownloadService
        // class.
        Intent intent = new Intent(context, DownloadService.class);

        // Pathname for the downloaded image.
        intent.putExtra("TYPECODE", type);
        intent.putExtra("SPORTCODE", sport);

        // Create and pass a Messenger as an "extra" so the
        // DownloadService can send back the pathname.
        intent.putExtra("MESSENGER", new Messenger(downloadHandler));
        return intent;
    }

    /**
     * @class ServiceHandler
     *
     * @brief An inner class that inherits from Handler and uses its
     *        handleMessage() hook method to process Messages sent to it from
     *        onStartCommnand() that indicate which url to download.
     */
    private final class ServiceHandler extends Handler {
        /**
         * Class constructor initializes the Looper.
         * 
         * @param Looper
         *            The Looper that we borrow from HandlerThread.
         */
        public ServiceHandler(Looper looper) {
            super(looper);
            log("public ServiceHandler(Looper looper)");

        }

        /**
         * A factory method that creates a Message to return to the
         * DownloadActivity with the downloaded sport and type.
         */
        private Message makeReplyMessage(int type, int sport, int result) {
            log("makeReplyMessage");

            Message message = Message.obtain();

            Bundle data = new Bundle();

            // Pathname for the downloaded image.
            data.putInt("TYPECODE", type);
            data.putInt("SPORTCODE", sport);
            message.arg1 = result;

            message.setData(data);
            return message;
        }

        /**
         * A factory method that creates a Message that contains information on
         * how to stop the Service.
         */
        private Message makeDownloadMessage(Intent intent, int startId) {
            log("makeDownloadMessage");

            Message message = Message.obtain();
            // Include Intent & startId in Message to indicate which URI
            // to retrieve and which request is being stopped when
            // download completes.
            message.obj = intent;
            message.arg1 = startId;
            return message;
        }

        /**
         * Retrieves the download response and sport/type and replies to the
         * DownloadActivity via the Messenger sent with the Intent.
         */
        private void downloadAndReply(Intent intent) {
            log("downloadAndReply");

            // Download the requested image.
            int sport = intent.getExtras().getInt("SPORTCODE");
            int type = intent.getExtras().getInt("TYPECODE");

            int result = downloadAndParse(type, sport);

            // Extract the Messenger.
            Messenger messenger = (Messenger) intent.getExtras().get(
                    "MESSENGER");

            // Send the pathname via the messenger.
            sendResult(messenger, type, sport, result);
        }

        /**
         * Send the result back to the DownloadActivity via the messenger.
         */
        private void sendResult(Messenger messenger, int type, int sport,
                int result) {
            log("sendResult");

            // Call factory method to create Message.
            Message message = makeReplyMessage(type, sport, result);

            try {
                // Send pathname to back to the DownloadActivity.
                messenger.send(message);
            } catch (RemoteException e) {
                Log.e(getClass().getName(), "Exception while sending.", e);
            }
        }

        public int downloadAndParse(int typeIndex, int sportIndex) {
            log("downloadAndParse");

            long id = 0;
            String link = null;
            int linkType = ARTICLES;
            String title = null;
            String description = null;
            String updated = null;

            String sport = sportArray[sportIndex];

            try {
                InputStream stream = null;

                try {
                    stream = downloadUrl(getUrl(getDlType(typeIndex), sport));

                    XmlPullParserFactory factory = XmlPullParserFactory
                            .newInstance();
                    factory.setNamespaceAware(false);
                    XmlPullParser xpp = factory.newPullParser();
                    xpp.setInput(stream, null);
                    boolean insideItem = false;

                    // Returns the type of current event: START_TAG,
                    // END_TAG,
                    // etc..
                    int eventType = xpp.getEventType();
                    while (eventType != XmlPullParser.END_DOCUMENT) {
                        if (eventType == XmlPullParser.START_TAG) {

                            if (xpp.getName().equalsIgnoreCase(KEY_ITEM)) {
                                insideItem = true;
                            } else if (xpp.getName().equalsIgnoreCase(KEY_GUID)) {
                                if (insideItem) {
                                    id = Long.valueOf(String.valueOf(
                                            xpp.nextText()).replace(" ", ""));
                                }
                            } else if (xpp.getName().equalsIgnoreCase(KEY_LINK)) {
                                if (insideItem) {
                                    link = xpp.nextText();

                                    if (link.contains("player")) {
                                        linkType = PLAYER_NEWS;
                                    }
                                }
                            } else if (xpp.getName()
                                    .equalsIgnoreCase(KEY_TITLE)) {
                                if (insideItem) {
                                    title = xpp.nextText();
                                }
                            } else if (xpp.getName().equalsIgnoreCase(
                                    KEY_DESCRIPTION)) {
                                if (insideItem) {
                                    description = xpp.nextText();
                                }
                            } else if (xpp.getName().equalsIgnoreCase(
                                    KEY_UPDATED)) {
                                if (insideItem) {
                                    updated = xpp.nextText();
                                }
                            }

                        } else if (eventType == XmlPullParser.END_TAG
                                && xpp.getName().equalsIgnoreCase("item")) {
                            insideItem = false;
                        }

                        eventType = xpp.next(); // / move to next element
                        RotoItem rItem = DatabaseManager.sInstance.newRotoItem(
                                id, sport, link, linkType, title, description,
                                updated);

                        DatabaseManager.sInstance.addRotoItem(rItem);
                    }

                    // Makes sure that the InputStream is closed after the
                    // class
                    // is
                    // finished using it.
                } finally {

                    if (stream != null) {
                        stream.close();
                    }
                }
            } catch (IOException e) {
                Log.e("DownloadXml - performDownload", "Connection Error");
                Log.e("IOException", e.toString());
                return 9;
            } catch (XmlPullParserException e) {
                Log.e("DownloadXml - performDownload", "Error in data set");
                Log.e("XmlPullParserException", e.toString());
                return 9;
            }
            Log.d("DownloadData", "sportIndex = " + String.valueOf(sportIndex));

            return sportIndex;

        }

        public DownloadType getDlType(int typeIndex) {
            if (typeIndex == PLAYER_NEWS) {
                return DownloadType.PLAYER_NEWS;
            } else {
                return DownloadType.ARTICLES;
            }
        }

        public String getUrl(DownloadType downloadType, String sport) {

            if (downloadType == DownloadType.PLAYER_NEWS) {
                return "http://www.rotoworld.com/rss/feed.aspx?sport=" + sport
                        + "&ftype=news&count=12&format=rss";
            } else {
                return "http://www.rotoworld.com/rss/feed.aspx?sport=" + sport
                        + "&ftype=article&count=12&format=rss";
            }
        }

        // Given a string representation of a URL, sets up a connection and gets
        // an input stream.
        private InputStream downloadUrl(String urlString) throws IOException {

            URL url = new URL(urlString);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setReadTimeout(10000 /* milliseconds */);
            conn.setConnectTimeout(15000 /* milliseconds */);
            conn.setRequestMethod("GET");
            conn.setDoInput(true);
            // Starts the query
            conn.connect();
            return conn.getInputStream();
        }

        public void handleMessage(Message message) {
            log("handleMessage");

            // Download the designated image and reply to the
            // DownloadActivity via the Messenger sent with the
            // Intent.
            downloadAndReply((Intent) message.obj);

            // Stop the Service using the startId, so it doesn't stop
            // in the middle of handling another download request.
            stopSelf(message.arg1);
        }
    }

    /**
     * Hook method called back to shutdown the Looper.
     */
    public void onDestroy() {
        log("onDestroy");

        mServiceLooper.quit();
    }

    /**
     * This hook method is a no-op since we're a Start Service.
     */
    public IBinder onBind(Intent arg0) {
        log("IBinder");
        return null;
    }

    public void log(String message) {
        Log.d("DownloadService", message);
    }
}

1 个答案:

答案 0 :(得分:0)

  

除了从此下载服务接收响应并重新启动直到所有网址都被下载之外,是否有更有效的方式从多个URL下载?

使用ThreadPoolExecutor并并行运行几个线程。转储您的Messenger - 和 - HandlerThread内容,并onStartCommand()将每个职位交给ThreadPoolExecutor。使用打包的事件总线(例如,greenrobot的EventBus,LocalBroadcastManager)让您的UI层了解已完成的工作。

ThreadPoolExecutor逻辑将是相当标准的Java。唯一的Android-isms是onStartCommand()作为工作的接收者并使用事件总线来传递结果。