当前位置: 首页 > news >正文

服务(service)使用指南

[TOC]

1. 服务是什么?

​ 服务是一种应用程序组件,当应用程序希望在不与用户交互的情况下执行长时间运行的操作,或者为其他应用程序提供的功能。每个服务类必须在其包的AndroidManifest.xml中具有相应的声明。

​ 特别需要注意的是,服务也是运行在主线程当中的,当你需要运行耗时任务的时候需要另外开辟子线程,否则会造成界面的卡死.

​ 需要知道的是,服务本身并不是一个线程或进程,它是依赖于创建服务时所在的应用程序进程,当应用程序停止这个服务也会终止运行.

2. 异步处理:

2.1 异步消息的处理机制:

​ Android中的异步消息处理机制主要由下面四部分组成.

​ 每当创建一个Handler实例的时候,它将自动绑定到创建该实例的线程上.所以当Handler在主线程中创建,里面的handlerMessage()方法执行的操作也是在主线程当中进行的.

功能
Message可以携带少量信息,用于在不同的线程之间交互数据.可以使用what字段或arg1,arg2,obj字段携带信息.
Handler用于发送和处理Message,发送使用SendMessage(),接收则使用handleMessage().
MessageQueue消息队列,主要存放所有通过Handler发送的消息,.每个线程中只会有一个MessageQueue对象
Looper是MessageQueue的管家,调用Looper的loop()方法以后就会进入无限循环,每当发现消息队列中存在消息的时候,就会把消息取出,并传递到Handler的handlerMessage()方法中.和上面一样每个线程中只有一个Looper对象.

一条Message经过这样的一个流程以后,也就由子线程进入了主线程,也就可以对UI进行相对应的修改了.整个异步消息处理的核心也就是如此

2.2 使用AsyncTask:

AsyncTask也是基于Handler封装而来的.由于AsyncTask是一个抽象类,因此想要使用AsyncTask就必须创建一个继承自AsyncTask的类,并指定泛型和重写以下四个方法.

用途
onPreExecute()gai该方法会在后台任务开始执行之前调用,用于进行一些界面上的初始化操作
doInBackground(Parans ...)这个方法中的所有代码都会在子线程中进行操作,任务一旦完成就可以通过return语句来将任务的执行结果返回,如果AsyncTask的第三个泛型参数指定为Void,就可以不返回任务执行结果.(如果想要更新任务进度的话,可以调用publishProgress(Progress ...)方法来完成)
onProgressUpdate(Progress ...)当后台任务调用了publishProgress(Progress ...),当前方法就会被调用,该方法中的参数就是在后台任务中传递过来的.在这个方法中可以对UI进行操作.利用传进来的参数就可以对界面进行更新.
onPostExecute(Result)当后台任务完成并通过return返回的时候,当前方法就被调用,返回的数据会作为参数传递到当前方法中,可以利用返回的数据来进行一些UI操作,比如提醒任务完成等...

实践

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        mySynnc mySyn = new mySynnc(); //异步任务的初始化
        mySyn.execute();  //当实现异步任务类的第一个参数指定不为空的时候,需要把要提交的参数放在.execute()中
    }
	
    
    //通过代码实践可以直观的看出整个异步任务的执行流程
    class mySynnc extends AsyncTask<Void, Integer, Boolean> {
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            Log.d(TAG, "onPreExecute: 初始化");

        }

        @Override
        protected Boolean doInBackground(Void... voids) {
            Log.d(TAG, "doInBackground: 后台");
            publishProgress(123);
            return null;

        }


        @Override
        protected void onPostExecute(Boolean aBoolean) {
            super.onPostExecute(aBoolean);
            Log.d(TAG, "onPostExecute: 任务完成,进入收尾阶段");
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
            Log.d(TAG, "onProgressUpdate: 更新");
        }


    }
}

复制代码

3. 服务的基本用法:

​ 通过androidstudio右键新建一个继承自Service的类,并在内部实现相对应的方法,因为服务是四大组件之一所以必须在ManiFest.xml中进行注册,否则无法使用(没反应).

3.1 定义一个服务:

public class MyService extends Service {
    private static final String TAG = "MyService";
    public MyService() {
        Log.d(TAG, "MyService: ");
    }

    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG, "onBind: ");
        // TODO: Return the communication channel to the service.
         return null;

    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "onCreate: ");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "onStartCommand: ");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy: ");
    }
}

复制代码

​ 定义一个服务必须重写 onBind(..) 方法,因为这是一个抽象方法,在实现类中必须被实例.

​ onCreate()方法在服务被创建时会被调用.

​ onStartCommond(...) 方法在每次服务启动的时候调用.

​ onDestroy() 方法 当活动停止这个服务或者活动被销毁的时候,该方法被调用.

​ 服务内部也可以自己调用stopSelf()来停止该服务.

3.2 启动和停止一个服务:

    protected void onCreate(Bundle savedInstanceState) {		
        //启动服务
        Intent intent=new Intent(this,MyService.class);
        startService(intent);
        //停止服务
        Intent intent2=new Intent(this,MyService.class);
        stopService(intent);	
    }
复制代码

通常这样启用服务十分的不方便,当我们启动服务以后服务就一直在后台处于运行状态,除非执行完毕,否则我们就不知道服务在前在做什么,做到哪里了? 所以我们就需要服务和活动进行通讯.

3.3 服务和活动进行通讯:

​ 在服务中创建一个Binder实例类,并在Binder类中提供相对应的方法给别人调用.

​ 服务会通过public IBinder onBind(Intent intent) ;方法将Binder对象返回给活动.

​ 服务:

public class MyService extends Service {
    private static final String TAG = "MyService";
    private Abinder mAbinder = new Abinder();

    public MyService() {
        Log.d(TAG, "MyService: ");
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mAbinder;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "onCreate: ");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "onStartCommand: ");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy: ");
    }

    class Abinder extends Binder {
        public void start() {
            Log.d(TAG, "start: ");
        }

        public void pause() {
            Log.d(TAG, "pause: ");
        }

        public void resume() {
            Log.d(TAG, "resume: ");
        }

        public void restart() {
            Log.d(TAG, "restart: ");
        }

        public void destory() {
            Log.d(TAG, "destory: ");
        }
    }
}
复制代码

活动:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private static final String TAG = "调试信息:";
    private Button btn1, btn2, btn3, btn4, btn5;
    private MyService.Abinder mAbinder;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
        Log.d(TAG, "onCreate: 当前活动线程:" + Thread.currentThread().getId());
    }

    private void initView() {
		...
    }

    private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.d(TAG, "onServiceConnected: ");
            mAbinder = (MyService.Abinder) service;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.d(TAG, "onServiceDisconnected: ");
        }
    };
......
    
}
复制代码

通过上面完整的代码案列可以知道,在当前活动中通过于服务进行绑定可以获得一个service的内部对象,这个对象可以对service进行一定的控制和获取状态.

活动中创建了一个ServiceConnection的匿名内部类,

**里面分别重写了 onServiceConnected(); onServiceDisconnected(); 这两个方法,当活动和服务进行绑定或解绑时他们会被调用. **

onServiceConnected(); 中 通过向下转型得到service的binder的实例,然后我们就可以通过这个实例对服务进行操作控制.

其中活动与服务的绑定方法接受三个参数 bindService(bindIntent,connection,BIND_AUTO_CREATE);

第一个参数是intent对象,指明我们的意图;

第二个参数是ServiceConnection的实例,它会在绑定成功或失败时回调这个对象中的方法;

第三个参数是BIND_AUTO_CREATE 这个当前Context提供的一个常量, 表示活动和服务进行绑定以后自动创建服务,所以服务中的onCreate()方法可以执行,但onStartCommand()方法不会执行.

注意点:当前程序中任何一个服务都可以与多个活动进行绑定,而且绑定后获取到的Binder实例也是相同的.

4. 服务的生命周期:

**Context中的strartService(): **

​ onCreate()->onStartCommand()->onDestory()

​ 当这个服务第一次被创建的时候才会调用onCreate();

​ 因为服务只有一个实例,所以不管调用多少次onStartCommand(),只需要调用stopService()就可以将服务销毁

**Context中的bindService(): **

​ onCreate->onBinder()->onDestroy()

​ 通过绑定来获取一个服务的持久化连接,这是会挥动服务中的onBind(),如果之前这个服务没有创建过,会先调用onCreate()方法. 之后就可以通过返回的Binder对象与服务进行通信. 调用unbindService()方法后 服务将会被销毁.

当服务既调用了startService()又调用了bindService()时,因为Android系统的机制,一个服务只要被启动或绑定就一直处于运行状态,只有两种情况同时不满足这个服务才可以被销毁,所以要同时调用stopService()和unbindService() 这个服务才可以调用onDestroy()

5. 更高级的服务:

因为服务几乎都是在后台运行的,并且服务的优先级还是比较低的,当系统出现内存不足的情况,就有可能回收掉正在后台运行的服务.如果不想因为内存不足导致服务被回收,就可以考虑使用前台服务.前台服务和普通的服务的最大区别就在于,他会有一个正在运行的图标在系统的状态了显示,类似通知.

5.1 前台服务:

只需要在Service的onCreate()方法中添加如下代码

    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "onCreate: ");
        Intent intent = new Intent(this, MainActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
        Notification notification = new NotificationCompat.Builder(this).setContentIntent
                (pendingIntent).setContentTitle("title").setContentText("text").setWhen(System
                .currentTimeMillis()).setSmallIcon(R.mipmap.ic_launcher).setLargeIcon
                (BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher)).build();
        startForeground(1, notification);

    }
复制代码

首先先创建一个通知,但和往常显示通过的方式不一样,它是通过调用startForeground()方法来显示;

这个方法接收两个参数:第一个参数时通知的Id;第二个参数则是构建出的Notification对象,调用这个方法以后就会将这个服务变成一个前台服务.

5.2 IntentService:

如果在服务中执行一些耗时的操作,可能需要在onStartCommand中开启子线程. 但是因为服务一直处于运行状态,想要让服务停止下来的话需要调用stopSelf() 或者stopService(); 但是经常会忘记开启线程或者调用停止服务的方法,所以Android 专门提供了一个IntentService类,这个类就解决了这两个问题.

用法:

​ 主要是创建一个继承自IntentService的类,并实现相应的方法;

方法说明
构造函数();启动IntentService时调用,并赋值一个字符串给父类用于调试式判断工作线程
onHandleIntent();这个方法中的代码将会在子线程中执行
onDestroy();只要上面的方法执行完毕,就会调用这个方法停止当前服务.
public class MyIntentService_ extends IntentService {
    private static final String TAG = "调试信息:";
    /**
     * Creates an IntentService.  Invoked by your subclass's constructor.
     *
     * @param name Used to name the worker thread, important only for debugging.
     */
    public MyIntentService_() {
        super("name");
        //name主要时用于调试时 方便知道工作在哪个线程
        Log.d(TAG, "MyIntentService_: ");
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        Log.d(TAG, "onHandleIntent: 服务中的线程:"+Thread.currentThread().getId());
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy: ");
    }
}
复制代码
 protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
        Log.d(TAG, "onCreate: 当前活动线程:" + Thread.currentThread().getId());
        Intent intent=new Intent(this,MyIntentService_.class);
        startService(intent);
    }
复制代码

通过调试信息可以看清楚IntentService的运行流程. 这样就十分方便了,后台的代码都在子线程中完成,并且执行完毕之后会自动停止服务.

相关文章:

  • 循环数组赋值给对象并push到数组中
  • spring源码解析bean初始化与依赖注入四
  • linux根分区满了如何处理,查找大文件方法
  • 机器中的灵魂会是量子比特么?
  • Android lrucache 实现与使用(Android内存优化)
  • 趣谈网络协议:像小说一样的网络协议入门课
  • 5.16 Stacks and Queues
  • iBatis和MyBatis在使用ResultMap对应关系时的区别
  • Java 架构师眼中的 HTTP 协议
  • Java多线程干货系列—(四)volatile关键字
  • first
  • Linux Swap扩容
  • 函数的默认参数:可有可无
  • os模块注意事项
  • 腾讯云全面开放,联手千万开发者共建超级大脑
  • ----------
  • JS 中的深拷贝与浅拷贝
  • 【挥舞JS】JS实现继承,封装一个extends方法
  • Angular 响应式表单 基础例子
  • iOS仿今日头条、壁纸应用、筛选分类、三方微博、颜色填充等源码
  • nodejs:开发并发布一个nodejs包
  • React Native移动开发实战-3-实现页面间的数据传递
  • SAP云平台里Global Account和Sub Account的关系
  • Spring Cloud(3) - 服务治理: Spring Cloud Eureka
  • SpriteKit 技巧之添加背景图片
  • yii2权限控制rbac之rule详细讲解
  • 初识 webpack
  • 大数据与云计算学习:数据分析(二)
  • 工作中总结前端开发流程--vue项目
  • 配置 PM2 实现代码自动发布
  • 七牛云假注销小指南
  • 浅谈Kotlin实战篇之自定义View图片圆角简单应用(一)
  • 王永庆:技术创新改变教育未来
  • 验证码识别技术——15分钟带你突破各种复杂不定长验证码
  • 原创:新手布局福音!微信小程序使用flex的一些基础样式属性(一)
  • 如何正确理解,内页权重高于首页?
  • ​​​​​​​GitLab 之 GitLab-Runner 安装,配置与问题汇总
  • ​软考-高级-系统架构设计师教程(清华第2版)【第9章 软件可靠性基础知识(P320~344)-思维导图】​
  • #pragma once
  • #我与Java虚拟机的故事#连载18:JAVA成长之路
  • (1)常见O(n^2)排序算法解析
  • (附源码)php投票系统 毕业设计 121500
  • (南京观海微电子)——I3C协议介绍
  • (强烈推荐)移动端音视频从零到上手(上)
  • (数据结构)顺序表的定义
  • (一)C语言之入门:使用Visual Studio Community 2022运行hello world
  • (一)Thymeleaf用法——Thymeleaf简介
  • (转)Linux下编译安装log4cxx
  • (转)关于多人操作数据的处理策略
  • (最优化理论与方法)第二章最优化所需基础知识-第三节:重要凸集举例
  • .axf 转化 .bin文件 的方法
  • .net MySql
  • .net 后台导出excel ,word
  • .net 简单实现MD5
  • @DateTimeFormat 和 @JsonFormat 注解详解