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.
精彩评论