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

Posted 双木青橙

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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中的数据和方法

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 从而引起来是否存在内存泄漏的问题,通过查看

以上是关于Service学习笔记02-实战 startService 与bindService的主要内容,如果未能解决你的问题,请参考以下文章

Service学习笔记03- 前台服务实战

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

Service学习笔记03- 前台服务实战

Service学习笔记03- 前台服务实战

Service学习笔记01-启动方式与生命周期

Service学习笔记01-启动方式与生命周期