Efiicient way to show 'Cant access network'

2019-03-10 20:54发布

I need to include this in my app

No Connection

When should it get triggered?

  • Any action triggered by user from UI requires internet and there's no internet
  • Any background activities (like asynchronous loading of images) requiring internet has no internet
  • NOT when the app is idle or background/user actions not requiring internet are being processed

What I tried?

  1. I use Volley. Triggered AlertDialog inside Response.ErrorListener of Volley, everytime I wrote a JsonObjectRequest

Problem: too repetitive, had too many lines of AlertDialog code

  1. Added a common class called Alerts and called the AlertDialog from it like

    DialogInterface.OnClickListener onClickTryAgain = new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            Swift.getInstance(ProfileActivity.this).addToRequestQueue(jsonObjectRequest);
        }
    };
    if(error instanceof TimeoutError) Alerts.timeoutErrorAlert(ProfileActivity.this, onClickTryAgain);
    else if(error instanceof NoConnectionError) Alerts.internetConnectionErrorAlert(ProfileActivity.this, onClickTryAgain);
            else Alerts.unknownErrorAlert(ProfileActivity.this);
    

My Alerts.class

    public class Alerts {


        public static void internetConnectionErrorAlert(final Context context, DialogInterface.OnClickListener onClickTryAgainButton) {
            String message = "Sometimes the internet gets a bit sleepy and takes a nap. Make sure its up and running then we'll give it another go";
            new AlertDialog.Builder(context)
                    .setIconAttribute(android.R.attr.alertDialogIcon)
                    .setTitle("Network Error")
                    .setMessage(message)
                    .setPositiveButton("Try Again", onClickTryAgainButton)
                    .setNegativeButton("Turn Internet On", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            Intent i = new Intent(Settings.ACTION_SETTINGS);
                            ((Activity) context).startActivityForResult(i, 0);
                        }
                    })
                    .show();
        }

        public static void timeoutErrorAlert(Context context, DialogInterface.OnClickListener onClickTryAgainButton) {
            String message = "Are you connected to internet? We guess you aren't. Turn it on and we'll rock and roll!";
            new AlertDialog.Builder(context)
                    .setIconAttribute(android.R.attr.alertDialogIcon)
                    .setTitle("Network Error")
                    .setMessage(message)
                    .setPositiveButton("Try Again", onClickTryAgainButton)
                    .show();
        }

        public static void unknownErrorAlert(Context context) {
            new AlertDialog.Builder(context)
                    .setIconAttribute(android.R.attr.alertDialogIcon)
                    .setTitle("Server Error")
                    .setMessage("We all have bad days! We'll fix this soon...")
                    .setPositiveButton("Hmm, I understand", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                        }
                    })
                    .show();
        }
    }

Problem: Try again button doesn't do what I wanted. Well, assuming someone gives a fix for that, I can say, it just replaced some 30 lines of code with 5 lines, well, but what about the transitions? I need to say connecting, till response arrives

  1. This time, I added a layout called ConnectingLayout covering the whole of the screen.

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center_vertical|center_horizontal"
        android:id="@+id/loading_layout"
        android:visibility="gone">
    
        <ProgressBar
            style="?android:attr/progressBarStyleLarge"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:indeterminate="true" />
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/loading"
            android:gravity="center_horizontal"
            android:textSize="25sp"
            android:fontFamily="sans-serif-light"
            android:layout_margin="5dp"
            android:singleLine="true" />
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/loading_description"
            android:gravity="center_horizontal"
            android:fontFamily="sans-serif-light"
            android:layout_margin="5dp"
            android:singleLine="true" />
    </LinearLayout>
    

and used it as in

    jsonObjectRequest = new JsonObjectRequest(Request.Method.POST, getString(R.string.api_root_path) + "/profile", getJson(), new Response.Listener<JSONObject>() {
        @Override
        public void onResponse(JSONObject response) {
            try {
                if(response.getBoolean("success")) {
                    Animations.fadeOut(loadingLayout,500);
                    Animations.fadeIn(mainLayout,500);
                    JSONObject dataJson = response.getJSONObject("data");
                    JSONObject userJson = dataJson.getJSONObject("user");
                    name.setText(userJson.getString("name"));
                    dob.setText(userJson.getString("dob").equals("null")?null:userJson.getString("dob"));
                    email.setText(userJson.getString("email"));
                    phone.setText(userJson.getString("mobile_no"));
                    promotionOffers.setChecked(userJson.getBoolean("offers"));
                } else {
                    Alerts.requestUnauthorisedAlert(ProfileActivity.this);
                    System.out.println(response.getString("error"));
                }
            } catch (JSONException e) { e.printStackTrace(); }
        }
    }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {
            DialogInterface.OnClickListener onClickTryAgain = new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    VolleyBaseClass.getInstance(ProfileActivity.this).addToRequestQueue(jsonObjectRequest);
                }
            };
            if(error instanceof TimeoutError) Alerts.timeoutErrorAlert(ProfileActivity.this, onClickTryAgain);
            else if(error instanceof NoConnectionError) Alerts.internetConnectionErrorAlert(ProfileActivity.this, onClickTryAgain);
            else Alerts.unknownErrorAlert(ProfileActivity.this);
            System.out.println("Response Error: " + error);
        }
    });
    Animations.fadeIn(loadingLayout, 500);
    Animations.fadeOut(mainLayout, 500);
    VolleyBaseClass.getInstance(this).addToRequestQueue(jsonObjectRequest);

Assume that Animations.fadeIn(View view, int ms); and Animations.fadeOut(View view, int ms); are properly defined and does the job.

Problem: Pain of repeating layouts in all acivities and writing code to fade

  1. Learnt about ViewStubs. Generated code programmatically to get the same ConnectingLayout. Now I can change text like saving, loading, etc.

Problem: Not a big leap, but... .., it's one more stepping stone I just landed on

  1. I don't need to say its connecting or loading, I just need to say 'Hey, your phone is processing someething'. So I just thought why not use the loading circle in SwipeRefreshLayouts ?

Problem: Great, it shows little loading circular notch on top, and if loading fails, i can call the alert, but where is turn wifi on, turn 3G on and other things, too big for an AlertDialog to accommodate all that, is there any other way to show try again?

  1. Looked about using same fragment in all activities and redirecting to a callActivityForResult (sorry that I can't add more than two links)

Problem: Hmm...! Taking the user to a new activity is what I find eating resources and bringing down performance considerably. And fragments?

I am not giving up! I know I'm learning it the hard way, but with great hopes that the developer community will help me.

2条回答
一夜七次
2楼-- · 2019-03-10 21:04

I have encountered the same needs and issues. I am working on a Volley based library jus where most the issues you have had i handle with rxJava global observables and of course Alerts class with static alerts/views generators. the connectivity handling I try to make it smooth using ConnectivityManager and NoConnectionPolicy concepts.

查看更多
成全新的幸福
3楼-- · 2019-03-10 21:23

Volley DETROYS all listeners as soon as it receives something from the server, regardless of whether the response is a proper response or an error response.

A request needs to constructed everytime it should be sent or resent.

These steps will help achieve it

  • Create a fragment with Retry and other necessary buttons like Wifi Settings, Mobile Network Settings
  • Create a full sized FrameLayout inside RelativeLayout on necessary views to accommodate the fragment created and hide it initially
  • When request fails, make the FrameLayout visible and the retry button should call the function that constructs and calls the request
查看更多
登录 后发表回答