【问题标题】:Best way to request data from server从服务器请求数据的最佳方式
【发布时间】:2016-07-10 01:17:15
【问题描述】:

我需要从我的服务器获取一些数据才能使我的应用正常运行。为此,我将使用 POST。据我所知,我必须在一个不能作为主线程的线程中请求数据。我发现将接收到的数据放入 UI 线程中定义的变量中有点困难。所以,我的问题是,这是最好的方法吗? 例如,在我的主要活动中使用在 AsyncTask 中调用的 setter 来设置定义的变量的值是否正确?或者这个选项可能更好?

Thread nt = new Thread(){
        @Override
    public void run(){

            try{

               //get data with POST and then something like main.setValue(data);
            }catch(Exception e){

                e.printStackTrace();
            }
        }

    };
    nt.start();

我已经读到我可能会使用接口来存档它,但这是一个我还不太了解的概念。我想直接使用返回数据的方法,但据我所知,这是不可能的。 谢谢你的时间!

编辑:根据 NoChinDeluxe 答案的新代码:

public class LoginHandler {

public static class Login extends AsyncTask<String, String, Integer> {


    LoginCallback listener;

    @Override
    protected Integer doInBackground(String... params) {


        URL url;

        postDataParams.put("name", params[0]);
        HashMap<String, String> postDataParams = new HashMap<String, String>();
        postDataParams.put("password", params[1]);

        try {

            url = new URL("http://mashiron.xyz/_03z/gmpia/proc.php");

            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setReadTimeout(15000);
            conn.setConnectTimeout(15000);
            conn.setRequestMethod("POST");
            conn.setDoInput(true);
            conn.setDoOutput(true);


            OutputStream os = conn.getOutputStream();
            BufferedWriter writer = new BufferedWriter(
                    new OutputStreamWriter(os, "UTF-8"));

            writer.write(HttpHandler.getPostDataString(postDataParams));
            writer.flush();
            writer.close();
            os.close();
            System.out.println("Respuesta: "+conn.getResponseCode());
            return conn.getResponseCode();

        } catch (Exception e) {
            e.printStackTrace();

            return 404;
        }
    }

    protected void onPostExecute(int result){
        System.out.println("Respuesta 2: "+result);

        listener.onResultReceived(result);
    }

}



public interface LoginCallback {

    void onResultReceived(int result);
}

}

编辑:为 NoChinDeluxe 添加了例外:

03-24 17:38:09.072 13312-13312/com.pitazzo.geomoments E/AndroidRuntime: 致命异常: main 进程:com.pitazzo.geomoments,PID:13312 java.lang.NullPointerException:尝试在空对象引用上调用接口方法“void com.pitazzo.geomoments.Handlers.LoginHandler$LoginCallback.onResultReceived(int)” 在 com.pitazzo.geomoments.Handlers.LoginHandler$Login.onPostExecute(LoginHandler.java:65) 在 com.pitazzo.geomoments.Handlers.LoginHandler$Login.onPostExecute(LoginHandler.java:17) 在 android.os.AsyncTask.finish(AsyncTask.java:636) 在 android.os.AsyncTask.access$500(AsyncTask.java:177) 在 android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:653) 在 android.os.Handler.dispatchMessage(Handler.java:102) 在 android.os.Looper.loop(Looper.java:135) 在 android.app.ActivityThread.main(ActivityThread.java:5300) 在 java.lang.reflect.Method.invoke(本机方法) 在 java.lang.reflect.Method.invoke(Method.java:372) 在 com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:904) 在 com.android.internal.os.ZygoteInit.main(ZygoteInit.java:699)

编辑:NoChainDeluxe 的更多代码

public class LoginActivity extends AppCompatActivity implements LoginHandler.LoginCallback{

EditText name;
EditText password;
Button login;
int code;


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.login_activity);

    /*
    if(logueado){

    }


     */


    name = (EditText) findViewById(R.id.loginuser);
    password = (EditText) findViewById(R.id.loginpassword);
    login = (Button) findViewById(R.id.loginlogin);

    login.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {

            String params[] = {name.getText().toString(), password.getText().toString()};
            System.out.println("Params: "+params.toString());

            new LoginHandler.Login().execute(params);
            System.out.println("Respuesta 4: "+code);

            if(code == 200){
                Toast toast1 =
                        Toast.makeText(getApplicationContext(),
                                "Iniciado sesión", Toast.LENGTH_SHORT);

                toast1.show();
            }else{

                Toast toast1 =
                        Toast.makeText(getApplicationContext(),
                                "Nombre de usuario y/o contraseña incorrectos: "+code, Toast.LENGTH_SHORT);

                toast1.show();

            }

        }
    });

}

public void onResultReceived(int resultado) {
    code = resultado;
    System.out.println("Respuesta 3: "+code);

}

}

【问题讨论】:

  • 根据您的需要使用 AysncTask。如果您使用它,则不需要任何 setter。在 AsyncTask 的 onPostExecute() 方法中,你可以更新你的 UI。

标签: android multithreading post server


【解决方案1】:

最好使用 AsyncTask 将数据传播到您的 UI 线程,只需使用 onPostExecute() 将结果设置在您的 Activity 的类上。

【讨论】:

    【解决方案2】:

    实现这一点的最佳方法是使用HttpURLConnectionAsyncTask 内进行网络调用,然后通过回调将结果传递回调用活动。这里有一些代码可以帮助您入门:

    您应该了解的第一件事是如何正确使用带有AsyncTask 的回调。这是一个定义回调接口的示例AsyncTask

    import android.os.AsyncTask;
    
    public class TestTask extends AsyncTask<String, Void, String> {
    
        TestTaskCallback listener;
    
        public TestTask(TestTaskCallback listener) {
            this.listener = listener;
        }
    
        protected String doInBackground(String... args) {
    
            String input = args[0];
            String output = "simulated return value";
    
            return output;
        }
    
        protected void onPostExecute(String result) {
            listener.onResultReceived(result);
        }
    
        public interface TestTaskCallback {
            void onResultReceived(String result);
        }
    }
    

    它的工作方式是,您定义一个公共接口,然后在您的 Activity 中实现该接口。它充当“侦听器”,等待发送给它的任何数据。我们定义了接口TestTaskCallback,因为我们将把我们的数据从AsyncTask发送到我们的调用Activity。

    那么在Activity中,我们需要实现这个接口,并在创建任务的时候传入一个我们实现的引用给任务。这样,当任务触发时,它就知道将结果发送到哪里,返回给我们的 Activity。示例实现可能如下所示:

    public class TestActivity extends AppCompatActivity implements TestTask.TestTaskCallback {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.your_layout);
    
            new TestTask(this).execute("Some input");
    
        }
    
        public void onResultReceived(String result) {
            Log.d("TEST TASK RESULT", result);
        }
    }
    

    所以我们的 Activity 实现了我们在 AsyncTask 中定义的接口,并注意到我们的 AsyncTask 引用了这个实现(通过构造函数传入)并在 onPostExecute() 方法中向它发送数据。这将允许您将结果发送到主 UI 线程,以便您可以适当地更新您的活动。

    剩下的唯一一件事就是实际拨打网络电话。我建议为此使用HttpURLConnection。您可以将此代码放在AsyncTaskdoInBackground() 方法中。

    我将向您展示我设置的示例 Web 服务调用。这显示了如何进行 Web 服务调用以检索 JSON 响应。它看起来像这样:

    //The JSON we will get back as a response from the server
    JSONObject jsonResponse = null;
    
    //Http connections and data streams
    URL url;
    HttpURLConnection httpURLConnection = null;
    OutputStreamWriter outputStreamWriter = null;
    
    try {
    
        //open connection to the server
            url = new URL("your_url_to_web_service");
            httpURLConnection = (HttpURLConnection) url.openConnection();
    
            //set request properties
            httpURLConnection.setDoOutput(true); //defaults request method to POST
            httpURLConnection.setDoInput(true);  //allow input to this HttpURLConnection
            httpURLConnection.setRequestProperty("Content-Type", "application/json"); //header params
            httpURLConnection.setRequestProperty("Accept", "application/json"); //header params
            httpURLConnection.setFixedLengthStreamingMode(jsonToSend.toString().getBytes().length); //header param "content-length"
    
            //open output stream and POST our JSON data to server
            outputStreamWriter = new OutputStreamWriter(httpURLConnection.getOutputStream());
            outputStreamWriter.write(jsonToSend.toString());
            outputStreamWriter.flush(); //flush the stream when we're finished writing to make sure all bytes get to their destination
    
            //prepare input buffer and get the http response from server
            StringBuilder stringBuilder = new StringBuilder();
            int responseCode = httpURLConnection.getResponseCode();
    
            //Check to make sure we got a valid status response from the server,
            //then get the server JSON response if we did.
            if(responseCode == HttpURLConnection.HTTP_OK) {
    
                //read in each line of the response to the input buffer
                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(httpURLConnection.getInputStream(),"utf-8"));
                String line;
                while ((line = bufferedReader.readLine()) != null) {
                    stringBuilder.append(line).append("\n");
                }
    
                bufferedReader.close(); //close out the input stream
    
                try {
                    //Copy the JSON response to a local JSONObject
                    jsonResponse = new JSONObject(stringBuilder.toString());
                } catch (JSONException je) {
                    je.printStackTrace();
                }
            }
    
    } catch (IOException ioe) {
        ioe.printStackTrace();
    } finally {
        if(httpURLConnection != null) {
            httpURLConnection.disconnect(); //close out our http connection
        }
    
        if(outputStreamWriter != null) {
            try {
                outputStreamWriter.close(); //close our output stream
            } catch (IOException ioe) {
                ioe.printStackTrace();
            }
        }
    }
    
    //Return the JSON response from the server.
    return jsonResponse;
    

    这几乎就是您想要做的事情所需要知道的全部内容。我意识到这是一次性扔给你的大量信息,但如果你花时间一点一点地完成它,你会发现它毕竟不是太难,实际上是一个非常强大的工具,你将一直使用编程 Android 应用程序!

    希望这会有所帮助。如有任何不完全理解的部分,请随时提出问题!

    【讨论】:

    • 太棒了!非常感谢,我会尝试并让您知道任何问题再次感谢!
    • 嘿NoChinDeluxe!我正在试验一个问题。正如你所说,我已经定义了我的异步任务,以及回调接口。但我注意到听众没有工作。在一些调试消息的帮助下,我发现问题是我的 onPostExecute() 方法从未被调用过。由于他的篇幅,我将在我的问题的新编辑中发布我的代码,希望你能找到我的错误。
    • try 块内的打印行怎么样:System.out.println("Respuesta: "+conn.getResponseCode()); execute()(我总是忘记这件事)。
    • 是的,它被调用了,我没有忘记调用 execute() 方法。我将其他调试消息放在 doInBackground() 正文中,并且始终调用该消息。我注意到在 AsynTask 主体内,我的 onPostExecute() 方法以灰色突出显示。但是一旦我将方法的参数从 int 更改为 Integer,它看起来就像 doInBackground() 方法。但是,随着这一更改,我的应用程序因 NullPointerException 而崩溃(在使用“结果”的代码的所有其他部分中,我从 int 更改为 Integer,但应用程序不断崩溃)。谢谢:)
    • 这里是对突出显示的东西的一点捕获:link
    【解决方案3】:

    您遇到的错误是因为从后台线程访问 UI 元素。

    AsyncTask 是一个基于线程池的 API,它在单独的线程中运行您的任务 ,但是您的 UI 部分在通常称为 UI thread 的线程中运行, 要更新对 ui 的任何更改,请将逻辑 onPostExecute()

    注意:使用okhttp 获得一致的http api,它也支持http2。他们的github wiki 非常有帮助,查看here 示例。

    【讨论】:

      猜你喜欢
      • 2022-12-05
      • 1970-01-01
      • 1970-01-01
      • 2018-10-03
      • 2018-01-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多