简体   繁体   中英

In android studio, how to prevent errors with buffered reader reading from a url, when the phone has no internet

In my app, when I press a button, a buffered reader should read a line of a text from a text file online.

As a test, if the text is read correctly, I want a toast to appear saying "success". If the read fails, such as because the phone has no connection to the internet, I want a toast to appear saying "failed".

However, if I turn on airplane mode, and then press the button, it simply seems to "hang" forever, and the "failed" toast never appears -- or it just crashes the app entirely.

This is the code I am using:

    button.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            new NotePadFileFromServer().execute();
        }
    });

public class NotePadFileFromServer extends AsyncTask<Void, Void, Void>{

    @Override
    protected Void doInBackground(Void... params) {
        try {
            url = new URL(TextFileURL);
            bufferReader = new BufferedReader(new InputStreamReader(url.openStream()));
            TextHolder = bufferReader.readLine();
            bufferReader.close();
        } catch (Exception e) {
            Toast.makeText(MainActivity.this, "Fail!", Toast.LENGTH_SHORT).show();
        }
        return null;
    }

    @Override
    protected void onPostExecute(Void finalTextHolder) {
        Toast.makeText(MainActivity.this, "Success!", Toast.LENGTH_SHORT).show();
        super.onPostExecute(finalTextHolder);
    }

}

I tried adding in a pre-check using ConnectivityManager to test if there is an internet connection as per this code: https://stackoverflow.com/a/58146646/4250107 , but that only works if the phone user has specifically turned off the internet, and the crashes occur again if the wifi function is turned on, but there is no internet. I then tried checking the internet connection, as per this code: https://stackoverflow.com/a/58146896/4250107 , but this also crashes the app, as apparently (?) attempting to ping a server does not work on Samsung phones.

EDIT: Final fixed code.

public class NotePadFileFromServer extends AsyncTask<Void, Void, String>{

    @Override
    protected String doInBackground(Void... params) {
        try {
            URLConnection url = new URL(TextFileURL).openConnection());
            url.setConnectTimeout(1000);
            url.setReadTimeout(1000);
            bufferReader = new BufferedReader(new InputStreamReader(url.getInputStream()));
            TextHolder = bufferReader.readLine();
            bufferReader.close();
            return "Success!";
        } catch (Exception e) {
            return "Fail!";
        }
    }

    @Override
    protected void onPostExecute(String success) {
        Toast.makeText(MainActivity.this, success, Toast.LENGTH_SHORT).show();
        super.onPostExecute(success);
    }
}

Yes, as you say ConnectivityManager will not help you because if you have wifi but no internet it will crash.

However, it is possible to check internet connection. I couldn't do it with ping (same as you), but i could when i try to open a socket to some of the opened ports (80 or 443). Here is a code using rxjava but you can adapt it to what you are using.

fun isOnline(context: Context?): Single<Boolean> {
return Single.fromCallable {
    try {
        // Connect to Google DNS to check for connection
        val timeoutMs = 2500
        val socket = Socket()
        val address = InetAddress.getByName("www.google.com")

        val socketAddress = InetSocketAddress(address, 443)

        socket.connect(socketAddress, timeoutMs)
        socket.close()

        true
    } catch (e: Exception) {
        false
    }
}.subscribeOn(Schedulers.io())
 .observeOn(AndroidSchedulers.mainThread())
}

In my case i opened the socket with my backend so also i can check if it is working. I put www.google.com in case you don't have a backend.

The way to use it is:

isOnline(context).subscribe { hasInternet -> 
    //Conditional check
} 

The app is crashing because you are trying to perform UI related task in the Background Thread when there is an exception. So, the following is responsible for the crash,

catch (Exception e) {
    Toast.makeText(MainActivity.this, "Fail!", Toast.LENGTH_SHORT).show(); 
}

So, you can avoid the crash by refactoring you code in the following way,

public class NotePadFileFromServer extends AsyncTask<Void, Void, String>{

    @Override
    protected String doInBackground(Void... params) {
        try {
            url = new URL(TextFileURL);
            bufferReader = new BufferedReader(new InputStreamReader(url.openStream()));
            TextHolder = bufferReader.readLine();
            bufferReader.close();
            return "Success!";
        } catch (Exception e) {
            return "Fail!";
        }
    }

    @Override
    protected void onPostExecute(String finalTextHolder) {
        Toast.makeText(MainActivity.this, finalTextHolder, Toast.LENGTH_SHORT).show();
        super.onPostExecute(finalTextHolder);
    }

}

And in case of timeout issue which you described here as hang, I would recommend you to use openConnection() (which returns a UrlConnection ) instead of openStream() . So that you can set shorter connection and read timeout.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM