Android开发学习之探究服务

(学习参考书:第一行代码第2版)
服务是Android中实现程序后台运行的解决方案,适合去执行那些不需要和用户交互而且还需要长时间运行的任务。服务的运行不依赖任何用户界面,及时程序被切换到后台,或者用户打开了另外一个应用程序,服务仍然能够保持正常运行。
服务不是运行在一个独立的线程当中的,而是依赖于创建服务时所在的应用程序进程。当某个应用程序进程被杀掉时,依赖于该进程的服务也会停止运行。
服务也不会自动开启线程,所有的代码默认运行在主线程当中。需要在服务的内部创建子线程,并在这执行具体的任务。

一、Android多线程

Android多线程跟Java多线程采用相同的语法,有三种方式使用线程

  1. 定义线程时只需要新建一个类继承自Thread,然后重写父类的run()方法,并且在里面编写耗时逻辑即可。创建Thread子类的实例后,调用它的start()方法就可以让run方法中的代码在子线程中运行了。
  2. 新建类实现Runnable接口,再重写run()方法。调用时创建实例传入Thread类的构造函数里,再调用Thread的start()方法即可。
  3. 采用匿名类的方式,直接new Thread(new Runnable(){}).start。

二、在子线程中更新UI

与许多其他的GUI库一样,Android的UI也是线程不安全的。也就是说,想要更新应用程序中的UI元素,则必须在主线程中进行,否则会出现异常。
为了解决有时候必须在子线程里去执行一些耗时任务,然后根据任务的执行结果来更新相应的UI控件。Android提供了一套异步消息处理机制,完美的解决了在子线程中进行UI操作的问题。
在子线程中进行UI操作的问题。

(1)基本用法

  1. 在主线程中创建一个Hanlder对象,并重写handleMessage()方法
  2. 当子线程需要进行UI操作时,就创建一个Message对象,并通过Hanlder将这条消息发送出去
  3. 之后这条消息会被添加到MessageQueue的队列中等待被处理
  4. Looper会一直尝试从MessageQueue中取出待处理消息,最后分发回Handler的handleMessage()方法中。
public static final int UPDATE_TEXT=1;
private TextView text;
private Handler handler = new Handler(Looper.getMainLooper()){
     
    @Override
    public void handleMessage(@NonNull Message msg) {
     
        switch (msg.what){
     
            case UPDATE_TEXT:
                text.setText("Hello Android!");
                break;
            default:
                break;
        }
    }
};
……
new Thread(new Runnable() {
     
    @Override
    public void run() {
     
        Message message = new Message();
        message.what =UPDATE_TEXT;
        handler.sendMessage(message);
    }
}).start();

(2)解析异步消息处理机制

Android中的异步消息处理机制主要由4个部分组成:

Message

Message是在线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程之间交换数据。

Handler

主要用于发送和处理消息。发送消息一般是Handler的sendMessage()方法,发出的消息经过一系列的辗转处理后,最终会传递到Handler的hanleMessage()方法中。

MessageQueue

消息队列,主要用于存放所有通过Hanler发送的消息。这部分消息会一直存放在消息队列中,等待被处理。每个线程中只会有一个MessageQueue对象。

Looper

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

(3)使用AsyncTask

AsycnTask可以更简单的从子线程切换到主线程。其背后的实现原理也是基于异步消息处理机制的。
AsycnTask是一个抽象类,如果想使用它,就必须创建一个子类去继承它。在继承时可以为AsycnTask类指定三个泛型参数,用途如下:

Params 在执行AsycnTask时需要传入的参数,可用于在后台任务中使用
Progress 后台任务执行时,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为进度单位
Result 当任务执行完毕后,如果需要对结果进行返回,则使用这里指定的泛型作为返回值类型

AsycnTask子类通常需要重写的方法:

onPreExecute() 该方法会在后台任务开始执行之前调用,用于进行一些界面上的初始化操作,比如显示一个进度条对话框

doInBackground(Params) 该方法的所有代码都会在子线程中运行,应该在这里去处理所有的耗时任务。任务一旦完成就可以通过return语句来将任务的执行结果返回,如果AsycnTask的第三个泛型参数指定为Void,可以不返回。该方法中不能进行UI操作;如需更新,如反馈任务进度,可以调用pubishProgress(Progress)方法完成。

onProgressUpdate(Progress) 当在后台任务中调用了pubishProgress(Progress)方法后,该方法会很快被调用。该方法携带的参数就是后台任务中传递过来的。在这个方法中可以对UI进行操作,利用参数中的数值就可以对界面元素进行相应的更新。

onPostExecute(Result) 当后台任务执行完毕并通过return语句进行返回时,这个方法很快被调用。返回的数据作为参数传递到此方法中,可以利用返回的数据进行UI操作。

简单来说,AsycnTask的使用诀窍是:在doInBackground()方法中执行具体的耗时操作,在onProgressUpdate()方法中进行UI操作,在onPostExecute()方法中执行一些任务的收尾工作。只需要调用publishProgress()方法,就可以轻松从子线程切换到UI线程了。

三、服务的基本用法

(1)定义服务

自定义的服务类是继承于Service类的,onBind方法时Service中唯一一个抽象方法,必须要在子类中实现。除此之外,还可以重写onCreate()每次服务创建时调用,onStartComand()每次服务启动时调用,onDestory()服务销毁时调用等方法。

(2)启动和停止服务

启动和停止服务是 借助Intent实现的。

  1. 启动服务:创建Intent对象,传入参数上下文和服务类实例;调用startService()方法,传入intent对象
  2. 停止服务:创建Intent对象,传入参数上下文和服务类实例;调用stopService()方法,传入intent对象

(3)活动与服务间通信

要实现活动和服务间的通信,需要使用到Service类重写的onBind()方法,具体方法如下:

  1. 创建一个专门继承Binder的类来对需要实现的功能进行管理
  2. 在Binder子类内部实现需要的功能
  3. 创建新建类的实例,在onBind()中返回
  4. 在活动中绑定服务,首先创建一个ServiceConnection的匿名类
  5. 在匿名类中重写onServiceConnection()和onServiceDisconnection()方法,这两个方法分别会在活动与服务成功绑定和连接断开的时候调用
  6. 在onServiceConnection()方法中通过下转型获得Binder子类的实例,这样在活动中根据具体场景可以调用Binder子类中任何public()方法,即实现指挥服务实现相应功能
  7. 通过构建和服务相关的Intent对象,调用bindService()方法将活动和服务绑定起来。该方法接收三个参数:Intent对象、ServiceConnection对象、标志位
  8. 如果需要解绑服务,调用unbindService()方法即可

(4)服务的生命周期

一旦项目在任何位置调用了startService()方法,相应的服务就会启动起来,并回调onStartCommand()方法。如果这个服务之前没有被创建过,onCreate()方法会先于onStartCommand()方法执行。服务启动后会一直保持运行状态,直到stopService()stopSelf()方法被调用。
还可以调用bindService()方法来获取一个服务的持久连接,这时就会回调服务中的onBind()方法。如果服务未被创建,onCreate()方法会先于onBind()执行。之后,调用方可以获取到onBind()方法力返回的IBinder对象实例,就可以自由与服务通信了。只要调用方和服务的链接没有断开,服务就会一直保持运行状态。
当调用startService()方法后,又去调用stopService()方法,这是服务中的onDestory()方法就会执行,表示服务已被销毁。类似的,调用了bindService()方法后,又调用unbindService()方法,onDestory()方法也会执行。当两个启动方法都被调用,则只有同时调用两个停止方法才能让onDestory()方法执行。

区别辨析:
stopSelf() 服务执行完成onCreate()后,停止service,调用onDestory()方法
stopService() 立即停止服务,调用onDestory()方法

(5)使用前台服务

服务几乎都是在后台运行的,优先级较低,容易被系统回收。如果希望服务一直保持运行状态,可以考虑使用前台服务。前台服务会一直有一个正在运行的图标在系统的状态栏显示,类似于通知。创建前台服务的方法如下:

  1. 在AndroidManifest.xml中添加前台服务的权限:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
  1. 创建一个通知
  2. 使用startForeground()方法传入id和Notification对象

(6)在子线程运行服务

服务中的代码默认运行在主线程当中,如果直接在服务里处理一些耗时操作,很容易出现程序无响应的情况。这时就需要在服务的每个具体方法里开启一个子线程,然后在子线程里处理耗时操作。故一个标准服务可写为:

public class MyService extends Service{
     
    ...

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
     
        new Thread(new Runnable() {
     
            @Override
            public void run() {
     
                //具体操作逻辑
		stopself();
            }
        })
        return super.onStartCommand(intent, flags, startId);
    }
}

处于子线程的服务一旦启动后,会一直处于运行状态,必须调用stopService()或者stopSelf()方法才能让服务停止下来。

你可能感兴趣的:(Android,android,java)