3

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

如果我: 1)设置线程以使用响应更新应用程序字段,下一个方法(在线程之外)在访问字段时抛出 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() {
// (...)
}
4

4 回答 4

8

你最好使用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 客户端应用程序

于 2011-01-23T20:25:08.710 回答
2

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

于 2011-01-23T20:27:13.933 回答
1

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

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

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

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

于 2011-01-23T23:10:06.507 回答
1

如果你真的想使用 aThread而不是 anAsyncTask你可以这样尝试:

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

从您的代码中,变量mHandler分配给new Handler(). 需要明确的是,在我的代码中,这应该是其本身的一个private字段。class如果遇到错误,您还需要在authenticate()方法中进行一些错误处理以发送消息以关闭对话框。

于 2011-01-23T21:31:40.337 回答