视频播放器 第3节 异步方式获取视频信息

第3节 异步方式获取视频信息

获取视频信息所需要的时间是个不能确定的事情。如果视频很少,也许几十毫秒就能完成,如果视频很多(比如几十个),也许就要花二十多秒。

安卓应用只有一个主线程-各个组件都是在这个线程中运行。作为组件的之一的Activity就是在这个线程中更新应用界面的,例如,用户点击界面上的一个按钮,按钮得到响应,整个过程就是在这个主线程里。所以这个主线程绝对不可以做耗时的操作。假如在按钮中做了耗时的操作,那么当它进行耗时操作的时候,你去点击界面上的其它按钮是不会有反应的,就好像程序冻在了那里。

我们的代码一旦连续占用这个线程超过一定的时间,系统就会弹出“程序无响应的”提示,这个提示叫做ANR-Applicatin No Response。

因此,我们可以考虑把获取视频信息的操作放到一个单独的线程thread中进行。

这就好比你在正在做一件事情A,突然另一件事情B来打扰你,你不得不停下手头的工作来完成,做完了才能继续之前的工作;这时如果有另外一个人(另一个线程)来帮助你,把事情B全部包揽了,那你就不用分心了。当另一个人把事情B做完后,告诉你一声就可以了。

3.1 异步操作

启动一个新的线程,分担耗时工作的方法是一种异步操作:我让你帮我做一件事情,布置任务后,我就去做其他的事情了,等你做完了再告诉我结果;

与它对应的是同步操作:我让你帮我做一件事情,布置任务后,我啥也不做,就等着你做完了告诉我结果;

获取视频信息是个异步操作,启动一个新线程-工作线程thread-查询视频信息,查询完成后,工作线程再将结果通知到主线程,让主线程将查询到结果的结果显示到界面上。界面的更新一定要在主线程中进行,不能在别的线程修改,否则系统后提示运行错误,这一点相当重要。因此我们一定要将查询的结果发送给主线程,让主线程处理界面的更新。

3.2 异步操作的方案

安卓系统提供的异步操作方案有:

  1. 创建工作线程thread和Handler,利用Handler在工作线程和主线程之间传递数据;
  2. 使用AsyncTask帮助类,AsyncTask中封装了工作线程,通过AsyncTask完成工作线程和主线程之间的数据传递;

这里虽然将AsyncTask看成是一个单独的方案,但实际上它也是通过方案1实现的,只不过对于使用者来讲更加方便而已。

这里我们选择方案2。因为,

  1. 使用场景简单,只是单个任务的异步操作,没有多个线程之间的数据同步考虑;
  2. 使用方便,不用考虑太多的新线程创建的细节;

3.3 AsyncTask的使用

3.3.1 AsyncTask的用法

AsyncTask需要被继承成为一个新的子类来使用,在被继承时,要指定三种参数的类型-Param Progress Result,还需要实现doInBackground(Param...)函数,此外通常还要实现onProgressUpdate(Progress...) onPostExecute(Result) 两个回调函数。

  1. doInBackground(Param... params)函数:传入参数的Param类型就是AsyncTask中指定的Param类型。它运行在新创建的工作线程当中。

    使用MyTask时,要在主线程中使用excute()方法传入不定长参数,让Task运行起来,

    不定长参数会以数组的形式传递到doInBackground()函数当中,

  2. onProgressUpdate(Progress... progresses)函数:传入参数的Progress类型就是AsyncTask中指定的Progress类型。

    doInBackground()中执行的是一个很耗时的工作,有时需要向主线程报告当前的运行状况,这就要使用到publishProgress()函数,publishProgress()也是使用的不定长参数,

    不定长参数会以数组的形式传递到onProgressUpdate()函数当中,

  3. onPostExecute(Result result)函数:传入参数的Result类型就是AsyncTask中指定的Result类型。

    doInBackground()函数返回的类型也是Result

    返回的结果作为参数传递给onPostExecute()函数,

  4. onCancel()函数会在调用者取消AsyncTask的工作的时候被触发。

    要取消AsyncTask的工作,首先要在主线程中调用cancel()方法,

    因为在doInBackground()中执行的是一个很耗时的工作,需要时不时的检查自己是否被取消执行了,

    最后,onCancelled()函数会被触发,这个函数会在主线程中被执行,


综合上面的分析,自定义一个AsyncTask的方法如下,

使用一个AsyncTask的方法如下,

3.3.2 获取视频信息的AsyncTask

根据我们的需要,自己定义个AsyncTaskVideoUpdateTask

  1. 不需要为新创建的线程传入参数;所以Param设置成Object
  2. 因为查询的过程很长,所以需要时不时通知主线程查询的状态,每查询到一条,就将视频数据传递给主线程;所以Progress设置成VideoItem
  3. 查询的结果已经在查询的过程中发送给了主线程,全部完成后,不需要再传递什么结果给主线程了,所以Result设置成Void
  4. 将查询视频信息的操作放到doInBackground()中进行,这是一个新创建的工作线程;
  5. 工作线程中,每发现一个视频,就通知给主线程;

在视频列表Activity创建的时候,启动VideoUpdateTask,开始查询符合我们要求的视频信息。

在视频列表Activity退出的时候,判断VideoUpdateTask是否还在运行,如果还在运行,就让它停止,

onCreate()onDestroy()是Activity生命周期的一部分,当一个Activity被创建的时候会调用到onCreate(),当Activity被退出销毁的时候会调用到onDestroy()。所以在这两个地方使用VideoUpdateTask是一个合适的选择。