Android系列之多线程用法与Service初步

  在Android中,我们有时会需要进行一些后台任务,好让我们在继续进行交互操作的时候,一些特定任务能继续执行。这个时候Service的作用就凸显了出来。服务的好处就在于它不依赖于任何用户界面,当用户使用另一个应用程序时,服务仍能继续执行。但,值得注意的是,Service并不是一个独立的进程,而是依赖于创建它的进程。当该进程被杀死时,则服务也会停止。并且,服务并不会创建一个线程来执行任务,服务中的任务仍然是在主线程中进行。所以,我们需要在服务内部手动创建子线程,避免主线程被阻塞。
多线程的用法:
  Android中线程的用法(Thread和Runnable)与Java一样,没有什么区别,在此不做赘述。
  在Android的UI中,其是线程不安全的,因此我们不能在子线程中进行UI的更新等操作,否则会出现异常。但是,我们有时会希望在子线程中执行一些特殊操作,如耗时操作,然后去更新UI,这个时候就需要Android提供的异步消息处理机制。
  异步消息处理机制主要由四大组件组成:Handler、Message、Looper、MessageQueue。其中:
  Handler:最重要的部分,也是我们主要的操作对象。通过它将消息Message投递到MessageQueue中,也是通过它处理从MessageQueue中得到的消息。
  Message:消息的实体。内部包含了多个参数,what作为消息的标志位,通过它识别消息。还有arg1,arg2可接收的整形参数。obj接收对象参数。
  Looper:管理MessageQueue,从MessageQueue中取出消息并将其传给Handler的handleMessage方法。Looper会循环遍历MessageQueue。
  MessageQueue:消息队列,存储了所有从Handler提交过来的消息。一个线程只会有一个MessageQueue。
  在此,将异步消息处理的过程梳理一遍:首先,在主线程中创建Handler对象,并重写handleMessage方法。然后在子线程中完成相应操作后,将数据放进Message中,通过handler将其投递至MessageQueue队列中。然后Looper循环遍历MessageQueue从中取出待处理的消息,将其交给handleMessage方法。由于handleMessage是在主线程中进行的,所以此时子线程的数据就传给了主线程。然后通过Message的what标志判断消息,通过arg1、arg2或obj参数取出数据。
  至此,一条消息就经子线程传递到了主线程,从而就可以执行UI操作了。
  不过,为了更方便的完成在子线程中进行UI操作,Android提供了AsyncTask工具,其内部也是实现的异步消息处理,不过已经对其进行了很好的封装。但由于AsyncTask是一个抽象类,所以我们需要一个子类去继承并实现其中的方法才能使用。在继承时通常需要传入三个参数:
Params:执行AsyncTask时需传入的参数。可用于后台使用。
Progress:对于后台执行的任务,若需要查看其进度时,则指定泛型作为进度单位。
Result:若需要返回结果,在此指定泛型作为返回数据的类型。
简单的定义如下:

class DownloadTask extends AsyncTask<Void, Integer, Boolean> {
……
}

  定义完之后,我们需要重写其中的方法。通常,我们需要重写其中的四个方法:
onPreExecute():这个方法会在后台任务开始执行之前调用,用于进行一些界面上的初始化操作,比如显示一个进度条对话框等。
doInBackground(Params…):这个方法中的所有代码都会在子线程中进行,任务完成,则通过return返回结果。
onProgressUpdate():当后台调用了publishProgress()方法,则这个方法很快会被调用。方法中携带的参数就是从后台传过来的参数,这个方法中可以执行UI操作。利用参数进行UI更新。
onPostExecute(Result):当后台任务完毕并通过return返回时,该方法会被调用。返回的参数传入到该方法中,通过这些参数进行一些UI操作。
较完整的使用方法如下:(摘自《第一行代码》)

class DownloadTask extends AsyncTask<Void, Integer, Boolean> {
    @Override
    protected void onPreExecute() {
        progressDialog.show(); // 显示进度对话框
    }
    @Override
    protected Boolean doInBackground(Void... params) {
        try {
        while (true) {
            int downloadPercent = doDownload(); // 这是一个虚构的方法
            publishProgress(downloadPercent);
            if (downloadPercent >= 100) {
                break;
            }
        }
        } catch (Exception e) {
        return false;
        }
    return true;
    }
    @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 succeeded",
            Toast.LENGTH_SHORT).show();
        } else {
            Toast.makeText(context, " Download failed",
            Toast.LENGTH_SHORT).show();
            }
        }
}

服务的用法:
  讲解完了Android中多线程的使用方法和异步消息处理机制后,就可以开始使用Service了。服务的启动方式有两种:startService()和bindService()。
  startService:
  首先需要创建一个子类继承自Service,并实现其中的onBind方法,因为只是启动服务而不需要从服务返回数据,所以这个方法返回null便可。然后在AndroidManifest文件中对服务进行注册就行了。示例如下:

public class MyService extends Service {
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}
//注册部分如下:
<service android:name=".MyService" >
</service>

仅仅需要这几步就完成了一个服务,不过这个服务什么也干不了。因此,为了让这个服务能真正的为我们服务,我们需要再重写几个方法:
onCreate():服务创建时调用的方法。
onStartCommand():服务启动时调用的方法。
onDestroy():服务被注销时调用的方法。
通常,我们希望启动服务时立刻去完成某些操作,则这些操作就可以写在onStartCommand方法中,当服务被注销时,我们需要进行一些资源的回收,则在onDestroy方法中执行。
  创建了服务后,我们当然需要知道如何启用和停止一个服务。启动和停止服务,同样是通过Intent进行意图导向。示例代码如下:

Intent startIntent = new Intent(this, MyService.class);
startService(startIntent); // 启动服务

Intent stopIntent = new Intent(this, MyService.class);
stopService(stopIntent); // 停止服务

其中的MyService就是我们自定义的服务。
通过startService()方法启动的Service服务,其生命周期是:
onCreate->onStartCommand->onDestroy()
值得注意的是,当该服务创建后,再通过该方式启动服务时就不会再调用onCreate方法了,而是直接调用onStartCommand方法。
bindService:
  那么有了服务后,如何实现服务和活动间的通信呢?这就要用到之前没有处理的onBind方法啦。我们注意到这个方法的返回值是IBinder类型,那么我们就可以在服务中新建一个内部类继承自IBinder,然后在其中添加一些参数,或者调用方法,甚至实现一些可以查看或更改服务中内容的方法。然后在onBind方法中返回此内部类。那么Activity中如何接受到这个返回的IBinder对象呢?
  此时,需要在Activity中通过ServiceConnection对象重写其内部的方法来获取IBinder对象。此部分代码示例如下:

private ServiceConnection connection = new ServiceConnection() {
    @Override
    public void onServiceDisconnected(ComponentName name) {}
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
    }
};

其中的onSeriviceConnected方法中的IBinder参数就是从服务中返回的IBinder对象。通过该对象,我们就可以对服务进行一些相关操作啦。
  同Activity一样,Service也有其生命周期。
  通过Context的startService方法开启一个服务,然后回调onStartCommand()方法,执行相应的操作。服务一旦被启动就会一直执行,指导调用了stopService或stopSelf方法注销了服务。并且,服务一旦被创建就只有一个实例,无论在开启多少次。并且每次调用startService方法时,则会执行onStartCommand方法,而不会去创建一个服务实例。除非服务被注销。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值