参考文章: Android Service使用详解 (service基础知识猛戳这里)
service概述
官方描述:
A Service is an application component representing either an application’s desire to perform a longer-running operation while not interacting with the user or to supply functionality for other applications to use. Each service class must have a corresponding declaration in its package’s AndroidManifest.xml. Services can be started with Context.startService() and Context.bindService().
一个Service服务是一个应用组件,表示当没有和用户交互时一个程序想要完成的并在后台长期运行的操作,或者一个支持的功能去供其他程序使用。 每一个 Service必须在
AndroidManifest.xml
中进行声明和注册。 Service服务可以通过Context.startService()
或者Context.bindService().
来启动。
说白了,一个Service就是就是在后台运行的一个组件,运行在主线程中
,没有可视化的界面。
service的分类和示例
- 继承类的普通Service(音乐播放器的实现)
- MyService代码
package com.example.www.servicedemo;
import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
public class MyService extends Service {
private final String TAG = "MyService";
private MediaPlayer mediaPlayer;
private int startId;
public enum Control {
PLAY, PAUSE, STOP
}
public MyService() {
}
@Override
public void onCreate() {
if (mediaPlayer == null) {
mediaPlayer = MediaPlayer.create(this, R.raw.happiness);
mediaPlayer.setLooping(false);
}
Log.e(TAG, mediaPlayer.toString());
Log.e(TAG, "onCreate");
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
this.startId = startId;
Log.e(TAG, "onStartCommand---startId: " + startId);
Bundle bundle = intent.getExtras();
if (bundle != null) {
Control control = (Control) bundle.getSerializable("Key");
if (control != null) {
switch (control) {
case PLAY:
play();
break;
case PAUSE:
pause();
break;
case STOP:
stop();
break;
}
}
}
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
Log.e(TAG, "onDestroy");
if (mediaPlayer != null) {
mediaPlayer.stop();
mediaPlayer.release();
}
super.onDestroy();
}
private void play() {
if (!mediaPlayer.isPlaying()) {
mediaPlayer.start();
}
}
private void pause() {
if (mediaPlayer != null && mediaPlayer.isPlaying()) {
mediaPlayer.pause();
}
}
private void stop() {
if (mediaPlayer != null) {
mediaPlayer.stop();
}
stopSelf(startId);
}
@Override
public IBinder onBind(Intent intent) {
Log.e(TAG, "onBind");
throw new UnsupportedOperationException("Not yet implemented");
}
}
- 调用代码:
package com.example.www.servicedemo;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
public class Activity2 extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_demo2);
}
public void playMusic(View view) {
Intent intent = new Intent(this, MyService.class);
Bundle bundle = new Bundle();
bundle.putSerializable("Key", MyService.Control.PLAY);
intent.putExtras(bundle);
startService(intent);
}
public void pauseMusic(View view) {
Intent intent = new Intent(this, MyService.class);
Bundle bundle = new Bundle();
bundle.putSerializable("Key", MyService.Control.PAUSE);
intent.putExtras(bundle);
startService(intent);
}
public void stopMusic(View view) {
Intent intent = new Intent(this, MyService.class);
Bundle bundle = new Bundle();
bundle.putSerializable("Key", MyService.Control.STOP);
intent.putExtras(bundle);
startService(intent);
//或者是直接如下调用
//Intent intent = new Intent(this, MyService.class);
//stopService(intent);
}
}
- 执行逻辑
a.playMusic
方法被调用时, 会开启一个 Service, 一次调用 Service的 onCreate , onStartCommand 方法。
b.pauseMusic
方法被调用时,会 调用 onStartCommand 方法
c.stopMusic
方法被调用时, 会调用 onDestroy 关闭Service,并释放相应的资源
- IntentService 服务队列
这是Service的一个实现类,在一个工作线程中完成逻辑的处理(非主线程),它会将所有的请求放到一个 工作队列中,然后一个一个的处理他们。
- 示例 IntentService
package com.example.www.app2;
import android.app.IntentService;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
public class MyIntentService extends IntentService {
private final String TAG = "MyIntentService";
public MyIntentService() {
super("MyIntentService");
}
@Override
protected void onHandleIntent(Intent intent) {
Bundle bundle = intent.getExtras();
if (bundle != null) {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.e(TAG, bundle.getString("key", "默认值"));
}
}
}
}
- 调用 Service
public void startService(View view) {
Intent intent = new Intent(this, MyIntentService.class);
Bundle bundle = new Bundle();
bundle.putString("key", "当前值:" + i++);
intent.putExtras(bundle);
startService(intent);
}
当中,startService(View view)方法与一个Button绑定,连续快速地多次点击Button,验证IntentService当中的日志是否依次输出,还是交叉着输出。 如图
可以看到 数字是依次一个个的输出,美不是交叉输出的。所以,IntentService是依次一个个的从队列中取出数据来处理。
- 绑定Service(Service和组件进行绑定)
- 示例Service
package com.example.www.app2;
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 MyBindService extends Service {
private IBinder myBinder;
private Random mGenerator;
private final String TAG = "MyBindService";
public class MyBinder extends Binder {
MyBindService getService() {
return MyBindService.this;
}
}
@Override
public void onCreate() {
Log.e(TAG, "onCreate");
myBinder = new MyBinder();
mGenerator = new Random();
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
Log.e(TAG, "onBind");
return myBinder;
}
@Override
public void onDestroy() {
Log.e(TAG, "onDestroy");
super.onDestroy();
}
@Override
public boolean onUnbind(Intent intent) {
Log.e(TAG, "onUnbind");
return super.onUnbind(intent);
}
@Override
public void onRebind(Intent intent) {
Log.e(TAG, "onRebind");
super.onRebind(intent);
}
public int getRandomNumber() {
return mGenerator.nextInt(100);
}
}
- 要绑定的 activity
package com.example.www.app2;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Toast;
public class BindActivity extends AppCompatActivity {
private MyBindService mService;
private boolean mBound = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_layout);
}
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
MyBindService.MyBinder binder = (MyBindService.MyBinder) service;
mService = binder.getService();
mBound = true;
int randomNumber = mService.getRandomNumber();
System.out.println("randomNumber" + randomNumber);
}
@Override
public void onServiceDisconnected(ComponentName name) {
mBound = false;
}
};
public void bindService(View view) {
Intent intent = new Intent(this, MyBindService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
public void unBindService(View view) {
if (mBound) {
unbindService(mConnection);
mBound = false;
}
}
public void getData(View view) {
if (mBound) {
Toast.makeText(this, "获取到的随机数:" + mService.getRandomNumber(), Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "服务未绑定", Toast.LENGTH_SHORT).show();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mBound) {
unbindService(mConnection);
mBound = false;
}
}
}
可以看到, 当Service和 组件进行绑定之后,除了调用 unbindService
可以解除绑定,当Service绑定的所有组件都销毁时,他也会跟着销毁。
- 在前台运行Service
前台服务被认为是用户主动意识到的一种服务,因此在内存不足时,系统也不会考虑将其终止。 前台服务必须在状态栏提供通知,放在“正在进行”标题下方,这意味着除非服务停止或从前台移除,否则不能清除通知
在启动servlet的时候把 Service 和 notification绑定到一起即可。(防止在内存不足的时候应用被杀死)
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e(TAG, "onStartCommand");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel("channel_id", "channel_name",
NotificationManager.IMPORTANCE_DEFAULT);
//是否绕过请勿打扰模式
channel.canBypassDnd();
//闪光灯
channel.enableLights(true);
//锁屏显示通知
channel.setLockscreenVisibility(VISIBILITY_SECRET);
//闪关灯的灯光颜色
channel.setLightColor(Color.RED);
//桌面launcher的消息角标
channel.canShowBadge();
//是否允许震动
channel.enableVibration(true);
//获取系统通知响铃声音的配置
channel.getAudioAttributes();
//获取通知取到组
channel.getGroup();
//设置可绕过 请勿打扰模式
channel.setBypassDnd(true);
//设置震动模式
channel.setVibrationPattern(new long[]{100, 100, 200});
//是否会有灯光
channel.shouldShowLights();
getNotificationManager().createNotificationChannel(channel);
}
Intent nfIntent = new Intent(this, MainActivity.class);
NotificationCompat.Builder notification = new NotificationCompat.Builder(this, "channel_id");
notification.setContentTitle("新消息来了");
notification.setContentText("周末到了,不用上班了");
notification.setSmallIcon(R.mipmap.ic_launcher);
notification.setAutoCancel(true);
notification.setContentIntent(PendingIntent.getActivity(this, 0, nfIntent, 0));
NotificationManager notificationManager = (NotificationManager) getSystemService(getApplication().NOTIFICATION_SERVICE);
notificationManager.notify(1, notification.build());
return super.onStartCommand(intent, flags, startId);
}