Android 面试3——Service

本文参考Android Service两种启动方式详解(总结版)写成,在此致谢!

1. Service 的两种启动方式

1. startService()
完整生命周期函数如下:

 onCreate() -> onStartCommand -> onDestroy() 

调用 onStartService() 方法启动后,该 Service会一直运行下去,直到调用 *stopService() * 或 stopSelf(),该 Service 才会停止运行并被销毁。

  • onCreate()
  1. Service 没有被创建,在调用 startService() 时会调用该方法
  2. Service 已经在运行,在调用 startService() 时不会调用该方法
  • onStartCommand()
    如果多次执行 startService() 方法,当前方法也会被多次调用,我们在该方法中根据传入的参数进行实际的操作。
  • onDestroy()
    在 Service 被销毁时执行该方法。

startService() 实例

  1. 创建 Service 实例
package com.example.servicetest;

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

public class ServiceByStart extends Service {
    public ServiceByStart() {
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.i("karl", "onCreate - Thread ID = "+ Thread.currentThread().getId());
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i("karl", "onStartCommand -startId = "+ startId +", Thread ID = "+ Thread.currentThread().getId());
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public IBinder onBind(Intent intent) {
        Log.i("karl", "onBind Thread ID = "+ Thread.currentThread().getId());
        return null;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.i("karl", "onDestroy Thread ID = "+ Thread.currentThread().getId());
    }
}
  1. 在 Activity 中启动 Service
package com.example.servicetest;

import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.i("karl", "Activity Thread ID = "+ Thread.currentThread().getId());
        Log.i("karl", "before service started");
        Intent intent = new Intent(this, ServiceByStart.class);
        startService(intent);
        Log.i("karl", "after service started");

        stopService(intent);
        Log.i("karl", "after service stopped");
    }
}

2. bindService()
完整生命周期函数如下:

onCreate() -> onBind() -> onUnbind() -> onDestroy()
  1. bindService() 启动的服务与调用者之间的关系是 client-server 关系。Service 只能有一个,但绑定在 Service 上的 client 可以有若干个。这里的 client 指组件,比如某个 Activity 等。
  2. client 可以通过 IBinder 接口获取到 Service 实例,从而实现在 client 中调用 Service 中自定义方法的需求,实现灵活交互。
  3. bindService() 方式启动的服务,其生命周期与和其绑定的 client 息息相关。当 client 被销毁时,会自动解除与相应 Service 的绑定,client 也可以调用 unbindService() 方法,显式解除绑定。当没有任何 client 与 Service 绑定时,Service 就会被销毁。

bindService 代码实例

问题:创建一个 Sevice,然后创建两个 Activity MainActivity 与 TestActivity,从 MainActivity 跳转到 TestActivity

  1. 创建 bind 方式的 Service
    要想让 Service 支持 bindService() 方式,需要做以下工作:
  • 在 Sevice 的 onBind() 方法中返回 IBinder 类型的实例
  • onBind() 方法中返回的 IBinder 实例中,要能够返回当前 Service 实例。最简单的方法就是在 service 中创建 Binder 的自定义内部类,加入类似 getService() 的方法返回 Service 实例
package com.example.servicetest;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;

import java.util.Random;

public class ServiceByBind extends Service {
    public ServiceByBind() {
    }

    // client 通过 Binder 实例获取到 Service 实例
    public class MyBinder extends Binder {
        public ServiceByBind getService() {
            return ServiceByBind.this;
        }
    }

    // 通过 Binder 实例,实现 client 与 server 之间的通信
    private Binder binder = new MyBinder();

    private final Random random = new Random();

    @Override
    public void onCreate() {
        super.onCreate();
        Log.i("karl", "onCreate - Thread ID = "+ Thread.currentThread().getId());
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i("karl", "onStartCommand -startId = "+ startId +", Thread ID = "+ Thread.currentThread().getId());
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public IBinder onBind(Intent intent) {
        Log.i("karl", "onBind Thread ID = "+ Thread.currentThread().getId());
        return binder;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        Log.i("karl", "onUnbind from : "+ intent.getStringExtra("from"));
        return false;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.i("karl", "onDestroy Thread ID = "+ Thread.currentThread().getId());
    }

    /**
     * 自定义方法,供外界调用
     * @return 一个随机的正整数
     */
    public int getRandomInt() {
        return random.nextInt();
    }

}
  1. client 端调用
    client 端需要做如下工作:
  • 创建 ServiceConnection 实例,并重写 onServiceConnected() 方法和 onServiceDisconnected() 方法
  • 当执行到 onServiceConnected() 方法时,通过 IBinder 实例获取到 Service 实例,实现 client 端与 server 端的连接
  • 当执行到 onServiceDisconnected() 方法时,client 端与 server 端断开连接

MainActivity.java 如下:

package com.example.servicetest;

import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity implements View .OnClickListener{

    private Button bind, unbind, start, finish;
    private ServiceByBind byBind = null;
    private boolean isBind = false;

    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            isBind = true;
            ServiceByBind.MyBinder myBinder = (ServiceByBind.MyBinder) service;
            byBind = myBinder.getService();
            Log.i("karl", "main - service bound ComponentName:"+ name.getShortClassName());
            int num = byBind.getRandomInt();
            Log.i("karl", "main - random int = "+ num);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            isBind = false;
            Log.i("karl", "main service unbound ComponentName:"+ name.getShortClassName());
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.i("karl", "main - onCreate() Thread ID : "+ Thread.currentThread().getId());

/*

        Log.i("karl", "Activity Thread ID = "+ Thread.currentThread().getId());
        Log.i("karl", "before service started");
        Intent intent = new Intent(this, ServiceByStart.class);
        startService(intent);
        Log.i("karl", "after service started");

        stopService(intent);
        Log.i("karl", "after service stopped");
*/
        initView();

    }

    private void initView() {
        bind = findViewById(R.id.bind);
        unbind = findViewById(R.id.unbind);
        start = findViewById(R.id.startActivity);
        finish = findViewById(R.id.finish);
    }

    @Override
    protected void onResume() {
        super.onResume();
        bind.setOnClickListener(this);
        unbind.setOnClickListener(this);
        start.setOnClickListener(this);
        finish.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.bind:
                Intent intent = new Intent(this, ServiceByBind.class);
                intent.putExtra("from", "main");
                Log.i("karl", "=============================");
                Log.i("karl", "main bindService");
                bindService(intent, connection, BIND_AUTO_CREATE);
                break;
            case R.id.unbind:
                if(isBind) {
                    Log.i("karl", "main unbindService");
                    unbindService(connection);
                }
                break;
            case R.id.startActivity:
                Intent intent1 = new Intent(this, TestActivity.class);
                Log.i("karl", "start activity test");
                startActivity(intent1);
                break;
            case R.id.finish:
                Log.i("karl", "main finished");
                finish();
                break;
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.i("karl", "main destroyed");
    }
}

TestActivity.java 如下:

package com.example.servicetest;

import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;

public class TestActivity extends AppCompatActivity implements View .OnClickListener{

    private ServiceByBind byBind = null;
    private boolean isBind = false;
    private Button bind, unbind, finish;


    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            isBind = true;
            ServiceByBind.MyBinder myBinder = (ServiceByBind.MyBinder) service;
            byBind = myBinder.getService();
            Log.i("karl", "test - service bound ComponentName:"+ name.getShortClassName());
            int num = byBind.getRandomInt();
            Log.i("karl", "test - random int = "+ num);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            isBind = false;
            Log.i("karl", "test service unbound ComponentName:"+ name.getShortClassName());
        }
    };


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

        initView();
    }

    private void initView() {
        bind = findViewById(R.id.bind);
        unbind = findViewById(R.id.unbind);
        finish = findViewById(R.id.finish);
    }

    @Override
    protected void onResume() {
        super.onResume();
        bind.setOnClickListener(this);
        unbind.setOnClickListener(this);
        finish.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.bind:
                Intent intent = new Intent(this, ServiceByBind.class);
                intent.putExtra("from", "test");
                Log.i("karl", "=============================");
                Log.i("karl", "test bindService");
                bindService(intent, connection, BIND_AUTO_CREATE);
                break;
            case R.id.unbind:
                if(isBind) {
                    Log.i("karl", "test unbindService");
                    unbindService(connection);
                }
                break;
            case R.id.finish:
                Log.i("karl", "test finished");
                finish();
                break;
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.i("karl", "test onDestroy");
    }
}

2. 如何保证 Service 不被销毁

  1. 调整 onStartCommand() 方法中的返回值
  • START_NOT_STICKY
    表示当 Service 运行的进程被 Android 系统强制杀掉之后,不会重新创建该 Service。
  • START_STICKY
    表示当 Service 运行的进程被 Android 系统强制销毁后,会重建 Service,但 onStartCommand() 方法中 intent 参数值为 null。
  • START_REDELIEVER_INTENT
    表示当 Service 运行的进程被 Android 系统强制销毁后,会重建 Service,并且在销毁当前 Service 之前保存 intent 参数的参数值。
  1. 提高 Service 的优先级
    AndroidManifest.xml 文件中的 标签中可以通过 android:priority = "1000" 这个属性设置最高优先级,1000 是最高值,如果数字越小则优先级越低,同时适用于广播。
  2. 提升 Service 进程的优先级
    可以使用 startForeground() 将 service 放到前台状态,这样低内存时,被杀死的概率会低一些。
  3. onDestroy() 方法中重启 Service
    当 service 走到 onDestroy() 时,发送一个自定义广播,当收到广播时,重新启动 service。

你可能感兴趣的:(Android 面试3——Service)