Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
446 views
in Technique[技术] by (71.8m points)

android - Best way to request data from server

I need to fetch some data from my server in order to make my app work. In order to do that, I will be using POST. As far as I know, I have to request that data in a thread which can not be the main thread. I am finding a little bit diffcult to put the data I am reciving in a variable defined in the UI thread. So, my question is, which is the best way to do it? Is it correct to set the value of a variable defined, for example, in my main activity, with a setter called inside an AsyncTask? Or might be better this option?

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

I have read that I may use Interfaces in order to archive that, but it is a concept that I do not understand very well yet. I would like to use directly a method which returns the data, but as far as I know, it is not possible. Thabks for your time!

EDIT: new code according to NoChinDeluxe answer:

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

}

EDIT: added exception for NoChinDeluxe:

03-24 17:38:09.072 13312-13312/com.pitazzo.geomoments E/AndroidRuntime: FATAL EXCEPTION: main Process: com.pitazzo.geomoments, PID: 13312 java.lang.NullPointerException: Attempt to invoke interface method 'void com.pitazzo.geomoments.Handlers.LoginHandler$LoginCallback.onResultReceived(int)' on a null object reference at com.pitazzo.geomoments.Handlers.LoginHandler$Login.onPostExecute(LoginHandler.java:65) at com.pitazzo.geomoments.Handlers.LoginHandler$Login.onPostExecute(LoginHandler.java:17) at android.os.AsyncTask.finish(AsyncTask.java:636) at android.os.AsyncTask.access$500(AsyncTask.java:177) at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:653) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:135) at android.app.ActivityThread.main(ActivityThread.java:5300) at java.lang.reflect.Method.invoke(Native Method) at java.lang.reflect.Method.invoke(Method.java:372) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:904) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:699)

EDIT: more code for 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);

}

}

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The best way to achieve this is to use an HttpURLConnection to make your web calls inside an AsyncTask and then pass the result back to your calling Activity through a callback. Here's some code to help you get started:

The first thing you should understand is how to properly use a callback with an AsyncTask. Here is an example AsyncTask that defines a callback interface:

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

The way this works is, you define a public interface that you then implement in your Activity. This acts as a "listener" that is waiting for any data that is sent through to it. We define the interface TestTaskCallback because we are going to be sending our data from our AsyncTask to our calling Activity.

Then in the Activity, we need to implement this interface, and pass in a reference to our implementation to the task when we create it. That way, when the task fires, it knows where to send the result, which is back to our Activity. An example implementation might look like this:

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

So our Activity implements the interface that we defined inside our AsyncTask, and notice that our AsyncTask takes the reference to this implementation (passed in through the constructor) and sends data to it in the onPostExecute() method. This will allow your result to be sent to the main UI thread so that you can update your Activity appropriately.

The only thing left is to actually make the web calls. I would recommend using an HttpURLConnection for this. You would put this code inside the doInBackground() method of your AsyncTask.

I'll show you an example web service call I have set up. This shows how to make a web service call to retrieve a JSON response. It looks something like this:

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

            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;

This is pretty much all you need to know to do exactly what it is you are trying to do. I realize this is a ton of info to throw at you all at once, but if you take your time and work through it piece by piece, you'll find it's not too difficult after all and is actually a VERY powerful tool that you'll use all the time programming Android apps!

Hope this helps. Feel free to ask questions for any parts you don't fully understand yet!


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...