Android Single Instance Global Callable Network Load Popup

  • 2021-10-24 23:56:23
  • OfStack

Recently, due to the project requirements, it is necessary to complete a global network loading pop-up window requirement. After truly completing this requirement, I feel that the most important thing is not the result, but the thinking.

When I first received this requirement, the first solution that came to mind was base class plus singleton. However, after actually doing it, it is found that because of the singleton, your pop-up window can only be displayed in activity where this singleton was created for the first time.

Then, after finding this problem, improve on this by 1. What if I don't use the context of activity, but adopt a global context similar to Application? Of course, due to limited personal ability, this idea was killed. Later, under the guidance of the tutor, style of dialog was set as a system-level pop-up window in the context of service, so there would be a potential situation at this time. If APP retreated to the background, this pop-up window would be displayed no matter which page the user was on when loading the network, which seriously affected the user experience.

Later, I returned my thinking to the starting point, and I need to realize two points: 1: global callability. 2: Single 1 instance.

Summarize the problems encountered under 1:

1. dialog must rely on activity

2. Because of the singleton, dialog can only be displayed on activity when the singleton is created for the first time

3. You can't use system-level pop-ups

OK, based on these problems and requirements, combined with their own knowledge.

dialog must rely on activity, so I will create an activity to host this dialog, and the background of activity is set to be transparent, and the effect is achieved.

At this time, there will be new problems. If you open this activity in a singleton, there will be many dialog objects, which violates the original intention. If you create dialog in a singleton, there will be many intent objects when you open activity, which is not worth the candle. The workaround is to create two singletons to ensure that both the intent object and the dialog object remain 1-only.

The actual test found that the first time it can be displayed normally, and the second time it will crash.

Reason: When activity is destroyed and recreated, the context will change. Because of singleton, the context of your dialog is the same as when the first activity was created, so when you call this dialog again, you will report an exception that activity does not exist.

There seems to be no solution here.

When I think about this problem again, I suddenly have an epiphany. Why do I have to use dialog? Since I have created an activity dedicated to hosting this dialog, and the life and death of activity is completely related to dialog1, why should I create another dialog? Can't you just write the layout of dialog in activity? When externally creating this activity, the animation will be played directly, and a closing method will be exposed to the externally. Moreover, the singleton of this activity instance can be guaranteed by singleton.

Think of it and do it. After trying and optimizing, the problem is solved perfectly.

The following is the specific implementation code:

activity to display:


public class NetWaitDialogActivity extends AppCompatActivity {
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_net_wait_dialog);
  // Will Activity Instance is added to the AppManager Stack of 
  MyActivityManager.getAppManager().addActivity(this);
 
  Transparentstatusbar();
 
  SimpleDraweeView netwait_dialog_gif = (SimpleDraweeView) findViewById(R.id.netwait_dialog_gif);
  // Show moving pictures 
  DraweeController draweeController_phone_wait = Fresco.newDraweeControllerBuilder()
    .setAutoPlayAnimations(true)
    // Settings uri, Load the local gif Resources 
    .setUri(Uri.parse("res://"+this.getPackageName()+"/"+R.drawable.wait))
    .build();
  netwait_dialog_gif.setController(draweeController_phone_wait);
 }
 
 /**
  *  Transparent status bar 
  */
 private void Transparentstatusbar() {
  ViewGroup contentFrameLayout = (ViewGroup) findViewById(Window.ID_ANDROID_CONTENT);
  View parentView = contentFrameLayout.getChildAt(0);
  if (parentView != null && Build.VERSION.SDK_INT >= 14) {
   parentView.setFitsSystemWindows(true);
   getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
  }
 }
 
 @Override
 protected void onDestroy() {
  super.onDestroy();
  Log.d(" Network loading pop-up window ", "NetWaitDialogActivity.onDestroy");
 }
 
 public static void dismiss(){
  Log.d(" Network loading pop-up window ", " Call dismiss() Method ");
  if (MyActivityManager.getAppManager().isActivityExist(NetWaitDialogActivity.class)){
   // Object for the specified class name Activity
   Log.d(" Network loading pop-up window ", " Call Activity End of administrative tools Activity");
   MyActivityManager.getAppManager().finishActivity(NetWaitDialogActivity.class);
  }
  else {
   Log.d(" Network loading pop-up window ", " The specified class does not exist, call the alternate method ");
   if (((Activity)NetWaitDialogContext).isFinishing() || ((Activity)NetWaitDialogContext).isDestroyed()) {
    Log.d(" Network loading pop-up window ", " Network pop-up window does not exist ");
   } else {
    Log.d(" Network loading pop-up window ", " Call forced shutdown ");
    ((Activity)NetWaitDialogContext).finish();
   }
 
  }
 }
}

Layout file:


<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 xmlns:fresco="http://schemas.android.com/apk/res-auto">
 
 
 <com.facebook.drawee.view.SimpleDraweeView
  android:id="@+id/netwait_dialog_gif"
  android:layout_width="360dp"
  android:layout_height="100dp"
  android:layout_centerInParent="true"
  fresco:roundedCornerRadius="20dp"></com.facebook.drawee.view.SimpleDraweeView>
 
 
</RelativeLayout>

Create transparent styles in style. xml:


<!--  Network loading activity Background  -->
 <style name="Transparent" parent="Theme.AppCompat.Light.NoActionBar">
  <item name="android:windowBackground">@android:color/transparent</item>
  <item name="android:windowIsTranslucent">true</item>
  <item name="android:windowAnimationStyle">@android:style/Animation</item>
  <item name="android:windowNoTitle">true</item>
 </style>

AndroidManifest. Setting styles in xml:


<activity android:name=".NetWaitDialogActivity"
   android:theme="@style/Transparent"></activity>

Singleton tool class:


public class NetWaitStatusUtils {
 private static NetWaitStatusUtils instance;
 private Intent intent;
 private Context context;
 
 private NetWaitStatusUtils(Context context) {
  this.context = context;
  intent = new Intent(context, NetWaitDialogActivity.class);
 }
 
 public static NetWaitStatusUtils getInstance(Context context) {
  if (instance == null) {
   instance = new NetWaitStatusUtils(context);
  }
  return instance;
 }
 
 public void show(){
  context.startActivity(intent);
 }
 
 public void dismiss(){
  NetWaitDialogActivity.dismiss();
 }
}

Get an instance in the base class:


netWaitDialog = NetWaitStatusUtils.getInstance(getApplication());

External invocation:


public class MainActivity extends IActivity {
 
 private Handler handler = new Handler();
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  Button load = findViewById(R.id.load);
  Button gotwo = findViewById(R.id.gotwo);
  load.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View v) {
    netWaitDialog.show();
    handler.postDelayed(new Runnable(){
     @Override
     public void run() {
      netWaitDialog.dismiss();
     }
    }, 3000);
   }
  });
  gotwo.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View v) {
    startActivity(new Intent(MainActivity.this,Main2Activity.class));
    finish();
   }
  });
 }
 
 @Override
 protected int getLayoutId() {
  return R.layout.activity_main;
 }
 
 
}

There is another point to note here, that is, the startup mode of activity, and singletask is recommended. However, this will also have a drawback, that is, it will destroy all activity instances from it to the top of the stack, and you can Baidu yourself.

I use an activity management class here:


package com.example.a9focus.sxt.base;
 
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.ActivityManager;
import android.content.Context;
import android.os.Build;
import android.util.Log;
 
import java.util.Stack;
 
/**
 * Created by HXY on 18-12-1.
 */
 
public class MyActivityManager {
 private static Stack<Activity> activityStack;
 private static MyActivityManager instance;
 
 private MyActivityManager(){}
 /**
  *  Single 1 Instances 
  */
 public static MyActivityManager getAppManager(){
  if(instance==null){
   instance=new MyActivityManager();
  }
  return instance;
 }
 /**
  *  Add Activity To the stack 
  */
 public void addActivity(Activity activity){
  if(activityStack==null){
   activityStack=new Stack<Activity>();
  }
  activityStack.add(activity);
  Log.d("MyActivityManager", activityStack.toString());
 }
 /**
  *  Gets the current Activity (Last in the stack 1 A pressed-in) 
  */
 public Activity currentActivity(){
  Activity activity=activityStack.lastElement();
  return activity;
 }
 /**
  *  End the current Activity (Last in the stack 1 A pressed-in) 
  */
 public void finishActivity(){
  Activity activity=activityStack.lastElement();
  if(activity!=null){
   activity.finish();
   activity=null;
  }
 }
 /**
  *  Ends the specified Activity
  */
 public void finishActivity(Activity activity){
  if(activity!=null){
   activityStack.remove(activity);
   activity.finish();
   activity=null;
  }
 }
 /**
  *  Object for the specified class name Activity
  */
 public void finishActivity(Class<?> cls){
//  try {
   for (Activity activity : activityStack) {
    if(activity.getClass().equals(cls) ){
     finishActivity(activity);
    }
   }
//  }catch (Exception e){
//   Log.d("MyActivityManager", " Specified class does not exist ");
//  }
 
 }
 
 /**
  *  Judge 1 A Activity  Does it exist 
  *
  * @param clz
  * @return
  */
 @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
 public boolean isActivityExist(Class<?> clz) {
  boolean res;
  Activity activity = getActivity(clz);
  if (activity!=null)
   Log.d("MyActivityManager", " Judge whether it exists or not Activity Instances  --> "+activity.toString());
  if (activity == null) {
   res = false;
  } else {
   if (activity.isFinishing() || activity.isDestroyed()) {
    res = false;
   } else {
    res = true;
   }
  }
  Log.d("MyActivityManager", " Specify Activity Existence state " + res);
  return res;
 }
 
 /**
  *  Get the designation activity Instances 
  *
  * @param clazz Activity  Class object of 
  * @return
  */
 public Activity getActivity(Class<?> clazz) {
  Activity returnActivity = null;
  for (Activity activity : activityStack) {
   if(activity.getClass().equals(clazz) ){
    returnActivity = activity;
    return returnActivity;
   }
  }
  return null;
 }
 
 /**
  *  End all Activity
  */
 public void finishAllActivity(){
  for (int i = 0, size = activityStack.size(); i < size; i++){
   if (null != activityStack.get(i)){
    activityStack.get(i).finish();
   }
  }
  activityStack.clear();
 }
 /**
  *  Exit the application 
  */
 public void AppExit(Context context) {
  try {
   finishAllActivity();
   ActivityManager activityMgr= (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
   activityMgr.restartPackage(context.getPackageName());
   System.exit(0);
  } catch (Exception e) {
   e.printStackTrace();
  }
 }
}

Related articles: