开发者

IntentService runs through a list, which can be reordered concurrently via onHandleIntent

开发者 https://www.devze.com 2023-02-28 15:30 出处:网络
I am using IntentService to download 200 large JPGs from a list. While this is loading, the user can skip through the not-loaded JPGs and load JPG #156 for example, but after it is loaded, it should c

I am using IntentService to download 200 large JPGs from a list. While this is loading, the user can skip through the not-loaded JPGs and load JPG #156 for example, but after it is loaded, it should continue loading the rest. So it's like a Lazy Loader... but it continues when it's idle.

I previously used onHandleIntent and put a loop from #1 to #200... which obviously doesn't work when I try to send another IntentService call for JPG #156. So the call to #156 only happens after onHandleIntent is done with #200.

I then changed it so onHandleIntent reorders request #156 to be at the top of the list, then requests the top of the list (and downloads the JPG), then removes it from the list. It then calls the IntentService again, which sounds rather risky from a recursive/stack overflow kinda way. It works sometimes and I can see file #156 being put first... sometimes.

Is there a better way to do this? A way I could think of would be to run it all through a database.

EDIT: This is what I have come up with:

code

public class PBQDownloader extends IntentService {
    int currentWeight = 0;
    PriorityBlockingQueue<WeightedAsset> pbQueue = new PriorityBlockingQueue<WeightedAsset>(100, new CompareWeightedAsset());
    public PBQDownloader() {
        super("PBQDownloader");
    }
    public PBQDownloader(String name) {
        super(name);
    }
    @Override
    protected void onHandleIntent(Intent intent) {
        String downloadUrl = "-NULL-";
        Bundle extras = intent.getExtras();
    开发者_Python百科    if (extras!=null) {
            downloadUrl = extras.getString("url");
            Log.d("onHandleIntent 1.1", "asked to download: " + downloadUrl);
        } else {
            Log.d("onHandleIntent 1.2", "no URL sent so let's start queueing everything");
            int MAX = 10;
            for (int i = 1; i <= MAX; i++) {
                // should read URLs from list
                WeightedAsset waToAdd = new WeightedAsset("url: " + i, MAX - i);
                if (pbQueue.contains(waToAdd)) {
                    Log.d("onStartCommand 1", downloadUrl + " already exists, so we are removing it and adding it back with a new priority");
                    pbQueue.remove(waToAdd); 
                }
                pbQueue.put(waToAdd);
            }
            currentWeight = MAX + 1;
        }
        while (!pbQueue.isEmpty()) {
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
            }
            WeightedAsset waToProcess = pbQueue.poll();
            Log.d("onHandleIntent 2 DOWNLOADED", waToProcess.url);
        }
        Log.d("onHandleIntent 99", "finished all IntentService calls");
    }
    @Override
    public int onStartCommand(Intent intent, int a, int b) {
        super.onStartCommand(intent, a, b);
        currentWeight++;
        String downloadUrl = "-NULL-";
        Bundle extras = intent.getExtras();
        if (extras!=null) downloadUrl = extras.getString("url");
        Log.d("onStartCommand 0", "download: " + downloadUrl + " with current weight: " + currentWeight);
        WeightedAsset waToAdd = new WeightedAsset(downloadUrl, currentWeight);
        if (pbQueue.contains(waToAdd)) {
            Log.d("onStartCommand 1", downloadUrl + " already exists, so we are removing it and adding it back with a new priority");
            pbQueue.remove(waToAdd); 
        }
        pbQueue.put(waToAdd);
        return 0;
    }
    private class CompareWeightedAsset implements Comparator<WeightedAsset> {
        @Override
        public int compare(WeightedAsset a, WeightedAsset b) {
            if (a.weight < b.weight) return 1;
            if (a.weight > b.weight) return -1;
            return 0;
        }
    }
    private class WeightedAsset {
        String url;
        int weight;
        public WeightedAsset(String u, int w) {
            url = u;
            weight = w;
        }
    }
}

code

Then I have this Activity:

code

public class HelloPBQ extends Activity {
    int sCount = 10;
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        Button tv01 = (Button) findViewById(R.id.tv01);
        Button tv02 = (Button) findViewById(R.id.tv02);
        Button tv03 = (Button) findViewById(R.id.tv03);
        tv01.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                doPBQ();
            }
        });
        tv02.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                doInitPBQ();
            }
        });
        tv03.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                sCount = 0;
            }
        });
    }

    private void doInitPBQ() {
        Intent intent = new Intent(getApplicationContext(), PBQDownloader.class);
        //intent.putExtra("url", "url: " + sCount);
        startService(intent);
    }    
    private void doPBQ() {
        sCount++;
        Intent intent = new Intent(getApplicationContext(), PBQDownloader.class);
        intent.putExtra("url", "url: " + sCount);
        startService(intent);
    }
}

code

Now the messy bit is that I have to keep an ever-increasing counter that runs the risk of going out of int bounds (WeightedAsset.weight) - is there a way to programmatically add to the queue and have it automatically be the head of the queue? I tried to replace WeightedAsset with a String, but it didn't poll() as I wanted, as a FIFO instead of a LIFO stack.


Here's how I'd try it first:

Step #1: Have the IntentService hold onto a PriorityBlockingQueue.

Step #2: Have onHandleIntent() iterate over the PriorityBlockingQueue, downloading each file in turn as it gets popped off the queue.

Step #3: Have onStartCommand() see if the command is the "kick off all downloads" command (in which case, chain to the superclass). If, instead, it's the "prioritize this download" command, re-prioritize that entry in the PriorityBlockingQueue, so it'll be picked up next by onHandleIntent() when the current download is finishing.

0

精彩评论

暂无评论...
验证码 换一张
取 消