Лучшая практика для диалога Android - как избежать ошибки sh приложения для диалога закрытия - PullRequest
3 голосов
/ 11 июля 2020

Я использую AlertDialog в своем приложении, когда мне нужно выполнить какую-либо сетевую операцию. В диалоговом окне отображается текст для пользователей и блокируется взаимодействие пользователей с приложением до завершения сетевой операции. Когда сетевая операция будет завершена, я вызову отклонение, чтобы закрыть диалоговое окно, а затем пользователи снова смогут взаимодействовать с приложением. Мой код выглядит примерно так:

//pseudo-code runs in UI/main thread
private void fun() {
    //business code here...
    
    mainExecutor.execute(new Runnable() {
        @Override
        public void run() {
            int timeLeftSec = 10;
            while (true)
            {
                final int timeLeft = timeLeftSec--;
                final String msg = "Time left ";
                mainHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        ShowDialog(msg + " " + timeLeft + " s");
                    }
                });
                
                if (CheckIfNetworkOpDone() || timeLeft < 0) {
                    mainHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            DismissProcessDialog();
                        }
                    });
                    break;
                } else {
                    try {
                        Thread.sleep(1000);
                    }catch (Exception e)
                    {
                        //
                    }
                }
            }
        }
    }
}

private void ShowDialog(String info)
{
    if (mainDialog == null || mainDialogTextView == null)
    {
        View dialogView = LayoutInflater.from(this).inflate(R.layout.processbar_dialog, null);

        if (mainDialog == null)
        {
            mainDialog = new AlertDialog.Builder(this).create();
            mainDialog.setView(dialogView);
            mainDialog.setCancelable(false);
            mainDialog.setCanceledOnTouchOutside(false);
        }
        if (mainDialogTextView == null)
        {
            mainDialogTextView = dialogView.findViewById(R.id.dialogTextView);
        }
    }

    mainDialogTextView.setText(info);
    mainDialog.show();
}

private void DismissProcessDialog()
{
    if (mainDialog != null)
    {
        try {
            Activity activity = (Activity) mainActivityContext;

            if (activity == null || activity.isDestroyed() || activity.isFinishing())
            {
                return;
            }

            Context context = ((ContextWrapper)(mainDialog.getContext())).getBaseContext();
            if (!(context instanceof Activity ))
            {
                return;
            }
            activity = (Activity) context;
            if (activity == null || activity.isDestroyed() || activity.isFinishing())
            {
                return;
            }

            if (mainDialog != null && mainDialog.isShowing())
            {
                mainDialog.dismiss();
            }
        }
        catch (Exception e)
        {
            //
        }
    }
}

    @Override
    protected void onDestroy() {
        if (mainDialog != null && mainDialog.isShowing())
        {
            mainDialog.dismiss();
        }
        mainDialog = null;

        super.onDestroy();
    }

Но бэкэнд Google Play показывает, что есть журнал сбоев, который нужно закрыть. Исключение:

java.lang.IllegalArgumentException: 
  at android.view.WindowManagerGlobal.findViewLocked (WindowManagerGlobal.java:517)
  at android.view.WindowManagerGlobal.removeView (WindowManagerGlobal.java:426)
  at android.view.WindowManagerImpl.removeViewImmediate (WindowManagerImpl.java:126)
  at android.app.Dialog.dismissDialog (Dialog.java:389)
  at android.app.-$$Lambda$oslF4K8Uk6v-6nTRoaEpCmfAptE.run (Unknown Source:2)
  at android.os.Handler.handleCallback (Handler.java:883)
  at android.os.Handler.dispatchMessage (Handler.java:100)
  at android.os.Looper.loop (Looper.java:214)
  at android.app.ActivityThread.main (ActivityThread.java:7356)
  at java.lang.reflect.Method.invoke (Native Method)
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:492)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:930)

ProgressDialog устарела, и вместо этого я использую AlertDialog. Причина, по которой я не использую ProgressBar, заключается в том, что я хочу заблокировать пользователя до завершения работы сети. Я обнаружил, что пользователь все еще может нажимать кнопки, когда отображается ProgressBar.

Есть ли какая-нибудь передовая практика, как исправить такие сбои или как справиться с этим сценарием, чтобы правильно отображать диалог?

Любые предложения будут оценены. Спасибо!

Ответы [ 4 ]

0 голосов
/ 21 июля 2020

Рассматривали ли вы управление взаимодействием пользователя с приложением?

Иногда я использую эти два метода, определенные в моем классе utils.

public class Utils {
    public static void disableUserInteraction(Activity activity) {
        activity.getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
                WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
    }

    public static void enableUserInteraction(Activity activity) {
        activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
    }
}

и называю их так:

 Utils.disableUserInteraction(activity);

Вышеупомянутые методы устанавливают соответствующий флаг, чтобы при необходимости действия пользователя игнорировались.

0 голосов
/ 20 июля 2020

Здесь разумнее использовать progressbar вместо alerttdialog . Если вы хотите запретить пользователю взаимодействовать с приложением во время его загрузки, вы можете сделать все компоненты на экране, кроме индикатора выполнения, невидимыми, или вы можете оставить их видимыми, но изменить их фокусируемость или кликабельность.

0 голосов
/ 20 июля 2020

Ваше исключение может произойти, когда пользователь покидает (или меняет) ваше приложение во время вашей сетевой операции, а ваш Activity (или Fragment) уничтожается (или воссоздается). Тогда больше нет диалогового окна, которое можно было бы отклонить, и возникает исключение, см. этот ответ .

Хотя приведенный выше ответ решает вашу проблему, он делает ошибку, удерживая ссылку на Activity в фоновой задаче. Раньше я позволил моему Activity реализовать интерфейс обратного вызова и вместо этого передать WeakReference этого обратного вызова фоновой задаче. Затем фоновая задача должна была проверить, был ли еще обратный вызов (Activity).

В приведенном выше решении все еще есть проблема, заключающаяся в том, что вам нужно повторно запускать задачу для каждого нового Activity, созданного при ротации (или любое другое изменение конфигурации). Современный подход заключается в том, чтобы позволить Activity (или Fragment) вместо этого наблюдать LiveData в вашем ViewModel. См. Официальную документацию .

Также рекомендуется использовать DialogFragment, потому что он восстанавливается при повторном создании. Обычный AlertDialog исчезает, когда вы поворачиваете экран (или переключаетесь в приложение после его уничтожения).

0 голосов
/ 18 июля 2020

Мне кажется, что есть две проблемы: вы вызываете диалог отклонения из фонового потока. Вы всегда должны выполнять вызовы пользовательского интерфейса из потока пользовательского интерфейса (в этом поможет runOnUi{...}). Во-вторых, вы запускаете al oop с while (true). Это крайне плохая практика, здесь вы должны передать условие, которое можно завершить внутри вашего l oop. Это будет продолжаться бесконечно, поэтому в настоящее время первый проход выполняет сетевой вызов. Скажем, он заканчивается мгновенно, второй проход попытался закрыть диалог. Затем третий проход соответствует тем же условиям, теперь у вас есть findViewLocked в журнале cra sh, потому что он пытается получить доступ к одному и тому же объекту из двух фоновых потоков.

...