Android 面试3——Service

2019-05-13  本文已影响0人  锦岳

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

1. Service 的两种启动方式

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

 onCreate() -> onStartCommand -> onDestroy() 

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

  1. Service 没有被创建,在调用 startService() 时会调用该方法
  2. Service 已经在运行,在调用 startService() 时不会调用该方法

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() 方式,需要做以下工作:
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 端需要做如下工作:

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() 方法中的返回值
  1. 提高 Service 的优先级
    AndroidManifest.xml 文件中的 <intent-filter> 标签中可以通过 android:priority = "1000" 这个属性设置最高优先级,1000 是最高值,如果数字越小则优先级越低,同时适用于广播。
  2. 提升 Service 进程的优先级
    可以使用 startForeground() 将 service 放到前台状态,这样低内存时,被杀死的概率会低一些。
  3. onDestroy() 方法中重启 Service
    当 service 走到 onDestroy() 时,发送一个自定义广播,当收到广播时,重新启动 service。
上一篇 下一篇

猜你喜欢

热点阅读