0

I am new to java and android but have set myself a challenge to create an app that my students can use to interact with some of the content in my class (which I hope to have ready for them by the next semester).

I would like the students to be able to sign in to the app with their email address (Google account) and the app also includes a quiz for each section of work (for which their performance is emailed back to me). I would like to ensure that at active connection is available before a network related task is executed. I have looked at a number of SO questions (Detect if android device is connected to the internet ; Detect whether there is an Internet connection available on Android ; How to check internet access on Android? InetAddress never times out ; Checking internet connection on android , How to check internet access on Android? InetAddress never times out ; How to check internet access on Android? InetAddress never times out ; ...) in trying to formulate my own code, but I am not really understanding how to use the AsyncTask to ensure the network request is not being executed by the main thread to prevent getting the NetworkOnMainThreadException. Please could someone guide me to the correct way to use the AsycTask or Threads to perform the Internet Check before performing the sign-in or grades-sending actions in my app.

At the moment I am calling my isNetworkAvailable method from my Activity that handles the sign in activity as follows:

 signInButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            if (isNetworkAvailable(getApplicationContext())) {
                progressDialog.show();
                Intent intent = Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient);
                startActivityForResult(intent, LOGIN_GOOGLE_NUMBER);
            } else {
                Toast.makeText(getApplicationContext(), "Please ensure that you are connected to the Internet and then try again", Toast.LENGTH_LONG).show();
            }
        }
    });

The method in for checking the Internet connectivity which is part of the Main Signin class looks like this:

// Check if the network is connected (and if Internet is working)
private boolean isNetworkAvailable(Context context) {
    //final ConnectivityManager connectivityManager = ((ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE));
    //return connectivityManager.getActiveNetworkInfo() != null && connectivityManager.getActiveNetworkInfo().isConnected();
    boolean connected = false;
    ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
    if (connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE).getState() == NetworkInfo.State.CONNECTED ||
            connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI).getState() == NetworkInfo.State.CONNECTED) {
        //we are connected to a network
        connected = true;
    } else
        connected = false;
    if (connected) {
        checkConnectivity isConnected = new checkConnectivity();
        if (isConnected.hasInternetAccess(getApplicationContext())) {
            return true;
        } else
            return false;
    } else
        return false;
}

and makes reference to the AsyncTask that I wanted to be accessible throughout the app as a separate public class:

public class checkConnectivity extends AsyncTask<Activity, Void, Boolean> {

private static final String TAG = "MyApplication Context";

protected Boolean doInBackground(Activity... activitys) {
    return hasInternetAccess(activitys[0]);
}

// To check for internet before performing sign in activity
public boolean hasInternetAccess(Context context) {
    try {
            HttpURLConnection urlc = (HttpURLConnection)
                    (new URL("http://clients3.google.com/generate_204")
                            .openConnection());
            urlc.setRequestProperty("User-Agent", "Android");
            urlc.setRequestProperty("Connection", "close");
            urlc.setConnectTimeout(1500);
            urlc.connect();
            return (urlc.getResponseCode() == 204 &&
                    urlc.getContentLength() == 0);
    } catch (IOException e) {
            Log.e(TAG, "Error checking internet connection", e);
    }
    return false;
    }
}

I have been coding this incrementally. If I just have the section to test the network connection then it works fine, but when I try and add the http url request then I run into problems.

What is the correct way to define a public class that I can use in different places in the app to ensure that there is an Internet connection, and how do I make the call in that specific activity? Any help will be most appreciated.

Conrad
  • 33
  • 1
  • 5
  • Somethings that I noticed was, you are passing the Activity to the `AsyncTask`. This can cause memory leak in your app and should not be done. Also, you can use networking library which will handle most of the networking related stuff such as caching etc. – Saran Sankaran Dec 21 '17 at 07:19
  • https://stackoverflow.com/a/42229749/5610842 Check this and let me know if any issues. – RaghavPai Jan 04 '18 at 09:17

2 Answers2

0

When I am stuck some where I usually refer the Plaid app which is good to learn many things. So this is how they are handling network connection in the Plaid app by a Developer at Google.

boolean connected = true;

@Override
protected void onResume() {
    super.onResume();
    checkConnectivity();
}

@Override
protected void onPause() {
    if (monitoringConnectivity) {
        final ConnectivityManager connectivityManager
                = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
        connectivityManager.unregisterNetworkCallback(connectivityCallback);
        monitoringConnectivity = false;
    }
    super.onPause();
}

private void checkConnectivity() {
    final ConnectivityManager connectivityManager
                = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
    final NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
    connected = activeNetworkInfo != null && activeNetworkInfo.isConnected();

    if(!connected){
        // do some action when not connected
        connectivityManager.registerNetworkCallback(
                    new NetworkRequest.Builder()
                    .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET).build(),
                    connectivityCallback);
            monitoringConnectivity = true;
    }

}

// Network call back
private ConnectivityManager.NetworkCallback connectivityCallback
        = new ConnectivityManager.NetworkCallback() {
    @Override
    public void onAvailable(Network network) {
        connected = true;
        if (adapter.getDataItemCount() != 0) return;
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                // do stuff when connected
            }
        });
    }

    @Override
    public void onLost(Network network) {
        connected = false;
    }
};

Here you can call the method checkConnectivity()and it will give the result in a field connected.

'AsyncTask' is never executed on the main thread and is safe to do long running task without without causing the UI to freeze. What you should do is create a separate public class (not a inner class) for the 'AsyncTask' and then you will be able to call that class from any activity you want.

Also, never pass Context or Activity to AsyncTask. It might cause memory leak.

EDIT: Based on the OP comment

How AsyncTask works is it takes three generic types <Start,Progress,Result>

  • Start indicates what type of parameters are you going to accept. This is delivered onPreExecute() as parameter.
  • Progress indicates the type which is responsible for updating Progress of the task e.g.downloading a file. This is delivered as parameter to onProgressUpdate().
  • Result indicates what type of result you got after the task finished. This is delivered as parameter to onPostExecute().

All the above three methods are on UI thread and try to do a network operation on them will give you the NetworkOnMainThreadException. However, doInBackground() is the only method executed in the background thread. It also returns a same type as Result(Explained above).

Now since, you want to use the same task at multiple places, what you can do is you can create a listener interface, which will call the activity who had called the task and pass the result as parameter to the activity.

Saran Sankaran
  • 2,335
  • 2
  • 19
  • 34
  • Dear @Saran, thank you for the feedback. Particularly for the information about not passing Context or Activity to AsyncTask, I am glad to learn that. The ability to monitor the network is also usefull. However, this still does not solve my primary problem, if the network is accessible, I still need to be able to check the Internet connection (which is where I am having problems with NetworkOnMainThread error). – Conrad Dec 21 '17 at 08:36
  • To check the availability of the network you need context. We cannot pass context to the `AsyncTask` so that can check network and initiate network operation. And `NetworkOnMainThread` is a error which is thrown when you try to do the network operation on main thread. So the only option left with us is check if we are connected to the network in MainThread and if connected start the Network operation on background thread. – Saran Sankaran Dec 21 '17 at 08:46
  • If you don't want to do the same thing at multiple place, you can create a static helper method, which will accept a Context, and all the parameters required for network call and then, in the method you can check network connectivity and then based on the result you can execute the task or not. – Saran Sankaran Dec 21 '17 at 08:50
  • Yes, but I don't merely want to check for Network connection (which I can do in the main thread), I want to test that Internet is working. And that I cannot do on the main thread (which is why I was trying to do it in the AsyncTask - with a poor understanding of how it actually works). The static helper method sounds good - but I don't know how to implement it and check Internet connection in the method. – Conrad Dec 21 '17 at 08:51
  • So what you mean to say is you want to check if the user is on WIFI or mobile network and then execute the task? – Saran Sankaran Dec 21 '17 at 08:54
  • No what I mean is that I want to check that WIFI or mobile network actually work, not that they are only connected. That is what I am trying to do in the hasInternetAccess method above. – Conrad Dec 21 '17 at 09:57
  • Ok then in the task, ping to google.com (read about it here https://stackoverflow.com/a/31677756/2758499) which will tell you if the network is actually working. Then based on the result you can proceed with the further task. If the internet is not working, just return false from the task to indicate the execution of the task failed. – Saran Sankaran Dec 21 '17 at 10:08
  • Yes I read through comments earlier on doing that but chose not to go the ping route as it does not work on some older models (e.g. Galaxy S3), which some of my students may be using. Would you be so kind as to show me how I could perform this in the public class without getting the networkOnMainThread error. – Conrad Dec 21 '17 at 10:27
0

I have made the public class for checking network connection. Try it

ConnectionChecker.java

public class ConnectionChecker {

    private Context mContext;

    public boolean isInternetAvailable() {
        ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo networkInfo = cm.getActiveNetworkInfo();
        return networkInfo!=null && networkInfo.isConnected();
    }
}

And wherever, you need to check internet connection then simply define it's instance by following way,

In my app I have defined in MainActivity.java

public class MainActivity extends BaseActivity {

public ConnectionUtil connectionUtil;

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        connectionUtil = new ConnectionUtil();
}

And I had checked network connection before API calling

Below, one sample method for API

public static void callLikeUnlikeWishApi(final MainActivity mActivity,long wishId, long updateWishId, int type) {

        if (mActivity.connectionUtil.isInternetAvailable()) {

            // Here you define your Api code...
        }  else {
        Toast.makeText(mActivity, "No network connection!!", Toast.LENGTH_SHORT).show();
   }
}
Fenil Patel
  • 1,528
  • 11
  • 28
  • Dear @Fenil thank you for the response. Unfortunately, it doesn't help me. I have been through this iteration in my code in order to check for network connection. Because where I live Internet connectivity is eratic, it is possible to be connected to a Wifi router but have no Internet connectivity. That is why I was trying to get the hasInternetAccess method to work. – Conrad Dec 21 '17 at 08:30