美文网首页
Service的简单使用

Service的简单使用

作者: 取了个很好听的名字 | 来源:发表于2019-08-16 15:34 被阅读0次

前言

文本主要介绍Service的简单使用,了解Service的各种服务(前台服务,后台服务(本地服务,远程服务))的创建和使用。

服务类型

服务按照不同的划分标准可以划分为如下几类:
按照是否在前台分为前台服务后台服务
按照是否在本地运行分为本地服务远程服务
按照后台服务能否与其他组件进行通信分为可以通信的服务不可以通信的服务
下面说明一下各种类型的服务。

前台服务

前台服务是可以让用户在通知栏上看到当前服务正在运行,用户可以操作(有的话就是可通信的前台服务,没有的话就是不可通信的前台服务)的服务。实例如下:

activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.zhqy.servicedemo.MainActivity">

    <Button
        android:id="@+id/btn_foreignservice_start"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:text="开启前台服务"
        />
    <Button
        android:id="@+id/btn_foreignservice_stop"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@+id/btn_foreignservice_start"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        android:layout_marginTop="10dp"
        android:text="关闭前台服务"
        />


</android.support.constraint.ConstraintLayout>

MainActivity.java

package com.zhqy.servicedemo;

import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    Button btn_foreignservice;
    Button btn_foreignservice_stop;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btn_foreignservice = findViewById(R.id.btn_foreignservice_start);
        btn_foreignservice_stop = findViewById(R.id.btn_foreignservice_stop);
        btn_foreignservice.setOnClickListener(this);
        btn_foreignservice_stop.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        Intent intent;
        switch (v.getId()) {
            case R.id.btn_foreignservice_start:
                intent = new Intent(this, ForeignService.class);
                startService(intent);
                break;
            case R.id.btn_foreignservice_stop:
                intent=new Intent(this,ForeignService.class);
                stopService(intent);
                break;
        }
    }
}

ForeignService.java

package com.zhqy.servicedemo;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.support.v4.app.NotificationCompat;
import android.support.v4.content.ContextCompat;
import android.util.Log;

/**
 * Created by jackal on 2019/8/16.
 */

public class ForeignService extends Service {
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        Log.e("ForeignService","onCreate");
        super.onCreate();
        Intent pendingIntent=new Intent(this,MainActivity.class);
        pendingIntent.setPackage("com.zhqy.servicedemo");
        PendingIntent activity = PendingIntent.getActivity(this, 0, pendingIntent, 0);
        //构建通知的builder
        NotificationCompat.Builder foreignServiceBuilder = new NotificationCompat.Builder(this, createNotificationChannel(this))
                .setContentTitle("前台服务")
                .setContentText("前台服务内容")
                .setContentIntent(activity)
                .setDefaults(NotificationCompat.DEFAULT_ALL)
                .setColor(ContextCompat.getColor(getApplicationContext(), R.color.colorPrimary))
                .setCategory(Notification.CATEGORY_REMINDER);
        Notification notification = foreignServiceBuilder.build();
        startForeground(1,notification);
    }

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

    @Override
    public void onDestroy() {
        Log.e("ForeignService","onDestroy");
        super.onDestroy();
    }


    public static String createNotificationChannel(Context context) {

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            String channelId = "channelId";
            CharSequence channelName = "channelName";
            String channelDescription ="channelDescription";
            int channelImportance = NotificationManager.IMPORTANCE_DEFAULT;

            NotificationChannel notificationChannel = new NotificationChannel(channelId, channelName, channelImportance);
            // 设置描述 最长30字符
            notificationChannel.setDescription(channelDescription);
            // 该渠道的通知是否使用震动
            notificationChannel.enableVibration(true);
            // 设置显示模式
            notificationChannel.setLockscreenVisibility(NotificationCompat.VISIBILITY_PUBLIC);

            NotificationManager notificationManager =
                    (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
            notificationManager.createNotificationChannel(notificationChannel);

            return channelId;
        } else {
            return null;
        }
    }

}

测试结果:


前台服务.jpg

这里有一个疑问为什么通知不显示通知的标题和内容,该疑问有知道请告诉在下一声,万分感谢。

后台服务

后台服务使我们最常见,使用最广泛的一种服务,示例如下:
BackForwardService.class:

package com.zhqy.servicedemo;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.util.Log;

/**
 * Created by jackal on 2019/8/16.
 */

public class BackForwardService extends Service {


    @Override
    public void onCreate() {
        Log.e("BackForwardService","onCreate");
        super.onCreate();
    }

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

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

    @Override
    public void onDestroy() {
        Log.e("BackForwardService","onDestroy");
        super.onDestroy();
    }
}

测试结果:

08-16 10:54:21.440 19443-19443/com.zhqy.servicedemo E/BackForwardService: onCreate
08-16 10:54:22.091 19443-19443/com.zhqy.servicedemo E/BackForwardService: onStartCommand
08-16 10:54:37.303 19443-19443/com.zhqy.servicedemo E/BackForwardService: onDestroy

这是最简单的后台服务,在onStartCommand方法中执行你需要执行的方法,但开发者无法干预service的代码执行,如果想要服务按照你的想法执行你需要的方法的话,则需要使用可通信的后台服务。

可通信的服务

可通信的后台服务是指在创建服务后的适当时机调用响应的方法去执行相关的代码,比如说播放音乐时按下暂停执行暂停的相关代码,按下播放按钮执行继续播放的相关代码。代码如下:
BackForwardService.class:

package com.zhqy.servicedemo;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.util.Log;

/**
 * Created by jackal on 2019/8/16.
 */

public class BackForwardService extends Service {


    @Override
    public void onCreate() {
        Log.e("BackForwardService","onCreate");
        super.onCreate();
    }

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

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.e("BackForwardService","onBind");
        //返回Binder的子类
        return new MyBinder();
    }

    @Override
    public void onDestroy() {
        Log.e("BackForwardService","onDestroy");
        super.onDestroy();
    }

    @Override
    public boolean onUnbind(Intent intent) {
        Log.e("BackForwardService","onUnbind");
        return super.onUnbind(intent);
    }

    class MyBinder extends Binder{
        //添加你需要执行的方法
        public void talk(){
            Log.e("talk","执行聊天的相关代码");
        }
    }
}

其中MyBinder是Binder的子类,在其中设置我们想要执行的相关方法,在onBind方法中返回该Binder对象。

MainActivity.java:

package com.zhqy.servicedemo;

import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    Button btn_foreignservice;
    Button btn_foreignservice_stop;
    Button btn_backforwardservice_start;
    Button btn_backforwardservice_stop;
    Button btn_backforwardservice_bind;
    Button btn_backforwardservice_unbind;

    private ServiceConnection serviceConnection=new ServiceConnection() {

        //服务绑定时会调用该方法
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //向下转型成MyBinder
            BackForwardService.MyBinder binder= (BackForwardService.MyBinder) service;
            binder.talk();
        }
        //服务解绑时会调用该方法
        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btn_foreignservice = findViewById(R.id.btn_foreignservice_start);
        btn_foreignservice_stop = findViewById(R.id.btn_foreignservice_stop);
        btn_backforwardservice_start = findViewById(R.id.btn_backforwardservice_start);
        btn_backforwardservice_stop = findViewById(R.id.btn_backforwardservice_stop);
        btn_backforwardservice_bind = findViewById(R.id.btn_backforwardservice_bind);
        btn_backforwardservice_unbind = findViewById(R.id.btn_backforwardservice_unbind);

        btn_foreignservice.setOnClickListener(this);
        btn_foreignservice_stop.setOnClickListener(this);
        btn_backforwardservice_start.setOnClickListener(this);
        btn_backforwardservice_stop.setOnClickListener(this);
        btn_backforwardservice_bind.setOnClickListener(this);
        btn_backforwardservice_unbind.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        Intent intent;
        switch (v.getId()) {
            case R.id.btn_foreignservice_start:
                intent = new Intent(this, ForeignService.class);
                startService(intent);
                break;
            case R.id.btn_foreignservice_stop:
                intent = new Intent(this, ForeignService.class);
                stopService(intent);
                break;
            case R.id.btn_backforwardservice_start:
                intent = new Intent(this, BackForwardService.class);
                startService(intent);
                break;
            case R.id.btn_backforwardservice_stop:
                intent = new Intent(this, BackForwardService.class);
                stopService(intent);
                break;
            case R.id.btn_backforwardservice_bind:
                //绑定
                intent=new Intent(this,BackForwardService.class);
                bindService(intent,serviceConnection ,Service.BIND_AUTO_CREATE);
                break;
            case R.id.btn_backforwardservice_unbind:
                //解绑
                intent=new Intent(this,BackForwardService.class);
                unbindService(serviceConnection);
                break;
        }
    }
}

activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.zhqy.servicedemo.MainActivity">

    <Button
        android:id="@+id/btn_foreignservice_start"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:text="开启前台服务"
        />
    <Button
        android:id="@+id/btn_foreignservice_stop"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@+id/btn_foreignservice_start"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        android:layout_marginTop="10dp"
        android:text="关闭前台服务"
        />
    <Button
        android:id="@+id/btn_backforwardservice_start"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@+id/btn_foreignservice_stop"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        android:layout_marginTop="10dp"
        android:text="开启后台服务"
        />
    <Button
        android:id="@+id/btn_backforwardservice_stop"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@+id/btn_backforwardservice_start"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        android:layout_marginTop="10dp"
        android:text="关闭后台服务"
        />
    <Button
        android:id="@+id/btn_backforwardservice_bind"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="绑定服务"
        app:layout_constraintTop_toBottomOf="@+id/btn_backforwardservice_stop"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        android:layout_marginTop="10dp"
        />
    <Button
        android:id="@+id/btn_backforwardservice_unbind"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="解绑服务"
        app:layout_constraintTop_toBottomOf="@+id/btn_backforwardservice_bind"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        android:layout_marginTop="10dp"
        />



</android.support.constraint.ConstraintLayout>

在绑定后台服务时需要传递一个ServiceConnection ,当组件绑定服务时会调用onServiceConnected方法,该方法返回了一个IBinder 的接口实现类的实例,其最终实现就是MyBinder,在下转型后获得了MyBinder的实例(这里我获得了MyBinder后直接调用了talk方法,实际使用时按照时机进行方法的调用),这是就可以在适当的实际调用MyBinde中的方法,当然在不需要服务时记得执行unbindService方法解绑服务。
测试结果如下:

08-16 11:09:55.466 29248-29248/com.zhqy.servicedemo E/BackForwardService: onCreate
08-16 11:09:55.467 29248-29248/com.zhqy.servicedemo E/BackForwardService: onBind
08-16 11:09:55.468 29248-29248/com.zhqy.servicedemo E/talk: 执行聊天的相关代码
08-16 11:10:05.947 29248-29248/com.zhqy.servicedemo E/BackForwardService: onUnbind
08-16 11:10:05.947 29248-29248/com.zhqy.servicedemo E/BackForwardService: onDestroy

本地服务

其实上面所介绍的服务都是本地服务,本地服务是指服务于调用它的组件在同一进程中,如果想要服务运行在一个单独的进程中,需要给服务指定响应的进程名。进程名的可以用 android:process="string"进行指定,顺便介绍一下service的主要属性:

android:enabled="true|false" :服务在app启动时是否自动创建,默认为false。
android:exported="true|false":是否允许其他app调用该服务,默认值为false。
android:permission="string":调用该服务需要的权限,app在调用该服务时应拥有该权限。

远程服务

远程服务是指服务与调用它的组件不在同一个进程中,由于不在同一进程中就形成了进程间通信(IPC),而Andorid提供了AIDL来实现进程间通信。远程服务就是用了AIDL技术来实现不同APP访问同一服务。代码如下:
服务端:
(1)创建AIDL文件


创建AIDL.png
// MyAdilInterface.aidl
package com.zhqy.remoteservicedemo;

// Declare any non-default types here with import statements

interface MyAdilInterface {
    //AIDL 允许使用的数据类型
    //1.基本数据类型
    //2.List,Map,其中的数据必须是AIDL允许使用的数据
    //3.自定义的数据结构,该类必须实现了parcelable接口 需要import导入
    //4.String 和 charSequence
    //5.实现android.os.Parcelable 接口的类(需要导入-import)
    void talk();
}

(2)创建RemoteService

实现实例化AIDL的stub类并实现AIDL接口定义的方法

package com.zhqy.remoteservicedemo;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.util.Log;

/**
 * Created by jackal on 2019/8/16.
 */

public class RemoteService extends Service {

    //实例化AIDL的stub类
    private MyAdilInterface.Stub mBinder = new MyAdilInterface.Stub() {

        //重写接口里定义的方法
        @Override
        public void talk() throws RemoteException {
            Log.e("RemoteService","客户端通过AIDL与远程后台成功通信");
        }
    };

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.e("RemoteService","onBind");
        return mBinder;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        Log.e("RemoteService","onUnbind");
        return super.onUnbind(intent);
    }

    @Override
    public void onCreate() {
        Log.e("RemoteService","onCreate");
        super.onCreate();
    }

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

    @Override
    public void onDestroy() {
        Log.e("RemoteService","onDestroy");
        super.onDestroy();
    }
}

(3)声明RemoteService
需要注意的是要将 android:exported="true",允许service能够被其他APP调用,还需要设置action。

 <service android:name=".RemoteService"
            android:exported="true"
            >
            <intent-filter>
                <action android:name="com.zhqy.remoteservicedemo.MyAdilInterface"></action>
            </intent-filter>
        </service>

服务端设置完成

客户端

(1)首先将服务端的AIDL文件夹赋值过来。


复制AIDL文件夹.png

(2)添加绑定解绑按钮

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.zhqy.remoteclientdemo.MainActivity">

    <Button
        android:id="@+id/btn_remoteservice_bind"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        android:text="绑定远程服务"
        />

    <Button
        android:id="@+id/btn_remoteservice_unbind"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@+id/btn_remoteservice_bind"
        android:layout_marginTop="10dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        android:text="解绑远程服务"
        />

</android.support.constraint.ConstraintLayout>

MainActivity.java
1.获取服务端返回的IBinder并下转型为AIDL接口对象
2.通过Intent指定服务端的服务名称和所在包,进行Service绑定;
3.适当时机进行接口方法的调用(实例中在创建ServiceConnection 时直接调用了,实际情况按照需求调用接口方法)

package com.zhqy.remoteclientdemo;

import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;

import com.zhqy.remoteservicedemo.MyAdilInterface;

public class MainActivity extends AppCompatActivity implements View.OnClickListener{
    Button btn_remoteservice_bind;
    Button btn_remoteservice_unbind;
    private ServiceConnection serviceConnection=new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //使用AIDLService1.Stub.asInterface()方法获取服务器端返回的IBinder对象
            //将IBinder对象传换成了MyAdilInterface接口对象
            MyAdilInterface myAdilInterface = MyAdilInterface.Stub.asInterface(service);
            try {
                //调用方法
                myAdilInterface.talk();
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btn_remoteservice_bind=findViewById(R.id.btn_remoteservice_bind);
        btn_remoteservice_unbind=findViewById(R.id.btn_remoteservice_unbind);
        btn_remoteservice_bind.setOnClickListener(this);
        btn_remoteservice_unbind.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        Intent intent;
        switch(v.getId()){
            case R.id.btn_remoteservice_bind:
                Log.e("MainActivity","绑定服务");
                //参数与服务器端的action要一致,即"服务器包名.aidl接口文件名"
                intent=new Intent("com.zhqy.remoteservicedemo.MyAdilInterface");
                //Android5.0后无法只通过隐式Intent绑定远程Service
                //需要通过setPackage()方法指定包名
                intent.setPackage("com.zhqy.remoteservicedemo");
                //绑定服务
                bindService(intent,serviceConnection, Service.BIND_AUTO_CREATE);
                break;
            case R.id.btn_remoteservice_unbind:
                intent=new Intent();
                intent.setAction("com.zhqy.remoteservicedemo.MyAdilInterface");
                //Android5.0后无法只通过隐式Intent绑定远程Service
                //需要通过setPackage()方法指定包名
                intent.setPackage("com.zhqy.remoteservicedemo");
                //解绑服务
                unbindService(serviceConnection);
                break;
        }
    }
}

测试结果如下:

08-16 15:31:39.190 22424-22424/com.zhqy.remoteclientdemo E/MainActivity: 绑定服务
08-16 15:31:39.199 21818-21818/com.zhqy.remoteservicedemo E/RemoteService: onCreate
08-16 15:31:39.200 21818-21818/com.zhqy.remoteservicedemo E/RemoteService: onBind
08-16 15:31:39.212 21818-21835/com.zhqy.remoteservicedemo E/RemoteService: 客户端通过AIDL与远程后台成功通信

Service使用方法介绍完毕。

相关文章

网友评论

      本文标题:Service的简单使用

      本文链接:https://www.haomeiwen.com/subject/sntfsctx.html