Search code examples
javaandroidwebviewcrashandroid-connectivitymanager

Android ConnectivityManager NetworkCallback crash


I'm new in Android world (as user but also as developer) and my Android App crash when execute myWebView.loadUrl("https://www.google.com"), but I don't understand why...

The goal is monitoring internet connection while the app is running: If the connection is lost -> loads the error html in the web view If the connection returns active -> reload the web view

Here's my app code:

Manifest

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.dedalos.amb">

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

    <application
        android:name=".MyApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".SplashScreen" android:theme="@style/SplashTheme">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

MainActivity

public class MainActivity extends AppCompatActivity {

    // WEB VIEW
    WebView myWebView = null;

    // NETWORK MONITORING
    Boolean isInternetConnected = false;
    ConnectivityManager.NetworkCallback networkCallback = null;
    ConnectivityManager connectivityManager = null;



    //////////////////// OVERRIDE ////////////////////

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        hideSystemUI();

        SharedPreferences prefs = getPreferences(MODE_PRIVATE);
        if (getToken().equals("")) {
            String uniqueID = UUID.randomUUID().toString();
            SharedPreferences.Editor editor = prefs.edit();
            editor.putString("token", uniqueID);
            editor.commit();
        }

        creaWebView();
        configuraWebView();

        initNetworkCallback();

    }

    @Override
    public void onResume() {
        super.onResume();

        if (myWebView == null) {
            creaWebView();
            configuraWebView();
        }

        if (networkCallback == null || connectivityManager == null)
            initNetworkCallback();

        isInternetConnected = (new ConnectionDetector(this).isConnectingToInternet());

        startNetworkMonitoring();

        if (isInternetConnected) {
            caricaWebView();
        }
        else {
            MyWebViewClient.loadError(myWebView);
        }

    }

    @Override
    public void onPause() {
        super.onPause();

        stopNetworkMonitoring();
    }



    private void creaWebView() {
        if (myWebView == null) {
            myWebView = (WebView) findViewById(R.id.ambWebViewLayout);
        }
    }
    private void configuraWebView() {
        WebSettings webSettings = myWebView.getSettings();
        webSettings.setJavaScriptEnabled(true);
        myWebView.setWebViewClient(new MyWebViewClient());
    }
    private void caricaWebView() {
        if (myWebView == null) {
            creaWebView();
            configuraWebView();
        }
        myWebView.loadUrl("https://www.google.com");
    }

    private void initNetworkCallback() {
        networkCallback = new ConnectivityManager.NetworkCallback() {
            @Override
            public void onAvailable(Network network) {
                if (!isInternetConnected) {
                    isInternetConnected = true;
                    caricaWebView();
                }

            }

            @Override
            public void onLost(Network network) {
                if (isInternetConnected) {
                    isInternetConnected = false;
                    MyWebViewClient.loadError(myWebView);
                }
            }
        };

        connectivityManager = (ConnectivityManager) this.getSystemService(Context.CONNECTIVITY_SERVICE);

    }
    private void startNetworkMonitoring() {
        if (networkCallback == null)
            initNetworkCallback();

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            connectivityManager.registerDefaultNetworkCallback(networkCallback);
        }
        else {
            NetworkRequest request = new NetworkRequest.Builder().addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET).build();
            connectivityManager.registerNetworkCallback(request, networkCallback);
        }

    }
    private void stopNetworkMonitoring() {
        connectivityManager.unregisterNetworkCallback(networkCallback);
    }

    private String getToken() {
        return getPreferences(MODE_PRIVATE).getString("token", "");
    }

    private void hideSystemUI() {

        View decorView = getWindow().getDecorView();
        decorView.setSystemUiVisibility(
                View.SYSTEM_UI_FLAG_IMMERSIVE
                        // Set the content to appear under the system bars so that the
                        // content doesn't resize when the system bars hide and show.
                        | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                        | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                        // Hide the nav bar and status bar
                        | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);

    }

}

MyWebViewClient

public class MyWebViewClient extends WebViewClient {

    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {

        if (url.startsWith("https://www.google.com")) {
            view.getContext().startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
            return true;
        }
        else {
            return false;
        }

    }

    @Override
    public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
        super.onReceivedError(view, request, error);

        if (error.getErrorCode() == -2) {
            loadError(view);
        }
        else {
            // TODO
        }

    }

    public static void loadError(WebView view) {

        String html =   "<!DOCTYPE HTML>\n" +
                        "<html>\n" +
                        "<head>\n" +
                        "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n" +
                        "<title>APP</title>\n" +
                        "</head>\n" +
                        "<body style=\"background:#e11020;font-family:Arial,Helvetica;\">\n" +
                        "<h1 style=\"color:rgba(255,255,255,0.9);text-align:center;padding:120px 20px 20px 20px;font-size:30px;\">No Internet connection</h1>\n" +
                        "</body>\n" +
                        "</html>";

        view.loadDataWithBaseURL(null, html, "text/html", "UTF-8", null);

    }
}

ConnectionDetector

public class ConnectionDetector {

    private Context mContext;

    public ConnectionDetector(Context context) {
        this.mContext = context;
    }

    /**
     * Checking for all possible internet providers
     * **/
    public boolean isConnectingToInternet() {
        ConnectivityManager connectivityManager = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            Network[] networks = connectivityManager.getAllNetworks();
            NetworkInfo networkInfo;
            for (Network mNetwork : networks) {
                networkInfo = connectivityManager.getNetworkInfo(mNetwork);
                if (networkInfo.getState().equals(NetworkInfo.State.CONNECTED)) {
                    return true;
                }
            }
        }
        else {
            if (connectivityManager != null) {
                //noinspection deprecation
                NetworkInfo[] info = connectivityManager.getAllNetworkInfo();
                if (info != null) {
                    for (NetworkInfo anInfo : info) {
                        if (anInfo.getState() == NetworkInfo.State.CONNECTED) {
                            return true;
                        }
                    }
                }
            }
        }

        return false;
    }
}

Thanks in advance


Solution

  • According to this you need to call all view related methods on UI thread. Just load the url on UI thread like this:

    myWebView.post(new Runnable() {
        @Override
        public void run() {
            myWebView.loadUrl("https://www.google.com");
        }
    });
    

    You also need to load your error html in the same way, like:

    view.post(new Runnable() {
        @Override
        public void run() {
            String html =   "<!DOCTYPE HTML>\n" +
                    "<html>\n" +
                    "<head>\n" +
                    "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n" +
                    "<title>APP</title>\n" +
                    "</head>\n" +
                    "<body style=\"background:#e11020;font-family:Arial,Helvetica;\">\n" +
                    "<h1 style=\"color:rgba(255,255,255,0.9);text-align:center;padding:120px 20px 20px 20px;font-size:30px;\">No Internet connection</h1>\n" +
                    "</body>\n" +
                    "</html>";
    
            view.loadDataWithBaseURL(null, html, "text/html", "UTF-8", null);
        }
    });