Apareciendo un diálogo de un hilo no-UI
-
09-10-2019 - |
Pregunta
Estoy desarrollando una aplicación de red, que está orientada grupo. Lo que pasa es que cuando estoy a punto de unirse a un grupo, se comprueba en primer lugar si el grupo es seguro, y si es así, pide usuario y contraseña. Obtener la seguridad del grupo puede tomar un par de segundos, así que generar un nuevo subproceso para todo el proceso. Me gustaría para que aparezca un cuadro de diálogo en caso de que el grupo requiere seguridad. Estoy pensando que puede tener que ver con hilos de fondo, es posible que no pueda aparecer cuadros de diálogo ... Pero la cosa es que necesito para comprobar la seguridad del grupo en el subproceso en segundo plano, ya que toma un poco de tiempo.
La esperanza alguien puede llegar a la solución o cualquier forma de pedir de usuario / contraseña sólo cuando el grupo es seguro. Aquí está el hilo de fondo:
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));
}
Donde showUserPasswordDialog hace (mActivity es la actividad que dio lugar a este hilo):
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();
}
Me estoy poniendo esta excepción:
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
El archivo de diseño XML es:
<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" />
Solución
Tiene razón en que no se pueden realizar operaciones de interfaz de usuario desde un subproceso en segundo plano. La manera de hacerlo es utilizar el Handler
que ya se han implementado para el cuadro de diálogo emergente. Así que algo como:
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;
}
}
};
A continuación, el método run sería:
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));
}
}
Otros consejos
You are correct. Background threads cannot pop up dialogs. What you need is a Handler
on your UI thread to pop up the dialog for you.
Although it is not a good idea idea to do it, and this answer is for completeness sake only, showing dialog form non-UI thread is possible.
handlerThread = HandlerThread("myHandlerThread")
handlerThread?.start()
handler1 = Handler(handlerThread?.looper)
handler1?.post {
AlertDialog
.Builder(this)
.setPositiveButton("ok") { dialogInterface: DialogInterface, i: Int -> dialogInterface.dismiss() }
.create()
.show()
}