Android四大组件之Service

2018-02-06  本文已影响11人  AndroidMaster

Service

一、基础知识

1、定义

服务,属于Android中的计算型组件

2、作用

提供需要在后台长期运行的服务(如复杂计算、下载等等)

3、特点

二、相关方法

1、Context相关方法

2、内部自动调用方法(生命周期方法)

在绑定本地Service的情况下,onBind(Intent service)方法返回的IBinder对象会传给ServiceConnection对象的onServiceConnected(ComponentName name, IBinder service)的service参数,IBinder相当于一个代理人的角色,实现互相通信,所以onBind方法不应该返回一个null(一般返回一个继承Binder类的对象,可以操作Service中的内容,一般使用private class指定,里面有多个方法,要暴露什么方法,使用接口去定义),在onServiceConnected方法就可以使用该代理人。作用:暴露一些方法,改变服务的状态

3、onStartCommand(Intent intent, int flags, int startId)

实际上onStartCommand的返回值int类型才是最最值得注意的,它有三种可选值, START_STICKY,START_NOT_STICKY,START_REDELIVER_INTENT,它们具体含义如下:

4、Service生命周期

4.1、单独调用
4.2、混合调用

说明:混合调用时,要两两对应,不要相互嵌套(类似于html标签)
服务只能解绑一次,多次会报错
建议在Activity的onDestroy方法中解绑掉服务
startService用于保证服务的后台运行,bindService用于调用服务的方法

三、Service分类

1、本地Service

这是最普通、最常用的后台服务Service。

1.1、使用步骤

步骤1:新建子类继承Service类
需重写父类的onCreate()、onStartCommand()、onDestroy()和onBind()方法
步骤2:构建用于启动Service的Intent对象
步骤3:调用startService()启动Service、调用stopService()停止服务
步骤4:在AndroidManifest.xml里注册Service

属性说明

2、可通信Service

实例Demo

步骤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:在Activity通过调用MyBinder类中的public方法来实现Activity与Service的联系,即实现了Activity指挥Service干什么Service就去干什么的功能

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;
        }
    }
}

3、前台Service

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

//用法很简单,只需要在原有的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,并在系统的状态栏显示出来
}

4、使用场景

四、其他

1、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();  
    }  
}  

2. IntentService

2.1、定义

Android里的一个封装类,继承四大组件之一的Service

2.2、作用

处理异步请求 & 实现多线程

2.1、使用场景

线程任务需按顺序在后台执行

2.1、使用步骤

步骤1:定义 IntentService的子类

需传入线程名称、复写onHandleIntent()方法

public class myIntentService extends IntentService {

    /** 
    * 在构造函数中传入线程名字
    **/  
    public myIntentService() {
        // 调用父类的构造函数
        // 参数 = 工作线程的名字
        super("myIntentService");
    }

    /** 
     * 复写onHandleIntent()方法
     * 根据 Intent实现 耗时任务 操作
     **/  
    @Override
    protected void onHandleIntent(Intent intent) {

        // 根据 Intent的不同,进行不同的事务处理
        String taskName = intent.getExtras().getString("taskName");
        switch (taskName) {
            case "task1":
                Log.i("myIntentService", "do task1");
                break;
            case "task2":
                Log.i("myIntentService", "do task2");
                break;
            default:
                break;
        }
    }

    @Override
    public void onCreate() {
        Log.i("myIntentService", "onCreate");
        super.onCreate();
    }
    /** 
     * 复写onStartCommand()方法
     * 默认实现 = 将请求的Intent添加到工作队列里
     **/  
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i("myIntentService", "onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        Log.i("myIntentService", "onDestroy");
        super.onDestroy();
    }
}

步骤2:在Manifest.xml中注册服务

<service android:name=".myIntentService">
    <intent-filter >
        <action android:name="cn.scu.finch"/>
    </intent-filter>
</service>

步骤3:在Activity中开启Service服务

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
            // 同一服务只会开启1个工作线程
            // 在onHandleIntent()函数里,依次处理传入的Intent请求
            // 将请求通过Bundle对象传入到Intent,再传入到服务里

            // 请求1
            Intent i = new Intent("cn.scu.finch");
            Bundle bundle = new Bundle();
            bundle.putString("taskName", "task1");
            i.putExtras(bundle);
            startService(i);

            // 请求2
            Intent i2 = new Intent("cn.scu.finch");
            Bundle bundle2 = new Bundle();
            bundle2.putString("taskName", "task2");
            i2.putExtras(bundle2);
            startService(i2);
            startService(i);  //多次启动
        }
    }

测试结果

3、进程优先级

4、AIDL

4.1、简介

为了实现跨进程通信(IPC),实现进程之间的数据交换

4.2、步骤

①:服务端创建.aidl文件
②:服务端创建Service,并在onBind中返回一个IBinder(接口对象(代理人))

IBinder binder = new IMyAidlInterface.Stub() {
    @Override
    public int add(int num1, int num2) throws RemoteException {
         return num1 + num2;
    }
};

③:客户端创建(复制)和服务端一样的.aidl文件(包名也必须一致)
④:客户端创建ServiceConnection的子类,并实现其onServiceConnected(ComponentName name, IBinder service),在方法中(service就是中间人)iMyAdil = IMyAidlInterface.Stub.asInterface(service),客户端可以使用服务端的方法了
⑤:客户端bindService

4.3、AIDL支持的数据类型(支持其实就是定义aidl的时候,参数可以使用的类型)

基本数据类型(除short),String,CharSequence,List(仅支持ArrayList),Map(仅支持HashMap),Parcelable

注意:
①:非基本数据类型,需要用in,out,inout指定数据的走向
②:复杂类型(如Book)必须实现Parcelable,且需要Book.aidl(内容parcelable Book;)—— 包名必须和Book.java相同,无论是否相同的包,都需要导入包
③:AIDL接口中只支持方法,不支持声明静态变量

参考文献

Android四大组件:Service史上最全面解析
Android 多线程 解析:IntentService(含源码解析)

上一篇 下一篇

猜你喜欢

热点阅读