Android:探究后台:服务 BindService Binder

gitHub:https://github.com/Gong-Shijie/ServiceDemo

服务Service

服务有两种启动方式:

  • startService()方式开始的服务
  • bindService()方式开启的服务

LivaData(Demo中使用到)依赖:

    implementation "android.arch.lifecycle:extensions:1.1.1"
    implementation "android.arch.lifecycle:viewmodel:1.1.1"
    implementation "android.arch.lifecycle:livedata:1.1.1"

启动和特性

服务仍然是运行在主线程,两种服务有着不同的生命周期,也可以搭配一起使用。
两种方法都可以实现数据从活动到服务,通过在intent添加数据,可以在服务内从intent内取出数据。

//在Intent内添加数据就可以在服务内从Intent取出
 startService(serviceIntent); 
 bindService(serviceIntent,serviceConnection,BIND_AUTO_CREATE);

在服务内获取来自intent的数据

    //Bind方式启动服务时候调用,返回IBinder类型的接口供活动调用
    @Override
    public IBinder onBind(Intent intent) {
     // intent即可取出携带数据
        Log.i("gong","sevice bind");
        return randomNumBinder;
    }

    //start方式开始服务时候会调用,如果处理耗时任务需要new线程
    //startID每一次开启一个服务,都会有不同的startID
    //在开启服务时候可以在intent填写数据实现交互
    @Override
    public int onStartCommand(Intent intent, int flags, final int startId) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                Log.i("gong","startID: "+startId);
                startRandomNumberGenerator();
            }
        }).start();
        return START_STICKY;
    }

start方式启动的服务

该方式的特点是和活动之间没有很强的交互性,可以利用LiveData增加数据交互性。
该种方式会调用onStartConmmand(),在该方法内我们实现需要进行的后台服务操作逻辑。
该函数的返回值的类型有三种,适用于不同类型的需求。

官方文档中的介绍

如果在服务中开启了线程,注意在服务Desdroy后,线程不会自动终止,需要自己实现在服务销毁后终止线程。线程会在执行run后终止。


bind方式启动的服务

该方式通过Binder机制,可以实现活动和服务的交互,这种交互很强,包括了数据上的交互和对服务行为上的控制。完全可以通过Binder获得Service实例从而控制调用Service内的方法。

bindService的几种类型和实现

  • Local binding指在应用进程内进行bind
  • Remote binding指跨进程实现的bind
    注意跨进程通信即(IPC)涉及的重要概念:
  • IBinder接口是用来交互的桥梁
  • Messenger继承自Binder也是实现IPC的一种方法
  • AIDL也是用于实现IPC
    local binding

    remote binding

    //Bind方式启动服务时候调用,返回IBinder类型的接口供活动调用
    @Override
    public IBinder onBind(Intent intent) {
        Log.i("gong","sevice bind");
        return randomNumBinder;
    }

    //构造返回的接口传递Service实例
    class RandomNumBinder extends Binder{
        MyService getService(){
            return MyService.this;
        }
    }

绑定端代码

 serviceConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                randomNumBinder = (MyService.RandomNumBinder) service;
                myService =  randomNumBinder.getService();
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {

            }
        };
        bindService(serviceIntent,serviceConnection,BIND_AUTO_CREATE);

服务service生命周期

生命周期

总结

需求选择启动方式

根据具体需要来启动Service,建立service后,可以理解为客户端和服务端。

  • 如果只是需要进行数据上的交互,建议start方式+ LiveData 即可保证service内的数据通过Livedata可以在活动中获取。
  • 如果还需要对服务进行行为控制,强交互,比如控制服务内的函数的调用,那么就需要我使用bindService方式启动服务,还可以搭配start方式后bind根据具体需求。
生命周期的问题:
  • 结束服务:其他组件调用stopService()或者 服务内自行调用stopself()
  • bind的service只有unbind后才可以调用stopservice来结束
  • 可以开启多个服务,调用一次stopService即可结束所有服务
  • bind也可以直接开启服务当所有bind的组件unbind后service就会销毁
  • 如果需要启动多个service可以使用官方文档推荐的方法使用Handler机制来处理
public class HelloService extends Service {
  private Looper mServiceLooper;
  private ServiceHandler mServiceHandler;

  // Handler that receives messages from the thread
  private final class ServiceHandler extends Handler {
      public ServiceHandler(Looper looper) {
          super(looper);
      }
      @Override
      public void handleMessage(Message msg) {
          // Normally we would do some work here, like download a file.
          // For our sample, we just sleep for 5 seconds.
          long endTime = System.currentTimeMillis() + 5*1000;
          while (System.currentTimeMillis() < endTime) {
              synchronized (this) {
                  try {
                      wait(endTime - System.currentTimeMillis());
                  } catch (Exception e) {
                  }
              }
          }
          // Stop the service using the startId, so that we don't stop
          // the service in the middle of handling another job
          stopSelf(msg.arg1);
      }
  }

  @Override
  public void onCreate() {
    // Start up the thread running the service.  Note that we create a
    // separate thread because the service normally runs in the process's
    // main thread, which we don't want to block.  We also make it
    // background priority so CPU-intensive work will not disrupt our UI.
    HandlerThread thread = new HandlerThread("ServiceStartArguments",
            Process.THREAD_PRIORITY_BACKGROUND);
    thread.start();

    // Get the HandlerThread's Looper and use it for our Handler
    mServiceLooper = thread.getLooper();
    mServiceHandler = new ServiceHandler(mServiceLooper);
  }

  @Override
  public int onStartCommand(Intent intent, int flags, int startId) {
      Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();

      // For each start request, send a message to start a job and deliver the
      // start ID so we know which request we're stopping when we finish the job
      Message msg = mServiceHandler.obtainMessage();
      msg.arg1 = startId;
      mServiceHandler.sendMessage(msg);

      // If we get killed, after returning from here, restart
      return START_STICKY;
  }

  @Override
  public IBinder onBind(Intent intent) {
      // We don't provide binding, so return null
      return null;
  }

  @Override
  public void onDestroy() {
    Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
  }
}

使用Handler机制细思极妙,官方文档需要经常查看。


MyServiceDemo代码

MyService.java

public class MyService extends Service {
    private static final int MAX = 100;
    private static final int MIN = 0;


    private MutableLiveData<String> mutableLiveData = new MutableLiveData<>();
    private String logStr = new String();
    private  int mRandomNumber=0;
    private Boolean mIsRandomGeneratorOn = true;
    private IBinder randomNumBinder = new RandomNumBinder();

    //Bind方式启动服务时候调用,返回IBinder类型的接口供活动调用
    @Override
    public IBinder onBind(Intent intent) {
        Log.i("gong","sevice bind");
        return randomNumBinder;
    }

    //构造返回的接口传递Service实例
    class RandomNumBinder extends Binder{
        MyService getService(){
            return MyService.this;
        }
    }

    @Override
    public boolean onUnbind(Intent intent) {
        Log.i("gong","sevice unbind!");
        return super.onUnbind(intent);
    }

    //start方式开始服务时候会调用,如果处理耗时任务需要new线程
    //startID每一次开启一个服务,都会有不同的startID
    //在开启服务时候可以在intent填写数据实现交互
    @Override
    public int onStartCommand(Intent intent, int flags, final int startId) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                Log.i("gong","startID: "+startId);
                startRandomNumberGenerator();
            }
        }).start();
        
        return START_STICKY;
    }
    private void startRandomNumberGenerator(){
        while (mIsRandomGeneratorOn){
            try{
                Thread.sleep(1000);
                if(mIsRandomGeneratorOn){
                    mRandomNumber =new Random().nextInt(MAX)+MIN;
                    logStr = logStr + mRandomNumber+"   ";
                    mutableLiveData.postValue(logStr);
                    Log.i("gong",""+mRandomNumber);
                }
            }catch (InterruptedException e){
            }
        }
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.i("gong","Service create!");
        logStr = "";
        mIsRandomGeneratorOn = true;


    }

    //销毁服务的时候记住关闭线程,线程在执行run内方法后终止
    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.i("gong","Service destroy!");
        mIsRandomGeneratorOn = false;

    }

    public int getmRandomNumber() {
        return mRandomNumber;
    }



    //提供livadata来更新数据到活动界面
    public MutableLiveData<String> getMutableLiveData() {
        return mutableLiveData;
    }
}

MainActivity.java


public class MainActivity extends AppCompatActivity implements View.OnClickListener {


   private TextView textView ;
    private TextView showLog ;
    private  Button startService ;
    private  Button bindService ;
    private  Button unBindService ;
    private Button stopService ;
    private  Button getnumber;
    Intent serviceIntent ;
    private MyService.RandomNumBinder randomNumBinder;
    private MyService myService;
    ServiceConnection serviceConnection;
    Boolean isBindService =false;




    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        iniview();
        serviceIntent = new Intent(this,MyService.class);

    }

    @Override
    public void onClick(View v) {
        switch(v.getId()){
            case R.id.startService:
                startService(serviceIntent);
                break;
            case R.id.bindService:
                bindmService();

                break;
            case R.id.unBindService:
                unbindmService();
                break;
            case R.id.stopService:
                stopService(serviceIntent);
                break;
            case R.id.getnumber:
               setObserver();
                setmText(myService.getmRandomNumber());
                break;
        }
    }

    private void setmText(int getmRandomNumber) {
        textView.setText(getmRandomNumber+"");
    }
    public void iniview(){
        textView = findViewById(R.id.textView);
        showLog = findViewById(R.id.showLog);
        startService = findViewById(R.id.startService);
        bindService = findViewById(R.id.bindService);
        unBindService = findViewById(R.id.unBindService);
        stopService = findViewById(R.id.stopService);
        getnumber = findViewById(R.id.getnumber);
        startService.setOnClickListener(this);
        bindService.setOnClickListener(this);
        unBindService.setOnClickListener(this);
        stopService.setOnClickListener(this);
        getnumber.setOnClickListener(this);
    }
    //bind service获取调动服务的接口myService可以来和Service交互
    public void bindmService(){
         serviceConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                randomNumBinder = (MyService.RandomNumBinder) service;
                myService =  randomNumBinder.getService();
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {

            }
        };
        bindService(serviceIntent,serviceConnection,BIND_AUTO_CREATE);
        isBindService = true;
    }
    public void unbindmService(){
        if(isBindService == true){
            unbindService(serviceConnection);
        }
        isBindService = false;
    }
    //根据Service内的Livedata来更新数据显示到界面
    public void setObserver(){
        myService.getMutableLiveData().observe(this, new Observer<String>() {
            @Override
            public void onChanged(String s) {
                showLog.setText(s);
            }
        });
    }

}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,527评论 5 470
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,314评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,535评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,006评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,961评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,220评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,664评论 3 392
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,351评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,481评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,397评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,443评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,123评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,713评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,801评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,010评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,494评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,075评论 2 341

推荐阅读更多精彩内容