Android 之Service使用攻略

Service无法自行启动,访问者启动它的方式分为两种,即startService(非绑定式)和bindService (绑定式),相关介绍如下:

    * startService:即非绑定式。访问者使用这种方式启动service后,service将不受访问者控制,也无法与访问者进行数据通信,它会无限地运行下去,必须调用stopSelf()方法或者其他组件(包括访问者)调用stopService()方法来停止。它的生命周期:onCreate->onStartCommand()->……>onDestory(),其中,onCreate用于初始化工作,多次调用startService启动同一个服务,onCreate方法只会被调用一次,onStartCommand会被调用多次,onDestory在销毁时也只会被调用一次。

    * bindService:即绑定式。访问者使用这种方式启动service后,被启动的service受访问者的控制,访问者将通过一个IBinder接口的对象与被绑定的service进行通信,并且可以通过unbindService()方法随时关闭service。一个service可以同时被多个访问者绑定,只有当多个访问者都主动解除绑定关系之后,系统才会销毁service。它的生命周期:onCreate->onBind->....>onUnbind->onDestory,其中,onBind用于返回一个通信对象(IBinder)给访问者,访问者可以通过该IBinder对象调用service的相关方法。当多个访问者绑定同一个service时,onCreate只会被调用一次,onBind、unOnbind方法会被调用与访问者数目相关的次数,onDestory在销毁时只会被调用一次。


 

startService和bindService,用哪个好?

开启服务时,它们与activity之间的关系:
1、startService开启服务以后,与activity就没有关联,不受影响,独立运行
2、bindService开启服务以后,与activity存在关联,退出activity时必须调用unbindService方法,否则会报ServiceConnection泄漏的错误。
因此,根据实际需求使用最好。


 

Service类若调用startService启动时,有个生命周期方法叫onStartCommand,这个方法有一个整型的返回值:
START_STICKY_COMPATIBILITY
START_STICKY
START_NOT_STICKY
START_REDELIVER_INTENT


默认情况下,当我们的服务因为系统内存吃紧或者其他原因被异常终止时,系统会尝试在某个时刻重新启动服务,

START_NOT_STICKY:服务不会重新创建,除非你再次调用startService

START_STICKY / START_STICKY_COMPATIBILITY:服务重新创建并启动,依次回调onCreate、onStartCommand,如果没有新的intent传给此service,onStartCommand接受的将是一个空的intent

START_STICKY_COMPATIBILITY:START_STICKY的兼容版本,2.0之下使用,它不保证一定调用onStartCommand.

START_REDELIVER_INTENT:服务重新创建并启动,依次回调onCreate,onStartCommand,并且会把最后一次传给此服务的intent重新发给onStartCommand

PS:
1、系统默认是START_STICKY。另外,为app保活着想,也应该设为START_STICKY
2、如果一个service被startService启动,然后其他组件再执行bindService绑定该service,service的
onCreate不会再被回调,而是直接回调onBind方法;当该组件unBindService时,service的onUnbind方法被回调,但是service不会被销毁,直到自己调用stopSelf方法或者其他组件调用stopService方法时才会被销毁。
 

1. Service分类

 

2.2 可通信的服务Service

  • 接下来将在Service的基础用法上,增设“与Activity通信”的功能,即使用绑定Service服务(Binder类、bindService()、onBind()、unbindService()、onUnbind())
  • 步骤1:在新建子类继承Service类,并新建一个子类继承自Binder类、写入与Activity关联需要的方法、创建实例
public class MyService extends Service {

    private MyBinder mBinder = new MyBinder();

    @Override
    public void onCreate() {
        super.onCreate();
        System.out.println("执行了onCreat()");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        System.out.println("执行了onStartCommand()");
        return super.onStartCommand(intent, flags, startId);


    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        System.out.println("执行了onDestory()");
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        System.out.println("执行了onBind()");
        //返回实例
        return mBinder;
    }


    @Override
    public boolean onUnbind(Intent intent) {
        System.out.println("执行了onUnbind()");
        return super.onUnbind(intent);
    }

    //新建一个子类继承自Binder类
    class MyBinder extends Binder {

        public void service_connect_Activity() {
            System.out.println("Service关联了Activity,并在Activity执行了Service的方法");

        }
    }
}
  • 步骤2:在主布局文件再设置两个Button分别用于绑定和解绑Service
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="scut.carson_ho.demo_service.MainActivity">

    <Button
        android:layout_centerInParent="true"
        android:id="@+id/startService"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="启动服务" />

    <Button
        android:layout_centerInParent="true"
        android:layout_below="@+id/startService"
        android:id="@+id/stopService"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="停止服务" />

    <Button
        android:layout_centerInParent="true"
        android:layout_below="@id/stopService"
        android:id="@+id/bindService"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="绑定服务" />

    <Button
        android:layout_centerInParent="true"
        android:layout_below="@id/bindService"
        android:id="@+id/unbindService"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="解绑服务"
        />
</RelativeLayout>

  • 步骤3:在Activity通过调用MyBinder类中的public方法来实现Activity与Service的联系

即实现了Activity指挥Service干什么Service就去干什么的功能

MainActivity.java

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private Button startService;
    private Button stopService;
    private Button bindService;
    private Button unbindService;

    private MyService.MyBinder myBinder;


    //创建ServiceConnection的匿名类
    private ServiceConnection connection = new ServiceConnection() {

        //重写onServiceConnected()方法和onServiceDisconnected()方法
        //在Activity与Service建立关联和解除关联的时候调用
        @Override
        public void onServiceDisconnected(ComponentName name) {
        }

        //在Activity与Service解除关联的时候调用
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //实例化Service的内部类myBinder
            //通过向下转型得到了MyBinder的实例
            myBinder = (MyService.MyBinder) service;
            //在Activity调用Service类的方法
            myBinder.service_connect_Activity();
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);


        startService = (Button) findViewById(R.id.startService);
        stopService = (Button) findViewById(R.id.stopService);

        startService.setOnClickListener(this);
        stopService.setOnClickListener(this);

        bindService = (Button) findViewById(R.id.bindService);
        unbindService = (Button) findViewById(R.id.unbindService);

        bindService.setOnClickListener(this);
        unbindService.setOnClickListener(this);

    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {

            //点击启动Service
            case R.id.startService:
                //构建启动服务的Intent对象
                Intent startIntent = new Intent(this, MyService.class);
                //调用startService()方法-传入Intent对象,以此启动服务
                startService(startIntent);
                break;

            //点击停止Service
            case R.id.stopService:
                //构建停止服务的Intent对象
                Intent stopIntent = new Intent(this, MyService.class);
                //调用stopService()方法-传入Intent对象,以此停止服务
                stopService(stopIntent);
                break;

            //点击绑定Service
            case R.id.bindService:
                //构建绑定服务的Intent对象
                Intent bindIntent = new Intent(this, MyService.class);
                //调用bindService()方法,以此停止服务

                bindService(bindIntent,connection,BIND_AUTO_CREATE);
                //参数说明
                //第一个参数:Intent对象
                //第二个参数:上面创建的Serviceconnection实例
                //第三个参数:标志位
                //这里传入BIND_AUTO_CREATE表示在Activity和Service建立关联后自动创建Service
                //这会使得MyService中的onCreate()方法得到执行,但onStartCommand()方法不会执行
                break;

            //点击解绑Service
            case R.id.unbindService:
                //调用unbindService()解绑服务
                //参数是上面创建的Serviceconnection实例
                unbindService(connection);
                break;

                default:
                    break;

        }
    }
}

2.2.2 Demo

carson.ho的Github地址:Demo_for_Service

 

2.3 前台Service

前台Service和后台Service(普通)最大的区别就在于:

  • 前台Service在下拉通知栏有显示通知(如下图),但后台Service没有;

     
  • 前台Service优先级较高,不会由于系统内存不足而被回收;后台Service优先级较低,当系统出现内存不足情况时,很有可能会被回收

2.3.1 具体使用

用法很简单,只需要在原有的Service类对onCreate()方法进行稍微修改即可,如下图:

@Override
    public void onCreate() {
        super.onCreate();
        System.out.println("执行了onCreat()");

        //添加下列代码将后台Service变成前台Service
        //构建"点击通知后打开MainActivity"的Intent对象
        Intent notificationIntent = new Intent(this,MainActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(this,0,notificationIntent,0);

        //新建Builer对象
        Notification.Builder builer = new Notification.Builder(this);
        builer.setContentTitle("前台服务通知的标题");//设置通知的标题
        builer.setContentText("前台服务通知的内容");//设置通知的内容
        builer.setSmallIcon(R.mipmap.ic_launcher);//设置通知的图标
        builer.setContentIntent(pendingIntent);//设置点击通知后的操作

        Notification notification = builer.getNotification();//将Builder对象转变成普通的notification
        startForeground(1, notification);//让Service变成前台Service,并在系统的状态栏显示出来

    }

2.3.2 测试结果

运行后,当点击Start Service或Bind Service按钮,Service就会以前台Service的模式启动(通知栏上有通知),如下图

3. 使用场景

各种Service的使用场景请看下图:


Service 与 Thread的区别

  • 结论:Service与 Thread无任何关系
  • 之所以有不少人会把它们联系起来,主要因为Service的后台概念

后台:后台任务运行完全不依赖UI,即使Activity被销毁 / 程序被关闭,只要进程还在,后台任务就可继续运行

  • 关于二者的异同,具体如下图:



     
  • 一般会将 Service和 Thread联合着用,即在Service中再创建一个子线程(工作线程)去处理耗时操作逻辑,如下代码:
@Override  
public int onStartCommand(Intent intent, int flags, int startId) {  
//新建工作线程
    new Thread(new Runnable() {  
        @Override  
        public void run() {  
            // 开始执行后台任务  
        }  
    }).start();  
    return super.onStartCommand(intent, flags, startId);  
}  
  
class MyBinder extends Binder {  
    public void service_connect_Activity() {  
  //新建工作线程
        new Thread(new Runnable() {  
            @Override  
            public void run() {  
                // 执行具体的下载任务  
            }  
        }).start();  
    }  
  
}  

 




链接:https://www.jianshu.com/p/d963c55c3ab9
链接:https://www.jianshu.com/p/e04c4239b07e
链接:https://blog.csdn.net/andrexpert/article/details/53485360 
https://www.jianshu.com/p/d870f99b675c

猜你喜欢

转载自blog.csdn.net/LVXIANGAN/article/details/85707072