티스토리 뷰

반응형

Stack Overflow에 자주 검색, 등록되는 문제들과 제가 개발 중 찾아 본 문제들 중에서 나중에도 찾아 볼 것 같은 문제들을 정리하고 있습니다.

Stack Overflow에서 가장 먼저 확인하게 되는 가장 높은 점수를 받은 Solution과 현 시점에 도움이 될 수 있는 가장 최근에 업데이트(최소 점수 확보)된 Solution을 각각 정리하였습니다.

 

아래 word cloud를 통해 이번 포스팅의 주요 키워드를 미리 확인하세요.

How to get the result of OnPostExecute() to main activity because AsyncTask is a separate class?

AsyncTask는 별도의 클래스인데, OnPostExecute()의 결과를 어떻게 메인 액티비티로 가져오나요?

 문제 내용 

I have this two classes. My main Activity and the one that extends the AsyncTask, Now in my main Activity I need to get the result from the OnPostExecute() in the AsyncTask. How can I pass or get the result to my main Activity?

제가 가지고 있는 두 개의 클래스는 메인 액티비티와 AsyncTask를 확장한 클래스입니다. 이제 메인 액티비티에서 AsyncTask의 OnPostExecute() 결과를 가져와야 합니다. 어떻게 하면 메인 액티비티에서 결과를 전달하거나 가져올 수 있을까요?

 

Here is the sample codes.

다음은 샘플 코드입니다.

 

My main Activity.

제 메인 액티비티 코드입니다.
public class MainActivity extends Activity{

    AasyncTask asyncTask = new AasyncTask();

    @Override
    public void onCreate(Bundle aBundle) {
        super.onCreate(aBundle);            

        //Calling the AsyncTask class to start to execute.  
        asyncTask.execute(a.targetServer); 

        //Creating a TextView.
        TextView displayUI = asyncTask.dataDisplay;
        displayUI = new TextView(this);
        this.setContentView(tTextView); 
    }

}

 

This is the AsyncTask class

이것은 AsyncTask 클래스입니다.
public class AasyncTask extends AsyncTask<String, Void, String> {

TextView dataDisplay; //store the data  
String soapAction = "http://sample.com"; //SOAPAction header line. 
String targetServer = "https://sampletargeturl.com"; //Target Server.

//SOAP Request.
String soapRequest = "<sample XML request>";    



@Override
protected String doInBackground(String... string) {

String responseStorage = null; //storage of the response

try {


    //Uses URL and HttpURLConnection for server connection. 
    URL targetURL = new URL(targetServer);
    HttpURLConnection httpCon = (HttpURLConnection) targetURL.openConnection();
    httpCon.setDoOutput(true);
    httpCon.setDoInput(true);
    httpCon.setUseCaches(false); 
    httpCon.setChunkedStreamingMode(0);

    //properties of SOAPAction header
    httpCon.addRequestProperty("SOAPAction", soapAction);
    httpCon.addRequestProperty("Content-Type", "text/xml; charset=utf-8"); 
    httpCon.addRequestProperty("Content-Length", "" + soapRequest.length());
    httpCon.setRequestMethod(HttpPost.METHOD_NAME);


    //sending request to the server.
    OutputStream outputStream = httpCon.getOutputStream(); 
    Writer writer = new OutputStreamWriter(outputStream);
    writer.write(soapRequest);
    writer.flush();
    writer.close();


    //getting the response from the server
    InputStream inputStream = httpCon.getInputStream(); 
    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
    ByteArrayBuffer byteArrayBuffer = new ByteArrayBuffer(50);

    int intResponse = httpCon.getResponseCode();

    while ((intResponse = bufferedReader.read()) != -1) {
        byteArrayBuffer.append(intResponse);
    }

    responseStorage = new String(byteArrayBuffer.toByteArray()); 

    } catch (Exception aException) {
    responseStorage = aException.getMessage(); 
    }
    return responseStorage;
}

protected void onPostExecute(String result) {

    aTextView.setText(result);

}       

}   

 

 

 높은 점수를 받은 Solution 

Easy:

이건 쉬워요.

 

  1. Create interface class, where String output is optional, or can be whatever variables you want to return.
  2. public interface AsyncResponse { void processFinish(String output); }
  3. Go to your AsyncTask class, and declare interface AsyncResponse as a field :
  4. public class MyAsyncTask extends AsyncTask<Void, Void, String> { public AsyncResponse delegate = null; @Override protected void onPostExecute(String result) { delegate.processFinish(result); } }
  5. In your main Activity you need to implements interface AsyncResponse.
  6. public class MainActivity implements AsyncResponse{ MyAsyncTask asyncTask =new MyAsyncTask(); @Override public void onCreate(Bundle savedInstanceState) { //this to set delegate/listener back to this class asyncTask.delegate = this; //execute the async task asyncTask.execute(); } //this override the implemented method from asyncTask @Override void processFinish(String output){ //Here you will receive the result fired from async class //of onPostExecute(result) method. } }
1. 인터페이스 클래스를 만들어, 반환할 변수를 String으로 지정하거나, 원하는 변수로 지정합니다.
2. public interface AsyncResponse { void processFinish(String output); }
3. AsyncTask 클래스에서 인터페이스 AsyncResponse를 필드로 선언합니다.
4. public class MyAsyncTask extends AsyncTask<void, string="" void,=""> { public AsyncResponse delegate = null; ``` typescriptCopy code `@Override protected void onPostExecute(String result) { delegate.processFinish(result); }` ``` }
5. 메인 액티비티에서 AsyncResponse 인터페이스를 구현해야 합니다.
6. public class MainActivity implements AsyncResponse{ MyAsyncTask asyncTask =new MyAsyncTask(); @Override public void onCreate(Bundle savedInstanceState) { ``` vbnetCopy code `//this to set delegate/listener back to this class asyncTask.delegate = this; //execute the async task asyncTask.execute();` ``` } //this override the implemented method from asyncTask @Override void processFinish(String output){ //Here you will receive the result fired from async class //of onPostExecute(result) method. } } </void,>

 


UPDATE

업데이트

 

I didn't know this is such a favourite to many of you. So here's the simple and convenience way to use interface.

이게 많은 분들에게 즐겨찾기에 해당되는 줄 몰랐네요. 그래서 인터페이스를 사용하는 간단하고 편리한 방법을 소개합니다.

 

still using same interface. FYI, you may combine this into AsyncTask class.

여전히 같은 인터페이스를 사용합니다. 참고로 이를 AsyncTask 클래스로 결합할 수 있습니다.

 

in AsyncTask class :

AsyncTask 클래스에서는 다음과 같이 작성할 수 있습니다.
public class MyAsyncTask extends AsyncTask<Void, Void, String> {

  // you may separate this or combined to caller class.
  public interface AsyncResponse {
        void processFinish(String output);
  }

  public AsyncResponse delegate = null;

    public MyAsyncTask(AsyncResponse delegate){
        this.delegate = delegate;
    }

    @Override
    protected void onPostExecute(String result) {
      delegate.processFinish(result);
    }
}

 

do this in your Activity class

다음과 같이 Activity 클래스에서 수행하세요.
public class MainActivity extends Activity {
  
   MyAsyncTask asyncTask = new MyAsyncTask(new AsyncResponse(){
    
     @Override
     void processFinish(String output){
     //Here you will receive the result fired from async class 
     //of onPostExecute(result) method.
     }
  }).execute();

 }

 

Or, implementing the interface on the Activity again

또는, 인터페이스를 다시 액티비티에 구현하는 방법도 있습니다.
public class MainActivity extends Activity 
    implements AsyncResponse{
      
    @Override
    public void onCreate(Bundle savedInstanceState) {

        //execute the async task 
        new MyAsyncTask(this).execute();
    }
      
    //this override the implemented method from AsyncResponse
    @Override
    void processFinish(String output){
        //Here you will receive the result fired from async class 
        //of onPostExecute(result) method.
    }
}

 

As you can see 2 solutions above, the first and third one, it needs to create method processFinish, the other one, the method is inside the caller parameter. The third is more neat because there is no nested anonymous class.

위에서 볼 수 있는 두 가지 해결책 중, 첫 번째와 세 번째 방법은 processFinish 메소드를 생성해야 합니다. 그리고 두 번째 방법은 caller 매개변수 안에 메소드가 있습니다. 세 번째 방법이 좀 더 깔끔합니다. 왜냐하면 중첩된 익명 클래스가 없기 때문입니다.

 

Tip: Change String output, String response, and String result to different matching types in order to get different objects.

팁: "String output", "String response", "String result"를 서로 다른 맞춤형 유형으로 변경하여 다른 객체를 가져올 수 있습니다.

 

 

 

 가장 최근 달린 Solution 

This answer might be late but I would like to mention few things when your Activity dependent on AsyncTask. That would help you in prevent crashes and memory management. As already mentioned in above answers go with interface, we also say them callbacks. They will work as an informer, but never ever send strong reference of Activity or interface always use weak reference in those cases.

이 답변은 늦을 수 있지만, AsyncTask에 종속된 Activity에서 몇 가지 사항을 언급하고 싶습니다. 이는 충돌과 메모리 관리를 방지하는 데 도움이 됩니다. 이미 위의 답변에서 언급한 대로 인터페이스, 콜백으로 진행하면 되고, 이들은 알리미 역할을 하지만, 절대로 Activity나 인터페이스의 강력한 참조를 보내지 마시고, 이러한 경우에는 언제나 약한 참조를 사용하십시오.

 

Please refer to below screenshot to findout how that can cause issues.

아래 스크린샷을 참조하여 어떻게 문제를 일으킬 수 있는지 확인해주세요.

 

enter image description here

As you can see if we started AsyncTask with a strong reference then there is no guarantee that our Activity/Fragment will be alive till we get data, so it would be better to use WeakReference in those cases and that will also help in memory management as we will never hold the strong reference of our Activity then it will be eligible for garbage collection after its distortion.

보시다시피, 우리가 강한 참조로 AsyncTask를 시작하면, 데이터를 받을 때까지 Activity/Fragment가 살아있을 것이 보장되지 않으므로, 그 경우에는 WeakReference를 사용하는 것이 좋습니다. 이렇게 하면 우리는 Activity의 강한 참조를 유지하지 않으므로, 파괴 후 가비지 수집 가능성이 있습니다. 이는 메모리 관리에도 도움이 됩니다.

 

Check below code snippet to find out how to use awesome WeakReference -

아래 코드 스니펫을 살펴보세요. 어떻게 멋진 WeakReference를 사용하는지 알 수 있습니다.

 

MyTaskInformer.java Interface which will work as an informer.

MyTaskInformer.java는 인포머 역할을 하는 인터페이스입니다.
public interface MyTaskInformer {

    void onTaskDone(String output);

}

 

MySmallAsyncTask.java AsyncTask to do long running task, which will use WeakReference.

MySmallAsyncTask.java: Activity에 종속된 장기 실행 작업을 수행하는 AsyncTask로, WeakReference를 사용합니다.
public class MySmallAsyncTask extends AsyncTask<String, Void, String> {

    // ***** Hold weak reference *****
    private WeakReference<MyTaskInformer> mCallBack;

    public MySmallAsyncTask(MyTaskInformer callback) {
        this.mCallBack = new WeakReference<>(callback);
    }

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

        // Here do whatever your task is like reading/writing file
        // or read data from your server or any other heavy task

        // Let us suppose here you get response, just return it
        final String output = "Any out, mine is just demo output";

        // Return it from here to post execute
        return output;
    }

    @Override
    protected void onPostExecute(String s) {
        super.onPostExecute(s);

        // Here you can't guarantee that Activity/Fragment is alive who started this AsyncTask

        // Make sure your caller is active

        final MyTaskInformer callBack = mCallBack.get();

        if(callBack != null) {
            callBack.onTaskDone(s);
        }
    }
}

 

MainActivity.java This class is used to start my AsyncTask implement interface on this class and override this mandatory method.

MainActivity.java는 AsyncTask를 시작하기 위해 사용되며, 이 클래스에서 인터페이스를 구현하고 이 필수 메서드를 재정의합니다.
public class MainActivity extends Activity implements MyTaskInformer {

    private TextView mMyTextView;

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

        mMyTextView = (TextView) findViewById(R.id.tv_text_view);

        // Start your AsyncTask and pass reference of MyTaskInformer in constructor
        new MySmallAsyncTask(this).execute();
    }

    @Override
    public void onTaskDone(String output) {

        // Here you will receive output only if your Activity is alive.
        // no need to add checks like if(!isFinishing())

        mMyTextView.setText(output);
    }
}

 

 

출처 : https://stackoverflow.com/questions/12575068/how-to-get-the-result-of-onpostexecute-to-main-activity-because-asynctask-is-a

반응형
댓글
공지사항
최근에 올라온 글