当 Activity 从 Web 服务请求 SoapObject 时,如何实现 ProgressDialog?

发布于 2024-10-13 14:43:17 字数 2033 浏览 4 评论 0原文

我知道带有线程的 ProgressDialog 问题已被问过很多次,但似乎没有一个解决方案适合我的项目。基本上我想做的是这样的: 1)当用户单击按钮时,活动向服务器发送身份验证请求 2) 执行此操作时会显示 ProgressDialog 3)当响应到来时,我想关闭 ProgressDialog 和由 Activity 读取和解释的返回对象

如果我: 1)设置线程用响应更新Application字段,下一个方法(在线程之外)在访问该字段时抛出NPE 2)如果我在线程中包含下一个方法,第二个方法会抛出“java.lang.RuntimeException:无法在未调用 Looper.prepare() 的线程内创建处理程序”

抱歉,文字很长,但我是完全失去了它......我的代码是这样的:

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() {
// (...)
}

I know that ProgressDialog with Threads questions have been asked many times but none of the solutions seem to work for my project. Basically what I want to do is this:
1) when a user clicks a button the Activity sends an auth request to the server
2) while this is being done a ProgressDialog is shown
3) when the reponse comes I want to dismiss the ProgressDialog and the return object to be read and interpreted by the Activity

If I:
1) set the Thread to update the Application field with the reponse, the next method (which is outside of the Thread) throws an NPE when accessing the field
2) if I include the next method in the Thread, the second method throws a 'java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()'

Sorry for a long text, but I am totally losing it over this... My code is sth like this:

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() {
// (...)
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

无风消散 2024-10-20 14:43:17

是 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();
    }
}

你最好使用 AsyncTask (这 我发现这个演示文稿非常有用(它讨论了 REST 应用程序,但您可以将相同的概念应用于不同类型的应用程序): 开发 Android REST 客户端应用程序

You better use AsyncTask (which is the Android way):

@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();
    }
}

By the way... I have found this presentation to be very useful (it talks about REST apps, but you can apply the same concept for different kind of apps): Developing Android REST client applications

牛↙奶布丁 2024-10-20 14:43:17

对于最后一个问题,请输入“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 (如建议的那样)。

For your last problem, put "Looper.prepare();" into your run() method.

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

Did you check if your response in your authenticate() method is working correctly? (using LogCat to display the response)

Otherwise, better use AsyncTask (like suggested).

野の 2024-10-20 14:43:17

如果您确实想使用 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< /code> 被分配给 new Handler()。需要明确的是,在我的代码中,这应该是 class 本身的一个 private 字段。您还需要在 authenticate() 方法中进行一些错误处理,以便在遇到错误时发送消息以关闭对话框。

If you really want to use a Thread instead of an AsyncTask you can try it this way:

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
        ...
    }
}

From your code it is not 100% clear where the variable mHandler is assigned to the new Handler(). To be clear, in my code this should be a private field on the class itself. You will also need some error handling in your authenticate() method to send a message to dismiss the dialog if you encounter an error.

蝶…霜飞 2024-10-20 14:43:17

正如其他人已经写的那样,AsyncTask 是继续的方法。

但是:AsyncTask 和 Threads 对于 UI 元素有一些陷阱:

如果您更改手机方向并且没有为您的 Activity 设置 android:configChanges="keyboardHidden|orientation" (意味着您必须自己处理 onConfigChange) )在 Manifest.xml 中,方向更改将破坏并重新创建您的 Activity 和 ContentView,并且还会断开 ProgressDialog 与可见窗口的连接(它已连接到旧窗口)。 Android 不会杀死你的线程或异步任务。如果 Activity 被破坏,它也不会杀死它。这些后台任务将继续执行,直到完成。

在 ContentView 被销毁后,尝试解雇()先前创建的 ProgressDialog 会引发异常,因为它不再是窗口的一部分。在做一些独立(异步)工作的情况下尝试/捕捉很多东西。当再次调用 onPostExecute() 时,您可能依赖的所有内容都可能消失或被不同的东西替换。
最终考虑在 Activity 的某个数组中注册您启动的每个 ASyncTask,并尝试在 Activity.onDestroy() 中取消它们。

As others already wrote, AsyncTask is the way to proceed.

BUT: AsyncTask and Threads have some pitfalls for UI elements:

If you change the phone orientation and did not set android:configChanges="keyboardHidden|orientation" for your Activity (means you have to handle onConfigChange by yourself) in the Manifest.xml, an orientation change will destroy and recreate your Activity and ContentView and also disconnect the ProgressDialog from the visible window (it's connected to the old one). Android is NOT going to kill your Thread or AsyncTask. It also won't kill it if the Activity is destroyed. Those background Task continue until they are done.

Trying to dismiss() your previsously created ProgressDialog throws an exception after the ContentView got destroyed, as it is not part of your window anymore. try/catch a LOT in situations doing something detached (async) work. Everything you possibly rely on just could have vanished or replaced by something different when onPostExecute() gets called again.
Eventually think about registering every ASyncTask you start in some Array in your Activity and try to cancel() them at your Activity.onDestroy().

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文