前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android必知必会的四大组件--Service

Android必知必会的四大组件--Service

作者头像
ClericYi
发布2020-06-23 15:18:07
7690
发布2020-06-23 15:18:07
举报
文章被收录于专栏:ClericYi's Blog

前言

写着一篇文章的原因,主要是因为在面试中,服务这个关键词的出现频率非常高。很多时候,面试官会问你,Service中能否进行耗时操作? 我们稍后就会揭晓那么这个答案。

生命周期

由图中可以直观的看出几点。

使用方法

Service方法需要在AndroidManifest.xml中进行注册

代码语言:javascript
复制
// 第一步:在AndroidManifest.xml中进行注册
<service android:name=".LocalService"/>

// 第二步:启动
① startService(Intent);
② bindService(Intent, ServiceConnection, Int);

// 第三步:解绑(使用方法② 启动时操作)
unBindService(ServiceConnection);

// 第四步:暂停
stopService(Intent);
代码语言:javascript
复制

Activity和Service的通信

ActivityService的通信其实就是基于IBinder来进行实现的。但是IBinder其实是一个接口,对我们而言一般使用他的实现类Binder并通过强制转换来完成操作。

代码语言:javascript
复制
/**
 * Service方法继承
 * onBind()是一个抽象方法。
 */
public class LocalService extends Service {
    private final IBinder binder = new ServiceBinder();

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }

    public class ServiceBinder extends Binder {
        LocalService getLocalService(){
            return LocalService.this;
        }
    }
}

以上代码,是一个用于通信的基础版本。

既然需要通信,那我们总需要知道对方是谁,如果使用的是startService(),上文已经提到他是独立于Activity的,所以势必使用的是bindService()

在上文的使用方法中已经提到了bindService()使用到的参数,IntentServiceConnectionInt

ServiceConnection

代码语言:javascript
复制
/**
 * bindService()方法中的参数之一。
 * 用于对service进行操作
 */
ServiceConnection connection = new ServiceConnection() {
           // Activity和Service绑定时调用
            @Override
            public void onServiceConnected(ComponentName name, IBinder binder) {
                // 基于Binder拿到我们要的Service
                service = ((LocalService.ServiceBinder)binder).getLocalService();
                // 干你需要干的事情
            }
            // Activity和Service解绑时调用
            @Override
            public void onServiceDisconnected(ComponentName name) {
                service = null;
            }
        };

Int

  • BIND_AUTO_CREATE收到绑定需求,如果Service尚未创建,则立即创建。
  • BIND_DEBUG_UNBIND用于测试使用,对unbind调用不匹配的调试帮助。
  • BIND_NOT_FOREGROUND不允许此绑定将目标服务的进程提升到前台调度优先级

这是一个已经存在于Service类中的值,这里并不全部例举,一般来说都是使用BIND_AUTO_CREATE

必须要调用的unbindService(ServiceConnection)。

Q1:为什么我们一定要调用这个方法,如果我们不解绑会出现什么样的问题?

经过测试,Logcat中爆出了这样的错误Activity has leaked ServiceConnection that was originally bound here。也就是说ServiceConnection内存泄漏了。这也是为什么我们一直说需要解绑的原因。

IntentService

代码语言:javascript
复制
public class LocalIntentService extends IntentService {
    /**
     * Creates an IntentService.  Invoked by your subclass's constructor.
     *
     * @param name Used to name the worker thread, important only for debugging.
     */
    public LocalIntentService(String name) {
        super(name);
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        
    }
}

先看一段我们的继承代码,和Service不同的地方就是,必须重写的方法是onHandleIntent(Intent intent)。那我们也和之前一样做一个源码导读好了。

IntentService源码导读

代码语言:javascript
复制
public abstract class IntentService extends Service {
    private volatile Looper mServiceLooper;
    @UnsupportedAppUsage
    private volatile ServiceHandler mServiceHandler;
    private String mName;
    private boolean mRedelivery;
    
    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            onHandleIntent((Intent)msg.obj);
            stopSelf(msg.arg1);
        }
    }
}

其实从整个代码的变量我们已经可以做一个猜测了。Looper+Handler+Service的组成成分。那它的处理过程势必依赖于一个Handler的通信机制。另外看到了ServiceHandler中的stopSelf()方法,我们也就清楚了一个问题为什么我们不需要去控制IntentService的暂停。

接下来从生命周期的角度来看看这个IntentService,因为Binder机制上是一致的,所以分析主线就是onCreate() --> onStartCommand() --> onDestroy()

onCreate()

代码语言:javascript
复制
public void onCreate() {
        // TODO: It would be nice to have an option to hold a partial wakelock
        // during processing, and to have a static startService(Context, Intent)
        // method that would launch the service & hand off a wakelock.

        super.onCreate();
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();

        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }
代码语言:javascript
复制

创建了一个HandlerThread,去初始化了LooperHandler,也就说明服务在内部处理。

onStartCommand()

代码语言:javascript
复制
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        onStart(intent, startId); // 1 -->
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY; // 2 -->
    }

这里出现了两个部分:(1)onStart()方法(2)mRedelivery变量,下面将着重介绍。

onStart()

代码语言:javascript
复制
public void onStart(@Nullable Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }
代码语言:javascript
复制

其他都是和Handler一致的,整体流程也就是Message的数据装载,再通过Handler进行一个发送。

mRedelivery

这个变量是干什么的?

代码语言:javascript
复制
/**
     * Constant to return from {@link #onStartCommand}: if this service's
     * process is killed while it is started (after returning from
     * {@link #onStartCommand}), and there are no new start intents to
     * deliver to it, then take the service out of the started state and
     * don't recreate until a future explicit call to
     * {@link Context#startService Context.startService(Intent)}.  The
     * service will not receive a {@link #onStartCommand(Intent, int, int)}
     * call with a null Intent because it will not be restarted if there
     * are no pending Intents to deliver.
     *
     * <p>This mode makes sense for things that want to do some work as a
     * result of being started, but can be stopped when under memory pressure
     * and will explicit start themselves again later to do more work.  An
     * example of such a service would be one that polls for data from
     * a server: it could schedule an alarm to poll every N minutes by having
     * the alarm start its service.  When its {@link #onStartCommand} is
     * called from the alarm, it schedules a new alarm for N minutes later,
     * and spawns a thread to do its networking.  If its process is killed
     * while doing that check, the service will not be restarted until the
     * alarm goes off.
     */
    public static final int START_NOT_STICKY = 2;

    /**
     * Constant to return from {@link #onStartCommand}: if this service's
     * process is killed while it is started (after returning from
     * {@link #onStartCommand}), then it will be scheduled for a restart
     * and the last delivered Intent re-delivered to it again via
     * {@link #onStartCommand}.  This Intent will remain scheduled for
     * redelivery until the service calls {@link #stopSelf(int)} with the
     * start ID provided to {@link #onStartCommand}.  The
     * service will not receive a {@link #onStartCommand(Intent, int, int)}
     * call with a null Intent because it will only be restarted if
     * it is not finished processing all Intents sent to it (and any such
     * pending events will be delivered at the point of restart).
     */
    public static final int START_REDELIVER_INTENT = 3;
代码语言:javascript
复制

一大段冗长的英文很烦,更何况我也就低分飘过6级的水平呢,哈哈哈哈!!

就不折磨你们了,直接做出一个解释吧。

  • START_NOT_STICKY:默认模式,这是一个容许被杀的模式,随时允许被叫停
  • START_REDELIVER_INTENT:告诉系统在崩溃后重新启动服务,并重新传递在崩溃时存在的意图。

好了,以上基本就是整个IntentService的介绍了,使用方法上来说应该也是比较简单了。

Thread和Service的区别

总结

  1. 在ANR机制中,Service的响应时长不能超过20s,其实也可以比较直观的看出,Service其实并不能进行所谓耗时操作。但是如果加上了Thread进行异步处理,那么其实他还是可以进行耗时操作的。(具体看你怎么进行回答,主要还是一个知识点,Service运行在主线程)
  2. Service存在的原因是Activity是一个频繁会被创建、销毁的组件,虽然我们同样可以通过Thread进行异步操作,但是当Activity实例被销毁时,相应的捆绑在Activity生命周期内的Thread实例我们也没有能力再去寻找了,且如果耗时过长可能会引发内存泄漏。
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-04-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 DevGW 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 必须要调用的unbindService(ServiceConnection)。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档