I have been working on getting my database backing up to work and I have reached a point where I am not sure what to do.
Basically at first the application opens a Login activity, the user logs in and their database file (if it exists) is downloaded from the Firebase Storage, and then the application navigates to the MainActivity.
In the MainActivity I call a method that sends the user's database file to Firebase Storage. I tried to manage the process by closing the database but since i couldn't fix an error of "E/ROOM: Invalidation tracker is initialized twice :/.", then I found an answer to use a checkpoint (Backup Room database). Now I implemented the forced checkpoint method.
(MarkerDao)
#RawQuery
int checkpoint(SupportSQLiteQuery supportSQLiteQuery);
(MarkerRepository)
public void checkPoint(){
Thread thread= new Thread(() -> markerDao.checkpoint(new SimpleSQLiteQuery("pragma wal_checkpoint(full)")));
thread.start();
}
(ViewModel)
public void setCheckpoint(){
repository.checkPoint();
}
(Database back-up method in the MainActivity)
private void copyDbToFirebase(){
String currentDBPath = "/data/data/"+ getPackageName() + "/databases/locations_table";
File dbBackupFile = new File(currentDBPath);
if (dbBackupFile.exists()){
markerViewModel.setCheckpoint();
// create file from the database path and convert it to a URI
Uri backupDB = Uri.fromFile(new File(currentDBPath));
// Create a StorageReference
StorageReference dbReference = storageRef.child("users").child(userId).child("user database").child("locations_table");
// Use the StorageReference to upload the file
if (userId != null){
dbReference.putFile(backupDB).addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
#Override
public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
Log.d(TAG, "onSuccess: "+4 + taskSnapshot);
Toast.makeText(getApplicationContext(), "Database copied to Firebase 4", Toast.LENGTH_LONG).show();
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.d(TAG, "onFailure: "+ e.getMessage());
}
});
}
}
}
If the user logs out, then the files in the "/data/data/"+ getPackageName() + "/databases/" are deleted, which I have manually confirmed by looking at the databases folder of the application.
My issue is that after the databases are deleted and a new user logs in, then the previous database data remains but when I manually check the app's data folder, then the /databases/ folder shows that the files were deleted and a new file is created but it doesn't show any WAL or SHM files and also I get the data of another database which is created when the application first runs, but that file is also not shown in the databases/ folder.
Can anyone explain why the folder doesn't show the files that should be present, where is the application getting the data that was deleted and how to fix it.
Edit: My application has multiple Room databases and I just realized that all the data is still readable after the files were deleted.
The method to delete the database files
private boolean deleteDatabaseFiles(File path) {
if(path.exists() ) {
File[] files = path.listFiles();
for(int i=0; i<files.length; i++) {
if(files[i].isDirectory()) {
deleteDatabaseFiles(files[i]);
}
else {
files[i].delete();
}
}
}
return true;
}
If you are using the same exact RoomDatabase object simply building another one over the same object will prevent any hold over cached data from showing up. I've tested this using multiple database swaps large and small and there is no bleed over.
If you are using a new Instance of the RoomDatabase object for every login try closing the old one after the user logs out. Room will typically close when not needed but if you need it to happen immediately, manually closing it is your best bet.
roomDb.getOpenHelper().close();
i have a class(location2.java) that finds location for me,I use this code in my class :
What is the simplest and most robust way to get the user's current location on Android?
and I have a service that override that abstract "locationResult";Now i want my service after running its codes,service doesn't finish and stay alive for receiving location from location2.java.
appreciating any help for this.
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Location2.LocationResult locate = new Location2.LocationResult() {
#Override
public void gotLocation(Location location1, Boolean Gps, Boolean Net) {
if (location1 != null) {
Log.e("Loc", String.valueOf(location1.getLatitude()));
}
try {
//this is a method that i want to be run after receiving location from location2.java
json_maker(location1, speed_computation(location1), Gps, Net);
} catch (JSONException e) {
e.printStackTrace();
}
}
};
Location2 location = new Location2();
location.getLocation(context, locate);
return Service.START_FLAG_REDELIVERY;
}
The most successful way is to use return START_STICKY.
"and if service wants to restart, multiple constants for example START_STICKY can be used.doesn't it?" - Yes, we can use.
START_STICKY
Constant to return from onStartCommand(Intent, int, int) if this service's process is killed while it is started (after returning from onStartCommand(Intent, int, int)), then leave it in the started state but don't retain this delivered intent. Developer guide: Android
I get some JSON data which contains some food menu items
Please note: this is just a sample, there are more than 2 images sometimes and many more menu items in the array!
{
"menu": [
{
"url": "/api/v1/menu/1",
"name": "Best Food",
"description": "really nice food",
"opening_time": "every day from 9am to 6pm",
"contact_email": "info#food.com",
"tel_number": "+54 911 3429 5762",
"website": "http://bestfood.com",
"images": [
{
"url": "https://blahblah/image1.jpg"
},
{
"url": "https://blahblah/image2.jpg"
}
]
},
]
}
Each item has some info and an array of image URLs.
I am using the Glide image library to process these images and Retrofit 2.0 to download the JSON data from the endpoint. All is well at this point.
However, I need to store this downloaded data for offline access.
Currently, I am using ORM Lite on my existing models to store all the JSON data in a database. This part is OK.
However, in my database I only store the image URLs as I was told that it is not good approach to store images (as blob) in the database.
So there is a section in my app to view the saved menu's with an option to download it for offline access if the user chooses to.
It is worth mentioning at this point, that I already have the raw menu information in the database because the user would have to view the menu in the first place to get it in DB.
But the problem is the images.
This is where I do not know how to proceed, but I list the solutions and problems that I am thinking about and was hoping people could advise me on what is the best course of action is.
Use a service to download the images. This I feel is mandatory because I do not know how many images there will be, and I want the download to proceed even if user exits the app
Glide has a download only option for images and you can configure where its cache is located (internal private or external public) as I read here and here. Problem is I do not feel comfortable with setting the cache size as I do not know what is required. I would like to set unlimited.
I need to be able to delete the saved menu data especially if its saved on the external public directory as this is not removed when the app is deleted etc. or if the user chooses to delete a saved menu from within the app. I was thinking I could store the file image URIs or location of the entire saved menu in database for this but not sure if this is a good way
I read in different sources and answers that in this use case for just caching images to SD card etc. that I should specifically use a network library to do so to avoid the allocation of a bitmap to heap memory. I am using OK HTTP in my app at the moment.
I'm using ormlite to store objects with urls too, I have a synchronization after the "sign in" screen on my app, on my experience I really recommend this library https://github.com/thest1/LazyList
It's very simple:
ImageLoader imageLoader=new ImageLoader(context);
imageLoader.DisplayImage(url, imageView);
This library saves the image using the url on the external sd with basic and simple configuration about the memory issues, so if you actually have two or more items with the same url this library works perfectly, the url and imageView are the parameters, if the image is not on the phone begins a new task and put the image in the view when the download is finish, and btw this library also saves the images encoded, so these pictures don't appear on the gallery.
Actually you only need these files to implement the library:https://github.com/thest1/LazyList/tree/master/src/com/fedorvlasov/lazylist
If you wanna manipulate some files, you can change the folder name in the FileCache class:
public FileCache(Context context){
//Find the dir to save cached images
...
cacheDir=new File(android.os.Environment.getExternalStorageDirectory(),"LazyList");
...
}
Where "LazyList" is the folder name, and them you can delete, move, etc.
Delete sample:
/**
* This method delete a file if exist
*/
public static void deleteFile(File file){
if(file!=null && file.exists()) {
file.delete();
}
}
Now I learned more about memory cache and the allocation of a bitmap to heap memory, for the first time manipulating images online and offline, I recommend this library, also when you learn more about it, you can implement and edit the library to your needs.
1: Use an IntentService to do your downloads.
http://developer.android.com/reference/android/app/IntentService.html
2: Set up your IntentService using AlarmManager so that it runs even if the
application is not running. You register with the AlarmManager
http://developer.android.com/reference/android/app/AlarmManager.html
There are a variety of ways you can have the AlarmManager start your
intent.
For Example:
// Register first run and then interval for repeated cycles.
alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime() + DEFAULT_INITIAL_RUN,
DEFAULT_RUN_INTERVAL, pi);
3: Storing Data
There are several options here depending on how public you want your
pictures/data to be.
http://developer.android.com/reference/android/os/Environment.html
Example: External Public Storage
File dirBackup = Environment.getExternalStoragePublicDirectory(
"YourDirectory" );
4: Downloading
Your option here. You can using anything from your current API to a
basic URLConnection.
You may want to look at:
http://developer.android.com/reference/android/app/DownloadManager.html
Also, watch your permissions you will need to add
and
Hope this points you in a useful direction.
Try out this library to manage images loading.
Use a service to download the images. This I feel is mandatory because
I do not know how many images there will be, and I want the download
to proceed even if user exits the app
All downloading done in worker threads so it's alive while application process is alive. There may a problem appear: application dies while loading is in progress. To workaround this I suggest to use AlarmManager in combination with Service. Set it up to start by timer, check you database or UIL cache for image files being not loaded and start their loading again.
Glide has a download only option for images and you can configure
where its cache is located (internal private or external public) as I
read here and here. Problem is I do not feel comfortable with setting
the cache size as I do not know what is required. I would like to set
unlimited.
UIL has several disc cache implementations out of the box including unlimited one. It also provides you cache interface so you can implement your own.
I need to be able to delete the saved menu data especially if its
saved on the external public directory as this is not removed when the
app is deleted etc. or if the user chooses to delete a saved menu from
within the app. I was thinking I could store the file image URIs or
location of the entire saved menu in database for this but not sure if
this is a good way
UIL generates unique filename for each loaded file using provided file link. You can delete any loaded image or cancel any download using link from your JSON.
I read in different sources and answers that in this use case for just
caching images to SD card etc. that I should specifically use a
network library to do so to avoid the allocation of a bitmap to heap
memory. I am using OK HTTP in my app at the moment.
UIL does it OK. It manages memory very accurately also provide you several options for memory management configuration. For example you can choose between several memory cache implementations out of the box.
In conclusion I suggest you to the visit the link above and read library documentation/description by yourself. It's very flexible and contatins lots of useful features.
Using a service can be a good option if you want the downloads to continue even if the user exits. Images that are stored in directories created using getExternalStorageDirectory() are automatically deleted when your app is uninstalled. Moreover you can check if the internal memory is large enough to store images. If you use this methods these images will be deleted upon the uninstallion of the app.
I use this class when downloading images, it caches the images, next time you will be downloading them it will just load from external memory, it manages the cache for you as well so you wont have to worry about setting cache to limited or unlimited, pretty efficient and fast.
public class ImageLoader {
MemoryCache memoryCache = new MemoryCache();
FileCache fileCache;
private Map<ImageView, String> imageViews = Collections
.synchronizedMap(new WeakHashMap<ImageView, String>());
ExecutorService executorService;
// Handler to display images in UI thread
Handler handler = new Handler();
public ImageLoader(Context context) {
fileCache = new FileCache(context);
executorService = Executors.newFixedThreadPool(5);
}
final int stub_id = R.drawable.placeholder;
public void DisplayImage(String url, ImageView imageView) {
imageViews.put(imageView, url);
Bitmap bitmap = memoryCache.get(url);
if (bitmap != null)
imageView.setImageBitmap(bitmap);
else {
queuePhoto(url, imageView);
imageView.setImageResource(stub_id);
}
}
private void queuePhoto(String url, ImageView imageView) {
PhotoToLoad p = new PhotoToLoad(url, imageView);
executorService.submit(new PhotosLoader(p));
}
private Bitmap getBitmap(String url) {
File f = fileCache.getFile(url);
Bitmap b = decodeFile(f);
if (b != null)
return b;
// Download Images from the Internet
try {
Bitmap bitmap = null;
URL imageUrl = new URL(url);
HttpURLConnection conn = (HttpURLConnection) imageUrl
.openConnection();
conn.setConnectTimeout(30000);
conn.setReadTimeout(30000);
conn.setInstanceFollowRedirects(true);
InputStream is = conn.getInputStream();
OutputStream os = new FileOutputStream(f);
Utils.CopyStream(is, os);
os.close();
conn.disconnect();
bitmap = decodeFile(f);
return bitmap;
} catch (Throwable ex) {
ex.printStackTrace();
if (ex instanceof OutOfMemoryError)
memoryCache.clear();
return null;
}
}
// Decodes image and scales it to reduce memory consumption
private Bitmap decodeFile(File f) {
try {
// Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
FileInputStream stream1 = new FileInputStream(f);
BitmapFactory.decodeStream(stream1, null, o);
stream1.close();
// Find the correct scale value. It should be the power of 2.
// Recommended Size 512
final int REQUIRED_SIZE = 70;
int width_tmp = o.outWidth, height_tmp = o.outHeight;
int scale = 1;
while (true) {
if (width_tmp / 2 < REQUIRED_SIZE
|| height_tmp / 2 < REQUIRED_SIZE)
break;
width_tmp /= 2;
height_tmp /= 2;
scale *= 2;
}
// Decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize = scale;
FileInputStream stream2 = new FileInputStream(f);
Bitmap bitmap = BitmapFactory.decodeStream(stream2, null, o2);
stream2.close();
return bitmap;
} catch (FileNotFoundException e) {
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
// Task for the queue
private class PhotoToLoad {
public String url;
public ImageView imageView;
public PhotoToLoad(String u, ImageView i) {
url = u;
imageView = i;
}
}
class PhotosLoader implements Runnable {
PhotoToLoad photoToLoad;
PhotosLoader(PhotoToLoad photoToLoad) {
this.photoToLoad = photoToLoad;
}
#Override
public void run() {
try {
if (imageViewReused(photoToLoad))
return;
Bitmap bmp = getBitmap(photoToLoad.url);
memoryCache.put(photoToLoad.url, bmp);
if (imageViewReused(photoToLoad))
return;
BitmapDisplayer bd = new BitmapDisplayer(bmp, photoToLoad);
handler.post(bd);
} catch (Throwable th) {
th.printStackTrace();
}
}
}
boolean imageViewReused(PhotoToLoad photoToLoad) {
String tag = imageViews.get(photoToLoad.imageView);
if (tag == null || !tag.equals(photoToLoad.url))
return true;
return false;
}
// Used to display bitmap in the UI thread
class BitmapDisplayer implements Runnable {
Bitmap bitmap;
PhotoToLoad photoToLoad;
public BitmapDisplayer(Bitmap b, PhotoToLoad p) {
bitmap = b;
photoToLoad = p;
}
public void run() {
if (imageViewReused(photoToLoad))
return;
if (bitmap != null)
photoToLoad.imageView.setImageBitmap(bitmap);
else
photoToLoad.imageView.setImageResource(stub_id);
}
}
public void clearCache() {
memoryCache.clear();
fileCache.clear();
}
}
To use it Just create an instance of it like
ImageLoader Imageloaer = new ImageLoader(getBaseContext());
Imageloaer.DisplayImage(imageUrl, imageView);
You should try downloading with this,
class DownloadFile extends AsyncTask<String,Integer,Long> {
ProgressDialog mProgressDialog = new ProgressDialog(MainActivity.this);// Change Mainactivity.this with your activity name.
String strFolderName;
#Override
protected void onPreExecute() {
super.onPreExecute();
mProgressDialog.setMessage("Downloading Image ...");
mProgressDialog.setIndeterminate(false);
mProgressDialog.setMax(100);
mProgressDialog.setCancelable(false);
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
mProgressDialog.show();
}
#Override
protected Long doInBackground(String... aurl) {
int count;
try {
URL url = new URL((String) aurl[0]);
URLConnection conexion = url.openConnection();
conexion.connect();
String targetFileName="downloadedimage.jpg";//Change name and subname
int lenghtOfFile = conexion.getContentLength();
String PATH = Environment.getExternalStorageDirectory()+"/myImage/";
File folder = new File(PATH);
if(!folder.exists()){
folder.mkdir();//If there is no folder it will be created.
}
InputStream input = new BufferedInputStream(url.openStream());
OutputStream output = new FileOutputStream(PATH+targetFileName);
byte data[] = new byte[1024];
long total = 0;
while ((count = input.read(data)) != -1) {
total += count;
publishProgress ((int)(total*100/lenghtOfFile));
output.write(data, 0, count);
}
output.flush();
output.close();
input.close();
} catch (Exception e) {}
return null;
}
protected void onProgressUpdate(Integer... progress) {
mProgressDialog.setProgress(progress[0]);
if(mProgressDialog.getProgress()==mProgressDialog.getMax()){
mProgressDialog.dismiss();
Toast.makeText(getApplicationContext(), "Download Completed !", Toast.LENGTH_LONG).show();
}
}
protected void onPostExecute(String result) {
}
}
This code will let you to download all the url of images,
new DownloadFile().execute("https://i.stack.imgur.com/w4kCo.jpg");
.....
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
Change Folder Name as you desired and try to set this images to app bitmap and also avoiding rotate error of images by using this.
The important things to think about here is
Thread , Service ,File , Json, Context,Receiver & if else & for
maybe i did not understand your question but this is not a big deal sir,
your programm your app to work in way where your app starts when the os broadcast onBootCompleted, then create a Thread where you are going to do a lot of code - getting your json file-(when you need it), since its an array you get your jsonObject images, whether its a thousand or million you just iterate it and use any approach to download it, i'd say use the traditional way of downloading your images so as you better control it.
With the help of File class you save it alongside Context you can get your app's cache's directory, which is an internal memory save it there and create a column in your database where you can save the path to the file in your database as String.
When your app start in onPrepareOptionsMenu() check if your app's cache's directory is empty-if not you have some files, now since you have every file and its respective path you can check if it exists with File.exist() if it does no need to download.
if you need pace you can always create new Threads. The Reciever was to be the guy who gets notified when your device boots, if else for a lot of logic checking, for for your loopings, Service to be able to do long running work and have a way to communicate between the UI and background thread.
sorry for the last paragraph i was just trying to buy space :)
I am writing a custom event and would like some help please. Most of what I am about to talk about is based on the help provided at Custom event listener on Android app
So here is my issue. I am writing an app that needs to download updated images from the web, store the images on the phone, then later display those images. Basically, I download any needed images during a splash screen. Then when the images are downloaded and stored, the splash screen clears and any necessary (newly downloaded) images are displayed on the screen. Here is the problem: the download process is done via an asynctask so the part where the images are loaded on to the screen can't be done inside the asynctask. It has to be done on the main UI thread. I would like to create an event and a custom event listener for the main thread to listen for that basically tells the main UI thread that it is safe to start loading the downloaded images from memory.
According to the discussion from the link above, I came up with this so far... a download listener interace
public interface DataDownloadListener {
void onDownloadStarted();
void onDownloadFinished();
}
an event class...
public class DataDownloadEvent {
ArrayList<DataDownloadListener> listeners = new ArrayList<DataDownloadListener>();
public void setOnDownload(DataDownloadListener listener){
this.listeners.add(listener);
}
}
My problem is that I don't understand where to put the last two steps in those instructions. I thought I would have to put the listener and event inside the class that actually initiates the downloads. But where? Here is my function that initiates the download and saves it to the device:
public String download(String sourceLocation) {
String filename = "";
String path = "";
try {
File externalStorageDirectory = Environment
.getExternalStorageDirectory();
URL urlTmp = new URL(sourceLocation);
filename = urlTmp.getFile()
.substring(filename.lastIndexOf("/") + 1);
path = externalStorageDirectory + PATH;
// check if the path exists
File f = new File(path);
if (!f.exists()) {
f.mkdirs();
}
filename = path + filename;
f = new File(filename);
//only perform the download if the file doesn't already exist
if (!f.exists()) {
Bitmap bitmap = BitmapFactory.decodeStream(urlTmp.openStream());
FileOutputStream fileOutputStream = new FileOutputStream(
filename);
if (bitmap != null) {
bitmap.compress(getFormat(filename), 50, fileOutputStream);
Log.d(TAG, "Saved image " + filename);
return filename;
}
}
else{
Log.d(TAG, "Image already exists: " + filename + " Not re-downloading file.");
}
} catch (MalformedURLException e) {
//bad url
} catch (IOException e) {
//save error
}
return null;
}
And the last step about registering the listener, where do I put that? The instructions say to put that somewhere during initialization. Does that mean in the onCreate method of my main activity? outside the class in the import section of the main activity? Never done a custom event before, so any help would be appreciated.
According to the discussion from the link above, I came up with this so far... a download listener interace
public interface DataDownloadListener {
void onDownloadStarted();
void onDownloadFinished();
}
an event class...
public class DataDownloadEvent {
ArrayList<DataDownloadListener> listeners = new ArrayList<DataDownloadListener>();
public void setOnDownload(DataDownloadListener listener){
this.listeners.add(listener);
}
}
Ok...
Now in your download procedure, at the start of the download, cycle all the elements on the listeners ArrayList and invoke the onDownloadStarted event to inform all your listeners that the download is just started (in this event i presume you'll need to open the splashscreen).
Always in your download procedure, at the and of the download, cycle all the elements on the listeners ArrayList and invoke the onDownloadFinished event to inform all your listeners that the download is finished (now close the splashscreen).
How to cycle listeners on download completed
foreach(DataDownloadListener downloadListener: listeners){
downloadListener.onDownloadFinished();
}
How to cycle listeners on download started
foreach(DataDownloadListener downloadListener: listeners){
downloadListener.onDownloadStarted();
}
Don't make it static if possible... In the class that you'll use to download your files, simply add what you put in your DataDownloadEvent class (listeners arrayList and facility methods for adding and removing). You have no immediate need to use a class in that way (static members I mean).
Example
public class DownloadFileClassExample{
private ArrayList<DataDownloadListener> listeners = new ArrayList<DataDownloadListener>();
public DownloadFileClassExample(){
}
public void addDownloadListener(DataDownloadListener listener){
listeners.add(listener);
}
public void removeDownloadListener(DataDownloadListener listener){
listeners.remove(listener);
}
//this is your download procedure
public void downloadFile(){...}
}
Then access you class in this way
DownloadFileClassExample example = new DownloadFileClassExample();
example.addDownloadListener(this); // if your class is implementing the **DataDownloadListener**
or use
example.addDownloadListener( new DataDownloadListener{...})
My application has a lot of optional data that can be downloaded so I decided to use a Service to handle all the downloads in the background, So I started learning it and here is where i got:
public class DownloadService extends IntentService{
public DownloadService() {
super("DownloadService");
}
#Override
protected void onHandleIntent(Intent intent) {
String URL=intent.getStringExtra("DownloadService_URL");
String FileName=intent.getStringExtra("DownloadService_FILENAME");
String Path=intent.getStringExtra("DownloadService_PATH");
try{
URL url = new URL(URL);
URLConnection conexion = url.openConnection();
conexion.connect();
InputStream input = new BufferedInputStream(url.openStream());
OutputStream output = new FileOutputStream(Path+FileName);
byte data[] = new byte[1024];
int count = 0;
while ((count = input.read(data)) != -1) {
output.write(data);
}
output.flush();
output.close();
input.close();
}
catch(Exception e){ }
}
}
The code from the main activity:
Intent ServiceIntent = new Intent(this,DownloadService.class);
ServiceIntent.putExtra("DownloadService_URL", "the url...");
ServiceIntent.putExtra("DownloadService_FILENAME", "Test1.rar");
ServiceIntent.putExtra("DownloadService_PATH", "/sdcard/test/");
startService(ServiceIntent);
Is the code used to download the files correct? Am I using the Service correctly?
I want to download a lot of files.. So should I startService for each different URL?
I would like to inform the user of the percentage done.. But the Service doesnt have a UI. Should I do that in the notification bar?
Thanks.
Is the code used to download the files correct?
I don't like the use of concatenation to create fully-qualified file paths (use the appropriate File constructor). Catching exceptions and not doing anything with them is a really bad idea. On Android 2.3 and higher, you should consider using DownloadManager.
Otherwise, it's probably OK for basic stuff.
I want to download a lot of files.. So should I startService for each different URL?
That should work fine. Note that they will be downloaded one at a time, as IntentService has only one background thread.
I would like to inform the user of the percentage done.. But the Service doesnt have a UI. Should I do that in the notification bar?
That would be one solution. A variation on that would be to have the service send an ordered broadcast, to be picked up by your activity if it is still on-screen or by a BroadcastReceiver that would do the Notification. Here is a blog post with more on that, and here is a tiny sample application demonstrating the concept.