Android--- Service

什么是 service

  • A Service is an application component that can perform long-running operations in the background
  • service没有UI
  • Additionally, a component can bind to a service to interact with it and even perform interprocess communication (IPC)
  • For example, a service can handle network transactions, play music, perform file I/O, or interact with a content provider, all from the background

注意

  • service不是一个进程或者一个线程, 它是在应用主进程中运行的组件;
  • 开发者可以在service中创建新的进程来处理耗时的活动, 来避免Application Not Responding (ANR) errors

后台服务 — background Service

  • 在后台运行的service
  • 后台服务在系统内存不足的时候,可能会被系统杀死

startService()

  • caller: startService() ---- invoke — calledService: onStartCommand()
  • 需要传入intent
Intnet sIntent = new Intent(this, MyService.class)
startService(sIntent)

onStartCommand()

  • 当一个component调用startService之后会启动service中的onStartCommand()
public int onStartCommand(Intent intent, int flags, int startId)
  • intent: 启动时,启动组件传递过来的Intent,如Activity可利用Intent封装所需要的参数并传递给Service
  • flags: 表示启动请求时是否有额外数据,可选值有 0,START_FLAG_REDELIVERY,START_FLAG_RETRY
  • 0, 在正常创建Service的情况下,onStartCommand传入的flags为0。
  • START_FLAG_REDELIVERY 这个值代表了onStartCommand()方法的返回值为 START_REDELIVER_INTENT,而且在上一次服务被杀死前会去调用stopSelf()方法停止服务。其中START_REDELIVER_INTENT意味着当Service因内存不足而被系统kill后,则会重建服务,并通过传递给服务的最后一个 Intent调用 onStartCommand(),此时Intent时有值的。
  • START_FLAG_RETRY, 该flag代表当onStartCommand()调用后一直没有返回值时,会尝试重新去调用onStartCommand()
  • startId : 指明当前服务的唯一ID,与stopSelfResult(int startId)配合使用,stopSelfResult() 可以更安全地根据ID停止服务

返回值

  • START_STICKY
    当Service因内存不足而被系统kill后,一段时间后内存再次空闲时,系统将会尝试重新创建此Service,一旦创建成功后将回调onStartCommand方法,但其中的Intent将是null,除非有挂起的Intent,如pendingintent,这个状态下比较适用于不执行命令、但无限期运行并等待作业的媒体播放器或类似服务。
  • START_NOT_STICKY
    当Service因内存不足而被系统kill后,即使系统内存再次空闲时,系统也不会尝试重新创建此Service。除非程序中再次调用startService启动此Service,这是最安全的选项,可以避免在不必要时以及应用能够轻松重启所有未完成的作业时运行服务。
  • START_REDELIVER_INTENT
    当Service因内存不足而被系统kill后,则会重建服务,并通过传递给服务的最后一个 Intent 调用 onStartCommand(),任何挂起 Intent均依次传递。与START_STICKY不同的是,其中的传递的Intent将是非空,是最后一次调用startService中的intent。这个值适用于主动执行应该立即恢复的作业(例如下载文件)的服务。

stopService(sIntent)

  • caller: stopService() ---- invoke — calledService: onDestroy()
  • 需要传入intent
Intnet sIntent = new Intent(this, MyService.class)
stopService(sIntent)

onDestroy()

  • 当一个component调用stopService之后会启动service中的onDestroy()
@Override
    public void onDestroy() {
        super.onDestroy();
        Log.i(TAG, "on destroy");
    }

前台服务 — foreground service

  • 后台服务在系统内存不足的时候,可能会被系统杀死. 所以为了保证service不被杀死, 可以使用前台服务
  • 前台服务会显示在通知栏中,展示正在运行的service
  • 通常在onStartCommand() 中调用startForeground()

startForeground()

public int startForeground(Integer NotificationID, Notification notification)

Example:

Intent notificationIntent = new Intent(this, ExampleActivity.class);
PendingIntent pendingIntent =
        PendingIntent.getActivity(this, 0, notificationIntent, 0);

Notification notification =
          new Notification.Builder(this, CHANNEL_DEFAULT_IMPORTANCE)
    .setContentTitle(getText(R.string.notification_title))
    .setContentText(getText(R.string.notification_message))
    .setSmallIcon(R.drawable.icon)
    .setContentIntent(pendingIntent)
    .setTicker(getText(R.string.ticker_text))
    .build();

// Notification ID cannot be 0.
startForeground(ONGOING_NOTIFICATION_ID, notification);

Bound services — 将service和组件绑定

  • 在安卓开发中, 开发者可以将service和别的组件绑定(通常是activity), 这样可以达到service和其他组件沟通的目的(如组件使用service的方法或属性)

首先在Activity中开启service, 会调用service中的onStartCommand:

private void startMyService() {
        Log.i(TAG, "Starting Service");
        Log.i(TAG, "Thread Id: " + Thread.currentThread().getId());
        startService(sIntent);
}
 @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i(TAG, "onStartComment");
        Log.i(TAG, "Thread Id: " + Thread.currentThread().getId() + " Start Id: " +  startId);
        if (!isRandOn) {
            Runnable runnable = () -> {
                Log.i(TAG, "Inside Run");
                isRandOn = true;
                generateRandomNumber();
            };
            Thread t = new Thread(runnable);
            t.start();
        }

        return START_NOT_STICKY; //not recreate the service after the service is crushed
    }

然后在service中使用binder

class MyLocalBinder extends Binder {
     public MyService getMyService() {
        return MyService.this;
     }
 }

private Binder myBinder = new MyLocalBinder();

@Override
public IBinder onBind(Intent intent) {
    // TODO: Return the communication channel to the service.
    Log.i(TAG, "on Bind");
    return myBinder;
}

在Activity中bind service, 通过以下代码拿到service object, 这样可以调用service中的函数

private void bindMyService() {
        if (myConnection == null) {
            Log.i(TAG, "Binding service");
            myConnection = new ServiceConnection() {
                @Override
                public void onServiceConnected(ComponentName name, IBinder ibinder) {
                    Log.i(TAG, "onServiceConnected");
                    MyService.MyLocalBinder myBinder= (MyService.MyLocalBinder) ibinder;
                    myService = myBinder.getMyService();
                    isBound = true;
                }

                @Override
                public void onServiceDisconnected(ComponentName name) {
                    Log.i(TAG, "onServiceDisconnected");
                    isBound = false;
                }
            };
        }
        bindService(sIntent, myConnection, BIND_AUTO_CREATE);
    }
private void getNumber() {
	if (isBound) {
    	tvRandomNumber.setText("Random Number " + myService.getRandomNumber());
    }
}

完整代码

MainActivity.java

package com.project.lab3;

import androidx.appcompat.app.AppCompatActivity;

import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity implements View.OnClickListener{
    private static final String TAG = "MainActivity";

    Button buttonStart, buttonStop;
    Button buttonBind, buttonUnBind;
    Button buttonGetRandomNumber;
    TextView tvRandomNumber;
    Intent sIntent;

    private ServiceConnection myConnection;
    MyService myService;
    boolean isBound;

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

        //initialize UI elements
        buttonStart = findViewById(R.id.btn_start);
        buttonStop = findViewById(R.id.btn_stop);
        buttonBind = findViewById(R.id.btn_bind);
        buttonUnBind = findViewById(R.id.btn_unbind);
        buttonGetRandomNumber = findViewById(R.id.btn_getNumber);
        buttonGetRandomNumber = findViewById(R.id.btn_getNumber);
        tvRandomNumber = findViewById(R.id.tv_random_number);

        //set on click listener
        buttonStart.setOnClickListener(this);
        buttonStop.setOnClickListener(this);
        buttonBind.setOnClickListener(this);
        buttonUnBind.setOnClickListener(this);
        buttonGetRandomNumber.setOnClickListener(this);
        sIntent = new Intent(this, MyService.class);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.btn_start:
                //TODO 1: START A SERVICE
                startMyService();
                break;
            case R.id.btn_stop:
                //TODO 2: STOP A SERVICE
                stopMyService();
                break;
            case R.id.btn_bind:
                //TODO 3: BIND A SERVICE
                bindMyService();
                break;
            case R.id.btn_unbind:
                //TODO 4: UNBIND A SERVICE
                unbindMyService();
                break;
            case R.id.btn_getNumber:
                //TODO 5: GET AND RANDOM NUMBER FROM SERVICE AND DISPLAY IT
                getNumber();
                break;
        }
    }

    private void getNumber() {
        if (isBound) {
            tvRandomNumber.setText("Random Number " + myService.getRandomNumber());
        }
    }

    private void unbindMyService() {
        if (isBound) {
            Log.i(TAG, "Unbinding the service");
            unbindService(myConnection);
            myConnection = null;
            isBound = false;
        }
    }

    private void bindMyService() {
        if (myConnection == null) {
            Log.i(TAG, "Binding service");
            myConnection = new ServiceConnection() {
                @Override
                public void onServiceConnected(ComponentName name, IBinder ibinder) {
                    Log.i(TAG, "onServiceConnected");
                    MyService.MyLocalBinder myBinder= (MyService.MyLocalBinder) ibinder;
                    myService = myBinder.getMyService();
                    isBound = true;
                }

                @Override
                public void onServiceDisconnected(ComponentName name) {
                    Log.i(TAG, "onServiceDisconnected");
                    isBound = false;
                }
            };
        }
        bindService(sIntent, myConnection, BIND_AUTO_CREATE);
    }

    private void stopMyService() {
        Log.i(TAG, "Stopping service");
        stopService(sIntent);
    }

    private void startMyService() {
        Log.i(TAG, "Starting Service");
        Log.i(TAG, "Thread Id: " + Thread.currentThread().getId());
        startService(sIntent);
    }
}

MyService.java

package com.project.lab3;
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 MyService extends Service {
    private static final String TAG = "MyService";
    private boolean isRandOn = false;
    int rNum;
    public MyService() {
    }

    class MyLocalBinder extends Binder {
        public MyService getMyService() {
            return MyService.this;
        }
    }

    private Binder myBinder = new MyLocalBinder();

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        Log.i(TAG, "on Bind");
        return myBinder;
    }

    private void generateRandomNumber() {
        while(isRandOn) {
            try {
                Thread.sleep(1000);
                rNum = new Random().nextInt(1000);
                Log.i(TAG, "Thread Id: " + Thread.currentThread().getId() + " Random Number " + rNum);
            } catch (Exception e) {
                Log.e(TAG, e.getMessage());
            }
        }
    }

    public int getRandomNumber() {
        return rNum;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i(TAG, "onStartComment");
        Log.i(TAG, "Thread Id: " + Thread.currentThread().getId() + " Start Id: " +  startId);
        if (!isRandOn) {
            Runnable runnable = () -> {
                Log.i(TAG, "Inside Run");
                isRandOn = true;
                generateRandomNumber();
            };
            Thread t = new Thread(runnable);
            t.start();
        }

        return START_NOT_STICKY; //not recreate the service after the service is crushed
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.i(TAG, "on destroy");
        isRandOn = false;
    }

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

在这里插入图片描述

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值