Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Android四大组件之Service回调补充 #41

Open
soapgu opened this issue May 16, 2021 · 0 comments
Open

Android四大组件之Service回调补充 #41

soapgu opened this issue May 16, 2021 · 0 comments
Labels
Demo Demo 安卓 安卓

Comments

@soapgu
Copy link
Owner

soapgu commented May 16, 2021

  • 前言

前面一篇是Service通过Broadcast来完成通知的功能。如果是Service自己独立来承担通知给调用端是怎样的那。上一篇并没有把这个问题进行到底。

  • 实现方式

我们知道Service是通过Binder这个中间人来完成Service和调用端的交互。那么我直接往接口里面加入回调定义怎么样?

public interface ICounter {
    /**
     * 获取当前计数
     * @return 计数值
     */
    Long getCount();

    void addListener( Consumer<Long> listener );

    void removeListener( Consumer<Long> listener );
}

改造如下,增加 Consumer这个回调。通过addListener和removeListener来加载和卸载回调

调用端实现如下

public class MainActivity extends AppCompatActivity {
        private final Consumer<Long> listener = count -> viewModel.setMessage( String.format( "Count:%s",count ) );

        private final ServiceConnection conn = new ServiceConnection(){
        //当服务被成功绑定的时候调用的方法.
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {//第二个参数就是服务中的onBind方法的返回值
            Logger.i( "-----onServiceConnected----" );
            iService = (ICounter) service;
            iService.addListener( listener );
        }

    @Override
    protected void onStop() {
        super.onStop();
        iService.removeListener( listener );
        unbindService(conn);
        iService = null;
    }

}

实现Consumer
在ServiceConnected时完成addListener
在onStop完成removeListener

  • 要解决的隐患

看客户端调用主要增加的回调卸载,和注销广播接收器一样。
但是对服务端来说解决的隐藏更复杂一些

  1. Service端拿到了调用端回调引用,必然有内存泄漏的隐患
  2. Service会对调用端的回调集合操作,会引发并发问题

MyCountService的修改如下(只部分代码)

public class MyCountService extends Service {
     private final MyBinder binder = new MyBinder();
    private final List<WeakReference<Consumer<Long>>> listeners = new CopyOnWriteArrayList<>();

    @Override
    public IBinder onBind(Intent intent) {
        Logger.i( "------MyCountService onBind-------" );
        return binder;
    }

   private void StartCountEngine() {
        disposables.add(
                Observable.interval( 3 , TimeUnit.SECONDS )
                        .subscribe( t -> {
                                    countValue = t;
                                    Logger.i( "<<<<<Sent Broadcasts" );
                                    if( !listeners.isEmpty() ) {
                                        listeners.removeIf(w -> w.get() == null);
                                        if( !listeners.isEmpty() )
                                            listeners.forEach(w -> {
                                                Consumer<Long> listener = w.get();
                                                if( listener != null )
                                                    listener.accept( t );
                                            });
                                    }
                                } ,
                                e -> Logger.e( e, "On Error" ),
                                ()-> Logger.i("Stop Engine"))
        );

    }

    private class MyBinder extends Binder implements ICounter {

        @Override
        public Long getCount() {
            return countValue;
        }

        @Override
        public void addListener(Consumer<Long> listener) {
            listeners.add( new WeakReference<>(listener) );
        }

        @Override
        public void removeListener(Consumer<Long> listener) {
            listeners.removeIf( w-> w.get() == listener );
        }
    }
}

1 使用CopyOnWriteArrayList来存储回调对象解决集合并发性问题
2 使用WeakReference来解决,调用端忘记注销回调,不会影响调用端的GC。

  • 总结

还是配合Broadcast来通知更方便更轻松。有现成的组件不用何必再多此一举那。

github上的相关代码
CountService(callback分支)

@soapgu soapgu added Demo Demo 安卓 安卓 labels May 16, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Demo Demo 安卓 安卓
Projects
None yet
Development

No branches or pull requests

1 participant