【问题标题】:Calling an AsyncTask from another asynctask in doInBackground method?在 doInBackground 方法中从另一个异步任务调用 AsyncTask?
【发布时间】:2014-06-01 12:15:32
【问题描述】:

我从 AsyncTask1 调用了 AsyncTask2...这是我的“场景”:

  • AsyncTask1 下载一个 rss 提要,解析 xml,然后在 doInBackground 方法中为每个识别的项目创建并执行 AsyncTask2。
  • doInBackground 方法中的 AsyncTask2 下载从 AsyncTask1 传递的项目的附件 url 属性,并在 onPostExecute 方法中将该项目添加到全局项目数组中,并将项目的更改通知给关联的适配器。

它运行良好并且没有崩溃,为什么? AsyncTasks 应该从 UI 线程 (threading rules) 运行,现在我对这个假设有点困惑。

抱歉英语不好,我希望问题足够清楚。

编辑 这里有一些代码... DownloadRssAsyncTask = AsyncTask2, RssAsyncTask = AsyncTask1

public class ParseActivity extends Activity {

public class FeedItemAdapter extends ArrayAdapter<FeedItem> {

    int resource;

    public FeedItemAdapter(Context context, int resource, List<FeedItem> items) {
        super(context, resource, items);

        this.resource = resource;
    }

    public View getView(int position, View convertView, ViewGroup parent) {

        LinearLayout myView;
        FeedItem item = getItem(position);

        if (convertView == null) {
            myView = new LinearLayout(getContext());
            String inflaterService = Context.LAYOUT_INFLATER_SERVICE;
            LayoutInflater li = (LayoutInflater) getContext().getSystemService(inflaterService);
            li.inflate(resource, myView, true);
        } else {
            myView = (LinearLayout) convertView;
        }

        TextView titleFeedItem = (TextView) myView.findViewById(R.id.itemTitle);
        TextView dateFeedItem = (TextView) myView.findViewById(R.id.itemDate);
        ImageView imageFeedItem = (ImageView) myView.findViewById(R.id.imageThumb);

        titleFeedItem.setText(item.mTitle);
        dateFeedItem.setText(item.mPubDate);
        imageFeedItem.setImageBitmap(item.bitmapEnclosure);

        return myView;
    }
}


private class DownloadRssAsyncTask extends AsyncTask<FeedItem, Void, FeedItem> {

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

        FeedItem item = params[0];
        if (item.mEnclosure == null) {
            Log.i("info: ", "no enclosure tag");
            item.bitmapEnclosure = null;
            return item;
        }

        try {
            URL imageUrl = new URL(item.mEnclosure);
            item.bitmapEnclosure = BitmapFactory.decodeStream(imageUrl.openStream());
        } catch (IOException e) {
            Log.e("error", "download image resource error: "+item.mEnclosure);
            item.bitmapEnclosure = null;
        }

        return item;
    }

    @Override
    protected void onPostExecute(FeedItem result) {
        items.add(result);
        arrayAdapter.notifyDataSetChanged();

        dbHelper.putItem(result.mGuid, result.mTitle, result.mDescription, result.mEnclosure, result.mPubDate);
    }
}


private class RssAsyncTask extends AsyncTask<String, Integer, Void> {

    @Override
    protected Void doInBackground(String... params) {
        int dimParams = params.length;
        for (int i=0; i<dimParams; i++) {
            Log.i("doInBackground", "rss feed num "+ (i+1) + " of "+ dimParams+ ": " + params[i]);
            refreshFeed(params[i]);
        }

        return null;
    }

    @Override
    protected void onPostExecute(Void result) {
        Log.i("onPostExecute in RssAsyncTask", "notifyDataSetChanged");
    }

}

public static class FeedItem {


    public String mAuthor;
    public String mCategory;
    public String mComments;
    public String mDescription; //r
    public String mEnclosure;
    public Bitmap bitmapEnclosure;
    public String mGuid;
    public String mLink;    //r
    public String mPubDate;
    public String mSource;
    public String mTitle;   //r

    public FeedItem() {
        // TODO Auto-generated constructor stub
    }

    @Override
    public String toString() {
        return
            "Data: "+mPubDate+
            "\nLink:\n"+mLink+
            "\nAutore:\n"+mAuthor+
            "\nTitolo:\n"+mTitle+
            "\nEnclosure:\n"+mEnclosure;
    }
}

private FeedReaderDbHelper dbHelper;
private FeedItemAdapter arrayAdapter;
private ArrayList<FeedItem> items;
private ListView myListView;

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

    items = new ArrayList<FeedItem>();
    new ArrayList<FeedItem>();

    myListView = (ListView) findViewById(R.id.myListView);
    arrayAdapter = new FeedItemAdapter(this, R.layout.feed_item, items);
    myListView.setAdapter(arrayAdapter);


    dbHelper = new FeedReaderDbHelper(this);

    //RssAsyncTask: download and parsing rss feed
    new RssAsyncTask().execute(getString(R.string.my_feed));

}

public void refreshFeed(String feed) {

    final String TAG = "refreshFeed";

    Log.i(TAG, feed);
    URL url = null;
    try {
        url = new URL(feed);

        HttpURLConnection httpConnection =  (HttpURLConnection) url.openConnection();
        int httpCode = httpConnection.getResponseCode();

        if (httpCode == HttpURLConnection.HTTP_OK) {
            processFeed(httpConnection.getInputStream());
        } else {
            Log.i(TAG, httpCode + httpConnection.getResponseMessage());
        }

    } catch (MalformedURLException e1) {
        Log.i(TAG, "MalformedUrlException in " + feed);
    } catch (IOException e) {
        Log.i(TAG, "IOException in " + url.toString());
    }
}

private void processFeed(InputStream inputStream ) {

    final String TAG = "processFeed";
    final String ITEM = "item";
    final String AUTHOR ="author";
    final String TITLE ="title";
    final String CATEGORY ="category";
    final String COMMENTS ="comments";
    final String DESCRIPTION ="description";
    final String GUID ="guid";
    final String LINK ="link";
    final String PUBDATE="pubDate";
    final String SOURCE ="source";
    final String ENCLOSURE = "enclosure";


    Log.i(TAG, inputStream.toString());
    XmlPullParserFactory pullParserFact;
    try {
        pullParserFact = XmlPullParserFactory.newInstance();
        pullParserFact.setNamespaceAware(true);

        XmlPullParser pullParser = pullParserFact.newPullParser();
        pullParser.setInput(inputStream, null);
        int eventType = pullParser.getEventType();

        while (eventType != XmlPullParser.END_DOCUMENT) {

            if (eventType == XmlPullParser.START_TAG && pullParser.getName().equals(ITEM)){
                final FeedItem item = new FeedItem();
                eventType = pullParser.next();

                while ( !(eventType == XmlPullParser.END_TAG && pullParser.getName().equals(ITEM)) ) {

                    if ( eventType == XmlPullParser.START_TAG ) {
                        String name = pullParser.getName();

                        switch (name) {
                        case AUTHOR:
                            item.mAuthor = pullParser.nextText();
                            break;
                        case TITLE:
                            item.mTitle = pullParser.nextText();
                            break;
                        case CATEGORY:
                            item.mCategory = pullParser.nextText();
                            break;
                        case COMMENTS:
                            item.mComments = pullParser.nextText();
                            break;
                        case DESCRIPTION:
                            item.mDescription = pullParser.nextText();
                            break;
                        case GUID:
                            item.mGuid = pullParser.nextText();
                            break;
                        case LINK:
                            item.mLink = pullParser.nextText();
                            break;
                        case PUBDATE:
                            item.mPubDate = pullParser.nextText();
                            break;
                        case SOURCE:
                            item.mSource = pullParser.nextText();
                            break;
                        case ENCLOSURE:
                            item.mEnclosure = pullParser.getAttributeValue(null, "url");
                        default:
                            break;
                        }

                    }

                    eventType = pullParser.next();
                }

                //download the optional enclosure resource and update UI
                new DownloadRssAsyncTask().execute(item);

            }

            eventType = pullParser.next();
        }


    } catch (XmlPullParserException e) {
        Log.i(TAG, "XmlPullparserException");
    } catch (IOException e) {
        Log.i(TAG, "IOException");
    }
}

}

【问题讨论】:

  • “线程规则”提到,从 UI 线程运行是从 Jelly Bean 自动完成的。
  • @pingw33n:在线程规则中...
  • 如果您包含您的代码会有所帮助。您是否专门在AsyncTask1doInBackground() 方法中创建并执行了这个AsyncTask2?抽象 AsyncTask 中不是 doInBackground 的每个方法都在 UI 线程上运行。
  • 另外,这些只是线程规则。如果您想要定义的行为,这是您必须遵循的。如果您在这些规则之外使用它,它不会必然因异常而失败,但您可能会得到一些未定义的行为。

标签: java android multithreading android-asynctask adapter


【解决方案1】:

因为AsyncTask的内部运作。

AsyncTask 内部使用静态Handler 实例,基本上是Android 线程通信的方式。使用Handler,您可以发送消息并在线程上运行代码;特别是,AsyncTask 使用它来运行其回调,例如 onPostExecute()

现在,当Handler 被初始化时,它绑定在初始化它的线程上。在AsyncTask 中,这是在类初始化/加载期间完成的:

private static final InternalHandler sHandler = new InternalHandler();

由于sHandler也是final,之后就不能修改了,回调会一直在那个线程上触发。

在您的情况下,您在onCreate() 中创建RssAsyncTask 的实例,该实例在UI 线程上运行。这会触发AsyncTask 类的加载并将AsyncTaskHandler 绑定到UI 线程。因此,从那时起,您的onPostExecute()s 将始终在 UI 线程上运行。尽管您在另一个后台线程中创建了一些 AsyncTasks。

线程规则希望确保类在 UI 线程 (see this) 上加载/初始化,并希望强制执行良好的线程实践。

另外,我推荐IntentService 用于简单的网络操作,而不是AsyncTask

【讨论】:

  • 很好的答案,谢谢!在哪里可以找到有关 AsyncTask 及其实现的更多信息?在开发人员 android 参考中,我看不到 AsyncTask 的静态处理程序。
  • 我编辑了一点我的答案以使其更易于访问。最好的来源是直接的代码,例如你可以在这里阅读 AsyncTask 代码:grepcode.com/file/repository.grepcode.com/java/ext/…
  • 还有其他两件事。首先,AsyncTask 的子类不是静态的,因此您隐藏了对 Activity 的引用。如果您旋转设备,Activity 实例将被销毁,但在任务自己销毁之前不会被垃圾收集。另外,任务会通知死掉的Activity,你不会看到结果。解决方案:IntentService。第二:接受答案,如果有帮助,那就帮助其他读者
猜你喜欢
  • 2015-01-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多