GVKun编程网logo

android – 尝试在后台解析带有AsyncTask的JSON时强制关闭

22

本篇文章给大家谈谈android–尝试在后台解析带有AsyncTask的JSON时强制关闭,同时本文还将给你拓展androidasynctask、AndroidAsyncTaskxml解析、Andro

本篇文章给大家谈谈android – 尝试在后台解析带有AsyncTask的JSON时强制关闭,同时本文还将给你拓展android asynctask、Android AsyncTask xml解析、Android AsyncTask 完全解析、Android AsyncTask 源码解析等相关知识,希望对各位有所帮助,不要忘了收藏本站喔。

本文目录一览:

android – 尝试在后台解析带有AsyncTask的JSON时强制关闭

android – 尝试在后台解析带有AsyncTask的JSON时强制关闭

我是Android开发的新手,我正在玩json数据.我设法让解析工作.我想显示一个ProgressDialog,我读到我需要使用AsyncTask.但是出于某种原因,我在doInBackground()中放入相同的工作代码时会立即关闭一个力量,尽管eclipse说一切都很好.

这是源代码:

public class HomeActivity extends Activity {


  public class BackgroundAsyncTask extends AsyncTask<Void, Integer, Void> {

      ProgressDialog dialog = new ProgressDialog (HomeActivity.this);


      @Override
      protected void onPreExecute() {
        dialog.setMessage("Loading...please wait");
        dialog.setIndeterminate(true);
        dialog.setCancelable(false);
        dialog.show();
      }

      protected void onPostExecute() {
             dialog.dismiss();
          }

      @Override
      protected Void doInBackground(Void... params) {         
            try {
                URL json = new URL("http://www.corps-marchia.de/jsontest.PHP");
                URLConnection tc = json.openConnection();
                BufferedReader in = new BufferedReader(new InputStreamReader(tc.getInputStream()));

                String line;
                while ((line = in.readLine()) != null) {
                    JSONArray ja = new JSONArray(line);
                    JSONObject jo = (JSONObject) ja.get(0);
                    TextView txtView = (TextView)findViewById(R.id.TextView01);
                    txtView.setText(jo.getString("text"));
                }
            } catch (MalformedURLException e) {
                e.printstacktrace();
            } catch (IOException e) {
                e.printstacktrace();
            } catch (JSONException e) {
                e.printstacktrace();
            }

       return null;
       }
     }


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        new BackgroundAsyncTask().execute();
    }

}

这是错误日志:

01-08 12:33:48.225: ERROR/AndroidRuntime(815): FATAL EXCEPTION: AsyncTask #1
01-08 12:33:48.225: ERROR/AndroidRuntime(815): java.lang.RuntimeException: An error occured while executing doInBackground()
01-08 12:33:48.225: ERROR/AndroidRuntime(815):     at android.os.AsyncTask$3.done(AsyncTask.java:200)
01-08 12:33:48.225: ERROR/AndroidRuntime(815):     at java.util.concurrent.FutureTask$Sync.innerSetException(FutureTask.java:274)
01-08 12:33:48.225: ERROR/AndroidRuntime(815):     at java.util.concurrent.FutureTask.setException(FutureTask.java:125)
01-08 12:33:48.225: ERROR/AndroidRuntime(815):     at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:308)
01-08 12:33:48.225: ERROR/AndroidRuntime(815):     at java.util.concurrent.FutureTask.run(FutureTask.java:138)
01-08 12:33:48.225: ERROR/AndroidRuntime(815):     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1088)
01-08 12:33:48.225: ERROR/AndroidRuntime(815):     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:581)
01-08 12:33:48.225: ERROR/AndroidRuntime(815):     at java.lang.Thread.run(Thread.java:1019)
01-08 12:33:48.225: ERROR/AndroidRuntime(815): Caused by: android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
01-08 12:33:48.225: ERROR/AndroidRuntime(815):     at android.view.ViewRoot.checkThread(ViewRoot.java:2932)
01-08 12:33:48.225: ERROR/AndroidRuntime(815):     at android.view.ViewRoot.requestLayout(ViewRoot.java:629)
01-08 12:33:48.225: ERROR/AndroidRuntime(815):     at android.view.View.requestLayout(View.java:8267)
01-08 12:33:48.225: ERROR/AndroidRuntime(815):     at android.view.View.requestLayout(View.java:8267)
01-08 12:33:48.225: ERROR/AndroidRuntime(815):     at android.view.View.requestLayout(View.java:8267)
01-08 12:33:48.225: ERROR/AndroidRuntime(815):     at android.view.View.requestLayout(View.java:8267)
01-08 12:33:48.225: ERROR/AndroidRuntime(815):     at android.view.View.requestLayout(View.java:8267)
01-08 12:33:48.225: ERROR/AndroidRuntime(815):     at android.widget.TextView.checkForRelayout(TextView.java:5521)
01-08 12:33:48.225: ERROR/AndroidRuntime(815):     at android.widget.TextView.setText(TextView.java:2724)
01-08 12:33:48.225: ERROR/AndroidRuntime(815):     at android.widget.TextView.setText(TextView.java:2592)
01-08 12:33:48.225: ERROR/AndroidRuntime(815):     at android.widget.TextView.setText(TextView.java:2567)
01-08 12:33:48.225: ERROR/AndroidRuntime(815):     at net.ajzele.demo.andy1.HomeActivity$BackgroundAsyncTask.doInBackground(HomeActivity.java:52)
01-08 12:33:48.225: ERROR/AndroidRuntime(815):     at net.ajzele.demo.andy1.HomeActivity$BackgroundAsyncTask.doInBackground(HomeActivity.java:1)
01-08 12:33:48.225: ERROR/AndroidRuntime(815):     at android.os.AsyncTask$2.call(AsyncTask.java:185)
01-08 12:33:48.225: ERROR/AndroidRuntime(815):     at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:306)
01-08 12:33:48.225: ERROR/AndroidRuntime(815):     ... 4 more
01-08 12:33:51.605: ERROR/WindowManager(815): Activity net.ajzele.demo.andy1.HomeActivity has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView@4051d0c0 that was originally added here
01-08 12:33:51.605: ERROR/WindowManager(815): android.view.WindowLeaked: Activity net.ajzele.demo.andy1.HomeActivity has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView@4051d0c0 that was originally added here
01-08 12:33:51.605: ERROR/WindowManager(815):     at android.view.ViewRoot.<init>(ViewRoot.java:258)
01-08 12:33:51.605: ERROR/WindowManager(815):     at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:148)
01-08 12:33:51.605: ERROR/WindowManager(815):     at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:91)
01-08 12:33:51.605: ERROR/WindowManager(815):     at android.view.Window$LocalWindowManager.addView(Window.java:424)
01-08 12:33:51.605: ERROR/WindowManager(815):     at android.app.Dialog.show(Dialog.java:241)
01-08 12:33:51.605: ERROR/WindowManager(815):     at net.ajzele.demo.andy1.HomeActivity$BackgroundAsyncTask.onPreExecute(HomeActivity.java:33)
01-08 12:33:51.605: ERROR/WindowManager(815):     at android.os.AsyncTask.execute(AsyncTask.java:391)
01-08 12:33:51.605: ERROR/WindowManager(815):     at net.ajzele.demo.andy1.HomeActivity.onCreate(HomeActivity.java:72)
01-08 12:33:51.605: ERROR/WindowManager(815):     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
01-08 12:33:51.605: ERROR/WindowManager(815):     at android.app.ActivityThread.performlaunchActivity(ActivityThread.java:1586)
01-08 12:33:51.605: ERROR/WindowManager(815):     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1638)
01-08 12:33:51.605: ERROR/WindowManager(815):     at android.app.ActivityThread.access$1500(ActivityThread.java:117)
01-08 12:33:51.605: ERROR/WindowManager(815):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:928)
01-08 12:33:51.605: ERROR/WindowManager(815):     at android.os.Handler.dispatchMessage(Handler.java:99)
01-08 12:33:51.605: ERROR/WindowManager(815):     at android.os.Looper.loop(Looper.java:123)
01-08 12:33:51.605: ERROR/WindowManager(815):     at android.app.ActivityThread.main(ActivityThread.java:3647)
01-08 12:33:51.605: ERROR/WindowManager(815):     at java.lang.reflect.Method.invokeNative(Native Method)
01-08 12:33:51.605: ERROR/WindowManager(815):     at java.lang.reflect.Method.invoke(Method.java:507)
01-08 12:33:51.605: ERROR/WindowManager(815):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
01-08 12:33:51.605: ERROR/WindowManager(815):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
01-08 12:33:51.605: ERROR/WindowManager(815):     at dalvik.system.NativeStart.main(Native Method)

任何提示?我希望你能帮助我搜索网络并没有找到任何有效的解决方案……提前致谢

解决方法:

检查docs for AsyncTask.您无法直接在doInBackground中操作UI.要进行UI更新,您需要调用publishProgress.您还需要更改AsyncTask的签名以传递String.所以像这样:

public class BackgroundAsyncTask extends AsyncTask<Void, String, Void> {
    TextView txtView = (TextView)findViewById(R.id.TextView01);
    ...
    protected void onProgressUpdate(String... message) {
        txtView.setText(message[0]);
    }
    ...
    @Override
    protected Void doInBackground(Void... params) {
         try {
             URL json = new URL("http://www.corps-marchia.de/jsontest.PHP");
             URLConnection tc = json.openConnection();
             BufferedReader in = new BufferedReader(new InputStreamReader(tc.getInputStream()));

             String line;
             while ((line = in.readLine()) != null) {
                 JSONArray ja = new JSONArray(line);
                 JSONObject jo = (JSONObject) ja.get(0);
                 publishProgress(jo.getString("text"));
             }
         } catch (MalformedURLException e) {
             e.printstacktrace();
         } catch (IOException e) {
             e.printstacktrace();
         } catch (JSONException e) {
             e.printstacktrace();
         }

    return null;
    }

android asynctask

android asynctask

asynctask<1, 2, 3>

3 doinbackground(1... param)

onprogressbarupdate(2 param)

onpostexcute(3 param)

执行顺序

onpreexecute

doinbackground

onpostexecute

其中可以通过    publishprogress触发onprogressupdate

其中onpreexecute doinbackground运行在非UI主线线程 所以无法更改或者设置UI控件

但是onpostexecute 何onprogressupdate运行在UI主线程

其中onpostexecute接受的参数是doinbackground的返回值

相同的C#中又委托代理机制用于非UI主线程可以修改UI线程中的UI控件

Android AsyncTask xml解析

Android AsyncTask xml解析

我尝试在AsyncTask中解析大型xml文档. FeinstaubActivity启动,但是我只看到黑屏,然后从我启动另一个Activity的地方回到RSSReaderActivity.

日志:

DEBUG/SntpClient(58): request time Failed: java.net.socketException: Address family not supported by protocol
WARN/KeyCharacterMap(623): No keyboard for id 0
WARN/KeyCharacterMap(623): Using default keymap: /system/usr/keychars/qwerty.kcm.bin
INFO/ActivityManager(58): Starting activity: Intent { cmp=de.test.testapp/.FeinstaubActivity }
DEBUG/AndroidRuntime(623): Shutting down VM
WARN/dalvikvm(623): threadid=1: thread exiting with uncaught exception (group=0x4001d800)
WARN/dalvikvm(623): threadid=7: thread exiting with uncaught exception (group=0x4001d800)
INFO/Process(623): Sending signal. PID: 623 SIG: 9
INFO/ActivityManager(58): Process de.test.testapp (pid 623) has died.
INFO/WindowManager(58): WIN DEATH: Window{4402be18 AtchDlg:de.test.testapp/de.test.testapp.RSSReaderActivity paused=false}
INFO/WindowManager(58): WIN DEATH: Window{4400dd68 de.test.testapp/de.test.testapp.RSSReaderActivity paused=false}
INFO/ActivityManager(58): Start proc de.test.testapp for activity de.test.testapp/.RSSReaderActivity: pid=631 uid=10035 gids={3003}
DEBUG/dalvikvm(32): GC_EXPLICIT freed 299 objects / 11488 bytes in 162ms
INFO/UsageStats(58): Unexpected resume of de.test.testapp while already resumed in de.test.testapp
DEBUG/dalvikvm(32): GC_EXPLICIT freed 57 objects / 2440 bytes in 138ms
INFO/ActivityManager(58): displayed activity de.test.testapp/.RSSReaderActivity: 411 ms (total 532 ms)
DEBUG/dalvikvm(32): GC_EXPLICIT freed 2 objects / 48 bytes in 126ms
DEBUG/dalvikvm(631): GC_FOR_MALLOC freed 3294 objects / 461928 bytes in 72ms
DEBUG/dalvikvm(631): GC_FOR_MALLOC freed 2750 objects / 557024 bytes in 59ms
DEBUG/dalvikvm(631): GC_FOR_MALLOC freed 2786 objects / 506288 bytes in 60ms
WARN/InputManagerService(58): Got remoteexception sending setActive(false) notification to pid 623 uid 10035

    Document document = null;

    protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.feinstaub);
            document = (Doxument) new ParseXMLFile().execute();
    }

    private class ParseXMLFile extends AsyncTask<Integer, Integer, Document>{

    @Override
    protected Document doInBackground(Integer... params) {
        Document parsedXML = null;
        try {
            parsedXML = builder.parse(getApplicationContext().getResources().openRawResource(R.raw.finedust));
        } catch (NotFoundException e) {
            // Todo Auto-generated catch block
            e.printstacktrace();
        } catch (SAXException e) {
            // Todo Auto-generated catch block
            e.printstacktrace();
        } catch (IOException e) {
            // Todo Auto-generated catch block
            e.printstacktrace();
        }
        return parsedXML;
    }

}

问候
浮动

解决方法:

啊,不确定您能做到这一点:http://developer.android.com/reference/android/os/AsyncTask.html显示execute返回void.

你要这个:

Document document = null;

    protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.feinstaub);
       new ParseXMLFile().execute();
    }

    void setDocument(Document document){ this.document = document; }

    private class ParseXMLFile extends AsyncTask<Integer, Integer, Document>{
    @Override public void onPostExecute(Document d){ setDocument(d); }

    @Override
    protected Document doInBackground(Integer... params) {
        Document parsedXML = null;
        try {
            parsedXML = builder.parse(getApplicationContext().getResources().openRawResource(R.raw.finedust));
        } catch (NotFoundException e) {
            // Todo Auto-generated catch block
            e.printstacktrace();
        } catch (SAXException e) {
            // Todo Auto-generated catch block
            e.printstacktrace();
        } catch (IOException e) {
            // Todo Auto-generated catch block
            e.printstacktrace();
        }
        return parsedXML;
    }

}

您需要做一些范围界定:传递父活动或以某种方式适当地声明setDocument方法,以便您可以从AsyncTask内部对其进行回调,但是应该这样做.

Android AsyncTask 完全解析

Android AsyncTask 完全解析

我们都知道,Android UI 是线程不安全的,如果想要在子线程里进行 UI 操作,就需要借助 Android 的异步消息处理机制。之前我也写过了一篇文章从源码层面分析了 Android 的异步消息处理机制,感兴趣的朋友可以参考 Android Handler、Message 完全解析,带你从源码的角度彻底理解。不过为了更加方便我们在子线程中更新 UI 元素,Android 从 1.5 版本就引入了一个 AsyncTask 类,使用它就可以非常灵活方便地从子线程切换到 UI 线程,我们本篇文章的主角也就正是它了。AsyncTask 很早就出现在 Android 的 API 里了,所以我相信大多数朋友对它的用法都已经非常熟悉。不过今天我还是准备从 AsyncTask 的基本用法开始讲起,然后我们再来一起分析下 AsyncTask 源码,看看它是如何实现的,最后我会介绍一些关于 AsyncTask 你所不知道的秘密。
视频教程:http://www.jikexueyuan.com/study/5/lid/22.html?hmsr=wenku_asynctask
教程下载:http://pan.baidu.com/s/1EkWrk 

Android AsyncTask 源码解析

Android AsyncTask 源码解析

转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38614699,本文出自:【张鸿洋的博客】

1、概述

相信大家对AsyncTask都不陌生,对于执行耗时任务,然后更新UI是一把利器,当然也是替代Thread + Handler 的一种方式。如果你对Handler机制还不了解,请看:Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系。

2、简单的例子

相信大家都写过这样的代码:

package com.example.zhy_asynctask_demo01;

import android.app.Activity;
import android.app.ProgressDialog;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

public class MainActivity extends Activity
{

	private static final String TAG = "MainActivity";
	private ProgressDialog mDialog;
	private TextView mTextView;

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

		mTextView = (TextView) findViewById(R.id.id_tv);

		mDialog = new ProgressDialog(this);
		mDialog.setMax(100);
		mDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
		mDialog.setCancelable(false);

		new MyAsyncTask().execute();

	}

	private class MyAsyncTask extends AsyncTask<Void, Integer, Void>
	{

		@Override
		protected void onPreExecute()
		{
			mDialog.show();
			Log.e(TAG, Thread.currentThread().getName() + " onPreExecute ");
		}

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

			// 模拟数据的加载,耗时的任务
			for (int i = 0; i < 100; i++)
			{
				try
				{
					Thread.sleep(80);
				} catch (InterruptedException e)
				{
					e.printStackTrace();
				}
				publishProgress(i);
			}

			Log.e(TAG, Thread.currentThread().getName() + " doInBackground ");
			return null;
		}

		@Override
		protected void onProgressUpdate(Integer... values)
		{
			mDialog.setProgress(values[0]);
			Log.e(TAG, Thread.currentThread().getName() + " onProgressUpdate ");
		}

		@Override
		protected void onPostExecute(Void result)
		{
			// 进行数据加载完成后的UI操作
			mDialog.dismiss();
			mTextView.setText("LOAD DATA SUCCESS ");
			Log.e(TAG, Thread.currentThread().getName() + " onPostExecute ");
		}
	}
}

进入某个Activity,Activity中需要的数据来自于网络或者其它耗时操作,可以在AsyncTask中onPreExecute完成一些准备操作,比如上例中显示进度对话框;然后在doInBackground完成耗时操作,在进行耗时操作时还能不时的通过publishProgress给onProgressUpdate中传递参数,然后在onProgressUpdate中可以进行UI操作,比如上例更新进度条的进度;当耗时任务执行完成后,最后在onPostExecute进行设置控件数据更新UI等操作,例如隐藏进度对话框。

效果图:


3、源码解析

注:本篇源码分析基于Andorid-17,因为和3.0之前版本变动较大,有必要标出。

那么大家一定好奇,AsyncTask在Android中是如何实现的,下面进行源码分析:从我们的执行异步任务的起点开始,进入execute方法:

public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
}
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {
        if (mStatus != Status.PENDING) {
            switch (mStatus) {
                case RUNNING:
                    throw new IllegalStateException("Cannot execute task:"
                            + " the task is already running.");
                case FINISHED:
                    throw new IllegalStateException("Cannot execute task:"
                            + " the task has already been executed "
                            + "(a task can be executed only once)");
            }
        }

        mStatus = Status.RUNNING;

        onPreExecute();

        mWorker.mParams = params;
        exec.execute(mFuture);

        return this;
    }
18行:设置当前AsyncTask的状态为RUNNING,上面的switch也可以看出,每个异步任务在完成前只能执行一次。
20行:执行了onPreExecute(),当前依然在UI线程,所以我们可以在其中做一些准备工作。
22行:将我们传入的参数赋值给了mWorker.mParams
23行:exec.execute(mFuture)

相信大家对22行出现的mWorker,以及23行出现的mFuture都会有些困惑。
mWorker找到这个类:

private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
        Params[] mParams;
}

可以看到是Callable的子类,且包含一个mParams用于保存我们传入的参数,下面看初始化mWorker的代码:

public AsyncTask() {
        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);

                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //noinspection unchecked
                return postResult(doInBackground(mParams));
            }
        };
//….
        
}

可以看到mWorker在构造方法中完成了初始化,并且因为是一个抽象类,在这里new了一个实现类,实现了call方法,call方法中设置mTaskInvoked=true,且最终调用doInBackground(mParams)方法,并返回Result值作为参数给postResult方法.可以看到我们的doInBackground出现了,下面继续看:

private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();
        return result;
}

可以看到postResult中出现了我们熟悉的异步消息机制,传递了一个消息message, message.what为MESSAGE_POST_RESULT;message.object= new AsyncTaskResult(this,result);

private static class AsyncTaskResult<Data> {
        final AsyncTask mTask;
        final Data[] mData;

        AsyncTaskResult(AsyncTask task, Data... data) {
            mTask = task;
            mData = data;
        }
    }

AsyncTaskResult就是一个简单的携带参数的对象。

看到这,我相信大家肯定会想到,在某处肯定存在一个sHandler,且复写了其handleMessage方法等待消息的传入,以及消息的处理。

private static final InternalHandler sHandler = new InternalHandler();
    private static class InternalHandler extends Handler {
        @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
        @Override
        public void handleMessage(Message msg) {
            AsyncTaskResult result = (AsyncTaskResult) msg.obj;
            switch (msg.what) {
                case MESSAGE_POST_RESULT:
                    // There is only one result
                    result.mTask.finish(result.mData[0]);
                    break;
                case MESSAGE_POST_PROGRESS:
                    result.mTask.onProgressUpdate(result.mData);
                    break;
            }
        }
}

哈哈,出现了我们的handleMessage,可以看到,在接收到MESSAGE_POST_RESULT消息时,执行了result.mTask.finish(result.mData[0]);其实就是我们的AsyncTask.this.finish(result),于是看finish方法

private void finish(Result result) {
        if (isCancelled()) {
            onCancelled(result);
        } else {
            onPostExecute(result);
        }
        mStatus = Status.FINISHED;
    }

可以看到,如果我们调用了cancel()则执行onCancelled回调;正常执行的情况下调用我们的onPostExecute(result);主要这里的调用是在handler的handleMessage中,所以是在UI线程中。如果你对异步消息机制不理解请看: Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系
最后将状态置为FINISHED。

mWoker看完了,应该到我们的mFuture了,依然实在构造方法中完成mFuture的初始化,将mWorker作为参数,复写了其done方法。

public AsyncTask() {
	...
        mFuture = new FutureTask<Result>(mWorker) {
            @Override
            protected void done() {
                try {
                    postResultIfNotInvoked(get());
                } catch (InterruptedException e) {
                    android.util.Log.w(LOG_TAG, e);
                } catch (ExecutionException e) {
                    throw new RuntimeException("An error occured while executing doInBackground()",
                            e.getCause());
                } catch (CancellationException e) {
                    postResultIfNotInvoked(null);
                }
            }
        };
}
16行:任务执行结束会调用:postResultIfNotInvoked(get());get()表示获取mWorker的call的返回值,即Result.然后看postResultIfNotInvoked方法

private void postResultIfNotInvoked(Result result) {
        final boolean wasTaskInvoked = mTaskInvoked.get();
        if (!wasTaskInvoked) {
            postResult(result);
        }
}
如果mTaskInvoked不为true,则执行postResult;但是在mWorker初始化时就已经将mTaskInvoked为true,所以一般这个postResult执行不到。
好了,到了这里,已经介绍完了execute方法中出现了mWorker和mFurture,不过这里一直是初始化这两个对象的代码,并没有真正的执行。下面我们看真正调用执行的地方。
execute方法中的:
还记得上面的execute中的23行:exec.execute(mFuture)

exec为executeOnExecutor(sDefaultExecutor, params)中的sDefaultExecutor

下面看这个sDefaultExecutor
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static class SerialExecutor implements Executor {
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        Runnable mActive;
        public synchronized void execute(final Runnable r) {
            mTasks.offer(new Runnable() {
                public void run() {
                    try {
                        r.run();
                    } finally {
                        scheduleNext();
                    }
                }
            });
            if (mActive == null) {
                scheduleNext();
            }
        }
        protected synchronized void scheduleNext() {
            if ((mActive = mTasks.poll()) != null) {
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
}
可以看到sDefaultExecutor其实为SerialExecutor的一个实例,其内部维持一个任务队列;直接看其execute(Runnable runnable)方法,将runnable放入mTasks队尾;
16-17行:判断当前mActive是否为空,为空则调用scheduleNext方法
20行:scheduleNext,则直接取出任务队列中的队首任务,如果不为null则传入THREAD_POOL_EXECUTOR进行执行。
下面看THREAD_POOL_EXECUTOR为何方神圣:
public static final Executor THREAD_POOL_EXECUTOR
          =new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
可以看到就是一个自己设置参数的线程池,参数为:

private static final int CORE_POOL_SIZE = 5;
private static final int MAXIMUM_POOL_SIZE = 128;
private static final int KEEP_ALIVE = 1;
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread(Runnable r) {
     return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
    }
 };
private static final BlockingQueue<Runnable> sPoolWorkQueue =
            new LinkedBlockingQueue<Runnable>(10);

看到这里,大家可能会认为,背后原来有一个线程池,且最大支持128的线程并发,加上长度为10的阻塞队列,可能会觉得就是在快速调用138个以内的AsyncTask子类的execute方法不会出现问题,而大于138则会抛出异常。
其实不是这样的,我们再仔细看一下代码,回顾一下sDefaultExecutor,真正在execute()中调用的为sDefaultExecutor.execute:
private static class SerialExecutor implements Executor {
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        Runnable mActive;
        public synchronized void execute(final Runnable r) {
            mTasks.offer(new Runnable() {
                public void run() {
                    try {
                        r.run();
                    } finally {
                        scheduleNext();
                    }
                }
            });
            if (mActive == null) {
                scheduleNext();
            }
        }
        protected synchronized void scheduleNext() {
            if ((mActive = mTasks.poll()) != null) {
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
}

可以看到,如果此时有10个任务同时调用execute(s synchronized)方法,第一个任务入队,然后在mActive = mTasks.poll()) != null被取出,并且赋值给mActivte,然后交给线程池去执行。然后第二个任务入队,但是此时mActive并不为null,并不会执行scheduleNext();所以如果第一个任务比较慢,10个任务都会进入队列等待;真正执行下一个任务的时机是,线程池执行完成第一个任务以后,调用Runnable中的finally代码块中的scheduleNext,所以虽然内部有一个线程池,其实调用的过程还是线性的。一个接着一个的执行,相当于单线程。

4、总结

到此源码解释完毕,由于代码跨度比较大,我们再回顾一下:

public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
}
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {
        if (mStatus != Status.PENDING) {
            switch (mStatus) {
                case RUNNING:
                    throw new IllegalStateException("Cannot execute task:"
                            + " the task is already running.");
                case FINISHED:
                    throw new IllegalStateException("Cannot execute task:"
                            + " the task has already been executed "
                            + "(a task can be executed only once)");
            }
        }

        mStatus = Status.RUNNING;

        onPreExecute();

        mWorker.mParams = params;
        exec.execute(mFuture);

        return this;
    }

18行:设置当前AsyncTask的状态为RUNNING,上面的switch也可以看出,每个异步任务在完成前只能执行一次。
20行:执行了onPreExecute(),当前依然在UI线程,所以我们可以在其中做一些准备工作。
22行:将我们传入的参数赋值给了mWorker.mParams ,mWorker为一个Callable的子类,且在内部的call()方法中,调用了doInBackground(mParams),然后得到的返回值作为postResult的参数进行执行;postResult中通过sHandler发送消息,最终sHandler的handleMessage中完成onPostExecute的调用。
23行:exec.execute(mFuture),mFuture为真正的执行任务的单元,将mWorker进行封装,然后由sDefaultExecutor交给线程池进行执行。


5、publishProgress

说了这么多,我们好像忘了一个方法:publishProgress

protected final void publishProgress(Progress... values) {
        if (!isCancelled()) {
            sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
                    new AsyncTaskResult<Progress>(this, values)).sendToTarget();
        }
}
也很简单,直接使用sHandler发送一个消息,并且携带我们传入的值;

private static class InternalHandler extends Handler {
        @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
        @Override
        public void handleMessage(Message msg) {
            AsyncTaskResult result = (AsyncTaskResult) msg.obj;
            switch (msg.what) {
                case MESSAGE_POST_RESULT:
                    // There is only one result
                    result.mTask.finish(result.mData[0]);
                    break;
                case MESSAGE_POST_PROGRESS:
                    result.mTask.onProgressUpdate(result.mData);
                    break;
            }
        }
}

在handleMessage中进行了我们的onProgressUpdate(result.mData);的调用。

6、AsyncTask曾经缺陷

记得以前有个面试题经常会问道:AsyncTask运行的原理是什么?有什么缺陷?

以前对于缺陷的答案可能是:AsyncTask在并发执行多个任务时发生异常。其实还是存在的,在3.0以前的系统中还是会以支持多线程并发的方式执行,支持并发数也是我们上面所计算的128,阻塞队列可以存放10个;也就是同时执行138个任务是没有问题的;而超过138会马上出现java.util.concurrent.RejectedExecutionException;

而在在3.0以上包括3.0的系统中会为单线程执行(即我们上面代码的分析);

空说无凭:下面看测试代码:

package com.example.zhy_asynctask_demo01;

import android.app.Activity;
import android.app.ProgressDialog;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

public class MainActivity extends Activity
{

	private static final String TAG = "MainActivity";
	private ProgressDialog mDialog;
	private TextView mTextView;

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

		mTextView = (TextView) findViewById(R.id.id_tv);

		mDialog = new ProgressDialog(this);
		mDialog.setMax(100);
		mDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
		mDialog.setCancelable(false);
		
		
		for(int i = 1 ;i <= 138 ; i++)
		{
			new MyAsyncTask2().execute();
		}
		
		//new MyAsyncTask().execute();

		
	}

	private class MyAsyncTask2 extends AsyncTask<Void,Void, Void>
	{

		@Override
		protected Void doInBackground(Void... params)
		{
			try
			{
				Log.e(TAG, Thread.currentThread().getName());
				Thread.sleep(10000);
			} catch (InterruptedException e)
			{
				e.printStackTrace();
			}
			return null;
		}
		
	}
}
可以看到我for循环中执行138个异步任务,每个异步任务的执行需要10s;下面使用2.2的模拟器进行测试:

输出结果为:

AsyncTask#1 - AsyncTask #128同时输出
然后10s后,另外10个任务输出。
可以分析结果,得到结论:AsyncTask在2.2的系统中同时支持128个任务并发,至少支持10个任务等待;

下面将138个任务,改成139个任务:

for(int i = 1 ;i <= 139 ; i++)
{
	new MyAsyncTask2().execute();
}
运行结果:会发生异常:java.util.concurrent.RejectedExecutionException ; 于是可以确定仅支持10个任务等待,超过10个则立即发生异常。
简单说一下出现异常的原因:现在是139个任务,几乎同时提交,线程池支持128个的并发,然后阻塞队列数量为10,此时当第11个任务提交的时候则会发生异常。

简单看一下源码:

public static final Executor THREAD_POOL_EXECUTOR
            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
看ThreadPoolExecutor的execute方法:

if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);

当阻塞队列满的时候workQueue.offer(command)返回false;然后执行addWorker(command,false)方法,如果返回false则执行reject()方法.

private boolean addWorker(Runnable firstTask, boolean core) {
…
int wc = workerCountOf(c);
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
…
}

可以看到当任务数目大于容量则返回false,最终在reject()中抛出异常。

上面就是使用2.2模拟器测试的结果;

下面将系统改为4.1.1,也就是我的测试机小米2s

把线程数改为139甚至1000,你可以看到任务一个接一个的在那缓慢的执行,不会抛什么异常,不过线程倒是1个1个的在那执行;


好了,如果现在大家去面试,被问到AsyncTask的缺陷,可以分为两个部分说,在3.0以前,最大支持128个线程的并发,10个任务的等待。在3.0以后,无论有多少任务,都会在其内部单线程执行;


至此,AsyncTask源码分析完毕,相信大家对AsyncTask有了更深的理解~~~



测试代码点击下载








我们今天的关于android – 尝试在后台解析带有AsyncTask的JSON时强制关闭的分享已经告一段落,感谢您的关注,如果您想了解更多关于android asynctask、Android AsyncTask xml解析、Android AsyncTask 完全解析、Android AsyncTask 源码解析的相关信息,请在本站查询。

本文标签: