震惊!Android-IPC-之Service-还可以这么理解(1)

1、Activity无法直接(间接通过广播等方法)拿到Service计数结果,也就是说没法拿到Service引用。
2、当Activity退出的时候,若不是主动停止Service,那么Service将不会被关闭。不太恰当的比喻是:“管生不管养”

而绑定开启Service正好可以解决上面的问题。
为了实现绑定开启Service,在上面Demo的基础上稍微做修改。

定义MyBinder继承自Binder:

public class MyBinder extends Binder {
//持有Service引用
private Service service;
public MyBinder(Service service) {
this.service = service;
}

//返回Service引用
public Service getService() {
return service;
}
}

在显示开启Service过程中,我们重写了onBind(xx),直接返回的是null,该场景下该方法并没有调用。而当绑定开启Service时,需要返回IBiner的引用给绑定者使用。

#MyService.java
public IBinder onBind(Intent intent) {
return new MyBinder(this);
}

返回的是MyBinder对象的引用,该对象持有了MyService引用。
绑定者在哪里接收IBinder的引用呢?
在Activity里定义ServiceConnection匿名内部类:

ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//service即是从onBind(xx)方法返回的(绑定者和Service同一进程)
MyBinder myBinder = (MyBinder)service;
//获取Service的引用
MyService myService = (MyService)myBinder.getService();
}

@Override
public void onServiceDisconnected(ComponentName name) {
//Service被销毁时调用(内存不足等,正常解绑不会走这)
}
};

有了ServiceConnection 引用,接着就需要和Service建立联系,建立联系的过程即是绑定开启Service的过程。

private void bindService() {
Intent intent = new Intent(this, MyService.class);
bindService(intent, serviceConnection, BIND_AUTO_CREATE);
}

从上面可以看出,绑定开启的流程:

1、将Service和ServiceConnection建立联系(绑定开启)
2、在Service的onBind(xx)方法里返回IBinder引用,该引用持有Service引用
3、绑定成功后会调用ServiceConnection的onServiceConnected(xx)返回IBinder引用
4、通过IBinder引用就能拿到Service引用,进而操作Service

以上回答了上面的第一个问题:绑定者(Activity)无法拿到Service引用。
来看看绑定开启Service调用方法流程:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

解绑Service

既然绑定时传入了ServiceConnection引用,可以猜测解绑时也需要传入ServiceConnection引用,不然无法确定解绑哪个Service。

private void unBindService() {
unbindService(serviceConnection);
}

手动调用该方法即可解绑Service。
当Activity绑定开启Service后,若是Activity销毁了,那么相应的Service也会被销毁掉。这就解答了第二个问题。

值得注意的是:
若是显示开启了Service,则无法用解绑方法关闭Service。
若是绑定开启了Service,则无法用显示关闭Service方法。

2、Service 执行耗时操作

在Service的onCreate(xx)方法里循环计数:

#MyService.java
@Override
public void onCreate() {
super.onCreate();

while(true) {
count++;
try {
Thread.sleep(1000);
} catch (Exception e) {

}
}
}

服务开启后,过一会就会提示ANR错误,说明onCreate(xx)是在主线程执行的。
来看看onCreate(xx)调用栈。

#ActivityThread.java
private class ApplicationThread extends IApplicationThread.Stub {

//IPC通信调用该方法,表明要创建服务
public final void scheduleCreateService(IBinder token,
ServiceInfo info, CompatibilityInfo compatInfo, int processState) {
updateProcessState(processState, false);
CreateServiceData s = new CreateServiceData();
s.token = token;
s.info = info;
s.compatInfo = compatInfo;
//发送Message
sendMessage(H.CREATE_SERVICE, s);
}

}

//中间调用省略
private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {

Message msg = Message.obtain();
msg.what = what;
msg.obj = obj;
msg.arg1 = arg1;
msg.arg2 = arg2;
if (async) {
msg.setAsynchronous(true);
}
//mH 为在ActivityThread 里构造的Handler,也就是说在主线程里构造的Handler。
mH.sendMessage(msg);
}

再看看接收Message的地方:

#ActivityThread.java
public void handleMessage(Message msg) {
switch (msg.what) {

case CREATE_SERVICE:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, ("serviceCreate: " + String.valueOf(msg.obj)));
//创建Service
handleCreateService((CreateServiceData) msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case BIND_SERVICE:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, “serviceBind”);
//绑定Service
handleBindService((BindServiceData) msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case UNBIND_SERVICE:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, “serviceUnbind”);
handleUnbindService((BindServiceData) msg.obj);
//解绑Service
schedulePurgeIdler();
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;

}
}

以上分支最终会调用到MyService重写的onCreate(xx)、onBind(xx)、onUnbind(xx)里,这些方法都是在主线程里被调用的。
既然Service各个方法是在主线程里执行,那么想要实现计数功能得子开启线程来完成此事。

#MyService.java
@Override
public void onCreate() {
super.onCreate();

new Thread(new Runnable() {
@Override
public void run() {
while(true) {
Log.d(“time:”, count + “”);
count++;
try {
Thread.sleep(1000);
} catch (Exception e) {

}
}
}
}).start();
}

public int getCount() {
return count;
}

Service一直在计数,计数结果怎么通知给调用者呢,此处假设调用者是Activity。
依然是两种方式:

1、如果是显示开启的Service,则Service可选择广播将数据发送给Activity
2、如果是绑定开启的Service,Activity拿到IBinder引用,进而拿到Service引用,最终可以调用getCount()获得计数值,并更新UI。

当然如果觉得每次开启子线程很麻烦,可以选择Android 提供的IntentService,该类里封装了HandlerThread,并提供回调方法用以执行耗时任务。

3、Service 与Thread、Manager关系

Service 与 Thread联系

既然Service无法直接执行耗时操作,那么需要Service干嘛呢,还不如直接开启子线程执行任务呢?
前面说了:Service是长时间在后台运行。
实际上说的是Service的生命周期,也就是说Service对象一直存在,当我们需要使用Service的时候,通过Intent或者IBinder找到它,进而使用它提供的功能。
同样实现计数功能:

  • 如果直接在Activity里开启Thread计数,当Activity退出的时候,要把Thread关闭了。再次开启Activity的时候,已经找不到Thread引用了,无法继续上次的累计计数。再者,就算不考虑内存泄漏,Activity退出时候不关闭Thread,再次开启Activity的时候,依然找不到Thread引用。
  • 另外如果想将计数功能抽出来,供多个Activity使用,直接使用Thread也无法实现多Activity共用计数功能。

上面问题的本质就是需要维护一个对Thread对象的引用。而Thread仅仅是个工具而已,没必要维护全局的引用(那是线程池要做的工作)。

Service 与 Manager

既然维护Thread全局引用方法不太推荐,那么实现一个单例的Manager(管理类)来持有Thread,进而使用Thread执行耗时任务,而外界通过调用这个Manager来获取数据,如下:

public class CountManager {
private static volatile CountManager instance;

private CountManager(){
startCount();
}

private int count = 0;

public static CountManager getInstance() {
if (instance == null) {
synchronized (CountManager.class) {
if (instance == null) {
instance = new CountManager();
}
}
}

return instance;
}

private void startCount() {
new Thread(new Runnable() {
@Override
public void run() {
while(true) {
Log.d(“time:”, count + “”);
count++;
try {
Thread.sleep(1000);
} catch (Exception e) {

}
}
}
}).start();
}

public int getCount() {
return count;
}
}

事实上不少的项目都是采用Activity + Manager方式来实现页面展示 + 数据获取。
Activity展示UI,后台通过Manager获取数据,如从数据库获取或者从从网络获取等,最后将数据反馈给Activity用以刷新UI。
到此你可能疑惑了,都有了Manager了,Service还有使用的必要吗?
答案是肯定的。
Service作为Android 四大组件之一,是广泛使用于Android 系统里的。

1、Service可以调整优先级,尽可能避免在资源紧张的时候被销毁
2、借助Service + Binder,实现Android 进程间通信

4、Service 进程间通信初相识

之前的例子分析的都是调用者和被调用处于同一进程,试想一下,如果它们不在同一进程还能互相调用吗?如进程A里的Activity需要使用进程B里的CountManager,显然无法直接调用。
而对于Service来说,借助于Binder可以实现此功能。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Android高级架构师

由于篇幅问题,我呢也将自己当前所在技术领域的各项知识点、工具、框架等汇总成一份技术路线图,还有一些架构进阶视频、全套学习PDF文件、面试文档、源码笔记。

  • 330页PDF Android学习核心笔记(内含上面8大板块)

  • Android学习的系统对应视频

  • Android进阶的系统对应学习资料

  • Android BAT部分大厂面试题(有解析)

好了,以上便是今天的分享,希望为各位朋友后续的学习提供方便。觉得内容不错,也欢迎多多分享给身边的朋友哈。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!
droid学习的系统对应视频**

  • Android进阶的系统对应学习资料

[外链图片转存中…(img-TXLOfdrF-1715425373157)]

  • Android BAT部分大厂面试题(有解析)

[外链图片转存中…(img-niwwovQ9-1715425373157)]

好了,以上便是今天的分享,希望为各位朋友后续的学习提供方便。觉得内容不错,也欢迎多多分享给身边的朋友哈。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值