Android Service 面试那些事

什么是服务

服务是Android中实现程序后台运行的解决方案,它非常适合用于去执行那些不需要和用户交互而且还要求长期运行的任务。并且它是在主线程中工作的。

Service 与 Thread 的区别

Thread:程序执行的最小单元,他是分配cpu的基本单位

Service:是Android的一种机制,服务是运行在主线程上的

虽然Thread和Service都可以在后台处理一些逻辑,都可以没有界面,但是Thread有一个最大的缺点,那就是无法控制。

如果使用Thread创建子线程处理耗时操作的时候,一般这个线程是由Activity控制的,Thread子线程运行是独立于Activity的,一旦Activity退出,没有主动关闭子线程中的 run() 方法,Thread 就不会被结束,一直会运行下去,当Activity关闭后,就丧失了持有对Thread的引用,即无法控制Thread,那么这条线程就变成了“野线程”,就再也没有办法进行监听和控制了。

而Service不同,Service一旦创建,就会一直运行在后台,Service也是一种轻量级的以Binder为载体的进程间的通信,但是不可以进行耗时操作。

Service 与 Thread 是两个完全不同的东西, 在各自的领域都有自己的生命周期和作用。

Service 的启动方式

Service 的启动方式主要两种:

  • 通过StartService启动Service
  • 通过bindService启动Service

通过StartService启动Service 的Demo

public class ServiceDemoActivity extends AppCompatActivity {
    private static final String TAG = "ServiceDemoActivity";

    @BindView(R.id.bt_bind)
    Button mBtBind;
    private Intent mIntent;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_service_demo);
        ButterKnife.bind(this);

        mIntent = new Intent(this, ServiceDemo.class);
    }

    @OnClick({R.id.bt_start, R.id.bt_stop, R.id.bt_bind, R.id.bt_unbind})
    public void onViewClicked(View view) {
        switch (view.getId()) {
            case R.id.bt_start:
                Intent startIntent = new Intent(this, ServiceDemo.class);
                startService(startIntent);
//                startService(mIntent);
                break;
            case R.id.bt_stop:
                Log.d(TAG, "方法:onViewClicked: 点击了停止服务");
                Intent stopIntent = new Intent(this, ServiceDemo.class);
                stopService(stopIntent);
//                stopService(mIntent);
                break;
        }
    }
}

通过bindService启动Service 的 demo:

public class ServiceDemoActivity extends AppCompatActivity {
    private static final String TAG = "ServiceDemoActivity";

    @BindView(R.id.bt_bind)
    Button mBtBind;
    private Intent mIntent;
    //绑定服务第一步:  先创建服务中的binder对象
    private ServiceDemo mServiceDemo = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_service_demo);
        ButterKnife.bind(this);

        mIntent = new Intent(this, ServiceDemo.class);

    }

    @OnClick({R.id.bt_start, R.id.bt_stop, R.id.bt_bind, R.id.bt_unbind})
    public void onViewClicked(View view) {
        switch (view.getId()) {
         
            case R.id.bt_bind:
                bindService(mIntent, mServiceConnection, Context.BIND_AUTO_CREATE);//绑定服务
                break;
            case R.id.bt_unbind:
                if (mServiceDemo != null) {
                    unbindService(mServiceConnection);
                    mBtBind.setText("绑定服务");
                }
                break;
        }
    }

    ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //绑定服务第二步:  获取到DownLoadService返回的binder对象,并开启服务
            mServiceDemo = ((ServiceDemo.MyBinder) service).startService();

        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    @Override
    protected void onDestroy() {
        if (mServiceDemo != null) {
            unbindService(mServiceConnection);
        }
        super.onDestroy();
    }
}

启动的Service:

public class ServiceDemo extends Service {
    private static final String TAG = "ServiceDemo";
    private Timer mTimer;

    @Override
    public void onCreate() {
        if (mTimer == null) {
            mTimer = new Timer();
        }
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        start();
        Log.d(TAG, "方法:onStartCommand: 开始服务");
        return super.onStartCommand(intent, flags, startId);
    }

    /**
     * @return : void
     * @date 创建时间: 2019/3/25
     * @author lady_zhou
     * @Description 开始服务计数
     */
    private void start() {
        mTimer.schedule(new TimerTask() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    Log.d(TAG, "方法:start: " + i);
                }
            }
        }, 1000, 5000);

    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        bind();
        Log.d(TAG, "方法:onBind: 服务已绑定");
        return new MyBinder();
    }

    private void bind() {
        mTimer.schedule(new TimerTask() {
            @Override
            public void run() {
                    for (int i = 0; i < 10; i++) {
                        Log.d(TAG, "方法:bind: " + i);
                    }
            }
        }, 1000, 5000);
    }

    public class MyBinder extends Binder {
        /**
         * 获取当前Service的实例
         *
         * @return
         */
        public ServiceDemo startService() {
            return ServiceDemo.this;
        }
    }

    @Override
    public void unbindService(ServiceConnection conn) {
        super.unbindService(conn);
    }
    @Override
    public void onDestroy() {
        Log.d(TAG, "方法:onDestroy: 服务销毁");
        if (mTimer != null) {
            mTimer.cancel();
            mTimer = null;
        }
        super.onDestroy();
    }

    @Override
    public void onRebind(Intent intent) {
        Log.d(TAG, "方法:onRebind: 重新绑定");
        bind();
        super.onRebind(intent);
    }
}

Service 的生命周期

从这张图可以看到,Service的生命周期有两种:

  • 调用StartService时Service的生命周期
  • 调用bindService时Service的生命周期

在调用StartService时,Service的生命周期主要是走:

onCreat() -> onStartCommand() -> Service run -> onDestroy()

在调用bindService时,Service的生命周期主要是走:

onCreat() -> onBind() -> Service run -> onUnbind() -> onDestroy()

6.gif
5.gif

根据这两个例子,发现不管是先启动服务再绑定服务还是先绑定服务再启动服务,如果想让服务停下来,那么解绑服务和停止服务都要调用(无先后顺序),否则服务是无法停下来的。

虽然服务是Android中实现程序后台运行的解决方案,但是不建议在Service中编写耗时的逻辑和操作,否则会引起ANR。Android 为了解决这个问题引入了IntentService

IntentService

IntentService

内部有一个工作线程HandlerThread来处理耗时操作(只会执行一个工作线程,执行完后会自动停止)

可以启动IntentService多次,每次耗时操作都会以队列的形式在IntentService的回调方法中执行,并且每次只执行一个工作线程,执行完第一个才会执行第二个,以此类推。

需要实现onHandleIntent()方法,当有多个任务时,会依次实现onHandleIntent()方法

IntentService

  1. IntentService是继承并处理异步请求的一个类
  2. 内有一个工作线程来处理耗时操作
  3. IntentService内部则是通过消息的方式发送给HandlerThread的,然后由Handler中的looper来处理消息

Service保活

  1. 在onStartCommand方法中将flag设置为START_STICKY;
 @Override
    public int onStartCommand(Intent intent, int flags, int startId){
    return Service.START_STICKY;
}

  1. 在AndroidManifest.xml中设置了android:priority
 <!--设置服务的优先级为MAX_VALUE-->
 <service android:name=".MyService"
          android:priority="100000">
 </service>
  1. 在onStartCommand方法中设置为前台进程
 @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
      Notification notification = new Notification(R.mipmap.ic_launcher, "服务正在运行",System.currentTimeMillis());
       Intent notificationIntent = new Intent(this, MainActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,notificationIntent,0);
        RemoteViews remoteView = new RemoteViews(this.getPackageName(),R.layout.notification);
        remoteView.setImageViewResource(R.id.image, R.mipmap.ic_launcher);
        remoteView.setTextViewText(R.id.text , "Hello,this message is in a custom expanded view");
        notification.contentView = remoteView;
        notification.contentIntent = pendingIntent;
        startForeground(1, notification);
        return Service.START_STICKY;

    }
  1. 在onDestroy方法中重启service
@Override
public void onDestroy() {
        super.onDestroy();
        startService(new Intent(this, MyService.class));
}
  1. 用AlarmManager.setRepeating(...)方法循环发送闹钟广播,接收的时候调用service的onstart方法
Intent intent = new Intent(MainActivity.this,MyAlarmReciver.class);
        PendingIntent sender = PendingIntent.getBroadcast( MainActivity.this, 0, intent, 0);

        // We want the alarm to go off 10 seconds from now.
        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(System.currentTimeMillis());
        calendar.add(Calendar.SECOND, 1);
        AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
        //重复闹钟
        /**
         *  @param type
         * @param triggerAtMillis t 闹钟的第一次执行时间,以毫秒为单位
         * go off, using the appropriate clock (depending on the alarm type).
         * @param intervalMillis 表示两次闹钟执行的间隔时间,也是以毫秒为单位
         * of the alarm.
         * @param operation 绑定了闹钟的执行动作,比如发送一个广播、给出提示等等
         */
        am.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), 2 * 1000, sender);
  1. 使用市面上第三方的消息推送SDK唤醒APP,例如 极光Jpush
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容