Android四大组件之一 Service

一、service主要分为两种状态

服务可以长时间运行在后台,没有图形界面的应用组件;可以后台播放音乐、下载文件等

  • 启动状态:启动之后就会在后台无限运行,除非手动关闭,并且不会有返回值
  • 绑定状态:通过bindService()绑定服务,可以进行交互,可以多个组件同时绑定到该服务,全部解绑之后服务将被注销。

二、启动状态

1.声明与文件清单

<service android:name="MyService"
    android:enabled="true"
    android:exported="true"
    android:process=":remote">
</service>

exported:能否被其他应用隐式调用,enabled是否可以创建实例,要注意设置运行的进程process,还有权限android:permission、android:label、android:isolatedProcess等。

2.MainActivity.class开启、关闭服务

package com.mrdouya.myservicetest;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;



public class MainActivity extends Activity {

    private Button btStrService;
    private Button btCloService;
    private Intent intent;

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

        btStrService = (Button) findViewById(R.id.bt_start_service);
        btCloService = (Button) findViewById(R.id.bt_close_service);

        intent = new Intent(MainActivity.this,MyService.class);

        btStrService.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //启动服务
                startService(intent);
            }
        });

        btCloService.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //关闭服务
                stopService(intent);
            }
        });
    }
}

3.创建服务创建

继承Service、一个构造方法、几个必要个方法覆盖(理解注释Override的意思,被标记未这样有什么作用,将会被怎样调用?)

package com.mrdouya.myservicetest;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.util.Log;

public class MyService extends Service {

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

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.d("MyService","onBind!");
        return null;
    }

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

    @Override
    public boolean onUnbind(Intent intent) {
        Log.d("MyService","onUnbind!");
        return super.onUnbind(intent);
    }

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

4.生命周期

生命周期按以下代码顺序执行,必然实现onCreate、onStartCommand两个方法;除此之外还有很多方法stopSelf()、onRebind()、onConfigurationChanged()、onLowMemory()等等

2019-01-03 08:26:06.676 1068-1089/system_process I/ActivityManager: Start proc 8815:com.mrdouya.myservicetest:remote/u0a93 for service com.mrdouya.myservicetest/.MyService
2019-01-03 08:26:07.226 8815-8815/? D/MyService: onCreate!
2019-01-03 08:26:07.228 8815-8815/? D/MyService: onStartCommand!
2019-01-03 08:26:20.451 8815-8815/com.mrdouya.myservicetest:remote D/MyService: onDestroy!

注意:

1)onCreate()、onStartCommand(Intent intent, int flags, int startId)onCreate()只会在第一次启动Service时调用,之后启动都不会调用该方法onStartCommand(Intent intent, int flags, int startId)每次启动都会调用它,主要在这个方法中执行一些逻辑

2)onStartCommand(Intent intent, int flags, int startId)的参数以及返回值intent是启动服务时传递过来的,activity可以封装信息传递给服务startId表示当前服务的唯一ID,配合方法stopSelfResult (int startId)使用flags表示启动请求时是否有额外的数据,它与onStartCommand的返回值相关联,关系如下:

  • 0:表示没有返回值
  • START_FLAG_REDELIVERY:表示返回值为START_REDELIVER_INTENT,内存不足会被回收,在回收之前会调用stopSelf()停止服务,之后会重建服务,并传递最后一个intent
  • START_FLAG_RETRY:表示onStartCommand如果一直没有返回值,则会尝试重新调用该方法onStartCommand

返回值,3种:

  • START_STICKY:因内存不足时被杀死后系统空闲时会重新创建Service,并且会回调onStartCommand,但其中的intent为空,除非有挂起的Intent
  • START_NOT_STICKY:服务被杀死之后不会重新创建服务,除非重新调用startService方法。
  • START_REDELIVER_INTENT:当Service因内存不足而被系统kill后,则会重建服务,并通过传递给服务的最后一个Intent 调用 onStartCommand(),任何挂起 Intent均依次传递

三、绑定状态

服务处于绑定状态时,形成客户端-服务器,Service相当于其中的服务器。Activity与Service绑定时,Acticity可以调用Service的方法、传递请求数据、实现进程通信等等。绑定服务的生命周期会随着服务解绑毁灭。服务的绑定服务需要我们提供一个 IBinder接口的实现类,该类用以提供客户端用来与服务进行交互的编程接口,该接口可以通过三种方法定义接口:

1.扩展接口Binder

适用于应用自身使用的服务,可以防止其他应用程序的调用;实现过程:在Service中定义MyBinder类继承Binder,MyBinder类即可以给服务类提供binder对象,又可以给客户端提供服务的实列,实现客户端和服务端的交互!客户端通过ServiceConnection的onServiceConnect方法获取到服务的对象和对应的binder对象;然后通过binderService绑定服务,服务对象调用服务的公共方法;unbinderServie解绑服务。

服务类:

package com.mrdouya.myservicetest;

import android.app.Service;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Binder;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.util.Log;

public class MyBinderService extends Service {
    private final String TAG = "MrDouYa";
    private boolean quit = true;
    
    @Override
    public void unbindService(ServiceConnection conn) {
        Log.d(TAG,"unbindService!");
        super.unbindService(conn);
    }

    /**
     *创建内部类继承Binder
     *给服务端MyBinderService提供Binder对像
     *给客户端提供MyBinderService的对象
     */
    public class MyBinder extends Binder{
        //获取MyBinderService的对象
        MyBinderService getService(){
            Log.d(TAG,"MyBinder return MyBinderService");
            return MyBinderService.this;
        }
    }

    //创建MyBinder的对象并提供给MyBinderService绑定
    private MyBinder myBinder = new MyBinder();
    private int iCount = 0;
    private Thread MyThread = null;

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG,"onBind return myBinder");
        //绑定binder
        return myBinder;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG,"onCreate!");
        MyThread = new Thread(){
            @Override
            public void run() {
                while(quit){
                    try {
                        iCount++;
                        sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

            }
        };
        MyThread.start();
    }

    @Override
    public void onDestroy() {
        Log.d(TAG,"onDestroy!");
        if(MyThread != null){
            MyThread.interrupt();
            MyThread = null;
        }
        quit = false;
        super.onDestroy();
    }

    /**
     *设置一个方法供客户端调用
     */
    public int MyBinderServiceCount(){
        Log.d(TAG,"MyBinderServiceCount' method");
        return iCount;
    }
}

客户端:

package com.mrdouya.myservicetest;

import android.app.Activity;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.view.View;
import android.widget.Button;
import android.util.Log;



public class MainActivity extends Activity implements View.OnClickListener {
    private Button btBinder;
    private Button btUnbinder;
    private Button btGetData;
    private ServiceConnection myServiceConnection;
    private final String TAG = "MrDouYa";
    private MyBinderService myBinderService;
    private Intent myBinderIntent;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        btBinder = (Button) findViewById(R.id.bt_binder_service);
        btUnbinder = (Button) findViewById(R.id.bt_unbinder_service);
        btGetData = (Button)findViewById(R.id.bt_service_get_data);
        btBinder.setOnClickListener(this);
        btUnbinder.setOnClickListener(this);
        btGetData.setOnClickListener(this);
        myBinderIntent = new Intent(this,MyBinderService.class);

        /**
         *ServiceConnection实际是服务中Unbinder方法传入的参数,主要用于服务绑定、解绑定时调用
         *onServiceConnected绑定时调用,可以带到服务端的binder对象、服务的对象
         * onServiceDisconnected只有在服务解绑定时调用
         */
        myServiceConnection = new ServiceConnection() {

            @Override
            public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
                Log.d(TAG,"ServiceConnection onServiceConnected");
                //获取到MyBinder的对象
                MyBinderService.MyBinder myBinder = (MyBinderService.MyBinder) iBinder;
                //通过MyBinder对象获取MyBinderService的对象
                myBinderService = myBinder.getService();
            }

            @Override
            public void onServiceDisconnected(ComponentName componentName) {
                Log.d(TAG,"ServiceConnection onServiceConnected");
                myBinderService = null;
            }
        };
    }


    @Override
    public void onClick(View view) {
        switch (view.getId()){

            case R.id.bt_binder_service :
                Log.d(TAG,"onClick binder service!");
                bindService(myBinderIntent,myServiceConnection,Service.BIND_AUTO_CREATE);
                break;

            case R.id.bt_unbinder_service :
                Log.d(TAG,"onClick unbinder service!");
                if(myBinderService != null) {
                    myBinderService = null;
                    unbindService(myServiceConnection);
                }
                break;

            case R.id.bt_service_get_data :
                if(myBinderService != null) {
                    Log.d(TAG,"onClick use service method ,iCount = "+myBinderService.MyBinderServiceCount());
                }else {
                    Log.d(TAG,"onClick can not use service,you don't binder it");
                }
                break;

        }
    }
}

执行log

2019-01-03 14:49:20.227 23513-23513/com.mrdouya.myservicetest D/MrDouYa: onClick binder service!
2019-01-03 14:49:20.254 23513-23513/com.mrdouya.myservicetest D/MrDouYa: onCreate!
2019-01-03 14:49:20.258 23513-23513/com.mrdouya.myservicetest D/MrDouYa: onBind return myBinder
2019-01-03 14:49:20.269 23513-23513/com.mrdouya.myservicetest D/MrDouYa: ServiceConnection onServiceConnected
2019-01-03 14:49:20.269 23513-23513/com.mrdouya.myservicetest D/MrDouYa: MyBinder return MyBinderService
2019-01-03 14:49:23.148 23513-23513/com.mrdouya.myservicetest D/MrDouYa: MyBinderServiceCount' method
2019-01-03 14:49:23.148 23513-23513/com.mrdouya.myservicetest D/MrDouYa: onClick use service method ,iCount = 3
2019-01-03 14:49:34.519 23513-23513/com.mrdouya.myservicetest D/MrDouYa: MyBinderServiceCount' method
2019-01-03 14:49:34.519 23513-23513/com.mrdouya.myservicetest D/MrDouYa: onClick use service method ,iCount = 15
2019-01-03 14:49:40.810 23513-23513/com.mrdouya.myservicetest D/MrDouYa: onClick unbinder service!
2019-01-03 14:49:40.823 23513-23513/com.mrdouya.myservicetest D/MrDouYa: onDestroy!
2019-01-03 14:49:45.217 23513-23513/com.mrdouya.myservicetest D/MrDouYa: onClick can not use service,you don't binder it

生命周期:onCreate、onBind、unbinder、onDestroy相比未绑定服务,没有执行onStartCommand方法需要注意,服务端怎么获取到Binder对象,怎么给客户端提供服务端的实例;客户端是如何绑定服务,以及获取到服务端的Binder对象,服务端的实例

2.使用Message

适用于自身应用中启动单独的进程的服务以及不同进程间的应用,可实现应用之间的交互。服务端首先定义服务的handler,将handler对象封装在Messenger对象中,在binder的过程返回用Messenger对象getBinder()方法获取的binder对象。在客户端用binderService()、ServiceConnection对象绑定服务,将handler对象封装在Messenger对象中,Messenger对象在ServiceConnection的onServiceConnected方法中绑定服务端的binder,并通过Message进行客户-服务端的交互。

MainActivity类代码:

package com.mrdouya.myservicetest;

import android.app.Activity;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.view.View;
import android.widget.Button;
import android.util.Log;



public class MainActivity extends Activity implements View.OnClickListener {
    
    private Button btMessageBinder;
    private Button btMessageUnBinder;
    private Button btMessageSend;
    private ServiceConnection myMessageServiceConnection;
    private Messenger messenger;
    private boolean mServiceFlg;



    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        btMessageBinder = findViewById(R.id.bt_Message_service_binder);
        btMessageUnBinder = findViewById(R.id.bt_Message_service_unbinder);
        btMessageSend = findViewById(R.id.bt_message_send_service);
        btMessageBinder.setOnClickListener(this);
        btMessageUnBinder.setOnClickListener(this);
        btMessageSend.setOnClickListener(this);

        myMessageServiceConnection =new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
                Log.d(TAG,"ServiceConnection  onServiceConnected!");
                messenger = new Messenger(iBinder);
                mServiceFlg = true;
            }

            @Override
            public void onServiceDisconnected(ComponentName componentName) {
                Log.d(TAG,"ServiceConnection  onServiceDisconnected!");
                messenger = null;
                mServiceFlg = false;
            }
        };
    }


    @Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.bt_Message_service_binder :
                Log.d(TAG,"onClick Message binder service!");
                bindService(new Intent(MainActivity.this,MyMessageService.class),myMessageServiceConnection,Service.BIND_AUTO_CREATE);
                break;

            case R.id.bt_Message_service_unbinder :
                Log.d(TAG,"onClick Message unbinder service!");
                if(mServiceFlg) {
                    unbindService(myMessageServiceConnection);
                    mServiceFlg = false;
                }
                break;

            case R.id.bt_message_send_service :
                    Log.d(TAG,"onClick message send!");
                    sendToService();
                break;

        }
    }

    private void sendToService() {
        if(!mServiceFlg){
            Log.d(TAG,"this is MainActivity,message is not send,mServiceFlg is "+mServiceFlg);
            return;
        }
        Message msg = Message.obtain(null,MyMessageService.MSG_FROM_CLIENT,0,0);
        //把接收服务器端的回复的Messenger通过Message的replyTo参数传递给服务端
        msg.replyTo = mReceivedReplyMessage;
        try {
            messenger.send(msg);
            Log.d(TAG,"this is MainActivity,message is send!");
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    private Messenger mReceivedReplyMessage = new Messenger(new MyReceiverReplyMsgHandler());

    private class MyReceiverReplyMsgHandler extends Handler{

        @Override
        public void handleMessage( Message msg) {
            switch (msg.what){
                case MyMessageService.MSG_FROM_CLIENT:
                    Log.d(TAG,"this is MainActivity,i have received message from Service!");
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }
}

MyMessageService类代码:

package com.mrdouya.myservicetest;

import android.app.Service;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.util.Log;

public class MyMessageService extends Service {

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

    @Override
    public boolean onUnbind(Intent intent) {
        Log.d(TAG,"MyMessageService, onUnbind");
        return super.onUnbind(intent);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG,"MyMessageService, onDestroy");
    }

    static final int MSG_FROM_CLIENT = 1;
    private final String TAG = "MrDouYa";

    /**
     * 内部类MyHander继承Handler
     * 用于接收客户端传来的消息
     * 给mMessenger传入Handler的实例对象
     */
    class MyHander extends Handler{

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case MSG_FROM_CLIENT :
                    Log.d(TAG,"this is MyMessageService, received message from client!");
                    //先将回复的消息封装到Bundle中
                    Bundle mBundle = new Bundle();
                    mBundle.putString("reply","this is MyMessageService,i have reply you!");
                    //获取一个Message对象,将mBundle放入message中
                    Message replyMessage = Message.obtain(null,MyMessageService.MSG_FROM_CLIENT);
                    replyMessage.setData(mBundle);
                    //获取一个Messenger对象,将message中信息回复出去
                    Messenger replyClient = msg.replyTo;
                    try {
                        replyClient.send(replyMessage);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }

    //创建Messenger实例接收Handler对象,并在onBind类里获取一个与客户端绑定的Binder
    final Messenger mMessenger = new Messenger(new MyHander());

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG,"MyMessageService, onBind");
        return mMessenger.getBinder();
    }
}

log结果:

2019-08-24 16:49:29.967 9277-9277/com.mrdouya.myservicetest D/MrDouYa: onClick Message binder service!
2019-08-24 16:49:30.561 9354-9354/com.mrdouya.myservicetest:remote D/MrDouYa: MyMessageService, onCreate
2019-08-24 16:49:30.563 9354-9354/com.mrdouya.myservicetest:remote D/MrDouYa: MyMessageService, onBind
2019-08-24 16:49:30.566 9277-9277/com.mrdouya.myservicetest D/MrDouYa: ServiceConnection  onServiceConnected!
2019-08-24 16:49:35.645 9277-9277/com.mrdouya.myservicetest D/MrDouYa: onClick message send!
2019-08-24 16:49:35.647 9277-9277/com.mrdouya.myservicetest D/MrDouYa: this is MainActivity,message is send!
2019-08-24 16:49:35.648 9354-9354/com.mrdouya.myservicetest:remote D/MrDouYa: this is MyMessageService, received message from client!
2019-08-24 16:49:35.663 9277-9277/com.mrdouya.myservicetest D/MrDouYa: this is MainActivity,i have received message from Service!
2019-08-24 16:49:58.917 9277-9277/com.mrdouya.myservicetest D/MrDouYa: onClick Message unbinder service!
2019-08-24 16:49:58.923 9354-9354/com.mrdouya.myservicetest:remote D/MrDouYa: MyMessageService, onUnbind
2019-08-24 16:49:58.923 9354-9354/com.mrdouya.myservicetest:remote D/MrDouYa: MyMessageService, onDestroy

注意:

1)app中单独开进程服务,在manifest文件中添加属性android:process=":remote",在1中使用该属性会在onServiceConnected中出现异:java.lang.ClassCastException:android.os.BinderProxy cannot be cast to …;
2)在sendToService()方法中调用的

        //把接收服务器端的回复的Messenger通过Message的replyTo参数传递给服务端
        msg.replyTo = mReceivedReplyMessage;

四、绑定服务注意点

1.多个客户端绑定同一个服务时,只有在第一个客户端绑定时,系统才会调用服务的 onBind() 方法来检索 IBinder,便可将同一 IBinder 传递至任何其他绑定的客户端。当最后一个客户端取消与服务的绑定时,系统会将服务销毁(除非 startService() 也启动了该服务)。

2.在客户端生命周期设置绑定和取消绑定操作,以便控制绑定状态下的Service,一般有以下两种情况:

  • 如果只需要在 Activity 可见时与服务交互,则应在 onStart() 期间绑定,在 onStop() 期间取消绑定。
  • 如果希望Activity 在后台停止运行状态下仍可接收响应,则可在 onCreate() 期间绑定,在 onDestroy()期间取消绑定。需要注意的是,这意味着 Activity在其整个运行过程中(甚至包括后台运行期间)都需要使用服务,因此如果服务位于其他进程内,那么当提高该进程的权重时,系统很可能会终止该进程。

3.通常情况下(注意),切勿在 Activity 的 onResume() 和 onPause() 期间绑定和取消绑定,因为每一次生命周期转换都会发生这些回调,这样反复绑定与解绑是不合理的。此外,如果应用内的多个 Activity 绑定到同一服务,并且其中两个 Activity 之间发生了转换,则如果当前 Activity 在下一次绑定(恢复期间)之前取消绑定(暂停期间),系统可能会销毁服务并重建服务,因此服务的绑定不应该发生在 Activity 的 onResume() 和 onPause()中。

4.我们应该始终捕获 DeadObjectException异常,该异常是在连接中断时引发的,表示调用的对象已死亡,也就是Service对象已销毁,这是远程方法引发的唯一异常,DeadObjectException继承自RemoteException,因此我们也可以捕获RemoteException异常。

5.应用组件(客户端)可通过调用 bindService() 绑定到服务,Android 系统随后调用服务的 onBind() 方法,该方法返回用于与服务交互的 IBinder,而该绑定是异步执行的。

五、启动服务以及绑定服务转换共存

Android系统只会为Service创建一次实例,所以不管是启动服务还是绑定服务,都是对服务的唯一实例对象进行操作。启动服务、绑定服务的先后顺序会有两种情况:

  • 先绑定后启动:以启动服务状态运行时,绑定服务会转为启动服务,之前绑定的宿主被销毁,服务会运行下去,直到停止服务或者被系统kill
  • 先启动后绑定:绑定后,启动服务不会转化为绑定服务,服务还是与宿主处于绑定状态,宿主被销毁后,服务会运行下去,直到停止服务或者被系统kill

六、前\后台服务与通知以及通知信道

Android 8.0以上系统常见的JE报错,系统不允许在用户不知道的情况下后台启动服务;后台服务必须在通知栏显示通知,这相当于将后台服务转化为前台服务,服务不停止,通知无法取消。要后台启动提供startForegroundService()方法、并且需要在服务端设置通知、调用startForeground()方法

MainActivity类:

package com.mrdouya.myservicetest;

import android.app.Activity;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.view.View;
import android.widget.Button;
import android.util.Log;



public class MainActivity extends Activity implements View.OnClickListener {

    private final String TAG = "MrDouYa";
    private Button btForegroundService;
    private Button bt_stop_Foreground_Service;
    private Intent myForegroundServiceIntent;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btForegroundService = findViewById(R.id.bt_start_Foreground_Service);
        bt_stop_Foreground_Service = findViewById(R.id.bt_stop_Foreground_Service);
        myForegroundServiceIntent = new Intent(MainActivity.this,MyForegroundService.class);
        btForegroundService.setOnClickListener(this);
        bt_stop_Foreground_Service.setOnClickListener(this);
    }


    @Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.bt_stop_Foreground_Service :
                Log.d(TAG,"onClick stop Foreground Service !");
                stopService(myForegroundServiceIntent);
                break;

            case R.id.bt_start_Foreground_Service :
                Log.d(TAG,"onClick Foreground Service!");
                this.startForegroundService(myForegroundServiceIntent);
                break;
        }
    }
}

MyForegroundService后台服务类:

package com.mrdouya.myservicetest;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.support.annotation.Nullable;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.util.Log;

public class MyForegroundService extends Service {

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.d("MrDouYa","MyForegroundService onBind");
        return null;
    }

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

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

    public void startForegroundChannel() {
            Log.d("MrDouYa","MyForegroundService startForegroundChannel");
            String channelId = "startForeground";
            String name = "Foreground chanel";
            NotificationChannel notificationChannel = new NotificationChannel(channelId,name,NotificationManager.IMPORTANCE_LOW);
            notificationChannel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
            NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
            notificationManager.createNotificationChannel(notificationChannel);

            Notification notification = new Notification.Builder(MyForegroundService.this,channelId)
                    .setSmallIcon(R.drawable.ic_launcher_foreground)
                    .setContentTitle("Foreground Service")
                    .setContentText("this is a Foreground Service")
                    .setWhen(System.currentTimeMillis())
                    .build();
            startForeground(1, notification);
    }

}

七、服务与线程的区别与关系

1.服务和线程是两个不同的东西,也没有关系!硬要扯点联系:

  • 服务是四大组件运行在UI线程中。
  • 服务与子线程经常搭配起来,后台做耗时任务。

2.线程是CPU的最小执行单元,常说的主线程就是UI线程。一些注意点就是子线程不能更新UI、主线程不能阻塞、子线程可以后台执行一些耗时操作

3.服务就是无法与用户交互的系统组件。注意后台启动问题、同样不能阻塞进行耗时操作,用于配合子线程后台处理耗时任务

八、生命周期与显(隐)启动

1.生命周期跟Acticity相似,主要是两种状态有不一样的生命周期
2.一般是使用显式启动,隐式启动用于不同应用之间的启动,但是从5.0之后隐式启动被禁止了。

显示启动:

Intent mIntent = new Intent(MainActivity.this,MyService.class);
startService(mIntent);

隐式启动:

Intent mIntent = new Intent();
mIntent.setAction("com.mrdouya.myservicetest.MyForegroundService");
startService(mIntent);

5.0之后会抛出异常,可以查看ContextImpl源码中的validateServiceIntent(Intent service)方法,内容如下:

  • 5.0之前,为打印的log提示:
1572     private void validateServiceIntent(Intent service) {
1573         if (service.getComponent() == null && service.getPackage() == null) {
1574             if (true || getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.KITKAT) {
1575                 Log.w(TAG, "Implicit intents with startService are not safe: " + service
1576                         + " " + Debug.getCallers(2, 3));
1577                 //IllegalArgumentException ex = new IllegalArgumentException(
1578                 //        "Service Intent must be explicit: " + service);
1579                 //Log.e(TAG, "This will become an error", ex);
1580                 //throw ex;
1581             }
1582         }
1583     }
  • 8.0根据SDK版本,打印log或者抛出异常:
1462      private void validateServiceIntent(Intent service) {
1463          if (service.getComponent() == null && service.getPackage() == null) {
1464              if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
1465                  IllegalArgumentException ex = new IllegalArgumentException(
1466                          "Service Intent must be explicit: " + service);
1467                  throw ex;
1468              } else {
1469                  Log.w(TAG, "Implicit intents with startService are not safe: " + service
1470                          + " " + Debug.getCallers(2, 3));
1471              }
1472          }
1473      }

解决方案:

1.设置需要启动服务的包名

Intent mIntent = new Intent();
mIntent.setAction("com.mrdouya.myservicetest.MyForegroundService");
mIntent.setPackage(getPackageName());//使不符合validateServiceIntent中的判断条件
startService(mIntent);

2.转化为显示调用

public static Intent getExplicitIntent(Context context, Intent implicitIntent) {
    // Retrieve all services that can match the given intent
     PackageManager pm = context.getPackageManager();
     List resolveInfo = pm.queryIntentServices(implicitIntent, 0);
     // Make sure only one match was found
     if (resolveInfo == null || resolveInfo.size() != 1) {
         return null;
     }
     // Get component info and create ComponentName
     ResolveInfo serviceInfo = resolveInfo.get(0);
     String packageName = serviceInfo.serviceInfo.packageName;
     String className = serviceInfo.serviceInfo.name;
     ComponentName component = new ComponentName(packageName, className);
     // Create a new intent. Use the old one for extras and such reuse
     Intent explicitIntent = new Intent(implicitIntent);
     // Set the component to be explicit
     explicitIntent.setComponent(component);
     return explicitIntent;
    }

Intent mIntent = new Intent();
mIntent.setAction("com.mrdouya.myservicetest.MyForegroundService");
final Intent serviceIntent=new Intent(getExplicitIntent(this,mIntent));//其实也是给Intent添加包名、类名
startService(serviceIntent);

九、保证服务的生存

1.将onStartCommand() 方法的返回值设为 START_STICKY或START_REDELIVER_INTENT
2.用户通过 settings -> Apps -> Running -> Stop 方式杀死Service 。这个过程会执行Service的生命周期,也就是onDestory方法会被调用,这时便可以在 onDestory() 中发送广播重新启动。也可以使用两个服务相互检测,当其中一个停止,就从另外一个服务启动,循环保证两个服务不死。
3.用户通过 settings -> Apps -> Running -> Force Stop 方式杀死Service 。无解!

参考

文章内容基本出自博客:https://blog.csdn.net/hdhhd/article/details/80612726

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值