美文网首页
Service概述

Service概述

作者: WhenMeet | 来源:发表于2023-02-27 10:35 被阅读0次

    知识点列表

    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方法

    相关文章

      网友评论

          本文标题:Service概述

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