android:如果一个 AsyncTask 已经在运行,则阻止另一个 AsyncTask 执行

Posted

技术标签:

【中文标题】android:如果一个 AsyncTask 已经在运行,则阻止另一个 AsyncTask 执行【英文标题】:android: prevent another AsyncTask from executing if one is already running 【发布时间】:2014-10-06 16:39:48 【问题描述】:

所以,我在活动开始时启动 AsyncTask。我正在尝试实现无限列表视图。当用户滚动到列表视图的底部时,AsyncTask 应该开始带来新数据。同时,如果用户向上滚动并再次向下滚动到底部,这就是我得到错误的地方。

以下是我试过的代码:

import java.util.ArrayList;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.app.ProgressDialog;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.widget.SearchView;
import android.support.v7.widget.SearchView.OnQueryTextListener;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
import android.widget.Toast;

public class CaseListingActivity extends ActionBarActivity 

    ListView mListViewCase;
    ArrayList<CaseDetails> mArrayList;
    CaseListAdapter mCaseListAdapter;
    View mView;

    CaseListingTask mCaseListingTask;

    int preLast;
    int currentPage = 1;
    public static final String URL = "http://xxxxxx.xxxxxxx/DC.svc/rest/GetSubject?PageNo=";
    public static final String GET_SUBJECT_RESULT = "GetSubjectResult";
    public static final String ID = "Id";
    public static final String SUB = "Sub";

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

        mArrayList = new ArrayList<CaseDetails>();
        mCaseListAdapter = new CaseListAdapter(this, mArrayList);

        mCaseListingTask = new CaseListingTask();

        mView = ((LayoutInflater) this
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(
                R.layout.listview_footer, null, false);

        mListViewCase = (ListView) findViewById(R.id.listViewCaseListing);
        mListViewCase.addFooterView(mView);
        mListViewCase.setAdapter(mCaseListAdapter);
        mListViewCase.setOnScrollListener(new OnScrollListener() 

            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) 
                // TODO Auto-generated method stub
                // if (scrollState == SCROLL_STATE_IDLE) 
                // if (mListViewCase.getLastVisiblePosition() >= mListViewCase
                // .getCount() - 1 - 1) 
                //
                // // if (mCaseListingTask.getStatus() !=
                // AsyncTask.Status.RUNNING) 
                // //
                // // 
                // if (mCaseListingTask.getStatus() == AsyncTask.Status.RUNNING)
                // 
                // // Do Something?
                //  else 
                // currentPage++;
                // // load more list items:
                // mCaseListingTask.execute(URL + currentPage);
                // 
                // 
                // 
            

            @Override
            public void onScroll(AbsListView view, int firstVisibleItem,
                    int visibleItemCount, int totalItemCount) 
                // TODO Auto-generated method stub
                switch (view.getId()) 
                case R.id.listViewCaseListing:

                    // Make your calculation stuff here. You have all your
                    // needed info from the parameters of this function.

                    // Sample calculation to determine if the last
                    // item is fully visible.
                    final int lastItem = firstVisibleItem + visibleItemCount;
                    if (lastItem == totalItemCount) 
//                      if (preLast != lastItem)  // to avoid multiple calls
//                          // for last item
//                          Log.d("Last", "Last");
//                          preLast = lastItem;

                            if (mCaseListingTask.getStatus() == AsyncTask.Status.FINISHED) 
                                currentPage++;
                                // load more list items:
                                mCaseListingTask.execute(URL + currentPage);
                            
//                      
                    
                
            
        );
        mListViewCase.setOnItemClickListener(new OnItemClickListener() 

            @Override
            public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
                    long arg3) 
                // TODO Auto-generated method stub
                Toast.makeText(getBaseContext(),
                        arg0.getItemAtPosition(arg2).toString(),
                        Toast.LENGTH_SHORT).show();
            
        );

        new CaseListingTask().execute(URL + currentPage);
    

    @Override
    public boolean onCreateOptionsMenu(Menu menu) 
        // TODO Auto-generated method stub
        getMenuInflater().inflate(R.menu.menu, menu);

        MenuItem searchItem = menu.findItem(R.id.action_search);
        SearchView searchView = (SearchView) MenuItemCompat
                .getActionView(searchItem);
        searchView.setOnQueryTextListener(new OnQueryTextListener() 

            @Override
            public boolean onQueryTextSubmit(String arg0) 
                // TODO Auto-generated method stub
                return false;
            

            @Override
            public boolean onQueryTextChange(String arg0) 
                // TODO Auto-generated method stub
                return false;
            
        );

        return super.onCreateOptionsMenu(menu);
    

    public class CaseListingTask extends AsyncTask<String, Void, String> 

        ProgressDialog mProgressDialog;

        @Override
        protected String doInBackground(String... params) 
            // TODO Auto-generated method stub

            String result = "";
            for (String string : params) 
                result = new JSONParser().getJSONFromUrl(string);
            
            return result;
        

        /*
         * (non-Javadoc)
         * 
         * @see android.os.AsyncTask#onPostExecute(java.lang.Object)
         */
        @Override
        protected void onPostExecute(String result) 
            // TODO Auto-generated method stub
            super.onPostExecute(result);

            try 
                JSONObject mJsonObject = new JSONObject(result);
                JSONArray mJsonArray = mJsonObject
                        .getJSONArray(GET_SUBJECT_RESULT);

                for (int i = 0; i < mJsonArray.length(); i++) 
                    JSONObject c = mJsonArray.getJSONObject(i);
                    mArrayList.add(new CaseDetails(c.getInt(ID), c
                            .getString(SUB)));
                
                mCaseListAdapter.notifyDataSetChanged();

                mListViewCase.removeFooterView(mView);
             catch (JSONException e) 
                // TODO Auto-generated catch block
                e.printStackTrace();
            
        

        /*
         * (non-Javadoc)
         * 
         * @see android.os.AsyncTask#onPreExecute()
         */
        @Override
        protected void onPreExecute() 
            // TODO Auto-generated method stub
            super.onPreExecute();
            // mProgressDialog = ProgressDialog.show(CaseListingActivity.this,
            // "Please Wait...", "Loading...", true, false);

            mListViewCase.addFooterView(mView, null, false);
        
    

如果您需要更多详细信息,请询问。谢谢。

【问题讨论】:

您遇到什么错误?如果遇到异常,始终在问题中包含 logcat。 @XaverKapeller 当然先生。等一下。 @XaverKapeller 抱歉,我无法重现它。因为我正在尝试使用不同的代码。但现在的情况是另一项任务还没有开始。 我已经从问题本身回滚了您的解决方案。你可以找到它in the revision history。请直接将其发布为答案。 【参考方案1】:

问题很简单:每个AsyncTask 只能执行一次。来自the documentation of AsyncTask

这个类必须遵循一些线程规则 正常工作:

必须在 UI 线程上加载 AsyncTask 类。这从 JELLY_BEAN 开始自动完成。 必须在 UI 线程上创建任务实例。 execute(Params...) 必须在 UI 线程上调用。 不要手动调用 onPreExecute()、onPostExecute(Result)、doInBackground(Params...)、onProgressUpdate(Progress...)。 任务只能执行一次(如果尝试第二次执行会抛出异常。)

您在onCreate() 中创建了一个CaseListingTask 的新实例,并尝试在您的OnScrollListener 中重复使用这个相同的实例。你需要做的是每次你想运行它时创建一个CaseListingTask 的新实例。

所以总结一下替换这个:

if (mCaseListingTask.getStatus() == AsyncTask.Status.FINISHED) 
    currentPage++;
    // load more list items:
    mCaseListingTask.execute(URL + currentPage);

有了这个:

if (mCaseListingTask.getStatus() == AsyncTask.Status.FINISHED) 
    currentPage++;
    mCaseListingTask = new CaseListingTask();
    mCaseListingTask.execute(URL + currentPage);

【讨论】:

谢谢先生。我正在尝试......我会通知你它是否有效。 我发布了工作解决方案。但我仍然不明白为什么需要mCaseListingTask = new CaseListingTask();。请介意解释一下。 谢谢先生。为您的解决方案。 好的。我明白你的意思了。你提到的Threading Rules的最后一点。再次感谢。

以上是关于android:如果一个 AsyncTask 已经在运行,则阻止另一个 AsyncTask 执行的主要内容,如果未能解决你的问题,请参考以下文章

android中 如果要你来设计AsyncTask,你怎么设计

Android Socket,AsyncTask,Handler内存泄漏

AsyncTask - 基本原理 图文剖析

Android AsyncTask工作原理

如何从android中的自定义适配器调用Activity asynctask

Android AsyncTask 和登录