Service概述

2023-02-27  本文已影响0人  WhenMeet

知识点列表

1.生命周期
2.启动方式
3.跨进程通信
4.双向通信

一 生命周期

Service的生命周期跟启动方式有关,启动方式有俩种,startService和bindService,对应生命周期如下


Service生命周期

二 启动方式

startService()

通过startService启动后,service会一直无限期运行下去,只有外部调用了stopService()或stopSelf()方法时,该Service才会停止运行并销毁。

调用startService的时候
第一次调用
构造方法——onCreate()——onStartCommand()

当第二次调用
直接调用onStartCommand()

当调用stopService()的时候
直接调用onDestory()

bindService()

bindService启动的服务和调用者之间是典型的client-server模式。调用者是client,service则是server端。service只有一个,但绑定到service上面的client可以有一个或很多个,这里所提到的client指的是组件,比如某个Activity。

bindService启动服务的生命周期与其绑定的client息息相关,client也可以明确调用Context的unbindService()方法与Service解除绑定。当所有的client与service解除绑定的时候,才会调用onUnbind,然后 Service会自行销毁。

startService和bindService区别

client可以通过IBinder接口获取Service实例,从而实现在client端直接调用Service中的方法以实现灵活交互,这在通过startService方法启动中是无法实现的。

三 跨进程通信

Service跨进程通信,设计到三个东西,Service,Client,AIDL,client通过bindService的方式可以和Service进行AIDL跨进程通信,在Service中定义好AIDL的Stub实现类,通过在Service的onBind回调中返回IBinder对象,client绑定时,获取到该IBinder对象,即可进行传递参数

AIDL文件

在android studio中,app/src/main,下面新建一个aidl文件夹,再在该aidl文件夹下按照包名再新建一个文件夹,这里面用来写aidl文件

interface IMyAidlInterface {

    int calculate(int x, int y);

}

建好之后,build一下,在build/generated/aidl_source_output_dir/debug/out/包名/,路径下,会生成真正用来通讯的文件,这其实就是AIDL的实际通讯过程,android为了简化,只需要我们定义好AIDL接口,自动给我们生成该类,实际上可以直接编写该类就可以进行通讯,里面包含一个内部静态类Stub,我们需要再定义个实现类继承该Stub,如下

public class MyAidlServiceImpl extends IMyAidlInterface.Stub {

    private final String TAG = "MyAidlServiceImpl";

    public MyAidlServiceImpl() {
    }

    @Override
    public int calculate(int x, int y) throws RemoteException {
        Log.d(TAG, x + y + "");
        return x + y;
    }

}
Service

Service里面用来通过onBind方法来传递该AIDL,实现通信

public class MyService extends Service {

    private MyAidlServiceImpl service;

    @Override
    public void onCreate() {
        super.onCreate();
        service = new MyAidlServiceImpl();
    }

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

客户端需要通过bindService来获取到Service的IMyAidlInterface,即可调用对应方法实现通信

public class MainActivity extends Activity {

    private final String TAG = "MainActivityInformation";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.method_one).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                connect();
                if (aidlInterface == null) {
                    return;
                }
                try {
                    Toast.makeText(MainActivity.this, "计算成功: " + aidlInterface.calculate(10, 20), Toast.LENGTH_SHORT).show();
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    private IMyAidlInterface aidlInterface;

    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            aidlInterface = IMyAidlInterface.Stub.asInterface(service);
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(MainActivity.this, "连接成功", Toast.LENGTH_SHORT).show();
                }
            });
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            aidlInterface = null;
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(MainActivity.this, "连接断开", Toast.LENGTH_SHORT).show();
                }
            });
        }
    };

}

四 双向通信

单向通信指的是,数据只能从client流向Service,而双向通信则是指数据可以在client和Service之间互相流通,双向通信通过inout和回调监听可以实现双向通信
提供一个用来传递的序列化的类

public class Person implements Parcelable {

    public String name;
    public int age;
    public String describe;

    public Person(){

    }

    ...

    @Override
    public String toString() {
        return "我叫" + name + ",今年" + age + "岁," + describe;
    }

    ...
}

然后定义对应的aidl文件

package visual.share.aidlservice;
parcelable Person;

一个用来监听和回调数据的AIDL

interface IMyAidlInterface {

    void inOutData(String name, int age, String describe, inout Person data);

    void registerCallBack(PersonResultListen listen);

    void unregisterCallBack(PersonResultListen listen);

    void callBackData();

}

一个回调监听

interface PersonResultListen {

    void result(inout Person person);

}

然后来实现IMyAidlInterface的实现类,如下

public class MyAidlServiceImpl extends IMyAidlInterface.Stub {

    private RemoteCallbackList<PersonResultListen> remoteCallbackList = new RemoteCallbackList<>();
    
    @Override
    public void inOutData(String name, int age, String describe, Person data) throws RemoteException {
        data.name = name;
        data.age = age;
        data.describe = describe;
    }

    @Override
    public void registerCallBack(PersonResultListen listen) throws RemoteException {
        if (listen != null) {
            remoteCallbackList.register(listen);
        }
    }

    @Override
    public void unregisterCallBack(PersonResultListen listen) throws RemoteException {
        if (listen != null){
            remoteCallbackList.unregister(listen);
        };
    }

    @Override
    public void callBackData() throws RemoteException {
        synchronized (remoteCallbackList){
            int count = remoteCallbackList.beginBroadcast();
            if (count > 0){
                Person person = new Person();
                person.name = "监听回调";
                person.age = 20;
                person.describe = "监听回调的描述";
                for (int i = 0; i < count; i++) {
                    remoteCallbackList.getBroadcastItem(i).result(person);
                }
            }
            remoteCallbackList.finishBroadcast();
        }
    }
}

这里我们的inOutData方法,里面用到了inout标识,用来实现双向通信,callBackData方法用来实现接口回调,而registerCallBack和unregisterCallBack则是用来注册和取消注册这个监听回调,需要注意的时候,这里使用的是RemoteCallbackList来存储回调队列,这个类专门用来处理跨进程通信的回调
定义一个Service,用来获取我们这个AIDL实现类

public class MyService extends Service {

    private MyAidlServiceImpl service;

    @Override
    public void onCreate() {
        super.onCreate();
        service = new MyAidlServiceImpl();
    }

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

}

然后在应用中就可以直接使用

public class MainActivity extends Activity {

    private final String TAG = "MainActivity";

    private Intent intent;

    @SuppressLint("MissingInflatedId")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        intent = new Intent(MainActivity.this, MyService.class);
        bindService();
        findViewById(R.id.inout_data).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (aidlInterface != null){
                    try {
                        Person person = new Person();
                        aidlInterface.inOutData("小明", 25, "长得很帅",person);
                        Toast.makeText(MainActivity.this, person.toString(), Toast.LENGTH_SHORT).show();
                    } catch (RemoteException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        });
        findViewById(R.id.callback_data).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (aidlInterface != null){
                    try {
                        aidlInterface.callBackData();
                    } catch (RemoteException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        });
    }

    private void bindService(){
        boolean flag = bindService(intent, connection,BIND_AUTO_CREATE);
        Log.d(TAG, "connect status" + flag);
    }

    private IMyAidlInterface aidlInterface;

    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            aidlInterface = IMyAidlInterface.Stub.asInterface(service);
            try {
                aidlInterface.asBinder().linkToDeath(deathRecipient, 0);
            } catch (RemoteException e) {
                throw new RuntimeException(e);
            }
            try {
                aidlInterface.registerCallBack(personResultListen);
            } catch (RemoteException e) {
                throw new RuntimeException(e);
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            aidlInterface = null;
        }
    };

    private PersonResultListen.Stub personResultListen = new PersonResultListen.Stub() {
        @Override
        public void result(Person person) throws RemoteException {
            Toast.makeText(MainActivity.this, person.toString(), Toast.LENGTH_SHORT).show();
        }
    };

    private IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {
        @Override
        public void binderDied() {
            try {
                aidlInterface.unregisterCallBack(personResultListen);
            } catch (RemoteException e) {
                throw new RuntimeException(e);
            }
            if (aidlInterface != null){
                aidlInterface.asBinder().unlinkToDeath(deathRecipient, 0);
            }
            bindService();
        }
    };
}

打开应用后就绑定服务,这里我们添加监听是在绑定成功之后添加,并且使用了死亡代理,Binder.DeathRecipient,当和服务断开的时候,再取消注册的监听,布局如下

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:orientation="vertical"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/inout_data"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginHorizontal="10dp"
        android:text="获取inout数据" />

    <Button
        android:id="@+id/callback_data"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginHorizontal="10dp"
        android:text="获取回调数据" />

</LinearLayout>

这里提供了俩个按钮,点击后,分别获得通过inout处理后的数据,和通过回调返回的数据,实现了双向通信。
最后测试demo结构如下


测试demo

IntentService

Service因为是运行在主线程,不能处理耗时任务,否则会可能会出现ANR问题,IntentService 是继承于 Service 并处理异步请求的一个类,在 IntentService 内有一个子线程来处理耗时操作,启动 IntentService 的方式和启动传统 Service 一样,同时,当任务执行完后,IntentService 会自动停止,而不需要我们去手动控制。

踩坑

1.binderService和unbinderService需要用使用同一个ServiceConnection

如果不使用同一个,当调用unbindService时,会提示Service not registered错误

2.onServiceDisconnected不回调

当bindService后,如果Client和Service连接成功,会调用ServiceConnection的onServiceConnected方法,但是调用unbindService解除绑定时,不会调用onServiceDisconnected方法,这是因为当Client和Service连接后,只有到Service因为异常原因崩溃,才会调用onServiceDisconnected方法

上一篇下一篇

猜你喜欢

热点阅读