Android 四大组件之旅
Activity
-
Activity 是每个App的排面 是用来展示界面的
-
官方定义:
Activity 是与用户交互的入口点。它表示拥有界面的单个屏幕。例如,电子邮件应用可能有一个显示新电子邮件列表的 Activity、一个用于撰写电子邮件的 Activity 以及一个用于阅读电子邮件的 Activity。尽管这些 Activity 通过协作在电子邮件应用中形成一种紧密结合的用户体验,但每个 Activity 都独立于其他 Activity 而存在。因此,其他应用可以启动其中任何一个 Activity(如果电子邮件应用允许)。例如,相机应用可以启动电子邮件应用内用于撰写新电子邮件的 Activity,以便用户共享图片。Activity 有助于完成系统和应用程序之间的以下重要交互:
- 追踪用户当前关心的内容(屏幕上显示的内容),以确保系统继续运行托管 Activity 的进程。
- 了解先前使用的进程包含用户可能返回的内容(已停止的 Activity),从而更优先保留这些进程。
- 帮助应用处理终止其进程的情况,以便用户可以返回已恢复其先前状态的 Activity。
- 提供一种途径,让应用实现彼此之间的用户流,并让系统协调这些用户流.
介绍Activity的简单用法
- Activity的生命周期
private const val TAG = "ShowActivity"
class ShowActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
/*
在onCreate方法里面 Activity被建立 这里会进行一些初始化的操作比如加载视图获取视图对象等等工作
Activity的建立 分为第一次建立 和 重新建立
第一次建立就是获取视图加载布局
但是重新建立就是将原先的数据保存在 savedInstanceState: Bundle? 这个里面
super.onCreate(savedInstanceState)这个方法被调用的时候就是将原来那些视图的数据和视图结构进行重建
*/
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_show)
Log.d(TAG, "onCreate 被调用")
}
override fun onStart() {
/*
在onCreate方法中将布局文件加载好了 现在就是可以看见页面的时候调用这个方法 但是还不可以交互
*/
super.onStart()
Log.d(TAG, "onStart 被调用")
}
override fun onResume() {
/*
当Activity可以和用户交互的时候就会调用这个方法
*/
super.onResume()
Log.d(TAG, "onResume 被调用")
}
override fun onPause() {
/*
当Activity可见但是不可交互的时候 就会调用这个方法 典型的例子就是QQ强制下线时的对话框的时候你虽然可以看见一些消息但是你已经不能去交互了
*/
super.onPause()
Log.d(TAG, "onPause 被调用")
}
override fun onStop() {
/*
当Activity不可见的时候就会被调用 这个主要是处理一些资源的释放
一般来说只要调用了onStop方法之后那么这个Activity会被操作系统标记为 可杀所以要在这个将一些资源释放调以及保存一些UI状态的数据
*/
super.onStop()
Log.d(TAG, "onStop 被调用")
}
override fun onDestroy() {
/*
现在就是Activity被操作系统销毁是要调用的方法了这里一般不怎么做处理
*/
super.onDestroy()
Log.d(TAG, "onDestroy 被调用")
}
}
-
这里由于android的发展 每个回调方法的使命有些不一样了 因为分屏模式的出现 小窗模式的大火 使得onPause方法不在是一个瞬时的方法了 他会一直都存在所以一些资源的释放最好就是在stop中来释放 一些UI状态的更新也可以放在onPause中不一定都要在onResume中 当然Google的解决办法是让其分屏的应用一直在运行的状态即onResume中但是国内的厂商要不要听就是另一回事了
-
Activity的跳转
- 显示跳转
- 利用 Intent 这个类是在组件中传递数据的类
- 具体来看应用
- 显示跳转
private const val TAG = "ShowActivity"
class ShowActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_show)
}
override fun onPause() {
super.onPause()
/*
主要看这里 我们在ShowActivity不可交互的时候进行跳转
调用 startActivity方法就可以了
里面传入的参数就是一个Intent对象
这个对象一眼就可以看出来 他是从当先的Activity跳转到MainActivity中的
*/
startActivity(Intent(this,MainActivity::class.java))
}
}
-
隐式跳转
-
隐式跳转 就先要理解一个概念了
我们的Activity是由系统级别来管理的 在操作系统中有一个类ActivityManager类来管理的
这样的好处就是可以调用其他应用的Activity类具体看案例
private const val TAG = "ShowActivity" class ShowActivity : AppCompatActivity() { private val btn : Button by lazy { findViewById(R.id.intent) } private val permission : String = android.Manifest.permission.CALL_PHONE override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_show) btn.setOnClickListener { requestPermission(permission) } } //拨打电话的方法 /* Intent.ACTION_CALL 这个是个action字符串 就是ActivityManger会在系统中的全部应用查找可以做这件事的Activity并跳转 */ fun call(){ val intent = Intent(Intent.ACTION_CALL, Uri.parse("tel:xxxx")) startActivity(intent) } /* 由于android6.0加入了运行是权限这里是在动态申请权限 清单文件中也要申请权限 <uses-permission android:name="android.permission.CALL_PHONE"/> 这是因为打电话是危险权限所以要在运行是来申请权限 */ fun requestPermission(permission:String){ val result = ActivityCompat.checkSelfPermission(this,permission) val granted = PackageManager.PERMISSION_GRANTED if (result != granted){ ActivityCompat.requestPermissions(this, arrayOf(permission),1) }else{ call() } } /* 申请权限的结果会这里得到判断 可以判断是否授权了 */ override fun onRequestPermissionsResult( requestCode: Int, permissions: Array<out String>, grantResults: IntArray ) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) if (requestCode == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED){ call() }else{ Toast.makeText(this,"拒绝授权",Toast.LENGTH_SHORT).show() } } }
Activity与Activity的通信
-
主要是用到一个方法 startActivityForResult
class ShowActivity : AppCompatActivity(){ private val btn : Button by lazy { findViewById(R.id.intent) } private val permission : String = android.Manifest.permission.CALL_PHONE override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_show) btn.setOnClickListener { startActivityForResult(Intent(this,MainActivity::class.java),0) } } /* 这个方法就是来接收 startActivityForResult(Intent(this,MainActivity::class.java),0) 他跳转的Activity返回的结果 这里就是MainActivity 当MainActivity被销毁的时候进入ShowActivity时这个方法会被调用 */ override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) Log.d(TAG, "onActivityResult: ${data?.getStringExtra("xixi")}") } } class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val intent = Intent() intent.putExtra("xixi","我(MainActivity)给你(ShowActivity)了一个消息") setResult(1,intent) } }
- 总结一下 : 就是通过Intent来传递数据的 返回数据是调用的方法不同而已
-
Service
-
Service 这个组件就是和Activity相反的了 Activity是排面 他就是一个后勤的保障人员 给Activity提供能量的
-
官方的解释 : 服务是一个通用入口点,用于出于各种原因使应用程序在后台运行。它是在后台运行以执行长时间运行的操作或为远程进程执行工作的组件。服 务不提供用户界面。例如,当用户在不同的应用程序中时,服务可能会在后台播放音乐,或者它可能会通过网络获取数据而不阻止用户与活动的交互。另一个 组件,例如活动,可以启动服务并让它运行或绑定到它以与之交互。
有两种类型的服务告诉系统如何管理应用程序:启动服务和绑定服务。
-
Service的生命周期
class CommonService : Service() {
private val binder:IBinder = MyBinder()
//粘合剂 这个可以暂时不管
inner class MyBinder: Binder(){
fun getService():CommonService = this@CommonService
}
/*
绑定服务
*/
override fun onBind(intent: Intent): IBinder {
Log.d(TAG, "onBind被调用")
return binder
}
/*
创建服务
*/
override fun onCreate() {
super.onCreate()
Log.d(TAG, "onCreate被调用")
}
/*
启动服务
*/
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Log.d(TAG, "onStartCommand被调用 flag${flags}")
return super.onStartCommand(intent, flags, startId)
}
/*
销毁服务
*/
override fun onDestroy() {
super.onDestroy()
Log.d(TAG, "onDestroy被调用")
}
/*
解绑服务
*/
override fun onUnbind(intent: Intent?): Boolean {
Log.d(TAG, "onUnbind被调用")
return true
}
}
- Service的两种运行方式
- 一种是普通服务 调用startService
- 一种是绑定服务 调用bindService
//普通服务
private const val TAG = "MainActivity"
class MainActivity : AppCompatActivity() {
private val myIntent by lazy { Intent(this,CommonService::class.java) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
startService(myIntent)//启动服务
}
override fun onStop() {
super.onStop()
stopService(myIntent)//停止服务
}
}
//绑定服务
//MainActivity类里面的代码
private const val TAG = "MainActivity"
class MainActivity : AppCompatActivity() {
private val myIntent by lazy { Intent(this,CommonService::class.java) }
private lateinit var myService : CommonService
private val bindBtn: Button by lazy {
findViewById(R.id.bind)
}
private val unBindBtn: Button by lazy {
findViewById(R.id.unbind)
}
//绑定服务必须实现的连接器
private val connection : ServiceConnection = object : ServiceConnection{
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
//这里的 service: IBinder? 就是在CommonService中onBinder返回来的binder
myService = (service as CommonService.MyBinder).getService()
}
override fun onServiceDisconnected(name: ComponentName?) {
Log.d(TAG, "连接失败")
}
}
@RequiresApi(Build.VERSION_CODES.O)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
bindBtn.setOnClickListener {
bindService(myIntent,connection, Context.BIND_AUTO_CREATE)//绑定服务
// Context.BIND_AUTO_CREATE 的意思是没有创建服务自动创建
}
unBindBtn.setOnClickListener {
unbindService(connection)//解绑服务
}
}
}
//CommonService类里面的代码
class CommonService : Service() {
private val binder:IBinder = MyBinder()
//粘合剂 这个就是一个联合器把组件和组件之前联合 进程和进程之间联合
inner class MyBinder: Binder(){
fun getService():CommonService = this@CommonService
}
override fun onBind(intent: Intent): IBinder {
return binder
}
@RequiresApi(Build.VERSION_CODES.O)
override fun onCreate() {
super.onCreate()
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
return super.onStartCommand(intent, flags, startId)
}
override fun onDestroy() {
super.onDestroy()
}
override fun onUnbind(intent: Intent?): Boolean {
return true
}
}
-
在 onUnbind 的返回值是大有讲究的 是false 表示只允许绑定一次 是 true 表示可以再次绑定 但是我写的Demo不成功
-
前台Service
简单的理解就是在Service类中发了一条通知
-
前台Service的作用 : 保证服务不被杀掉
package com.example.androidlearn
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.Service
import android.content.Intent
import android.os.Binder
import android.os.Build
import android.os.IBinder
import android.util.Log
import androidx.annotation.RequiresApi
import java.lang.Exception
private const val TAG = "CommonService"
class CommonService : Service() {
private val binder:IBinder = MyBinder()
//粘合剂
inner class MyBinder: Binder(){
fun getService():CommonService = this@CommonService
}
override fun onBind(intent: Intent): IBinder {
Log.d(TAG, "onBind被调用")
return binder
}
@RequiresApi(Build.VERSION_CODES.O)
override fun onCreate() {
super.onCreate()
Log.d(TAG, "onCreate被调用")
//这里我默认了设备是Andrid8.0以上
//获取NotificationManger
val manger = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
//创建信道
val channel = NotificationChannel("serviceNotification","服务前台通知",NotificationManager.IMPORTANCE_DEFAULT)
manger.createNotificationChannel(channel)
//创建消息
val notification = Notification.Builder(this,"serviceNotification").apply {
setSmallIcon(R.drawable.ic_launcher_foreground)
setContentText("IOTU")
setContentTitle("这是标题")
}.build()
startForeground(1,notification)
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Log.d(TAG, "onStartCommand被调用 flag${flags}")
return super.onStartCommand(intent, flags, startId)
}
override fun onDestroy() {
super.onDestroy()
Log.d(TAG, "onDestroy被调用")
}
override fun onUnbind(intent: Intent?): Boolean {
Log.d(TAG, "onUnbind被调用")
return true
}
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
return super.onStartCommand(intent, flags, startId)
}
//说明: flags: Int
1.START_STICKY : 服务进程被杀后 或将服务重置为开始状态 清空Intent
2.START_NOT_STICKY : 不重启服务
3.START-REDELIVER-INTENT : 重传Intent,重启服务
4.START-STICKY_COMPATIBILITY: START_STICKY的兼容版本 不一定能有效
广播
-
广播的出现是为了更好的解决通信问题 就好像家里的网线一样数量只有那么多如果要联网设备一多就不够用了这时移动的路由器就出现了只要我们有无线网卡我们就能联WiFi 这也是广播的基本原理想WiFi一样可以被多个设备接收
-
官方解释:借助广播接收器组件,系统能够在常规用户流之外向应用传递事件,从而允许应用响应系统范围内的广播通知。由于广播接收器是另一个明确定义的应用入口,因此系统甚至可以向当前未运行的应用传递广播。例如,应用可通过调度提醒来发布通知,以告知用户即将发生的事件。而且,通过将该提醒传递给应用的广播接收器,应用在提醒响起之前即无需继续运行。许多广播均由系统发起,例如,通知屏幕已关闭、电池电量不足或已拍摄照片的广播。应用也可发起广播,例如,通知其他应用某些数据已下载至设备,并且可供其使用。尽管广播接收器不会显示界面,但其可以创建状态栏通知,在发生广播事件时提醒用户。但广播接收器更常见的用途只是作为通向其他组件的通道,旨在执行极少量的工作。例如,它可能会根据带 JobScheduler 的事件调度 JobService 来执行某项工作
广播接收器作为 BroadcastReceiver 的子类实现,并且每条广播都作为
Intent
对象进行传递。如需了解详细信息,请参阅 BroadcastReceiver 类。 -
实现广播接收的三大步骤
- 创建广播
- 创建广播接收器
- 开关广播接收器
- 具体代码操作
private const val TAG = "MainActivity"
class MainActivity : AppCompatActivity() {
private val broadcastBtn: Button by lazy {
findViewById(R.id.boradcast)
}
private lateinit var receiver: MyBroadcastReceiver
@RequiresApi(Build.VERSION_CODES.O)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//在Activity中注册BroadcastReceiver,不一定要在Activity中注册在哪里都可以
//这里的注册是四大组件的必须的做法 不这样做安卓系统识别不出来
receiver = MyBroadcastReceiver()
val intentFilter = IntentFilter()
intentFilter.addAction("IOTU")
//注册广播
registerReceiver(receiver,intentFilter)
broadcastBtn.setOnClickListener {
//隐式的intent
val intent = Intent("IOTU")
//发送广播
sendBroadcast(intent)
}
}
override fun onStop() {
super.onStop()
//注销广播
unregisterReceiver(receiver)
}
}
//广播接收器中的逻辑
class MyBroadcastReceiver : BroadcastReceiver() {
@RequiresApi(Build.VERSION_CODES.O)
//最重要的就是这个方法
// 1.当广播接收器接收到广播时就会调用这个方法
override fun onReceive(context: Context?, intent: Intent?) {
if (intent?.action == "IOTU") {
//发送通知
val manger = context
?.getSystemService(NOTIFICATION_SERVICE) as NotificationManager
//创建通知渠道
val channel =
NotificationChannel(
"broadcast", "广播前台通知", NotificationManager.IMPORTANCE_DEFAULT
)
//创建通知
val notification = Notification.Builder(context, "broadcast").apply {
setSmallIcon(R.drawable.ic_launcher_foreground)
setContentTitle("广播前台通知")
setContentText("广播前台内容")
}.build()
manger.createNotificationChannel(channel)
manger.notify(1, notification)
}
}
}
- 想一想其他的组件是怎么注册的 我想你应该知道 那就是清单文件虽然android studio贴心的帮我们注册了
静态广播
-
静态广播就是在清单文件中我们手动申请的了 但是在android8.0的大改后静态广播的威力就小了很多 只能接收自己APP里的广播了不能接收系统的广播以及其他应用的广播了
-
具体的代码实现
private const val TAG = "MainActivity"
class MainActivity : AppCompatActivity() {
private val staticBroadcastBtn: Button by lazy {
findViewById(R.id.static_broadcast)
}
@RequiresApi(Build.VERSION_CODES.O)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
staticBroadcastBtn.setOnClickListener {
val intent = Intent("android.story")
//这里最重要 android8.0后的大改已经删除了大部分的静态注册目的就是为了防止在app结束后还能接收广播
//componentName使用这个来给静态的广播指定包名及组件路径有了他现在才能使用静态的广播
val componentName = ComponentName(this,"com.example.androidlearn.MyStaticBroadcastReceiver")
intent.component = componentName
sendBroadcast(intent)
}
}
override fun onStop() {
super.onStop()
}
}
//清单文件
<receiver android:name=".MyStaticBroadcastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.story"/>
</intent-filter>
</receiver>
//静态注册的广播类
class MyStaticBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
Toast.makeText(context,
"我是静态广播接收了这条广播(action) ${intent?.action}",Toast.LENGTH_SHORT).show()
}
}
常用的系统广播
- 第一个就是分钟的广播 当分钟发生变化时系统就会发送广播
- 广播的名称是: Intent.ACTION-TIME_TICK
//时间广播接收器
class TimeBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
val btn = (context as SystemBroadcastActivity).findViewById<Button>(R.id.time)
btn.text = "我是时间广播接收器: ${intent?.action}"
Toast.makeText(context,"我是时间广播接收器: ${intent?.action}",Toast.LENGTH_SHORT).show()
}
}
//显示变化的 SystemBroadcastActivity类
class SystemBroadcastActivity : AppCompatActivity() {
private val timeBtn : Button by lazy {
findViewById(R.id.time)
}
private lateinit var receiver : TimeBroadcastReceiver
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_system_broadcast)
}
override fun onStart() {
super.onStart()
receiver = TimeBroadcastReceiver()
//其实就是过滤器不一样
val filter = IntentFilter(Intent.ACTION-TIME_TICK)
registerReceiver(receiver, filter)
}
override fun onStop() {
super.onStop()
unregisterReceiver(receiver)
}
}
- 网络变化的系统广播
- 名称: “android.net.conn.CONNECTIVITY_CHANGE”
//显示变化的 SystemBroadcastActivity类
class SystemBroadcastActivity : AppCompatActivity() {
private val timeBtn : Button by lazy {
findViewById(R.id.time)
}
private lateinit var receiver : InternetBroadcastReceiver
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_system_broadcast)
}
override fun onStart() {
super.onStart()
receiver = InternetBroadcastReceiver()
//其实就是过滤器不一样
val filter = IntentFilter("android.net.conn.CONNECTIVITY_CHANGE")
registerReceiver(receiver, filter)
}
override fun onStop() {
super.onStop()
unregisterReceiver(receiver)
}
}
class InternetBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
val btn : Button = (context as SystemBroadcastActivity).findViewById(R.id.internet)
val netWorkInfo = intent?.getParcelableExtra<NetworkInfo>("networkInfo")
//因为intent传进来的数据要解包才知道是什么样的数据否则只是知道网络变化了
val str = "${netWorkInfo?.subtype} ${netWorkInfo?.typeName}\n${netWorkInfo?.subtypeName} ${netWorkInfo?.state.toString()}"
btn.text = str
}
}
-
定时器广播
-
这就要借助定时器管理器了AlarmManger
//定时器广播接收器
class AlarmBroadcastReceiver : BroadcastReceiver() {
@RequiresApi(Build.VERSION_CODES.S)
@SuppressLint("ServiceCast")
override fun onReceive(context: Context?, intent: Intent?) {
Toast.makeText(context,"闹钟响铃",Toast.LENGTH_SHORT).show()
val vb:Vibrator = context?.getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as Vibrator
vb.vibrate(500)
}
}
//显示变化的类
class SystemBroadcastActivity : AppCompatActivity() {
private val timeBtn : Button by lazy {
findViewById(R.id.time)
}
private lateinit var receiver : AlarmBroadcastReceiver
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_system_broadcast)
}
override fun onStart() {
super.onStart()
receiver = AlarmBroadcastReceiver()
val filter = IntentFilter("android")
registerReceiver(receiver, filter)
sendAlarm()
}
override fun onStop() {
super.onStop()
unregisterReceiver(receiver)
}
@SuppressLint("UnspecifiedImmutableFlag")
fun sendAlarm(){
val intent = Intent("android")
val pIntent = PendingIntent.getBroadcast(this,0,intent,PendingIntent.FLAG_UPDATE_CURRENT)
//得到系统级别的AlarmManger管理器
val manger = getSystemService(ALARM_SERVICE) as AlarmManager
//设置响铃的时间
val delayTime = System.currentTimeMillis() + 2*100
//版本兼容 android6.0以后 提供了设备是空闲状态也会执行定时器 而set在黑屏情况下不一定会执行定时器
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
manger.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP,delayTime,pIntent)
}
manger.set(AlarmManager.RTC_WAKEUP,delayTime,pIntent)
}
}
- 系统的广播是由系统来发送的我们不需要来发送 就算定时器广播我们显示的调用了发送 但是还是系统级别不是我们APP级别的
内容提供器
-
对于内容提供器我并不打算深究因为内容提供器并不怎么常用 所以代码的实例就会少很多 你可以说我摆烂
-
真正的要实现内容共享 有三个组件 1.ContentProvider 2.ContentResolver 3.ContentObserver 存储数据的底层还是SQLite
-
ContentProvider : 就是让其他的应用能访问到我们提供的数据 其他应用对我们的提供数据增删改查
-
ContentResolver : 就是我们去修改其他APP的数据 一般是系统App 对其他应用数据增删改查
-
ContentObserver: 就是观察某个数据有变化就就会触发监听事件
-
内容观察器要用内容解析器去注册 在ContentObserver内部去实现onChange方法 当关心的数据发生变化时就可以触发这个方法
-
获取数据就要用内容URI