Skip to content

Instantly share code, notes, and snippets.

@museofwater
Last active March 22, 2022 09:17
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save museofwater/6373048 to your computer and use it in GitHub Desktop.
Save museofwater/6373048 to your computer and use it in GitHub Desktop.
Android code snippet showing how to handle timeouts and server errors cleanly in a web view
public class AsyncMultiplayerSetupActivity extends Activity {
private static final String TAG = AsyncMultiplayerSetupActivity.class.getName();
public static final String UTF_8 = "UTF-8";
private WebView wvSignin;
private String url = "http://localhost:9000/signin";
private ProgressDialog progressLoadUrl;
private SigninWebViewClient webViewClient;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
registerUser(url);
}
private void registerUser(String url) {
webViewClient = new SigninWebViewClient(getResources().getInteger(R.integer.timeout));
setContentView(R.layout.setup);
wvSignin = (WebView)findViewById(R.id.wvSignin);
wvSignin.getSettings().setJavaScriptEnabled(true);
wvSignin.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
wvSignin.setWebViewClient(webViewClient);
loadUrl(url);
}
private void loadUrl(String url) {
if (!NetworkUtil.checkNetwork(this)) {
setSigninFailureResult();
}
// Show progress
progressLoadUrl =
ProgressDialog.show(this, getString(R.string.CONNECTING_TITLE),
getString(R.string.CONNECTING_MSG));
webViewClient.prepareToLoadUrl();
wvSignin.loadUrl(url);
}
private void setSigninFailureResult() {
setResult(getResources().getInteger(R.integer.RESPONSE_FAILED_CODE));
finish();
}
private void setSigninResult() {
setResult(getResources().getInteger(R.integer.RESPONSE_OK_CODE));
}
private class SigninWebViewClient extends WebViewClient {
/**
* Timeout for page load in seconds
*/
private int timeout;
private String urlLoading;
boolean pageLoaded = false;
// Flag to instruct the client to ignore callbacks after an error
boolean hasError = false;
private Handler timeoutHandler;
private AlertDialog alertDialog;
private SigninWebViewClient(int timeout) {
this.timeout = timeout;
timeoutHandler = new Handler();
}
// Called by activity before requesting load of a url
private void prepareToLoadUrl() {
this.hasError = false;
this.pageLoaded = true;
this.urlLoading = null;
}
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
if (hasError) {
return;
}
urlLoading = url;
// timeout has expired if this flag is still set when the message is handled
pageLoaded = false;
Runnable run = new Runnable() {
public void run() {
// Do nothing if we already have an error
if (hasError) {
return;
}
// Dismiss any current alerts and progress
dismissProgress();
dismissErrorAlert();
if (!pageLoaded) {
showTimeoutAlert();
}
}
};
timeoutHandler.postDelayed(run, this.timeout*1000);
}
@Override
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
// Ignore future callbacks because the page load has failed
hasError = true;
dismissProgress();
showServerErrorAlert();
}
@Override
public void onPageFinished(WebView view, String url) {
if (hasError) {
return;
}
pageLoaded = true;
dismissProgress();
dismissErrorAlert();
urlLoading = null;
// Do whatever processing you need to on page load here
}
private void showTimeoutAlert() {
showErrorAlert(R.string.TIMEOUT_TITLE, R.string.TIMEOUT_MSG);
}
private void showServerErrorAlert() {
showErrorAlert(R.string.SERVER_ERROR_TITLE,R.string.SERVER_ERROR_MSG);
}
private void showErrorAlert(int titleResource, int messageResource) {
AlertDialog.Builder builder = new AlertDialog.Builder(AsyncMultiplayerSetupActivity.this);
// Add the buttons
builder.setTitle(titleResource)
.setMessage(messageResource)
.setPositiveButton(R.string.RETRY, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// Try to load url again
loadUrl(urlLoading);
dialog.dismiss();
}
});
builder.setNegativeButton(R.string.CANCEL, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// User cancelled the dialog
setSigninFailureResult();
dialog.cancel();
}
});
// Create the AlertDialog
alertDialog = builder.create();
alertDialog.show();
}
private void dismissProgress() {
if (progressLoadUrl != null && progressLoadUrl.isShowing()) {
progressLoadUrl.dismiss();
}
}
private void dismissErrorAlert() {
if (alertDialog != null && alertDialog.isShowing()) {
alertDialog.dismiss();
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment