1
votes

I have a Firebase storage which contains photos. I'm organizing the photos by name 1,2,3 and so on.... I'm trying to get all the photos dowload URLs , so in the future i will enter them into Arraylist of URLS and present them in a photo gallery using Glide ( that's why i'm only looking for the URLS )

I'm looking for a method that will keep giving me the Urls only if the onSucsess is called, when onFailure is called (becuase there are no more photos) i want the loop to end.

I'm was trying using the Firebase's getDownloadUrl method and added a boolean which will trigger false when onFailure is called. And increment my photoOrder int which starts at 1,and by that will change the path of the selected photo.

public class Photopackges extends AppCompatActivity {

public static boolean shouldRun = true;
public static final String TAG = "debug";
public static int photoOrder = 1;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_photopackges);

    //just normal firebase setup
    FirebaseStorage storage = FirebaseStorage.getInstance();


     // while shouldRun boolean is true keep going , until it will be defined false
    while (shouldRun == true) {

          // define my Firebase storage path,which contain one folder called "test" and three photos 1.jpg,2.jpg,3.jpg
         // the number of the photo is represented in the photoOrder int.
        String storagePath = "test/" + String.valueOf(photoOrder) + ".jpg";
        StorageReference ref = storage.getReference().child(storagePath);

        try {

            ref.getDownloadUrl().addOnSuccessListener(new OnSuccessListener<Uri>() {
                @Override
                public void onSuccess(Uri uri) {

                    // trying to define that if the action was successful , keep on going.
                    shouldRun = true;
                    Log.e(TAG, "sucsess! " + uri);


                }
            }).addOnFailureListener(new OnFailureListener() {
                @Override
                public void onFailure(@NonNull Exception e) {

                    // trying to define that if the action has failed , stop.
                    shouldRun = false;
                    Log.e(TAG, "Fail! " + e);
                }
            });


        } catch (Exception e) {
            Log.e(TAG, "Error! " + e);
            break;


        }

        //increment the photoOrder so it will go to the next path.
        photoOrder++;
        Log.e(TAG,"value of photoOrder "+photoOrder);
    }
}
}

the log -

Loginfo

I don't have a problem that he sends more requests before he got the answers. I just need him to stop when it gets

StorageException: Object does not exist at location.

I was guessing that there must be a simpler way to get all the storage using firebase..

Thanks for the help !

2

2 Answers

4
votes

Okay , so after alot of trying i was able to make it, using the wait() and notify() methods.

Now i can get ArrayList with all the download URLS of the photos in my FirebaseStorage, which can contain unknown number of photos. notice that's only because i had to rename them before i entered them into my storge as (1,2,3...etc).

For this to work you need to :

  1. Set up a FirebaseStorage.
    1. Create a folder (mine is called test) and populate with images.

i was able to get this Logcat,which is what i wanted See Log

public static final String TAG = "eyaldebug";
public static PathGenerator pathGenerator;
public static ArrayList<String>photoURLs;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_photopackges);

    //Initialize the whole process

    photoURLs = new ArrayList<>();

    pathGenerator = new PathGenerator();

    new UrlProducer(pathGenerator);


}


/**
 * This class contains the GeneratePaths method,which responsible
 * of making new paths.
 * Also we have a simple set - get methods of the booleans.
 */

class PathGenerator {
    //start photo number
    int photoNumber = 0;
    //boolean that indicates if the path has been used already or not.
    boolean pathIsTaken = false;
    //our storage path.
    String path;

    /**
     * This method will generate a new path.
     * while path is taken , wait because we didn't use it yet.
     *
     * @param photoNumber
     */

    public synchronized String generatePath(int photoNumber) {

        while (pathIsTaken)
        {
            try {wait();} catch (Exception e) {}
        }
        this.photoNumber = photoNumber;
        path = "test/" + String.valueOf(photoNumber) + ".jpg";
        pathIsTaken = true;
        Log.e("eyaldebug", "path is :  " + path);
        return path;
    }

    /**
     * Simple set method.
     * @param value
     */

    public synchronized void setPathisSet(boolean value)
    {
        this.pathIsTaken = value;
    }

    /**
     * Unfreeze the thread, we call this method after onSucsess.
     */

    public synchronized void unfreeze( )
    {
        notifyAll();
    }

}


/**
 * Our URLProducer calls will take the paths,and will
 * send HTTP request to the storage that we'll get a
 * download URL returned.
 * later we'll be using Glide\Picasso to display those images.
 */

class UrlProducer implements Runnable {

    PathGenerator mPathGenerator;

    //initialize a String type ArrayList which will contain our URLS.
    public  ArrayList<String>photoURLs = new ArrayList<>();

    //constructor that will be called in the activity
    public UrlProducer(PathGenerator mPathGenerator) {
        this.mPathGenerator = mPathGenerator;

        Thread b = new Thread(this, "UrlProducer");
        b.start();
    }

    /**
     * Here we a simple download URL method using FirebaseStorage.
     * for the documentation for FirebaseStoarge download go to :
     *
     * https://firebase.google.com/docs/storage/android/download-files
     *
     * IF the task was successful we UNfreeze the threads so it will
     * keep sending us new URLS.
     * IF the onFailure was called the stroage is must likely empty and
     * we should stop trying to get new photos.
     */

    @Override
    public void run() {


        int photoNumber =0 ;

        while (true) {


            photoNumber ++;

            try {
                FirebaseStorage storage = FirebaseStorage.getInstance();
                StorageReference ref = storage.getReference();


                ref.child(pathGenerator.generatePath(photoNumber)).getDownloadUrl().addOnSuccessListener(new OnSuccessListener<Uri>() {
                    @Override
                    public void onSuccess(Uri uri) {

                        Log.e(TAG, "Success! " + uri);

                        //add the URL into the ArrayList
                        photoURLs.add(String.valueOf(uri));

                       //tell the generate method that the path has been used.
                        pathGenerator.setPathisSet(false);

                        //Unfreeze the thread so it will continue generate new paths.
                        pathGenerator.unfreeze();

                    }
                }).addOnFailureListener(new OnFailureListener() {
                    @Override
                    public void onFailure(@NonNull Exception e) {

                        //When onFailure is called shutdown,and output the given ArrayList.

                        Log.e(TAG,"onFailure was called , storage is empty!");
                        Log.e(TAG,"----------------------------------------");
                        for(String singleUrl :photoURLs)
                        {
                            Log.e(TAG,""+singleUrl)   ;
                        }
                    }
                });


            }catch (Exception e){e.printStackTrace();}


        }
    }

}
0
votes

Well, the problem is due to the fact that the call ref.getDownloadUrl() is asynchronous. So when your first onFailure method gets called, there have already been many other calls made to the asynchronous method, since your loop runs faster and does not wait for the response of the previous call.

So my advice would be that you should store your URLs in the firebase RealtimeDatabase and fetch those URLs and then load them with glide.

You can see the docs if you want to learn about dealing with list of URLs https://firebase.google.com/docs/database/android/lists-of-data