Всплывающее диалоговое окно из потока, не являющегося пользовательским интерфейсом


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

Надеюсь, что кто-нибудь может придумать решение или любой способ спросить пользователя/pass только тогда, когда группа находится в безопасности. Вот фоновый поток:

public void run() {

 secInf = mGroupId.getSecurityInformation();
 if (secInf.getAdmissionLevel() == CreateGroupDialog.PRIVATE_KEY_ACCESS) {
  showUserPasswordDialog();
 } else {
  mService.joinGroup(mGroupId);
  // Notifies handler to dismiss ProgresDialog and start activity
  mHandler.sendMessage(Message.obtain(mHandler,
      GroupsActivity.JOIN_SUCCESSFUL));
 }

Где showUserPasswordDialog делает (mActivity-это активность, которая породила этот поток):

 private void showUserPasswordDialog() {
  AlertDialog dialog;
  // add this to your code
  // This example shows how to add a custom layout to an AlertDialog
  LayoutInflater factory = LayoutInflater.from(mActivity);
  final View textEntryView = factory.inflate(
    R.layout.alert_dialog_text_entry, null);

  AlertDialog.Builder builder = new AlertDialog.Builder(mActivity);
  builder.setIcon(R.drawable.alert_dialog_icon);
  builder.setTitle(R.string.ask_user_password);
  builder.setView(textEntryView);
  builder.setPositiveButton(R.string.ok_text,
    new DialogInterface.OnClickListener() {
     public void onClick(DialogInterface dialog, int whichButton) {
      String userName = ((EditText) mActivity
        .findViewById(R.id.username_edit_alert_dialog))
        .getText().toString();
      String password = ((EditText) mActivity
        .findViewById(R.id.password_edit_alert_dialog))
        .getText().toString();
      Credentials cred = new CredentialsL1(userName, password);

      mSmeppService.joinGroup(mGroupId, cred);

      mHandler.sendMessage(Message.obtain(mHandler,
        GroupsActivity.JOIN_SUCCESSFUL));
    });
  builder.setNegativeButton(R.string.cancel_text,
    new DialogInterface.OnClickListener() {
     public void onClick(DialogInterface dialog, int whichButton) {
      dialog.cancel();
      mHandler.sendMessage(Message.obtain(mHandler,
        GroupsActivity.DISMISS_PROGRESS_DIALOG));
     }
    });

  dialog = builder.create();

  /* I found this somewhere, but didn't work either */
  // Window window = dialog.getWindow();
  // WindowManager.LayoutParams lp = window.getAttributes();
  // lp.token = textEntryView.getWindowToken();
  // lp.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
  // window.setAttributes(lp);
  // window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);

  dialog.show();

 }

Я получаю это исключение:

12-13 19:18:31.823: ERROR/AndroidRuntime(1702): FATAL EXCEPTION: Thread-38
12-13 19:18:31.823: ERROR/AndroidRuntime(1702): android.view.InflateException: Binary XML file line #17: Error inflating class android.widget.EditText
12-13 19:18:31.823: ERROR/AndroidRuntime(1702):     at android.view.LayoutInflater.createView(LayoutInflater.java:513)
12-13 19:18:31.823: ERROR/AndroidRuntime(1702):     at com.android.internal.policy.impl.PhoneLayoutInflater.onCreateView(PhoneLayoutInflater.java:56)
12-13 19:18:31.823: ERROR/AndroidRuntime(1702):     at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:563)
12-13 19:18:31.823: ERROR/AndroidRuntime(1702):     at android.view.LayoutInflater.rInflate(LayoutInflater.java:618)
12-13 19:18:31.823: ERROR/AndroidRuntime(1702):     at android.view.LayoutInflater.inflate(LayoutInflater.java:407)
12-13 19:18:31.823: ERROR/AndroidRuntime(1702):     at android.view.LayoutInflater.inflate(LayoutInflater.java:320)
12-13 19:18:31.823: ERROR/AndroidRuntime(1702):     at android.view.LayoutInflater.inflate(LayoutInflater.java:276)
12-13 19:18:31.823: ERROR/AndroidRuntime(1702):     at org.pfc.threads.GroupJoinerThread.showUserPasswordDialog(GroupJoinerThread.java:76)
12-13 19:18:31.823: ERROR/AndroidRuntime(1702):     at org.pfc.threads.GroupJoinerThread.run(GroupJoinerThread.java:52)
12-13 19:18:31.823: ERROR/AndroidRuntime(1702): Caused by: java.lang.reflect.InvocationTargetException
12-13 19:18:31.823: ERROR/AndroidRuntime(1702):     at android.widget.EditText.<init>(EditText.java:51)
12-13 19:18:31.823: ERROR/AndroidRuntime(1702):     at java.lang.reflect.Constructor.constructNative(Native Method)
12-13 19:18:31.823: ERROR/AndroidRuntime(1702):     at java.lang.reflect.Constructor.newInstance(Constructor.java:446)
12-13 19:18:31.823: ERROR/AndroidRuntime(1702):     at android.view.LayoutInflater.createView(LayoutInflater.java:500)
12-13 19:18:31.823: ERROR/AndroidRuntime(1702):     ... 8 more
12-13 19:18:31.823: ERROR/AndroidRuntime(1702): Caused by: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
12-13 19:18:31.823: ERROR/AndroidRuntime(1702):     at android.os.Handler.<init>(Handler.java:121)
12-13 19:18:31.823: ERROR/AndroidRuntime(1702):     at android.text.method.MetaKeyKeyListener$MetaKeyDropbackHandler.<init>(MetaKeyKeyListener.java:605)
12-13 19:18:31.823: ERROR/AndroidRuntime(1702):     at android.text.method.MetaKeyKeyListener.<init>(MetaKeyKeyListener.java:96)
12-13 19:18:31.823: ERROR/AndroidRuntime(1702):     at android.text.method.BaseKeyListener.<init>(BaseKeyListener.java:25)
12-13 19:18:31.823: ERROR/AndroidRuntime(1702):     at android.text.method.TextKeyListener.<init>(TextKeyListener.java:66)
12-13 19:18:31.823: ERROR/AndroidRuntime(1702):     at android.text.method.TextKeyListener.getInstance(TextKeyListener.java:83)
12-13 19:18:31.823: ERROR/AndroidRuntime(1702):     at android.widget.TextView.<init>(TextView.java:806)
12-13 19:18:31.823: ERROR/AndroidRuntime(1702):     at android.widget.EditText.<init>(EditText.java:55)
12-13 19:18:31.823: ERROR/AndroidRuntime(1702):     ... 12 more

XML-файл макета является:

   <EditText
       android:id="@+id/username_edit_alert_dialog"
       android:enabled="false"
       android:layout_height="wrap_content"
       android:layout_width="fill_parent"
       android:layout_marginLeft="20dip"
       android:layout_marginRight="20dip"
       android:scrollHorizontally="true"
       android:autoText="false"
       android:capitalize="none"
       android:gravity="fill_horizontal"
       android:textAppearance="?android:attr/textAppearanceMedium" />
  <!-- Password -->
   <TextView
       android:id="@+id/password_view"
       android:layout_height="wrap_content"
       android:layout_width="wrap_content"
       android:layout_marginLeft="20dip"
       android:layout_marginRight="20dip"
       android:text="@string/password_view_text"
       android:gravity="left"
       android:textAppearance="?android:attr/textAppearanceMedium" />

   <EditText
       android:id="@+id/password_edit_alert_dialog"
       android:enabled="false"
       android:layout_height="wrap_content"
       android:layout_width="fill_parent"
       android:layout_marginLeft="20dip"
       android:layout_marginRight="20dip"
       android:scrollHorizontally="true"
       android:autoText="false"
       android:capitalize="none"
       android:gravity="fill_horizontal"
       android:password="true"
       android:textAppearance="?android:attr/textAppearanceMedium" />

2 4

2 ответа:

Вы правы, что не можете выполнять операции пользовательского интерфейса из фонового потока. Способ сделать это-использовать Handler, который вы уже реализовали, чтобы открыть диалоговое окно. Итак, что-то вроде:

private Handler handler = new Handler(){
    @Override
    public void handleMessage(Message msg) {
        switch(msg.what){
        case GroupsActivity.DISMISS_PROGRESS_DIALOG:
            //your existing dismiss code
            break;
        case GroupsActivity.CREATE_PROGRESS_DIALOG:
            //create the dialog
            break;
        }
    }
};

Тогда ваш метод запуска будет:

public void run() {

    secInf = mGroupId.getSecurityInformation();
    if (secInf.getAdmissionLevel() == CreateGroupDialog.PRIVATE_KEY_ACCESS) {
        // do stuff
        mHandler.sendMessage(Message.obtain(mHandler,
            GroupsActivity.CREATE_PROGRESS_DIALOG));
        // do more stuff
    } else {
        mService.joinGroup(mGroupId);
        // Notifies handler to dismiss ProgresDialog and start activity
        mHandler.sendMessage(Message.obtain(mHandler,
            GroupsActivity.JOIN_SUCCESSFUL));
    }
}

Вы правы. Фоновые потоки не могут открывать диалоговые окна. То, что вам нужно, это Handler в потоке пользовательского интерфейса, чтобы открыть диалоговое окно для вас.