Android Question [SOLVED] Getting crash using Job.Download method in "Custom" okHTTPUtils2

JohnC

Expert
Licensed User
Longtime User
I need to do both of these download tasks (with targetSDK=22):

  1. Get HTML from a webpage on a website
  2. Download a large (20mb) apk file from a website

I am able to download the HTML from a website using the standard okHTTPUtils2 (version 2.82) with this code:

B4X:
Sub cmdGetWebpage_Click
    Dim job1 As HttpJob
 
    job1.Initialize("", Me)
 
    job1.Download("https://mywebsite.com/index.html")

End Sub

Sub JobDone (Job As HttpJob)
 
    If Job.Success = True Then
        WebView1.LoadHtml(Job.GetString)
    End If
 
    Job.Release

End Sub

And it works great.

Then, I tried to download a 20MB file using this code:

B4X:
Sub DownloadAndSave (Url As String, Dir As String, FileName As String) As ResumableSub
    Dim j As HttpJob
    j.Initialize("", Me)
       j.Download(Url)
    Wait For (j) JobDone(j As HttpJob)
    If j.Success Then
        Dim out As OutputStream = File.OpenOutput(Dir, FileName, False)
        File.Copy2(j.GetInputStream, out)
           out.Close
    End If
 
       j.Release
    Return j.Success
End Sub

But for some reason, the job always "completes" with the downloaded file being only 40k in size (resulting in an invalid file)

So, then I found this "custom" version of okHTTPUtils2 for downloading large files (and unselected default okHTTPUtils2 v.2.82 and instead using OKHTTP v1.22 with this new "Custom" okHTTPUtils2 lib):


And it properly downloads the 20MB file great!

But when I tried to download the webpage using the same code I posted above but with using this other "custom" okHTTPUtils2, it gave me this error:

B4X:
Logger connected to: 192.168.1.109:5555
--------- beginning of system
--------- beginning of main
--------- beginning of crash
*** Service (httputils2service) Create ***
** Service (httputils2service) Start **
httputils2service_hc_responsesuccess (java line: 178)
java.lang.ClassCastException: java.lang.Object cannot be cast to b4a.example.download.downloadservice$_jobtag
at b4a.example.download.httputils2service._hc_responsesuccess(httputils2service.java:178)
    at java.lang.reflect.Method.invoke(Native Method)
at anywheresoftware.b4a.BA.raiseEvent2(BA.java:196)
at anywheresoftware.b4a.BA$2.run(BA.java:370)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6740)
    at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
java.lang.RuntimeException: java.lang.ClassCastException: java.lang.Object cannot be cast to b4a.example.download.downloadservice$_jobtag
at anywheresoftware.b4a.BA.raiseEvent2(BA.java:233)
at anywheresoftware.b4a.BA$2.run(BA.java:370)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6740)
    at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
Caused by: java.lang.ClassCastException: java.lang.Object cannot be cast to b4a.example.download.downloadservice$_jobtag
at b4a.example.download.httputils2service._hc_responsesuccess(httputils2service.java:178)
    at java.lang.reflect.Method.invoke(Native Method)
at anywheresoftware.b4a.BA.raiseEvent2(BA.java:196)
... 8 more

It seems the below line is were the error is:
B4X:
Sub hc_ResponseSuccess (Response As OkHttpResponse, TaskId As Int)
    ' ********** Modified code *************
    Dim cs As CountingOutputStream
    cs.Initialize(File.OpenOutput(TempFolder, TaskId, False))
    Dim j As HttpJob = TaskIdToJob.Get(TaskId)
    Dim jt As JobTag = j.Tag   '<================ Error Line
    jt.CountingStream = cs
    jt.Total = Response.ContentLength
    If jt.Data.url = "" Then
        Log("Job cancelled before downloaded started")
        cs.Close
    End If
    Response.GetAsynchronously("response", cs , _
        True, TaskId)
    '**************************************
End Sub

My guess is that to use the downloading service (to download huge files), you need to pass the custom "Tag" type. But you don't pass a TAG type when using the regular Job.Download method, so it errors when the job.download method tries to use the same routine that the download service uses (which assumes the TAG type is being used), but with the job.download method the Tag is not used, so it crashes. But this is just a guess.

If I do these below steps, then the regular Job.Download works:
  1. Remove the three modules that are part of the "custom" okHTTPutils2 lib
  2. Remove "okHTTP" (v 1.22) and replace it with the standard okHTTPUtils2 (v 2.82)
  3. Comment out the lines that now give an error in the code because we are no longer using the "Custom" lib
  4. Run the app and click "Get" and it will not error and properly display the webpage.
Attached is a project that demonstrates this issue.

I spent over a day trying different things to get it to work so that I can do both tasks using the same lib, so any help would be greatly appreciated.
 

Attachments

  • Large-Crash.zip
    12.1 KB · Views: 136
Last edited:
Solution
i hope this isn't a waste of your time.
so here's the way it would work (i think):
copy MergedOkHttpUtils2.b4xlib to your add'l libraries folder
remove any references to HttpJob, HttpUtils2Services and DownloadService modules in your project.
and unselect any reference to OkhttpUtils2 in the ide tab of your project.
select (check) MergedOkHttpUtils2 in your ide's libraries manager tab

you will need to declare a global flag drgottjrsflag in your main module.
when you use the downloadservice routine, it will automatically set the flag.
when it is done, it resets the flag.
if you want to do a normal okhttp download, reset the flag before you call
the download method (j.download or job.download or whatever you're calling it).

when the job...

drgottjr

Expert
Licensed User
Longtime User
this isn't really an answer to your question, but i have no problem downloading a 15MB file from my website (i didn't have a 20MB file handy). you just need to set the j.request.timeout value to something high (default is 30 seconds). i toyed with the idea of melding the 2 okhttputils versions at one point but let it slide. that "tag" element stood out immediately as the issue. anyway, if you add a little progress wheel ("working...") and a timer, it's passable (imho).
 
Upvote 0

JohnC

Expert
Licensed User
Longtime User
Hi, the problem isn't downloading the big file - the code from that other thread works fine to do that.

The problem is that if I try to use the Job.Download method of that "custom" version of okHTTPUtils2, it crashes when I try to download a simple webpage.

However, Job.Download works fine with the "official" okHTTPUtils2 (v 2.82), it just doesn't work with the "custom" version.

My app needs to do both functions, so I need to fix the Job.Download method in the custom version.
 
Upvote 0

DonManfred

Expert
Licensed User
Longtime User
Are you using job.TAG in your code?

The custom one is using the tag to store infos about the download if i remember correctly.
Maybe you are setting a tag which the custom lib does´nt expect?
 
Upvote 0

drgottjr

Expert
Licensed User
Longtime User
so, just to clarify, the normal okhttputils2 loads all files - big or small. i took that as the point of your post. ie, you don't need the modified version since the standard version works for both cases. anyway, we're up to v2.94 (or something) now.

more to your point, the only difference between the 2 versions of okhttputils2 is the hc_ResponseSuccess sub. you could simply add the modified code to the standard code and have a flag to tell you which one to use (eg, if you run the download service module, global flag is set. otherwise flag is not set). when job is complete and hc_responsesuccess is called, you test to see where to branch. everything else is the same. that "tag" will take care of itself since it only appears in the service module and in the few lines of modified code that would now run in the unified hc_responsesuccess sub.
 
Upvote 0

drgottjr

Expert
Licensed User
Longtime User
i hope this isn't a waste of your time.
so here's the way it would work (i think):
copy MergedOkHttpUtils2.b4xlib to your add'l libraries folder
remove any references to HttpJob, HttpUtils2Services and DownloadService modules in your project.
and unselect any reference to OkhttpUtils2 in the ide tab of your project.
select (check) MergedOkHttpUtils2 in your ide's libraries manager tab

you will need to declare a global flag drgottjrsflag in your main module.
when you use the downloadservice routine, it will automatically set the flag.
when it is done, it resets the flag.
if you want to do a normal okhttp download, reset the flag before you call
the download method (j.download or job.download or whatever you're calling it).

when the job is complete, the ResponseSuccess sub is raised in okhttputils2.
if drgottjrsflag is set, erel's modified code is run. if the flag is not set, the normal
code is run.

i didn't spend a lot of time, finecombing instances of when i think the flag needs to be
handled, but:
1) so long as it is unset when doing a normal download, the standard code should run.
2) so long as it is set when doing a large file download, erel's modified code should run.

the standard okhttputils2 code never has to do anything with the flag. it just needs to be
reset before you start the download.
the modified okhttputils2 code sets the flag when you invoke the large file download service.
when the job is complete (or cancelled), the flag is reset.
i regret the slight inconsistency regarding when the flag should be set. the download service
module written for the large file download routine is a little different from how we handle normal
files downloads. the key is to make sure the flag is set or reset before the download method is
called. (and presumably reset on completion of the download). you could set the flag before
calling the download service. that way, you set/reset the flag from the main module yourself.

again, i apologize if i still don't follow what you're looking to do.
 

Attachments

  • MergedOkHttpUtils2.b4xlib
    5.8 KB · Views: 137
Last edited:
Upvote 2
Solution

JohnC

Expert
Licensed User
Longtime User
Are you using job.TAG in your code?

The custom one is using the tag to store infos about the download if i remember correctly.
Maybe you are setting a tag which the custom lib does´nt expect?
Hey Don,

Yup - the problem was related to TAG.

But the normal Job.Download method didn't support a way to pass the TAG data, I even tried assigning the tag data to the .TAG property after initializing it, but it still gave an error.
 
Upvote 0

JohnC

Expert
Licensed User
Longtime User
you will need to declare a global flag drgottjrsflag in your main module.
when you use the downloadservice routine, it will automatically set the flag.
when it is done, it resets the flag.
if you want to do a normal okhttp download, reset the flag before you call
the download method (j.download or job.download or whatever you're calling it).

Thanks @drgottjr - This worked!

When I set the flag accordingly before calling each different method, the selective running of those two different versions of hc_ResponseSuccess will allow me to use both functions!
 
Upvote 0

drgottjr

Expert
Licensed User
Longtime User
yesss! feel free to change the name of the flag;)
if you're going to use this thing, just make sure there are no unexpected cases where the flag fails to get changed when it should. i've never used the large file download routine, but, mercifully, erel the magician kept the modified code very localised, so it was possible to unify both versions of okhttputils2 without a big mess. i hope.
 
Upvote 0
Top