Service学习笔记02-实战 startService 与bindService

0. 前言

1. 启动服务的两种方式区别

启动Service的两种方式是startServicebindService, 区别如下:

  1. 生命周期,startService 调用Service后,会顺序执行onCreate()onStartCommand()方法。且onCreate()只有首次调用startService才会执行,只有当调用stopSelf()或者stopService时Service才会停止运行

    bindService 调用后会依次执行onCreate()onBind()方法,且多次执行bindService时,onCreate()onBind()方法并不会被多次调用,直到调用者的Context被销毁或者调用unBindService,Service才会被销毁。
  2. 关联性,onBind()会返回一个IBinder对象,由此调用者可以与Service进行关联,调用者可以据此获得Service对象,调用Service的方法。而startService只能启动Service服务,然后onCreateonStartCommand()依次执行,而调用者无法与Service产生任何关联。
  3. 跨进程,两种方式都支持跨进程,只要Service 在AndroidManifest.xml的声明的属性值android:exported="false" ,不同的是,无论是进程内还是跨进程,startService调用方式都是一样的,而bindService() 如果是跨进程的,与进程内不一样,要使用AIDL 方式。

2. 启动服务实战

2.1 bindService()启动服务

通过绑定服务的方式可以在客户端获取对应Service的引用,从而完成与Service的交互。主要过程如下图所示:

  • 首先,在继承Service的自定义服务中新建一个继承自IBinder的内部类,在IBinder中获取myService的引用
  • 在服务的onBinder()方法中将Service中的IBinder对象注入(这个方法会在绑定服务成功的时候被调用,客户端可以通过这个方法获得IBinder对象,进而获取Service的引用)
  • 在客户端声明一个ServiceConnection 对象,在ServiceConnection对象的onServiceContected() (这个方法就是在绑定服务成功的时候调用的)可以获得服务中的IBinder对象
  • 通过获得的IBinder对象获取Service引用,可以获取Service中的数据和方法
    binderService方式启动服务流程

2.1.1 代码实战

首先,我们先定义一个TestService,增加myBinder的内部类,在里面通过一个public类返回TestService 对象,然后重写onBind方法将Binder类返回给客户端,这是必要的一步,这样客户端就可以通过bind方式获取到我们的Service

public class TestService extends Service {
    private static final String TAG = "TestService";
    private static final Random generator = new Random();

    //通过binder实现调用client与Service之间的通信
    private MyBinder binder;

    //要想自己的Service 支持bindService的启动方式,就必须在Service的OnBind方法中返回一个IBinder类型的实例
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.i(TAG, "TestService -> onBind,Thread:" + Thread.currentThread().getName());
        return binder;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        Log.i(TAG, "TestService -> onUnbind,Thread:" + intent.getStringExtra("from"));
        return false;
    }

    public class MyBinder extends Binder {
        public TestService getService() {
            return TestService.this;
        }
    }

    @Override
    public void onCreate() {
        Log.i(TAG, "TestService -> onCreate,Thread:" + Thread.currentThread().getName());
        super.onCreate();
        binder=new MyBinder();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i(TAG, "TestService -> onStartCommand,StartId: " + startId + "Thread:" + Thread.currentThread().getName());
        return START_NOT_STICKY;
    }

    @Override
    public void onDestroy() {
        Log.i(TAG, "TestService -> onDestroy,Thread:" + Thread.currentThread().getName());
        super.onDestroy();
    }

    //Service 暴露出去供client调用的公共方法
    public int getRandomNumber() {
        return generator.nextInt();
    }
}

然后在Activity当中通过bindService来启动Service

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private static final String TAG="MainActivity";

    private TestService service=null;

    private boolean isBound=false;

    private ServiceConnection connection=new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder binder) {
            isBound=true;
            TestService.MyBinder myBinder=(TestService.MyBinder)binder;
            service= myBinder.getService();
            Log.i(TAG,"MainActivity onServiceConnected");
            int num=service.getRandomNumber();
            Log.i(TAG,"MainActivity 中调用TestService的getRandomNumber方法,结果:"+num);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            isBound=false;
            Log.i(TAG,"MainActivity onServiceDisconnected");
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.i(TAG,"MainActivity -> onCreate,Thread:"+Thread.currentThread().getName());

        Button bindServiceButton=(Button)findViewById(R.id.btnBindService);
        bindServiceButton.setOnClickListener(this);
    }
    
    @Override
    public void onClick(View v) {
        Log.i(TAG,"onClick has into");
        if(v.getId()==R.id.btnBindService){
            Intent intent=new Intent(this,TestService.class);
            intent.putExtra("from","ActivityA");
            Log.i(TAG,"MainActivity 执行 bindService");
            bindService(intent,connection,BIND_AUTO_CREATE);
        }else if(v.getId()==R.id.btnUnBindService){
            if(isBound){
                Log.i(TAG,"MainActivity 执行 unBindService");
                unbindService(connection);
            }
        }
    }
}    

在MainActivity 当中先是创建一个实现了ServiceConnection 的成员变量connection,里面主要需要重写onServiceConnected方法,在里面去创建IBinder 对象,并且获取Serivice对象

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private static final String TAG="MainActivity";
    private TestService service=null;
    private boolean isBound=false;

    private ServiceConnection connection=new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder binder) {
            isBound=true;
            TestService.MyBinder myBinder=(TestService.MyBinder)binder;
            service= myBinder.getService();
            Log.i(TAG,"MainActivity onServiceConnected");
            int num=service.getRandomNumber();
            Log.i(TAG,"MainActivity 中调用TestService的getRandomNumber方法,结果:"+num);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            isBound=false;
            Log.i(TAG,"MainActivity onServiceDisconnected");
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.i(TAG,"MainActivity -> onCreate,Thread:"+Thread.currentThread().getName());

        Button bindServiceButton=(Button)findViewById(R.id.btnBindService);
        bindServiceButton.setOnClickListener(this);
        Button unBindServiceButton=(Button)findViewById(R.id.btnUnBindService);
        unBindServiceButton.setOnClickListener(this);
        Button startActivityButton=(Button)findViewById(R.id.btnStartActivity);
        startActivityButton.setOnClickListener(this);
        Button coastRandomNumButton=(Button)findViewById(R.id.btnCoastRandomNum);
        coastRandomNumButton.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        Log.i(TAG,"onClick has into");
        if(v.getId()==R.id.btnBindService){
            Intent intent=new Intent(this,TestService.class);
            intent.putExtra("from","ActivityA");
            Log.i(TAG,"MainActivity 执行 bindService");
            bindService(intent,connection,BIND_AUTO_CREATE);
        }else if(v.getId()==R.id.btnUnBindService){
            if(isBound){
                Log.i(TAG,"MainActivity 执行 unBindService");
                unbindService(connection);
            }
        }else if(v.getId()==R.id.btnStartActivity){
            Intent intent=new Intent(this,ActivityB.class);
            Log.i(TAG,"MainActivity 启动ActivityB");
            startActivity(intent);
        }else if(v.getId()==R.id.btnFinish){
            Log.i(TAG,"MainActivity 执行 finish");
            this.finish();
        }else if(v.getId()==R.id.btnCoastRandomNum){
            Log.i(TAG,"MainActivity 执行 coastRandom");
            Log.i(TAG,"random num :"+service.getRandomNumber());
        }
    }

    @Override
    protected void onDestroy() {
        Log.i(TAG,"MainActivity -> onDestroy");
        super.onDestroy();
    }
}
  • 问题1:在同一个Activity 重新执行bindService方法会如何?
    重新执行ServiceConnection的onServiceConnected方法
  • 问题2: bindService 或者unBindService 不成对出现会如何?
    经过验证:重复执行bindService 方法只会执行一次Service的onBind 和onCreate 方法,bindService内部做了单例机制,但是如果多次执行unBindService方法会直接抛异常java.lang.IllegalArgumentException: Service not registered
  • 问题3:为什么已经执行了unBindService方法还是可以引用绑定service 的方法?
    问题3 从而引起来是否存在内存泄漏的问题,通过查看
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值