Как да внедрите ProgressDialog, докато Activity изисква SoapObject от уеб услуга?

Знам, че въпросите за ProgressDialog с теми са задавани много пъти, но изглежда нито едно от решенията не работи за моя проект. По принцип това, което искам да направя, е следното: 1) когато потребител щракне върху бутон, Activity изпраща заявка за удостоверяване до сървъра 2) докато това се прави, се показва ProgressDialog 3) когато дойде отговорът, искам да отхвърля ProgressDialog и върнатият обект да бъде прочетен и интерпретиран от дейността

Ако аз: 1) настроя нишката да актуализира полето за приложение с отговора, следващият метод (който е извън нишката) хвърля NPE при достъп до полето 2) ако включа следващия метод в нишката, вторият метод хвърля 'java.lang.RuntimeException: Не може да се създаде манипулатор в нишка, която не е извикала Looper.prepare()'

Съжалявам за дългия текст, но напълно се губя от това... Моят код е sth като този:

public class XXX extends Activity implements OnClickListener {

// (...)
private SoapObject returnObject;
private String response;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    // (...)
        authProgressDialog = ProgressDialog.show(XXX.this, "", "Authenticating...", true, false);
        new Thread(new Runnable() {
            @Override
            public void run() {
                authenticate(); // method that calls the API via SOAP
                authenticateReal(); // method that handles the response
            }
        }).start();

        mHandler = new Handler() {
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                switch (msg.what) {
                    case 10:
                        authProgressDialog.dismiss();
                        break;
                }
            }
        };
    }
}

public void authenticate() {
    // API stuff (...)
    AndroidHttpTransport aht = new AndroidHttpTransport(URL);
    try {
        aht.call(SOAP_ACTION, soapEnvelope);
        returnObject = (SoapObject) soapEnvelope.getResponse();
        response = returnObject.getProperty("ResponseStatus").toString();
    }
    catch (Exception e) {
        e.printStackTrace();
    }
    finally {
        mHandler.sendEmptyMessage(10);
    }
}

// Method that needs to access returnObject and reponse objects and
// it is here where the NPE's or other exceptions are thrown
public void authenticateReal() {
// (...)
}

person TomaszRykala    schedule 23.01.2011    source източник
comment
Може да пропускам нещо напълно фундаментално за темите тук, защото никога не съм ги чел достатъчно tbh... съжалявам за глупавия въпрос. Съдържанието на authenticateReal() може лесно да бъде прехвърлено към блока try на autenticate().   -  person TomaszRykala    schedule 23.01.2011


Отговори (4)


По-добре използвайте AsyncTask (което е начинът за Android):

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    new TheTask().execute();
}

private class TheTask extends AsyncTask<Void, Void, Void>{

    @Override
    protected void onPreExecute() {
        authProgressDialog = ProgressDialog.show(XXX.this, "", "Authenticating...", true, false);
    }

    @Override
    protected Void doInBackground(Void... params) {
        authenticate(); // method that calls the API via SOAP
        authenticateReal(); // method that handles the response
        return null;
    }

    @Override
    protected void onPostExecute(Void result) {
        authProgressDialog.dismiss();
    }
}

Между другото... Намерих тази презентация за много полезна (тя говори за REST приложения, но можете да приложите същата концепция за различни видове приложения): Разработване на Android REST клиентски приложения

person Cristian    schedule 23.01.2011
comment
Много благодаря, колега. Това оправи проблема :) - person TomaszRykala; 24.01.2011
comment
Точно от каквото имах нужда!! Благодаря!! Без това диалоговият прозорец дори няма да се покаже, докато изпълнението не приключи, защото е асинхронно! - person bluediapente; 31.03.2011

За последния си проблем поставете "Looper.prepare();" във вашия метод run().

        @Override
        public void run() {
            Looper.prepare();
            authenticate(); // method that calls the API via SOAP
            authenticateReal(); // method that handles the response
        }

Проверихте ли дали вашият отговор във вашия метод authenticate() работи правилно? (използване на LogCat за показване на отговора)

В противен случай по-добре използвайте AsyncTask (както е предложено).

person Rainer    schedule 23.01.2011
comment
Благодаря за ценния коментар. Аз също опитах вашето решение, но моят клас е по-чист, когато вместо това се използва ASyncTask. - person TomaszRykala; 24.01.2011

Ако наистина искате да използвате Thread вместо AsyncTask, можете да опитате по следния начин:

public class XXX extends Activity implements OnClickListener {
...
    private static int HANDLER_MESSAGE_AUTH_REQUEST_COMPLETE = 10;
...
    private void performAuthentication(){
        authProgressDialog = ProgressDialog.show(XXX.this, "", "Authenticating...", true, false);
        Thread backgroundThread = new Thread() {
            @Override
            public void run() {
                authenticate();
            }
        }
        backgroundThread.start();
    }

    private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            switch(msg.what){
            case HANDLER_MESSAGE_AUTH_REQUEST_COMPLETE:
                authProgressDialog.dismiss();
                break;
            }
        }
    }

    private void authenticate(){
        // existing authenticate code goes here
        ...
        Message msg = Message.obtain();
        msg.what = HANDLER_MESSAGE_AUTH_REQUEST_COMPLETE;
        handler.sendMessage(msg);
        // existing authenticateReal code goes here
        ...
    }
}

От вашия код не е 100% ясно къде променливата mHandler е присвоена на new Handler(). За да бъде ясно, в моя код това трябва да е поле private на самото class. Ще ви е необходима и обработка на грешки във вашия authenticate() метод, за да изпратите съобщение за отхвърляне на диалоговия прозорец, ако срещнете грешка.

person dave.c    schedule 23.01.2011
comment
Здравейте, да, Handler също беше частно поле, просто го пропуснах от кода си по погрешка. Сигурен съм, че вашето решение може да е проработило за мен, но отговорът на ASyncTask е това, което реши проблема ми. Изглежда ASyncTask е за предпочитане. Благодаря ви много за отговора. - person TomaszRykala; 24.01.2011

Както други вече писаха, AsyncTask е начинът да продължите.

НО: AsyncTask и Threads имат някои клопки за елементите на потребителския интерфейс:

Ако промените ориентацията на телефона и не сте задали android:configChanges="keyboardHidden|orientation" за вашата активност (означава, че трябва сами да се справите с onConfigChange) в Manifest.xml, промяната на ориентацията ще унищожи и пресъздаде вашата активност и ContentView и също ще прекъсне връзката на ProgressDialog от видимия прозорец (свързан е със стария). Android НЯМА да убие вашата нишка или AsyncTask. Освен това няма да го убие, ако активността бъде унищожена. Тези фонови задачи продължават, докато не бъдат готови.

Опитът да dismiss() вашия предварително създаден ProgressDialog хвърля изключение, след като ContentView е унищожен, тъй като той вече не е част от вашия прозорец. опитайте/хванете МНОГО в ситуации, като правите нещо отделно (асинхронно) работа. Всичко, на което евентуално разчитате, може да е изчезнало или заменено с нещо различно, когато onPostExecute() бъде извикан отново. В крайна сметка помислете за регистриране на всяка ASyncTask, която стартирате, в някакъв масив във вашата дейност и опитайте да ги отмените() във вашия Activity.onDestroy().

person Oliver    schedule 23.01.2011