一.特殊的线程
1.AsynTask
底层用到了线程池,封装了线程池和Handler,主要是为了方便开发者在子线程中更新UI
2.IntentService(不完整???)
内部采用HandlerThread来执行任务,当任务执行完毕后IntentService会自动退出,底层直接使用了线程(从任务执行的角度来看,IntentService的作用很像一个后台线程,但是IntentService是一种服务,他不容易被系统杀死从而可以尽量保证任务的执行)
3.HandlerThread
是一种具有消息循环的线程,在它的内部可以使用Handler,底层直接使用了线程
二.Android中的线程形态
1.1 参数及方法介绍
1.AsyncTask是一个抽象的泛型类,它提供了Params,Progress和Result这三个泛型参数,其中Params表示参数的类型,Progress表示后台任务的执行进度的类型,而Result则表示后台任务的返回结果的类型。
如果AsyncTask确实不需要传递具体的参数,那么这三个泛型参数可以用Void来代替,AsyncTask这个类的声明如下所示。
public abstract class AsyncTask<Params ,Progress ,Result>
(1)onPreExecute
在主线程执行,在异步任务执行之前,此方法会被调用,一般可用于做一些准备工作
(2)doInBackground(Params...params)
在线程池中执行,此方法用于执行异步任务,params参数表示异步任务的的输入参数,在此方法中可以通过publishProgress方法来更新任务的进度,publicProgress方法会调用onProgressUpdate方法。另外此方法需要返回计算结果给onPostExecute
(3)onProgressUpdate(Progress...values)
在主线程中执行,当后台任务的执行进度发生改变时此方法会被调用。
(4)onPostExecute(Result result)
在主线程中执行,异步任务执行之后,此方法会被调用,其中Result参数是后台任务的返回值,即doInBackgroud的返回值
(5)onCancelled
在主线程中执行 异步任务取消时调用,这个时候onPostExecute则不会调用
1.2 AsyncTask在具体的使用过程中也是有一些条件限制
(1)AsyncTask的类必须在主线程中加载,这就意味着第一个访问AsncTask必须发生在主线程,当然这个过程在Android4.1及以上版本中已经被系统已经自动完成。在Android5.0的源码中,可以查看ActivityThread的main方法,他会调用AsyncTask的init方法,这就满足了AsyncTask的类必须在主线程中进行加载这个条件(为什么要满足这个条件,因为要切回主线程的Handler必须是在主线程中创建,然后这个Handler是AsyncTask的内部静态类,所以这个类必须在主线程加载)
(2)AsyncTask的对象必须在主线程中创建
(3)Execute方法必须在UI线程调用
(4)一个AsyncTask对象只能执行一次,即只能调用一次execute方法,否则会报运行异常
(5)在Android1.6之前,AsyncTask是串行执行任务的,Android1.6的时候AsyncTask开始采用线程池里处理并行任务,但是Android3.0开始,为了避免AsyncTask所带来的并发错误,AsyncTask又采用一个线程来串行执行任务,尽管如此,在Android3.0以及后续的版本中,我们仍然可以通过AsynTask的executeOnExecutor方法并行执行任务
1.3源码分析
public static final Executor SERIAL_EXECUTOR = new SerialExecutor(); private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
@MainThread public final AsyncTask<Params, Progress, Result> execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params); }
sDefaultExecutor串行的线程池
//executeOnExecutor //1
@MainThread 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; }
//2
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 occurred while executing doInBackground()", e.getCause()); } catch (CancellationException e) { postResultIfNotInvoked(null); } } };
可以看到把Params封装成FutureTask(这个事并发类在这充当Runnable)对象
最上面写了public static final Executor SERIAL_EXECUTOR = new SerialExecutor()
默认用的就是这个线程池
//3
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); } } }
这时候就是把任务队列mTasks
scheduleNext()一个AsyncTask任务执行完后,调用这个来执行下一个AsyncTask
//4
mWorker = new WorkerRunnable<Params, Result>() { public Result call() throws Exception { mTaskInvoked.set(true); Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); //noinspection unchecked Result result = doInBackground(mParams); Binder.flushPendingCommands(); return postResult(result); } };
由于FutureTask的run方法会调用mWorker的call方法最终会在线程池中执行,在这个方法中先将mTaskInvoked设为true,表示当前任务已经被调用过了,然后执行AsyncTask的doInBackgroud方法,接着传递给PostResult
AsyncTaks中有两个线程池
1.SerialExecutor
用于任务的队列
2.THREAD_POOL_EXECUTOR(也叫InternalHandler)
用于将执行环境从线程池切换到主线程
//5
private Result postResult(Result result) { @SuppressWarnings("unchecked") Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult<Result>(this, result)); message.sendToTarget(); return result; }
//6
private static Handler getHandler() { synchronized (AsyncTask.class) { if (sHandler == null) { sHandler = new InternalHandler(); } return sHandler; } }
//7
private static class InternalHandler extends Handler { public InternalHandler() { super(Looper.getMainLooper()); } @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; } } }
可以看出sHandler是一个静态的Handler对象,为了能够将执行环境切换到主线程
这就要求1.sHandler这个对象必须在主线程中创建(在哪创建,handler里面的各种函数才能在哪执行,这里需要在主线程)----------->2.由于静态成员会在加载类的时候进行初始化,因此这就变相要求AsyncTask的类必须在主线程创建(因为sHandler是AsyncTask的静态成员变量),否则同一个进程中的AsyncTask都将无法正常工作
//8
private void finish(Result result) { if (isCancelled()) { onCancelled(result); } else { onPostExecute(result); } mStatus = Status.FINISHED; }
1.4简单实验(证明AsyncTaks即可串行也是并行)
public class MainActivity extends Activity implements View.OnClickListener { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); findViewById(R.id.bt_start_task).setOnClickListener(this); } @Override public void onClick(View v) { //串行 new MyAsyncTask("我是串行task1").execute(); // new MyAsyncTask("我是串行task2").execute(); // new MyAsyncTask("我是串行task3").execute(); // new MyAsyncTask("我是串行task4").execute(); // new MyAsyncTask("我是串行task5").execute(); // public final AsyncTask<Params, Progress, Result> execute(Params... params) { // return executeOnExecutor(sDefaultExecutor, params); // } //并行 new MyAsyncTask("我是并行task1").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, ""); new MyAsyncTask("我是并行task2").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, ""); new MyAsyncTask("我是并行task3").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, ""); new MyAsyncTask("我是并行task4").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, ""); new MyAsyncTask("我是并行task5").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, ""); } private static class MyAsyncTask extends AsyncTask<String ,Integer,String>{ private String mName = "AsyncTask"; public MyAsyncTask(String mName) { super(); this.mName = mName; } @Override protected String doInBackground(String... params) { SystemClock.sleep(3000); return mName; } @Override protected void onPostExecute(String s) { super.onPostExecute(s); SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); System.out.println("xcqw "+mName+" execute finish at"+df.format(new Date())); } } }
可以通过看打印得出既可以串行也可以并行,但是为什么呢?
串行调用execute和并行调用executeOnExecutor 最后都是调用 executeOnExecutor,只是传的参数不一样
//串行传的参数(默认参数)
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); } } }
从队列中依次取出任务然后调用THREAD_POOL_EXECUTOR.execute(mActive);
//并行传的参数THREAD_POOL_EXECUTOR
每次都直接调用这个THREAD_POOL_EXECUTOR.execute,所以当然并行了
1.5简单实践
//版本更新功能用AsyncTask实现
//updateProgressDialogTask.java
package com.weixin.updateprogressdialog.task; import android.app.Activity; import android.app.AlertDialog; import android.content.Intent; import android.net.Uri; import android.os.AsyncTask; import android.os.Environment; import android.os.Handler; import com.weixin.updateprogressdialog.dialog.UpdateProcessDialog; import com.weixin.updateprogressdialog.utils.ToastUtils; import java.io.BufferedInputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; /** * Created by xiongchao on 2015/10/29. */ public class UpdateTask extends AsyncTask<Object, Integer, File> { //下载的路径 private String urlpath; private UpdateProcessDialog mydialog; private Handler threadhandler; private Activity activity; private FileOutputStream fos; private BufferedInputStream bis; private InputStream is; //下载成功后重命名的文件 private String updateSuccessFilePath; //下载过程中存放apk的地址 private String updatingStorePath; //当前运行的线程 private UpdateTask mCurrentTaks; public static boolean isStopDownload = false; // public boolean isStopDownload() { // return isStopDownload; // } // // public void setIsStopDownload(boolean isStopDownload) { // this.isStopDownload = isStopDownload; // } public void setUpdateComponents(UpdateTask Task,String serverversion,String updatingStorePath) { this.mCurrentTaks = Task; this.updateSuccessFilePath = Environment.getExternalStorageDirectory()+"/MyWeiXinUpDate/"+"update1"+serverversion+".apk"; this.updatingStorePath = updatingStorePath; } public UpdateTask(Activity activity,String urlpath, UpdateProcessDialog dialog,Handler threadhandler) { this.activity = activity; this.urlpath = urlpath; this.mydialog = dialog; this.threadhandler = threadhandler; } // 主线程运行, 预加载 @Override protected void onPreExecute() { super.onPreExecute(); } // 子线程运行, 异步加载逻辑在此方法中处理 @Override protected File doInBackground(Object... params) { System.out.println("xcq " + "任务开始"); urlpath = (String) params[0]; //如果相等的话表示当前的sdcard挂载在手机上并且是可用的 if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { try { URL url = new URL(urlpath); System.out.println("xcq " + "去请求连接"); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); //设置连接超时 conn.setConnectTimeout(5000); //设置下载超时 conn.setReadTimeout(60000); //是否连接成功//如果没有网络会在这里直接跳到异常 System.out.println("xcq " + "去请求响应码"); //响应码这阻塞时间较长,所以先打开dialog threadhandler.post(new Runnable() { @Override public void run() { System.out.println("xcq mydialog 3"+mydialog); if(mydialog != null) { mydialog.show(); } } }); int code = conn.getResponseCode(); if( 200 == code) { File file = new File(updatingStorePath); File suceessFile = new File(updateSuccessFilePath); //如果已经下载过直接去安装 if (suceessFile.exists()) { return suceessFile; } //获取到文件的大小 long length = conn.getContentLength(); is = conn.getInputStream(); fos = new FileOutputStream(file); bis = new BufferedInputStream(is); byte[] buffer = new byte[1024]; int len; int total = 0; boolean renameflag = false; while ((len = bis.read(buffer)) != -1) { fos.write(buffer, 0, len); total += len; if (isStopDownload) { stopDownLoad(); break; } int percent = (int) (total * 100 / length); if(percent == 100){ System.out.println("xcq " + "下载成功"); //如果下载成功,下载文件重命名 renameflag = file.renameTo(suceessFile); if(renameflag == false){ throw new Exception("文件重命名失败"); } } //获取当前下载量//进入progressupdate更新 publishProgress(percent); } fos.close(); bis.close(); is.close(); myDialogDismiss(mydialog); if(mCurrentTaks != null && renameflag){ System.out.println("xcq " +"is"+isStopDownload); return suceessFile; }else{ //如果到这就是中断更新了//还有可能是下载成功后文件重命名失败 System.out.println("xcq " +"is"+isStopDownload); return null; } } else{ //连接服务器失败//服务器返回参数不是200(OK) ToastUtils.show(activity, "连接服务器异常,请检查网络"); } } catch (Exception e) { System.out.println("xcq 异常" + e.toString()); try {if (fos != null){fos.close();}} catch (IOException e1) {e1.printStackTrace();} try {if (bis != null) fos.close();} catch (IOException e1) {e1.printStackTrace();} try {if (fos != null) is.close();} catch (IOException e1) {e1.printStackTrace();} ToastUtils.show(activity,"下载失败,请检查网络"); stopDownLoad(); e.printStackTrace(); } } else { ToastUtils.show(activity,"请检查sd卡是否安装正确"); stopDownLoad(); return null; } return null; } // 主线程运行, 更新进度 @Override protected void onProgressUpdate(Integer... values) { int i = (int) values[0]; System.out.println("xcq Update进度" + i+"isStopDownload"+isStopDownload); if (mydialog != null) { mydialog.show(); mydialog.updateProgressAndTitle(i); } super.onProgressUpdate(values); } // 主线程运行, 更新主界面 @Override protected void onPostExecute(File result) { if (mCurrentTaks != null) { if (result != null && !mCurrentTaks.isCancelled()) { stopDownLoad(); Intent intent = new Intent(); //执行动作 intent.setAction(Intent.ACTION_VIEW); //执行的数据类型 intent.setDataAndType(Uri.fromFile(result), "application/vnd.android.package-archive"); activity.startActivity(intent); System.out.println("xcq onPostExecute"); } } stopDownLoad(); } private void myDialogDismiss(AlertDialog dialog) { if (dialog != null) { dialog.dismiss(); dialog = null; System.out.println("xcq dialog置null"); } } private void myTaskCancel() { if (mCurrentTaks != null) { mCurrentTaks.cancel(true); mCurrentTaks = null; } } private void stopDownLoad() { myTaskCancel(); myDialogDismiss(mydialog); isStopDownload = false; } }
//FirstActivity.java
public class FirstActivity extends Activity { private UpdateProcessDialog mydialog; private String serverVersion; private UpdateTask task; private String result = "自行添加下载地址"; private String updatestorepath = Environment.getExternalStorageDirectory()+"/MyWeiXinUpDate/update.apk"; private Handler threadhandler= null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_first); if(threadhandler == null){ threadhandler = new Handler(); } } public void update(View view) { if (!UpdateTask.isStopDownload) { isVersionsUpdate("1.1"); } else { Toast.makeText(FirstActivity.this, "小点即可,切勿狂按", Toast.LENGTH_SHORT).show(); } } private void isVersionsUpdate(final String versions) { try { final JSONObject json = new JSONObject(result); JSONObject requstResult = json.getJSONObject("RequstResult"); serverVersion = json.get("Versions").toString(); final String apkurl = json.getString("Download"); //这个state是从维信理财直接抄过来的 int state = requstResult.getInt("State"); if (state == 0) { if (versions.compareTo(json.get("Versions").toString()) >= 0) { Toast.makeText(FirstActivity.this, "已是最新版本", Toast.LENGTH_SHORT).show(); } else { AlertDialog.Builder builder = new AlertDialog.Builder(FirstActivity.this); builder.setTitle("有新版本" + json.get("Versions") + ",是否現在更新?"); builder.setPositiveButton("真的确定", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { //开启asynctask下载 getApkFromServer(apkurl); //download(apkurl); } }); builder.setNegativeButton("取消", null); builder.create().show(); } } } catch (Exception e) { System.out.println("xcq 异常11" + e.toString()); e.printStackTrace(); } } // 方法一 public void getApkFromServer(String url) { mydialog = UpdateProcessDialog.showDeleteDialog(FirstActivity.this, 100, new UpdateProcessDialog.OnDeleteCancelListener() { @Override public void onCancel() { UpdateTask.isStopDownload = true; } }, false); //dialog一创建就show(为了初始化),所以这里就关闭显示 mydialog.dismiss(); task = new UpdateTask(FirstActivity.this,url, mydialog,threadhandler); task.setUpdateComponents(task,serverVersion,updatestorepath); task.execute(url); }
2.HandlerThread
Thread(普通) run方法执行了一个耗时任务
HandlerThread内部创建了消息队列,外部需要通过Hanlder类的消息方式来通知他执行一个具体任务,由于HandlerThread的run方法是一个无限循环,因此当明确不需要再使用HandlerThread时,用quit或者quitSafely方法来终止线程的执行。
3.IntentService
三: Android线程池
线程池的优点
(1)重用线程池的线程,避免线程的创建和销毁所带来的性能开销
(2)可以控制线程池的最大并发数,避免大量的线程之间相互抢占系统资源而导致阻塞
(3)能够对线程进行简单的管理,并提供定时执行以及指定间隔循环执行
Android线程池的概念源于Java中的Executor,Executor是一个接口,Android中真正的线程池的实现为ThreadPoolExecutor,Android中的线程池都是直接或间接通过配置ThreadPoolExecutor来实现的.
1.5.1ThreadPoolExecutor
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}
corePoolSize :核心线程数,默认情况下,即使他们处于闲置状态(IDLE),核心线程也会在线程池中一直存活.
除非将ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为True,那么闲置的核心线程在等待新任务到来时会有超时策略,这个时间间隔由KeepAliveTime所指定,超过这个时间核心线程就会被停止。
maximumPoolSize:线程池所能容纳的最大线程数,当活动线程数达到这个数值后,后续的新任务将会被阻塞。
keepAliveTime
非核心线程:超过这个时长,非核心线程就会被回收
核心线程(当ThreadPoolExecutor的allowThreadTimeOut属性设置为True才会受影响):影响上面有说
Unit:用于指定KeepAliveTime参数的时间单位
workQueue
线程池中的任务队列,通过线程池的execute方法提交的Runnable对象会存储在这个参数
中
threadFactory
线程工厂,为线程池提供创建新线程的功能。ThreadFactory是一个接口,他只有一个方法:Thread newThread(Runnable r)
defaultHandler:
当线程池无法执行新任务时,这可能是由于任务队列已满或者无法成功执行任务,有几个策略,默认是abortPolicy,用了这个就会直接抛异常
1.5.2ThreadpoolExecutor执行任务时大致遵循如下规则:
(1).如果线程池的线程数量未达到核心线程的数量,那么会直接启动一个核心线程来执行任务
(2)如果线程池中的线程数量已经达到或者超过核心线程的数量,那么任务会被插入任务队列中等待执行
(3)如果步骤2插入失败,这往往是任务队列满了,这个时候如果线程数量位达到线程池规定的最大值,那么会立即启动一个非核心线程来执行任务。
(4)如果步骤3的线程数量已经达到线程池规定的最大值,那么拒绝执行此任务,就会调用defaultHandler
哪些参数的配置在AsyncTask中可以清晰的看出来