为啥我的 AsyncTask 在主线程上运行?

Posted

技术标签:

【中文标题】为啥我的 AsyncTask 在主线程上运行?【英文标题】:Why is my AsyncTask running on the main thread?为什么我的 AsyncTask 在主线程上运行? 【发布时间】:2015-08-16 11:53:34 【问题描述】:

我在容器 Activity 中有一个选项卡片段。

我想下载一些数据以显示在选项卡中。在选项卡中,我创建了一个异步任务,我在 onCreateView 中填充片段布局后执行该任务。当我这样做时,AsyncTask 的 doInBackground... 工作发生在主线程上,并且视图在完成之前不会加载。我的所有进度...任务完成后,日志会同时显示。

但是,如果我在片段布局中放置一个按钮并启动 asynctask 作为对按钮单击的响应,它会按预期工作,我会得到正常间隔的进度更新。

那么,如果 AsyncTask 是我的 Fragment 启动时发生的第一件事,为什么还要在主线程上运行呢?而且,我怎样才能防止这种情况发生?我的最终目标是在数据下载时显示进度轮。

编辑。这是我调用 AsyncTask 的地方。

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) 

    final View rootView = inflater.inflate(R.layout.temp_my_moments, container, false);

    getMyClips(getActivity(), rootView, p_session, p_person,progressBar);

//If I use this button, it works fine:
//    test = (Button) rootView.findViewById(R.id.testbutton);
//    test.setOnClickListener(new View.OnClickListener() 
//        @Override
//        public void onClick(View v) 
 //           progressBar =(ProgressBar) rootView.findViewById(R.id.myMomentsProgress);
  //          getMyClips(getActivity(), rootView, p_session, p_person, progressBar);
  //      
 //   );
    return rootView;

这里是 getMyClips():

public void getMyClips(Context context,View view,String thisSession,String thisPerson,ProgressBar progressBar) 
    Log.d("Currently running", "getMyClips");
        JSONObject params = new JSONObject();
        try 
            params.put("Function", clipsMine_apiCall);
            params.put("p_session", thisSession);
            params.put("p_person", thisPerson);
         catch (JSONException e1) 
            e1.printStackTrace();
        

        ApiClipCaller webServiceTask = new ApiClipCaller(context,view,progressBar);
        if (webServiceTask.hasConnection()) 
            webServiceTask.execute(params);
         else 
                //TODO no internet connection
        

编辑这是我的 AsyncTask。这是在 Fragment 内部:

public class ApiClipCaller extends AsyncTask<JSONObject, Integer, ApiResponse> 
public Context context;
public String clipID;
public String sessionID;
public String personID;
public String functionName;
public View view;
public ProgressBar progressBar;
public final String apiURL = "....";  //need to keep private
LinearLayout loading;

public ApiClipCaller(Context c, View v,ProgressBar progressBar) 
this.context = c;
this.view = v;
this.progressBar = progressBar;
loading = (LinearLayout) view.findViewById(R.id.loadingMyMoments);


    @Override 
    protected ApiResponse doInBackground(JSONObject... params) 
        JSONObject realParams = params[0];
        try 
            functionName = realParams.getString("Function");
         catch (JSONException e) 
            e.printStackTrace();
        
        ApiResponse responseObject = masterFunction(realParams);

        //making some work to see if it is running correctly
        for (int i=10;i<=100;i += 10)
        
            try 
                Thread.sleep(1000);
                publishProgress(i);

             catch (InterruptedException e) 
                e.printStackTrace();
            
        

        return responseObject;
    

    @Override
    protected void onProgressUpdate(Integer... progress) 
        loading.setVisibility(View.VISIBLE);
        progressBar.setProgress(progress[0]);
        Log.d("progress",Integer.toString(progress[0]));
    

    @Override
    protected void onPostExecute(ApiResponse responseObject) 
        loading.setVisibility(View.GONE);
        if (functionName.equals(clipsMine_apiCall)) 
            JSONObject clipObject = responseObject.getResponseJSONObject();

           //just testing here to see if I can get data back in the fragment view when it is done. 
///This works as expected.
  String responseString = responseObject.getResponseString();
             TextView showAsyncResults = (TextView) view.findViewById(R.id.testAsyncTask);
             showAsyncResults.setText(responseString);
        
        super.onPostExecute(responseObject);
    

【问题讨论】:

向我们展示您的 AsyncTask 代码。 我不知道为什么这个问题值得这么多反对? 向我们展示您如何在片段中调用 asyncTask。你是直接调用 doInBackground 吗? 那么您到底对什么感兴趣?对这种现象的解释(这样它就不会再次发生)?或者深入回答为什么AsyncTask 是这样设计的? @fullmeriffic execute(...) 方法上的 javadoc 明确指出:This method must be invoked on the UI thread。您通过在错误的线程上调用它来破坏该方法的合同,因此您会得到不正确的行为。如果你想从主线程开始你的AsyncTask - 将它作为一个事件发布在 UI 线程上(老实说我不知道​​是什么方法,但我对 Swing 的经验告诉我这应该很容易)跨度> 【参考方案1】:

所以,答案似乎是我做错了。当第一个活动或片段加载时,不可能启动 asynctask,因为 onCreate 等方法实际上不在 UI 线程上。因此,正如 Ordous 所指出的,AsyncTask 不能直接从那里执行。

相反,解决方案似乎是在应用程序首次启动时加载信息,方法是创建一个扩展应用程序的类并从那里进行加载工作,因为这必然在主线程上。可以从活动中调用其中的方法来访问布局并制作进度条或类似内容。

参考:http://developer.android.com/reference/android/app/Application.html 一个很好的例子:http://www.intertech.com/Blog/androids-application-class/

【讨论】:

这不能作为新线程启动new myAsync(this);

以上是关于为啥我的 AsyncTask 在主线程上运行?的主要内容,如果未能解决你的问题,请参考以下文章

从 url 下载 pdf 文件在主线程上工作,但是当我使用 asynctask 时出错

android AsyncTask 怎么返回值给UI线程

为啥我的 AsyncTask 会冻结我的 UI 线程?

AsyncTask 和 Thread 区别

为啥 NSManagedObjectContext 队列在主线程上执行?

别再傻傻得认为AsyncTask只可以在主线程中创建实例和调用execute方法