Android后台处理

mac2024-04-08  26

        在Android中,UI线程中是不能执行耗时操作的,UI线程中执行耗时操作会导致ANR。子线程中也不能更新UI组件,否则会抛出异常。因此,Android提供了一套异步处理机制。

一、Handler

         现在有两个线程,UI线程和计算线程,现在有一个耗时操作,要想实现将耗时操作放在计算线程中执行,并将计算结果用来更新UI,就可以用Handler,步骤如下:

在UI线程中定义一个Handler,以匿名内部类的形式创建,并重写 handleMessage(Message msg) 方法在handleMessage方法中设置各种message的UI更新方法,用message.what字段来区分不同的message通过计算线程的构造器,将UI线程中的Handler传给计算线程,在计算线程中计算完成后,创建一个Message对象msg,设置msg.what,并调用handler的sendMessage(msg)这样,UI线程就能识别出对应的message,并更新UI

原理:

        异步消息处理主要由4部分组成:

Message

        Message可以在线程间传递消息,使用what字段来区分,使用obj字段来携带数据

Handler

         Handler用于发送和处理消息,用sendMessage()方法发送消息,用handleMessage()方法处理消息

MessageQueue

           MessageQueue用于存放所有通过Handler发送的消息,每个线程只有一个MessageQueue对象

Looper

           Looper是每个线程中用来管理MessageQueue的东西。调用Looper的loop()方法后,就会进入无限循环,每当发现MessageQueue中存在消息,就将消息取出,并传递到handleMessage()方法中处理。每个线程只有一个Loop对象。

 

整个异步处理机制的流程图如下:

                

 

二、AsyncTask

           AsyncTask的底层也是基于异步处理机制实现的,不过它的封装性更高,使用的时候可以不需要知道异步处理细节

它主要有4中方法:

onPreExecute()          (可以更新UI)

           在后台任务开始之前调用,用于进行一些界面的初始化操作,比如显示进度条对话框等

doInBackground()    (不能更新UI)

           这个方法中的所有代码都会在子线程中执行,任务完成之后,通过return将执行结果返回。如果AsyncTask的第三个泛型参数是void,就可以不返回结果。这个方法中不能更新UI,只能用于计算,如果要用UI反馈当前任务进度,可以用publishProgress(Progress...)

onProgressUpdate(Progress...)   (可以更新UI)

          调用了publishProgress(Progress...)后,该方法会马上调用,该方法的参数就是从后台任务中传过来的,在该方法中可以进行UI操作,可以利用参数值对UI进行更新

onPostExecute()  (可以更新UI)

       当后台任务执行完,并return之后,该方法会被调用,return的数据会作为参数传进来,可以利用返回数据进行UI操作,比如提醒任务执行结果等。

 

下面定义了一个用于下载的AsyncTask:

public class DownloadTask extends AsyncTask<Void,Integer,Boolean> { @Override protected Boolean doInBackground(Void... voids) { try { while (true){ int downLoadPercent = downLoad(); publishProgress(downLoadPercent); if (downLoadPercent >= 100){ break; } } }catch (Exception e){ return false; } return true; } @Override protected void onPreExecute() { progressDialog.show(); } @Override protected void onProgressUpdate(Integer... values) { //在这里更新下载进度 progressDialog.setMessage("downloaded " + values[0] + "%"); } @Override protected void onPostExecute(Boolean result) { progressDialog.dismiss();//关闭下载进度框 //显示下载结果 if (result){ Toast.makeText(context,"download succeed!",Toast.LENGTH_SHORT).show(); }else { Toast.makeText(context,"download failed",Toast.LENGTH_SHORT).show(); } } }

调用 new DownTask().execute() 即可执行该任务

 

原理:

重点记住三个泛型参数<Param,Progress,Result>

Params 是AsyncTask.execute()方法里的输入参数,此处为url,也是doInBackground()方法的输入参数Progress 是阶段性的数据,此处为Integer,是doInBackground()的中间参数,也是publishProgress()方法的输入参数,onProgressUpdate()的输入参数Result 是运行结果,是doInBackground()方法的返回结果,也是onPostExecute()方法的输入参数

 

三、Service

         Service是Android中实现程序后台运行的解决方案,适合去执行长期存在并且不需要和用户交互的任务,服务的运行不依赖界面,始终可以在后台执行。但是Service其实是运行在主线程上,并没有开辟子线程去执行后台任务,因此Service不能执行耗时操作,要想执行耗时操作可以采用IntentService,它开辟了子线程。

    使用方法:

创建一个myService类,继承Service在AndroidManifest.xml中注册该Service重写onBind()、onStartCommand()、onCreate()、onDestroy()等方法onBind()会在context中调用bindService()之后回调onStartCommand()会在服务启动后回调onCreate()在该服务第一次创建的时候调用onDestroy()在context调用stopService()后或者service自己调用stopSelf()之后回调,可以回收一些资源

启动服务

在Activity中利用Intent并调用startService()和stopService()方法来启动和停止服务

绑定Activity和Service,两者通过Binder通信,Binder对象通过ServiceConnection里的方法传递

在Service中创建一个Binder对象,通过onBind()方法将该对象传到Activity中在Activity中利用匿名内部类创建一个ServiceConnection对象,重写onServiceConnected()方法,该方法中可以获取到Service的onBind()返回的Binder对象。然后利用Intent和ServiceConnection,调用bindService(),可以将Activity和Service绑定起来,要解绑两者就调用unbindService(connection)

Activity和Service绑定之后,就可以利用Service里的binder执行一系列操作,即指挥Service操作

 

Service生命周期

                                               

 

Service分类:

 

 

 

最新回复(0)