Reputation: 367
We have an internal app that checks for updates when launched. It is not released on Play Store because it is for internal use only.
The update code below was working fine on my test device (API 23). However I have recently released an update which downloads on the test device but reports:
Parse Error - There was a problem while parsing the package
On my own device (API 24) the app downloads the app and then crashes.
public class UpdateApp extends AsyncTask<String,Void,Void> {
private Context context;
public void setContext(Context contextf){
context = contextf;
}
@Override
protected Void doInBackground(String... arg0) {
try {
URL url = new URL(arg0[0]);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setDoOutput(true);
conn.connect();
File file = context.getCacheDir();
file.mkdirs();
outputFile = new File(file, "update.apk");
if(outputFile.exists()){
outputFile.delete();
}
FileOutputStream fos = new FileOutputStream(outputFile);
InputStream is = conn.getInputStream();
byte[] buffer = new byte[1024];
int len1 = 0;
while ((len1 = is.read(buffer)) != -1) {
fos.write(buffer, 0, len1);
}
fos.close();
is.close();
outputFile.setReadable(true, false);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(outputFile), "application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
} catch (Throwable ex) {
Toast.makeText(context, ex.toString(), Toast.LENGTH_LONG).show();
}
return null;
}
On both devices the updated APK is downloaded before either throwing an error (API 23) or crashing (API 24). I do not understand how the app can just crash as all the code is in a try
block which has a catch
for the Throwable
class so it should catch
all errors and exceptions!
The following is the logcat
output from my API 24 device whilst updating after the file is downloaded. Other than pointing to the problem being with the AysncTask
class, I cannot work out anything useful from this.
2018-11-04 16:47:27.593 966-1017/? E/PROXIMITY: ProximitySensor: unknown event (type=3, code=0)
2018-11-04 16:47:27.704 23431-23476/uk.co.letsdelight.letsdelight E/AndroidRuntime: FATAL EXCEPTION: AsyncTask #1
Process: uk.co.letsdelight.letsdelight, PID: 23431
java.lang.RuntimeException: An error occurred while executing doInBackground()
at android.os.AsyncTask$3.done(AsyncTask.java:318)
at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:354)
at java.util.concurrent.FutureTask.setException(FutureTask.java:223)
at java.util.concurrent.FutureTask.run(FutureTask.java:242)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:243)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
at java.lang.Thread.run(Thread.java:761)
Caused by: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
at android.os.Handler.<init>(Handler.java:208)
at android.os.Handler.<init>(Handler.java:122)
at android.widget.Toast$TN.<init>(Toast.java:351)
at android.widget.Toast.<init>(Toast.java:106)
at android.widget.Toast.makeText(Toast.java:265)
at uk.co.letsdelight.letsdelight.Update$UpdateApp.doInBackground(Update.java:123)
at uk.co.letsdelight.letsdelight.Update$UpdateApp.doInBackground(Update.java:83)
at android.os.AsyncTask$2.call(AsyncTask.java:304)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:243)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
at java.lang.Thread.run(Thread.java:761)
2018-11-04 16:47:27.716 966-989/? W/ActivityManager: Force finishing activity uk.co.letsdelight.letsdelight/.MainActivity
2018-11-04 16:47:27.745 966-23481/? W/AES: Exception Log handling...
What else can I do to try to resolve this problem and perhaps make the code more robust so that we do not have issues with further updates?
Upvotes: 0
Views: 124
Reputation: 3186
Sometimes java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
happens in AsyncTasks because you are calling execute
in a background thread.
you can attach a debugger and check if you are executing this asyncTask in background or not. you can use Evaluator to get value of Thread.currentThread().getId()
if it returns 1 it means you are on the main thread if not, try to call execute in main thread of your app
Upvotes: 0
Reputation: 1006564
Step #1: Replace your Toast
with something else. You cannot show a Toast
on a background thread, which is what the error message is telling you (Can't create handler inside thread that has not called Looper.prepare()
). A typical solution, given that you are still using AsyncTask
, is to hold onto the Exception
in a field in the AsyncTask
, then do something with it in onPostExecute()
, as that is executed on the main application thread.
Step #2: Always log exceptions. At minimum, you should be logging to LogCat. If you have integrated ACRA, Crashlytics, or similar solutions, you should be taking steps to ensure that your exceptions get reported there as well. Right now, your catch
block is only attempting to show a Toast
, which means you have no way of knowing what the specific problem is (e.g., lack of connectivity).
Step #3: One certain problem on Android 7.0+ is that you cannot use Uri.fromFile()
very well, as file
Uri
values are banned. On those devices, use FileProvider
to make the APK available to the installer, and use FileProvider.getUriForFile()
to get the Uri
to put in your Intent
. Unfortunately, on Android 6.0 and older devices, you cannot use FileProvider
, as the installer will not support content
Uri
values. So, you will need to detect your version (Build.VERSION.SDK_INT
) and use the right Uri
based on version.
Upvotes: 1