从 AsyncTask Android 返回数据

Posted

技术标签:

【中文标题】从 AsyncTask Android 返回数据【英文标题】:Return data from AsyncTask Android 【发布时间】:2012-02-13 19:52:32 【问题描述】:

我试图在 SO 上引用类似的问题,但没有得到任何帮助。

在我的 android 应用程序中,我计划实现用户访问过的最近引用,即类似于最近访问的网络页面。

以下是我正在遵循的步骤:

1.) 每当用户打开任何公司视图时,从数据库中获取公司符号

2.) 然后将当前符号与 dateTime 一起存储在数据库中。

3.) 对于从数据库中获取的所有符号,获取它们的 current value%Change 并在列表中显示 公司名称、当前值和 %Change

问题出现在ASyncTask 类中,因为postExecute 方法不允许它的返回类型为void 以外的任何类型。

我做错了吗?

任何帮助都是救命稻草!!!

String[] rsym,rcmp,rchg;
rdbM = new RecentDBManager(CompanyView.this);
try 
Calendar date1 = Calendar.getInstance();
SimpleDateFormat dateformatter = new SimpleDateFormat(
                            "dd/MM/yyyy HH:mm:ss");

String currentdate = dateformatter.format(date1.getTime());

rdbM.openDB();

//STEP 1
rsym = rdbM.getRecent_sym();

//STEP 2                
rdbM.setData(currentsymbol, currentdate);

rdbM.closeDB();

 catch (Exception e) 
throw new Error(" *** ERROR in DB Access *** "+ e.toString());


 //STEP 3
        for(int i=0;i<rsym.length;i++)
        
            DownloadRecentQuote quotetask = new DownloadRecentQuote();
            recentquotetask
            .execute(new String[]  "http://abc.com/stockquote.aspx?id="
                        + rsym[i] );

 //CURRENT VALUE and %CHANGE which should be returned from ASyncTask class

            rcmp[i]=valuearr[0];
            rchg[i]=valuearr[1];
            

            list1 = new ArrayList<HashMap<String, String>>();
            HashMap<String, String> addList1;

            for (int i = 0; i < limit; i++) 
            
                addList1 = new HashMap<String, String>();
                addList1.put(RecentSym_COLUMN, rsym[i]);
                addList1.put(RecentCMP_COLUMN, rcmp[i]);
                addList1.put(RecentChg_COLUMN, rchg[i]);

                list1.add(addList1);

                RecentAdapter adapter1 = new RecentAdapter(
                    CompanyView.this, CompanyView.this, list1);
                listrecent.setAdapter(adapter1);
            


private class DownloadRecentQuote extends AsyncTask<String, Void, String> 
    /* Fetching data for RecentQuote information */
    @Override
    protected String doInBackground(String... urls) 
        String response = "";
        for (String url : urls) 
            DefaultHttpClient client = new DefaultHttpClient();
            HttpGet httpGet = new HttpGet(url);
            try 
                HttpResponse execute = client.execute(httpGet);
                InputStream content = execute.getEntity().getContent();

                BufferedReader buffer = new BufferedReader(
                        new InputStreamReader(content));
                String s = "";
                while ((s = buffer.readLine()) != null) 
                    response += s;
                

             catch (Exception e) 
                e.printStackTrace();
            
        
        return response;
    

    @Override
    protected void onPostExecute(String result) 

        arr1 = result.split("@");

        if (arr1[0].length() != 0) 

            if (arr1[0].equals("1")) 

                arr = arr1[1].split(";");

            //RETURN 2 STRINGS
                            String valuearr[];
                valuearr[0] = arr[3];
                valuearr[1] = arr[6].concat("%");
                //return valuearr;
            
        
    

【问题讨论】:

如果你使用 BroadCasteReceiver developer.android.com/reference/android/content/… 会更好。它可以让你通过意图传递数据。 【参考方案1】:

postExecute() 无法返回值,因为它会返回给谁或什么?您调用 AsyncTask 的原始方法已消失,因为您的 AsyncTask 正在后台运行。这是异步的意思,当 AsyncTask.execute() 返回时它仍在后台运行,因此 postExecute() 无法返回值,因为没有什么可以返回。

相反,您的 AsyncTask 需要对您的 Activity 或其他对象的引用,以便它可以将您的值发回给它。在您的代码中,您调用 execute() 后的行不存在,因为您的任务尚未完成。相反,您应该创建一个名为 updateSymbol(currentPrice, percentChange) 的方法,将所有代码移到 execute() 之下,然后在 AsyncTask 中传递对 Activity 的引用。然后从 onPostExecute() 方法调用 updateSymbol(currentPrice, percentChange)。

但是,如果您有一个对 Activity 的引用,请注意它可能会在您的 doInBackground() 运行时被销毁,并且当 postExecute() 运行时,它应该只删除结果或不尝试更新 UI。例如,用户旋转手机导致 Activity 被销毁。我发现最好在 Activity 中保存对 AsyncTask 的引用,以便在 Activity 被销毁时它可以取消()它。您可以调用 AsyncTask.cancel() 然后检查您的任务是否被取消:

public void postExecute( String result ) 
    if( !isCanceled() ) 
       // do your updating here
       activity.setSymbol( result );
    

为所有活动创建一个基类非常容易,因此您可以轻松跟踪 AsyncTask 的运行:

public class BaseActivity extends Activity 

   List<AsyncTask> runningTasks;

   public void onStop() 
       for( AsyncTask task : runningTasks ) 
          task.cancel(true);
       
   

   public AsyncTask start( AsyncTask task ) 
      runningTasks.add( task );
      return task;
   

   public void done( AsyncTask task ) 
      runningTasks.remove( task );
   

一些快速指示。您不需要执行(new String[] "blah" + blah )。 Java 中的 Varargs 允许您执行此操作。执行(“废话”+废话)。您也正在捕获异常并继续而不真正处理它们。当某些事情真的发生时会很困难,因为您的应用程序会捕捉到它们,并且就像什么都没发生一样继续。如果您遇到错误,您可能希望向用户提供一些反馈并停止尝试执行该过程。停止,向用户显示一个错误,然后让他们做接下来的事情。将 catch 块移动到方法的底部。

【讨论】:

非常感谢。我是 android 开发的新手,并试图理解这一点。 在您的 AsyncTask 中您应该传递对 Activity 的引用???? 是什么意思 DownloadRecentQuote task = new DownloadRecentQuote( this ) 假设您在活动中执行此操作。如果您没有在活动中执行 DownloadRecentQuote,那么您可以将引用传递给正在创建 DownloadRecentQuote 实例的实例,然后找出某种方法来从那里更新 UI。看起来您正在活动中创建 DownloadRecentQuote,因为我看到一个带有适配器的列表变量。【参考方案2】:

本质上,AsyncTask.onPostExecute() 是在执行 AsyncTask 的 doInBackground() 并返回执行结果之后,您可以在其中做任何您想做的事情。这应该被认为是最佳实践。

当从 UI 线程调用 AsyncTask().execute() 时(注意该方法必须从 UI 线程调用),Android 框架会创建一个工作线程并开始运行您在 AsyncTask.doInBackground() 中编写的任何内容在这个工作线程上。此时(调用 new AsyncTask().execute() 之后),UI 线程继续执行 new AsyncTask().execute() 之后的代码。所以现在在运行时,您有两个线程(UI 和工作线程)同时运行。

但是 AsyncTask 的执行结果是在何时何地从工作线程返回给 UI 线程的呢?

工作线程 (doInBackground()) 完成并返回 UI 线程的点是 AysncTask.onPostExecute()。只要 AsyncTask 完成,框架就会保证在 UI 线程上调用此方法。换句话说,我们不在乎 AsyncTask.onPostExecute() 在运行时何时何地被调用,我们只需要保证它最终会在未来的某个阶段被调用。这就是该方法不返回执行结果的原因——相反,它要求执行结果作为唯一的方法参数从 doInBackground() 中传入。

另外,Android API 提供了在编码时返回 AsyncTask 执行结果的方法,AsyncTask.get():

MyAsyncTask myAsyncTask = new MyAsyncTask();

// This must be called from the UI thread:
myAsyncTask.execute();

// Calling this will block UI thread execution:
ExecutionResult result = myAsyncTask.get();

请记住,AsyncTask.get() 会阻塞调用线程的执行,如果在 UI 线程上调用它,您可能会收到 ANR 异常。 这是使用 AsyncTask 的有效负载.get(),通过在 UI 线程上调用它,您实际上是在使您的 AsyncTask(工作线程)与 UI 线程同步运行(通过使 UI 线程等待)。总而言之,这是可行的,但不推荐。

【讨论】:

我不认为myAsyncTask.get() 会长时间阻塞UI,因为它只是从onPostExecute() 获取计算数据。 除了第14行“未来”部分,你的答案是完美的。【参考方案3】:

仅供参考,因为这篇文章有点旧:

我创建了一个 Activity 类,它有一个 onStart() 方法和一个用于 AsyncTask 的单独类。根据我的测试,在 doInbackground() 方法之后,结果将首先发送到活动,然后 onPostExecute() 将运行。这是因为基于 logcat,我首先有我的第一个响应数据(由服务器发送),然后这个响应将再次从活动中显示,最后一个消息在 onPostExecute() 将显示。

活动代码:

@Override
    protected void onStart() 
        super.onStart();
        String str = "***";
        if(isConnectedToInternet())
            myAsyncTask.execute();

            try 
                if(myAsyncTask.get())
                    str = myAsyncTask.getResponseMsg();
             catch (InterruptedException e) 
                e.printStackTrace();
             catch (ExecutionException e) 
                e.printStackTrace();
             catch (CancellationException e) 
                e.printStackTrace();
            
        
        Log.i("Data returned by server2:", str);
    

AsyncTask 代码:

public class MyAsyncTask extends AsyncTask<Void, Void, Boolean> 

    private URL url;
    private HttpURLConnection conn;
    private String strResponseMsg;

    public MyAsyncTask(String url) throws MalformedURLException
        this.url = new URL(url);
    


    @Override
    protected void onPreExecute() 
        Log.i("Inside AsyncTask", "myAsyncTask is abut to start...");
    

    @Override
    protected Boolean doInBackground(Void... params) 
        boolean status = false;

        try 
            conn = (HttpURLConnection) url.openConnection();
            conn.setConnectTimeout(Manager.ConnTimeout);
            conn.setReadTimeout(Manager.ReadTimeout);

            int responseCode = conn.getResponseCode();
            Log.i("Connection oppened", "Response code is:" + responseCode);
            if (responseCode == HttpURLConnection.HTTP_OK) 
                BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
                if (in != null) 
                    StringBuilder strBuilder = new StringBuilder();
                    // Read character by character              
                    int ch = 0;
                    while ((ch = in.read()) != -1)
                        strBuilder.append((char) ch);

                    // Showing returned message
                    strResponseMsg = strBuilder.toString(); 
                    Log.i("Data returned by server:", strResponseMsg);

                    status = true;
                
                in.close();
            

         catch (IOException e) 
            e.printStackTrace();
               

        return status;
    

    @Override
    protected void onPostExecute(Boolean result) 
        Log.i("Inside AsyncTask", "myAsyncTask finished its task. Returning data to caller...");
    

    public String getResponseMsg()
        return strResponseMsg;
    

【讨论】:

以上是关于从 AsyncTask Android 返回数据的主要内容,如果未能解决你的问题,请参考以下文章

从Android中的AsyncTask返回一个值[重复]

从Android中的AsyncTask返回一个值[重复]

Android AsyncTask在完成工作后返回值

如何在android的Asynctask中显示json响应[重复]

Android 使用 AsyncTask 连接 MySql 并以 JSON 或 String 的形式返回数据

通过AsyncTask访问后台后得到的返回数据在Android端显示