1. 配置清单
1.1 build.gradle 添加 lifecycle 库
dependencies {
implementation 'androidx.lifecycle:lifecycle-service:2.6.0-alpha02'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.0-alpha03'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.6.0-alpha03'
// implementation 'androidx.work:work-runtime-ktx:2.7.1'
}
1.2 AndroidManifest.xml 添加权限
<!-- 前台服务 -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<!-- 发送通知 -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
1.3 添加服务
<service
android:name=".MyService"
android:enabled="true"
android:exported="true" />
1.4 Activity 添加单例模式
<activity
android:name=".MainActivity"
android:launchMode="singleInstance"
android:exported="true">
1.5 添加系统矢量图标 ic_android_black_24dp.xml
2. 布局文件 activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
android:textSize="34sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.376" />
</androidx.constraintlayout.widget.ConstraintLayout>
3. 创建是否打开权限工具类 UtilsNew.kt
object UtilsNew {
//监测通知权限
fun isPermissionOpen(context: Context?): Boolean {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationManagerCompat.from(context!!).importance != NotificationManager.IMPORTANCE_NONE
} else NotificationManagerCompat.from(context!!).areNotificationsEnabled()
}
fun openPermissionSetting(context: Context) {
try {
val localIntent = Intent()
localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
//直接跳转到应用通知设置的代码:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
localIntent.action = Settings.ACTION_APP_NOTIFICATION_SETTINGS
localIntent.putExtra(Settings.EXTRA_APP_PACKAGE, context.getPackageName())
context.startActivity(localIntent)
return
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
localIntent.action = "android.settings.APP_NOTIFICATION_SETTINGS"
localIntent.putExtra("app_package", context.getPackageName())
localIntent.putExtra("app_uid", context.getApplicationInfo().uid)
context.startActivity(localIntent)
return
}
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) {
localIntent.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
localIntent.addCategory(Intent.CATEGORY_DEFAULT)
localIntent.data = Uri.parse("package:" + context.getPackageName())
context.startActivity(localIntent)
return
}
//4.4以下没有从app跳转到应用通知设置页面的Action,可考虑跳转到应用详情页面,
if (Build.VERSION.SDK_INT >= 9) {
localIntent.action = "android.settings.APPLICATION_DETAILS_SETTINGS"
localIntent.data = Uri.fromParts("package", context.getPackageName(), null)
context.startActivity(localIntent)
return
}
localIntent.action = Intent.ACTION_VIEW
localIntent.setClassName(
"com.android.settings",
"com.android.setting.InstalledAppDetails"
)
localIntent.putExtra(
"com.android.settings.ApplicationPkgName",
context.getPackageName()
)
} catch (e: Exception) {
e.printStackTrace()
println(" cxx pushPermission 有问题")
}
}
}
4. 创建服务 MyService.kt
const val CHANNEL_ID = "my notification channel ID"
class MyService : LifecycleService() {
private var mTAG = "MyTag"
private var number = 0
val numberLive = MutableLiveData(0)
@SuppressLint("UnspecifiedImmutableFlag")
override fun onCreate() {
super.onCreate()
lifecycleScope.launch {
while (true) {
delay(1_000)
numberLive.value = numberLive.value?.plus(1)
Log.i(mTAG, "onCreate ${number++}")
}
}
createChannel()
val notificationIntent = Intent(this, MainActivity::class.java)
val pendingIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
PendingIntent.getActivity(this, 1, notificationIntent, PendingIntent.FLAG_IMMUTABLE)
else
PendingIntent.getActivity(this, 1, notificationIntent, PendingIntent.FLAG_ONE_SHOT)
val notification = NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_android_black_24dp)
.setWhen(System.currentTimeMillis())
.setContentTitle("This is a title.")
.setContentText("This is a notification.")
.setContentIntent(pendingIntent)
.build()
startForeground(1, notification)
}
//内部类
inner class MyBinder: Binder(){
val getService = this@MyService
}
override fun onBind(intent: Intent): IBinder {
super.onBind(intent)
return MyBinder()
}
//创建 通道ID
private fun createChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// Create the NotificationChannel
val name = "My notification channel No.1"
//IMPORTANCE_HIGH IMPORTANCE_DEFAULT
val importance = NotificationManager.IMPORTANCE_DEFAULT
val mChannel = NotificationChannel(CHANNEL_ID, name, importance)
// Register the channel with the system; you can't change the importance
// or other notification behaviors after this
val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(mChannel)
}
}
}
5. 调用 MainActivity.kt
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//also apply 里边的主语不一样
if (!UtilsNew.isPermissionOpen(this)){
UtilsNew.openPermissionSetting(this)
}else{
//跳转Service
jumpService()
}
}
private fun jumpService(){
val textView: TextView = findViewById(R.id.textView)
val serviceConnection = object : ServiceConnection{
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
(service as MyService.MyBinder).getService.numberLive.observe(this@MainActivity){
textView.text = it.toString()
}
}
override fun onServiceDisconnected(name: ComponentName?) {
}
}
Intent(this,MyService::class.java).apply {
startService(this)
bindService(this,serviceConnection,Context.BIND_AUTO_CREATE)
}
}
}
6. 效果图