Android Question I need help with a loop

eSolution

Member
Licensed User
Longtime User
I have a Sub that is sending a request to a webservice and gets back a JSON string. I send that string to another Sub to be parsed and I get a value from that JSON string that I store in a global variable.

So far so good, I make the call to the url, I get the Json, I parse it and get the required value and I store it. The value is a NextPageToken that I need to use in the same request if I want to get the next set of JSON data from the server.

Now I try to put everything in a Do Until loop that will play until the NextPageToken is equal with the last value recorded. The problem is that if I put the main Sub in the loop then the loop will continue without waiting for the JSON response and for the JSON string to be parsed.

How can I stop a loop until a certain action or a condition is met?


Or may be somebody can teach me how to get the entire data that I need when the data is split in "pages" in JSON format and require multiple requests?

the URL for the request is this:
B4X:
https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&maxResults=50&pageToken=&playlistId=PLsyeobzWxl7oZ-fxDYkOToURHhMuWD1BK&key=yourYTAPIkey
The playlist is a random one with over 150 videos inside.
First request is with pageToken empty and the following requests should use the value of pageToken from the JSON response from the first request.

Thank you
 
Last edited:

stevel05

Expert
Licensed User
Longtime User
it would be easier to help if you post some code so that we can see exactly what you are doing.

Are you using HTTPUtils2 to send the request? If so you should be checking the result in it's _complete callback and not using a loop to wait for the result. If not, you probably should be.
 
Upvote 0

eSolution

Member
Licensed User
Longtime User
This is the code:
B4X:
Sub Process_Globals
    'These global variables will be declared once when the application starts.
    'These variables can be accessed from all modules.
    Public NextPageToken As String
    Public PageToken As String
End Sub

Sub Globals
    'These global variables will be redeclared each time the activity is created.
    'These variables can only be accessed from this module.
    Dim parser As JSONParser


    Dim playlistID As String = "PLsyeobzWxl7oZ-fxDYkOToURHhMuWD1BK"
    Dim YTAPIkey As String = "your Youtube API key"
  
    'Private Button1 As Button
End Sub

Sub Activity_Create(FirstTime As Boolean)
    'Do not forget to load the layout file created with the visual designer. For example:
    'Activity.LoadLayout("P1")
    If FirstTime Then

        GetYTVideos

        Else
        GetYTVideos
    End If
  
End Sub

Sub Activity_Resume

End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub

Private Sub GetYTVideos()

    'Do While PageToken = NextPageToken  
        Log("PageToken: " & PageToken)
      
        YTPlayListRequest
    'Loop
End Sub

Private Sub YTPlayListRequest()
    'PageToken = NextPageToken

    Dim URLtoGET As String = "https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&maxResults=50&pageToken=" & NextPageToken & "&playlistId="& playlistID &"&key=" & YTAPIkey
      
    Log(URLtoGET)
      
    Dim J As HttpJob
        J.Initialize("ytGetJob", Me)
        J.Download (URLtoGET)
        J.GetRequest.SetHeader("Accept","text/html, application/xhtml+xml, image/jxr, */*")
        J.GetRequest.SetHeader("Host", "www.googleapis.com")
        J.GetRequest.SetHeader("Connection", "Keep-Alive")
      
    ProgressDialogShow("Searching for Tutorial Videos...")
  
    Log("Searching for videos")
End Sub

Sub JobDone (Job As HttpJob)
      
Dim ytGETresult As String
  
    Log("Job Done Started ")
  
   Log("JobName = " & Job.JobName & ", Success = " & Job.Success)
 
   If Job.Success = True Then
      Select Job.JobName
         Case "ytGetJob"
            'print the result to the logs
            ytGETresult = Job.GetString
          
            ProgressDialogHide

            Log(Job.GetString)
                      
                'ParseResult(Job.GetString)
                ParseResponse(Job.GetString)
     End Select
   Else
      Log("Error: " & Job.ErrorMessage)
      ToastMessageShow("Error: " & Job.ErrorMessage, True)
   End If
   Job.Release
End Sub

Private Sub ParseResponse(text As String)
  
parser.Initialize(text)

Log("parser started ")

Dim root As Map = parser.NextObject
    Dim NextPageToken As String = root.Get("nextPageToken")
    Dim items As List = root.Get("items")
        For Each colitems As Map In items
            Dim snippet As Map = colitems.Get("snippet")
                Dim playlistID As String = snippet.Get("playlistId")
                Dim resourceId As Map = snippet.Get("resourceId")
                    Dim videoId As String = resourceId.Get("videoId")
                Dim title As String = snippet.Get("title")
                Dim thumbnails As Map = snippet.Get("thumbnails")
          
'                    Dim standard As Map = thumbnails.Get("standard")
'                    Dim width As Int = standard.Get("width")
'                    Dim url As String = standard.Get("url")
'                    Dim height As Int = standard.Get("height")
          
                    Dim default As Map = thumbnails.Get("default")
                    Dim width As Int = default.Get("width")
                    Dim url As String = default.Get("url")
                    Dim height As Int = default.Get("height")
          
'                    Dim high As Map = thumbnails.Get("high")
'                    Dim width As Int = high.Get("width")
'                    Dim url As String = high.Get("url")
'                    Dim height As Int = high.Get("height")

'                    Dim medium As Map = thumbnails.Get("medium")
'                    Dim width As Int = medium.Get("width")
'                    Dim url As String = medium.Get("url")
'                    Dim height As Int = medium.Get("height")

        Next
Log("NextPageToken " & NextPageToken)
'PageToken = NextPageToken


End Sub

You need HTTP, HTTPUtils2 and JSON libraries to run it.

The loop is commented in the code atm.
 
Last edited:
Upvote 0

stevel05

Expert
Licensed User
Longtime User
So you just need to update the PageToken variable where it's commented out currently at the end of parse response, then call the next YTPlayListRequest after Job.Release in the JobDone sub and after checking for Job.Success and that you haven't had all of the pages.

How do you know when you have the last page?
 
Upvote 0

stevel05

Expert
Licensed User
Longtime User
BTW, are the YTAPIkey and PlaylistID personal codes? You will want to remove them if they are.
 
Upvote 0

eSolution

Member
Licensed User
Longtime User
PlaylistID is a random list with 150 something videos chosen for this test. the APIkey is personal but I intend to delete the app from my account very soon so the key will be invalid. No one would bother to run the code and help me if I ask them to do the extra steps to generate their own APIkey :)

I tried to do it this way and the loop is not waiting for the JSON to be received and parsed to get the NextPageToken, I have no idea why.
My idea was to put the YTPlayListRequest in a loop and every time the loop is completed to have a new NextPageToken that I will use in the next iteration and so on. The loop will compare PageToken and NextPageToken variables to see if they are the same. When an iteration starts I make store the old NextPageToken value in the PageToken variable then I run the code in the loop, get the new NextPageToken, the loop compare it with the PageToken and so on... at some point the PageToken and NextPageToken should remain the same and the loop should stop...

The code inside the loop is working if is not in the loop (like it is now, with the "Do Until" commented out) but is not completed inside the loop.

May be I did not understood your explanation and I miss something...
 
Upvote 0

stevel05

Expert
Licensed User
Longtime User
OK Try this:

B4X:
Sub Process_Globals
    'These global variables will be declared once when the application starts.
    'These variables can be accessed from all modules.
    Public NextPageToken As String
    Public PageToken As String
    Public Completed = False
End Sub

Sub Globals
    'These global variables will be redeclared each time the activity is created.
    'These variables can only be accessed from this module.
    Dim parser As JSONParser


    Dim playlistID As String = "PLsyeobzWxl7oZ-fxDYkOToURHhMuWD1BK"
    Dim YTAPIkey As String = "AIzaSyCCU4tGcGaNf3GVjWoyoTgd6rHgn8TsMtE"
   
    'Private Button1 As Button
End Sub

Sub Activity_Create(FirstTime As Boolean)
    'Do not forget to load the layout file created with the visual designer. For example:
    'Activity.LoadLayout("P1")
    If FirstTime Then

        GetYTVideos

        Else
        GetYTVideos
    End If
   
End Sub

Sub Activity_Resume

End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub

Private Sub GetYTVideos()

    'Do While PageToken = NextPageToken  
        Log("PageToken: " & PageToken)
       
        YTPlayListRequest
    'Loop
End Sub

Private Sub YTPlayListRequest()
    'PageToken = NextPageToken

    Dim URLtoGET As String = "https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&maxResults=50&pageToken=" & NextPageToken & "&playlistId="& playlistID &"&key=" & YTAPIkey
       
    Log(URLtoGET)
       
    Dim J As HttpJob
        J.Initialize("ytGetJob", Me)
        J.Download (URLtoGET)
        J.GetRequest.SetHeader("Accept","text/html, application/xhtml+xml, image/jxr, */*")
        J.GetRequest.SetHeader("Host", "www.googleapis.com")
        J.GetRequest.SetHeader("Connection", "Keep-Alive")
       
    ProgressDialogShow("Searching for Tutorial Videos...")
   
    Log("Searching for videos")
End Sub

Sub JobDone (Job As HttpJob)
       
Dim ytGETresult As String
   
    Log("Job Done Started ")
   
   Log("JobName = " & Job.JobName & ", Success = " & Job.Success)
   If Job.Success = True Then
      Select Job.JobName
         Case "ytGetJob"
            'print the result to the logs
            ytGETresult = Job.GetString
           
            ProgressDialogHide

            'Log(Job.GetString)
                       
                'ParseResult(Job.GetString)
                ParseResponse(Job.GetString)
               
     End Select
   Else
      Log("Error: " & Job.ErrorMessage)
      ToastMessageShow("Error: " & Job.ErrorMessage, True)
   End If
   Job.Release
   
   'Call the next page if there was not error and the page token has changed
   If Job.Success And Not(Completed) Then YTPlayListRequest
End Sub

Private Sub ParseResponse(text As String)
   
parser.Initialize(text)
   
Log("parser started ")

Dim root As Map = parser.NextObject
    Dim NextPageToken As String = root.Get("nextPageToken")
    Dim items As List = root.Get("items")
        For Each colitems As Map In items
            Dim snippet As Map = colitems.Get("snippet")
                Dim playlistID As String = snippet.Get("playlistId")
                Dim resourceId As Map = snippet.Get("resourceId")
                    Dim videoId As String = resourceId.Get("videoId")
                Dim title As String = snippet.Get("title")
                Dim thumbnails As Map = snippet.Get("thumbnails")
           
'                    Dim standard As Map = thumbnails.Get("standard")
'                    Dim width As Int = standard.Get("width")
'                    Dim url As String = standard.Get("url")
'                    Dim height As Int = standard.Get("height")
           
                    Dim default As Map = thumbnails.Get("default")
                    Dim width As Int = default.Get("width")
                    Dim url As String = default.Get("url")
                    Dim height As Int = default.Get("height")
           
'                    Dim high As Map = thumbnails.Get("high")
'                    Dim width As Int = high.Get("width")
'                    Dim url As String = high.Get("url")
'                    Dim height As Int = high.Get("height")

'                    Dim medium As Map = thumbnails.Get("medium")
'                    Dim width As Int = medium.Get("width")
'                    Dim url As String = medium.Get("url")
'                    Dim height As Int = medium.Get("height")

        Next
Log("NextPageToken " & NextPageToken)

'Check that the page token has changed, if not then set the completed flag
If PageToken = NextPageToken Then Completed = True

PageToken = NextPageToken

End Sub

It loads the next page when the last is complete. The parser currently fails, but you need to change it to check the availability of the elements before assigning them.
 
Upvote 0

eSolution

Member
Licensed User
Longtime User
Thank you, I modified your idea a little, I noticed that the last PageToken value will always be "null" so I checked for that before calling the sub again.
Everything work now.
Now I need to populate a list with the values and add to that list on every call.
API key deleted from the google project.

Thank you!
 
Last edited:
Upvote 0
Top