Android Service

Service 简介

ServiceAndroid四大组件之一,一般而言,Activity 用于提供一个可供交互的屏幕界面,以此完成一些任务。而Service则与 Activity 不同,它主要用于执行一些不需要与用户进行交互且需要长时间运行的任务。值得注意是,Service虽然是处理后台任务的,但它仍然运行在主线程中,所以,如果需要执行耗时任务,还需要开启一个子线程,不然会出现 ANR(Application Not Responding)现象。庆幸的是,Android中存在一个 IntentService类,它拥有自己的线程,是独立于 主线程的,所以我们需要使用Service的时候可以直接继承自 IntentService

Service 的两种启动方法

  1. Context.startService()

如果我们需要程序在后台执行一项任务,即使当前用户并没有与程序发生交互,而且这项任务的执行并不需要用户的干预,即不需要与用户发生过多的交互,我们可以选择该方法。

Context.startService()方法调用后,系统会首先调用 ServiceonCreate()方法(第一次运行调用该方法,之后就不再调用),然后调用 onStartCommand(Intent, int, int)。该Service 会一直运行下去,直到 Context.stopService()或者 StopSelf()被调用。

onStartCommand(Intent, int, int) 方法会返回一个整形常量,该常量用于告诉系统如何处理 Service的重启操作,其中三个返回值为

返回 START_STICKY 代表当前系统出于某些原因关闭Service 时,Service 会被重新启动。但是,当系统重启 Service 时, onStartCommand()参数中的Intent会被置为 null

返回START_NOT_STICKY 意味着 Service不会在系统关闭它时重新启动,适合执行一次性的操作 。

返回 START_REDELIVER_INTENT的效果和 START_STICKY 基本一样,但是 onStartCommand()会接收到Service 被销毁之前接收到的最后一个 Intent

  1. Context.bindService()

对应于上一个方法,如果该后台任务需要与用户发生频繁的交互,可以采用该方法,当然这种方法比上一种方法复杂些。

Context.bindService() 方法调用后,如果该 Service 还不存在,那么 首先调用onCreate()方法,然后调用onBind(Intent)方法。 该 Service会一直运行只要Connection 是建立着的。

Service 的使用

1.如果采用的是Context.startService(),简单用法如下:

public class MyService extends Service{

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

@Override
public void onCreate() {
    super.onCreate();
    Log.d("MyService", "onCreate");
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    Log.d("MyService", "onStartCommand");
    return super.onStartCommand(intent, flags, startId);
}
}

然后在Activity 中调用startService(Intent) 即可。

这种方法,我们可以在onCreate()方法中初始化一些我们需要的变量,在 onStartCommand()方法中执行任务,因为该方法 在每次启动服务时都会被调用,而onCreate() 只在服务创建时执行一次。
使用这种方法,当服务完成时,可以使用发送 Broadcast的方法通知Activity已经完成,然后停止该服务。

2.使用 Context.bindService(),简单用法如下

public class MyLocalService extends Service {

    private LocalBinder mLocalBinder = new LocalBinder();

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

    public void doLongRunningOperation() {
        //执行耗时任务
    }

    public class LocalBinder extends Binder {
        public MyLocalService getService() {
            return MyLocalService.this;
        }
    }
}

public class MainActivity extends AppCompatActivity implements ServiceConnection{

    private MyLocalService mLocalService;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    protected void onResume() {
        super.onResume();
        Intent intent = new Intent(this,MyLocalService.class);
        bindService(intent,this,BIND_AUTO_CREATE);
    }

    @Override
    protected void onPause() {
        super.onPause();
        if (mLocalService != null){
            unbindService(this);
        }
    }

    @Override
    public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
        mLocalService = ((MyLocalService.LocalBinder)iBinder).getService();
    }

    @Override
    public void onServiceDisconnected(ComponentName componentName) {
        mLocalService = null;
    }
}

如图示,当在Activity 中调用 bindService() 之后,接着就会调用 Service 中的onBind() 方法,该方法会返回一个IBinderonServiceConnected()方法,然后就可以通过 IBinder 获得该 Service对象,接着就可以像使用一个 普通 Java对象使用它了。

请注意,以上两种方法在执行耗时任务时,都需要手动的去开启一个子线程,为了方便和高效,我们可以使用 IntentService工具类。

IntentService 的使用

IntentServiceService中包装了一个处理后台线程的 Handler,多个调用会被内部的Hnadler放到队列中,所以能确保在同一时间只能有一个 Intent被处理,基于IntentServiceService 会一直处于启动状态,直到队列中没有要处理的任务。

public class MyIntentService extends IntentService{

    public static String ACTION_DOWNLOAD = "com.example.myintentservice.download"; 
public MyIntentService(String name) {
        super(name);
        setIntentRedelivery(false);
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        String action = intent.getAction();
        if (ACTION_DOWNLOAD.equals(action)){
            download();
        }
    }

    private void download(){
        //Download
    }
}

这个时候,我们需要在清单文件中为 Service 添加相应的intent-filter,然后以带有特定actionIntent调用 Context.startService()方法。

Service 的通信

上面已经提到过很多次,如果使用Context.startService(),和 Activity 交互是很不方便的,就好像Activity 提醒 Service你可以运行了,然后Service就开始自顾自的运行,直到运行结束,通过 Broadcast Receiver发送一条广播,告诉Activity我完成任务了,你可以调用 stopService()了。如果仅仅是这样,是很不利于ServiceActivity 之间进行大规模快速更新操作的。那么该怎么办呢?

Context.bindService 天生就是解决这个问题的,谁让咱在创建的时候还能返回一个IBinder呢。 下面就来使用它完成一个下载任务

public class MyLocalService extends Service {

    private LocalBinder mLocalBinder = new LocalBinder();
    private Callback mCallback;

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

    public void doLongRunningOperation() {
        new MyAsyncTask().execute();
    }

    public class LocalBinder extends Binder {
        public MyLocalService getService() {
            return MyLocalService.this;
        }
    }

    public interface Callback{
        public void onOperationProgress(int progress);
        public void onOperationCompleted(boolean complete);
    }

    public void setCallback(Callback callback){
        mCallback = callback;
    }

    private final class MyAsyncTask extends AsyncTask<Void,Integer,Boolean>{

        @Override
        protected Boolean doInBackground(Void... voids) {
            int progress = 0;
            for (progress = 0;progress<=100;progress+=25) {
                publishProgress(progress);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return true;
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            if (mCallback != null){
                mCallback.onOperationProgress(values[0]);
            }
        }

        @Override
        protected void onPostExecute(Boolean aBoolean) {
            if (mCallback != null && aBoolean){
                mCallback.onOperationCompleted(true);
            }
        }
    }
}

public class MainActivity extends AppCompatActivity implements ServiceConnection,MyLocalService.Callback{

    private MyLocalService mLocalService;
    private Button mButton;
    private ProgressDialog mDialog;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mButton = (Button) findViewById(R.id.start);
        mDialog = new ProgressDialog(this);
        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mDialog.setTitle("Download");
                mDialog.show();
                mLocalService.doLongRunningOperation();
            }
        });
    }

    @Override
    protected void onResume() {
        super.onResume();
        Intent intent = new Intent(this,MyLocalService.class);
        bindService(intent,this,BIND_AUTO_CREATE);
    }

    @Override
    protected void onPause() {
        super.onPause();
        if (mLocalService != null){
            mLocalService.setCallback(null);
            unbindService(this);
        }
    }

    @Override
    public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
        mLocalService = ((MyLocalService.LocalBinder)iBinder).getService();
        mLocalService.setCallback(this);
    }

    @Override
    public void onServiceDisconnected(ComponentName componentName) {
        mLocalService = null;
    }

    @Override
    public void onOperationProgress(int progress) {
        mDialog.setMessage(progress+"%");
    }

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

推荐阅读更多精彩内容

  • 前言:本文所写的是博主的个人见解,如有错误或者不恰当之处,欢迎私信博主,加以改正!原文链接,demo链接 Serv...
    PassersHowe阅读 1,392评论 0 5
  • Service 作为android 四大组件之一,主要用于再后台处理一些耗时的逻辑或者去执行一些长期运行的任务。 ...
    ACE_Killer09阅读 576评论 0 4
  • 描述 Service通常总是称之为“后台服务”,其中“后台”一词是相对于前台而言的,具体是指其本身的运行并不依赖于...
    pkqgo阅读 646评论 1 3
  • 要说大学和高中最大的不一样,我体会最深的就是没有同桌了,
    遥远有多远阅读 182评论 0 0
  • 今年六月份,我带着几个学生去北京参加第八届全国青少年文化遗产知识大赛,孩子们一举拿下了两个团体奖,四个个人奖,特别...
    马少军阅读 498评论 0 4