IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android--- Service -> 正文阅读

[移动开发]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);
    }
}

在这里插入图片描述

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-04-01 23:32:51  更:2022-04-01 23:34:04 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 19:37:11-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码