当前位置: 首页 > news >正文

Android四大组件——Service详解

        Service 为后台运行,不可见,没有界面。优先级高于Activity(内存不足时先杀掉Activity),运行在主线程且不能做耗时操作。

一、Service 启动方式

1、startService()

        通过 startService 启动后,service会一直无限期运行下去,当外部调用了 stopService() 或stopSelf() 方法时,该Service才会停止运行并销毁。当系统资源不足时, 会回收一些不重要的service,service 被系统回收也会停止运行并被销毁。

生命周期

onCreate()

1、如果 service 没被创建过,调用 startService() 后会执行 onCreate() 回调;
2、如果 service 已处于运行中,调用 startService() 不会执行 onCreate() 方法。

        此方法适合完成一些初始化工作。

onStartCommand()

        如果多次执行了 startService() 方法,那么 Service 的 onStartCommand() 方法也会相应的多次调用。

onBind()

        Service中的onBind()方法是抽象方法,Service类本身就是抽象类,所以onBind()方法是必须重写的,即使我们用不到。

         使用 startService 方法启动 Service,onBind() 方法基本用不到,重写时返回一个 null 即可。

onDestory()

        在销毁的时候会执行Service该方法。

代码实例

MainActivity.java

public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// 启动serviceIntent intent = new Intent(MainActivity.this, TestService.class);startService(intent);}
}

TestService.java

public class TestService extends Service {private static final String TAG = "TestService";@Overridepublic IBinder onBind(Intent intent) {return null;}@Overridepublic void onCreate() {Log.d(TAG, "onCreate");super.onCreate();}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {Log.d(TAG, "onStartCommand");return super.onStartCommand(intent, flags, startId);}@Overridepublic void onDestroy() {Log.d(TAG, "onDestroy");super.onDestroy();}
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.xiaoxu.testdemo"><applicationandroid:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true"android:theme="@style/Theme.MainActivity "><serviceandroid:name=".TestService"android:enabled="true"android:exported="true" /><activityandroid:name=".MainActivity"android:exported="true" ><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity></application>
</manifest>

2、bindService

        bindService 启动的服务和调用者之间是典型的 client-server 模式。调用者是 client,service则是 server 端。service只有一个,但绑定到 service 上面的 client 可以有一个或多个。这里所提到的 client 指的是组件,比如某个Activity。
        client 可以通过 IBinder 接口获取 Service 实例,从而实现在 client 端直接调用 Service 中的方法以实现灵活交互,这在通过 startService 方法启动中是无法实现的。
        bindService 启动服务的生命周期与其绑定的 client 息息相关。当 client 销毁时,client 会自动与 Service 解除绑定。当然,client 也可以明确调用 Context 的 unbindService() 方法与 Service 解除绑定。当没有任何 client 与 Service 绑定时,Service 会自行销毁。

生命周期

onCreate()

        当服务通过 onStartCommand() 和 onBind() 被第一次创建的时候,系统调用该方法。该调用要求执行一次性安装。

onBind()

        当其他组件想要通过 bindService() 来绑定服务时,系统调用该方法。如果你实现该方法,你需要返回 IBinder 对象来提供一个接口,以便客户来与服务通信。你必须实现该方法,如果你不允许绑定,则直接返回 null。

onUnbind()

        当客户中断所有服务发布的特殊接口时,系统调用该方法。

onRebind()

        当新的客户端与服务连接,且此前它已经通过onUnbind(Intent)通知断开连接时,系统调用该方法。

onDestroy()

        当服务不再有用或者被销毁时,系统调用该方法。你的服务需要实现该方法来清理任何资源,如线程,已注册的监听器,接收器等。

代码实例

MainAcivity.java

public class MainAcivity extends Activity{private TextService mService = null;protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Intent intent = new Intent(this, TestService.class);bindService(intent, conn, Context.BIND_AUTO_CREATE);}private ServiceConnection conn = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder binder) {MyBinder mBinder = (MyBinder)binder;mService = mBinder.getService();String name = mService.getUserName();}@Overridepublic void onServiceDisconnected(ComponentName name) {}};@Overrideprotected void onDestroy() {super.onDestroy();unbindService(conn);}
}

MyService.java

public class TestService extends Service{//通过 binder 实现调用者 client 与 Service 之间的通信private MyBinder binder = new MyBinder();// client 可以通过 Binder 获取 Service 实例public class MyBinder extends Binder {public MyService getService() {return TestService.this;}}@Overridepublic void onCreate() {super.onCreate();}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {return START_NOT_STICKY;}@Overridepublic IBinder onBind(Intent intent) {return binder;}@Overridepublic boolean onUnbind(Intent intent) {return false;}@Overridepublic void onDestroy() {super.onDestroy();}// getUserName 是 Service 暴露出去供 Client 调用的公共方法public int getUserName() {return "XiaoXu";}
}

3、startForegroundService

        Android 8.0 系统不允许后台应用创建后台服务,只能使用 startForegroundService() 启动服务,创建服务后,应用必须在 5 秒内调用该服务的 startForeground() 显示一条可见通知,声明有服务在挂着,不然系统会停止服务 + ANR 提示。
        系统的要求 Notification 要加 Channel,并且要在 onStartCommand 中执行 startForeground(),因为这个主要是针对后台保活的服务,如果在服务运行期间,再次使用 startForegroundService() 启动服务,那么这次就不会调用服务的 onCreate 方法,只会调用onStartCommand 方法。如果不在 onStartCommand 方法里再挂个通知的话,系统会认为你使用了 startForegroundService 却不在 5 秒内给通知,很傻地就停止服务 + ANR 提示。

代码实例

MainActivity.java

public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// 启动serviceIntent intent = new Intent(this, TestService.class) ;startForegroundService(intent);}
}

TestService.java

public class TestService extends Service {private static final String TAG = "TestService";private Notification notification;public static final String CHANNEL_ID_STRING = "service_01";@Overridepublic IBinder onBind(Intent intent) {return null;}@Overridepublic void onCreate() {super.onCreate();}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID_STRING,getString(R.string.app_name),NotificationManager.IMPORTANCE_LOW);manager.createNotificationChannel(mChannel);notification = new Notification.Builder(getApplicationContext(), CHANNEL_ID_STRING).build();// 下面第一个参数不能为 0startForeground(1, notification);return super.onStartCommand(intent, flags, startId);}@Overridepublic void onDestroy() {super.onDestroy();}
}

        可以看到使用  startForegroundService 启动方式与 startService 基本相同,只是在 Service 的 onStartCommand 增加弹出通知的操作。startForeground 方法中的第一个参数不能为 0,是因为这个通知不能与正常通知 id 相同,否则通知会被“隐藏”,这不是官方想看到的,就会出现 Context.startForegroundService() did not then call Service.startForeground() 异常。

二、其他

1、使用场景

startService

        前台应用调用 startService 启动 Service,Service 独立运行,不需要与 Activity 进行交互。

bindService

        前台应用调用 bindService 启动 Service,Android 与 Service 进行绑定,并且 Activity 可以得到 Service 的实例,实现与 Service 的交互。

startForegroundService

        后台应用调用 startForegroundService 启动 Service,Android 8.0 之后强制要求,以提示用户。例如:在收到开机广播时启动服务,使用 startService 会无法启动,只能使用 startForegroundService 进行启动并弹出通知提示用户。

2、隐藏通知

        有时在使用 startForegroundService 启动服务时,想隐藏常驻通知。

1)显示通知后马上调用 stopForeground(true) 取消通知。在 Android 11 中测试,服务被杀掉。

2)提前注册一个没有声音没有震动的通知channel,注册一个无声无振动的通知渠道。startForeground 时与该通知使用同一个 id,就可以隐藏我们的通知了。(未尝试)

参考:Android8 避免startForeground方法弹出通知

3、AIDL Service死亡监听

        服务的死亡监听我们使用第二种启动方式。

启动 Service

private void bindService(Context context) {Intent serviceIntent = new Intent();serviceIntent.setAction("com.cx.test");serviceIntent.setPackage("com.cx.test.MyService");boolean bindSuccess = context.bindService(serviceIntent, mServiceConnection, Service.BIND_AUTO_CREATE);
}

绑定结果回调

private IPushInterface mIPushInterface;private ServiceConnection mServiceConnection = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName componentName, IBinder iBinder) {if (iBinder != null) {//通过AIDL拿到服务mIPushInterface = IPushInterface.Stub.asInterface(iBinder);try {//服务死亡监听if (mIPushInterface != null) {mIPushInterface.asBinder().linkToDeath(mDeathRecipient, 0);}} catch (RemoteException e) {e.printStackTrace();}}}@Overridepublic void onServiceDisconnected(ComponentName componentName) {//连接失败}
};

死亡监听回调

private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {@Overridepublic void binderDied() {if (mContext != null) {//重新绑定服务bindService(mContext);}}
};
http://www.lryc.cn/news/9558.html

相关文章:

  • svg转png
  • 教你如何搭建人事OA-员工管理系统,demo可分享
  • C++递推基础知识
  • 【Python入门第十天】Python 布尔
  • WebDAV之π-Disk派盘+Piktures
  • Revit问题:Navisworks中导入的rvt模型角度不正确调整
  • 最全正则验证
  • 阿里云服务器入门使用流程 新手学习教程
  • git学习
  • 新建一个完整的react项目和完善初始项目
  • HIVE 安装
  • jsp游泳馆门票管理系统Myeclipse开发mysql数据库web结构java编程计算机网页项目
  • C++ ---智能指针详解
  • 企业带宽控制管理
  • MybatisPlus实现分页效果并解决错误:cant found IPage for args!
  • C语言赋值(关系)运算符和逗号运算符
  • 几种在Linux/window下查询外网IP的办法。
  • 【nodejs-05】黑马nodejs学习笔记05-数据库基本操作01
  • 零基础、学历无优势、逻辑能力一般”,能转行做程序员吗?
  • 第五章.与学习相关技巧—Batch Normalization
  • Zynq非Video Mixer方案实现视频叠加输出,无需SDK配置,提供工程源码和技术支持
  • 从零实现Web服务器(二): 线程池以及线程池的作用,Get和Post的区别,项目中如何编写数据库连接池,定时器优化非活跃连接
  • 为什么伟大的产品只专注做一件事
  • pycharm远程连接服务器,并单步调试服务器上的代码
  • JVM05 方法区
  • 盘点3个.Net开发的WMS仓库管理系统
  • Linux下Java项目开机自动启动
  • 基于SpringBoot的智慧社区网站
  • 数据分析与SAS学习笔记3
  • 天干地支蓝桥杯国赛