任何技能的熟练都离不开扎实的基础知识,时不时的回顾这些基础内容会对开发技能的理解有很好的帮助。
随着学习的进度随时更新基础知识
一、前言
本章内容甚多,参考了许多大能的文章,感谢各位大能的分享。
Android之Activity全面解析
Android Activity 你所需要知道的一切
务必知道的Android service的完全详解
Android Service一些知识与理解
Android BroadcastReceiver详解
Android四大组件:BroadcastReceiver史上最全面解析
Android四大组件——ContentProvider(基础篇)
Android ContentProvider详解
二、四大组件之Activity
1. Activity生命周期
完整生命周期
onCreate-->onStart-->onResume-->onPause-->onStop-->onDestory
可视生命周期
onStart-->onResume-->onPause-->onStop
前台生命周期
onResume-->onPause
两个Activity先后启动时的调用方式
第一个Activity的启动顺序
onCreate() >> onStart() >> onResume()
第二个Activity启动时
(OneActivity)onPause() >> (TwoActivity)onCreate() >> onStart() >> onResume() >> (OneActivity)onStop()
返回到第一个Activity时
(TwoActivity)onPause() >> (OneActivity)onRestart() >> onStart() >> onResume() >> (TwoActivity)onStop() >> onDestroy()
如果给第二个activity设置透明窗口主题(android:theme="@style/Base.Theme.AppCompat.Dialog"
)
从第一个activity跳转到第二个activity过程:
AActivity: onCreate() >> onStart() >> onResume() >> onPause()(A依然可见但不可交互)
BActivity: onCreate() >> onStart() >> onResume()(B以窗口对话框形式在AActivity上面,可见可交互)
点击空白处或者返回:
BActivity: onPause()
AActivity: onResume()
BActivity: onStop() >> onDestory()
Activity被销毁时,数据保存问题
系统通过调用onSaveInstanceState()和onRestoreInstanceState()来保存和恢复数据。
- onSaveInstanceState():Activity被异常终止,在Activity即将被销毁且有机会重新显示的情况下,系统会自动调用;Android3.0之前在onPause()之前调用,Android3.0之后是在onPause()之后onStop()之前调用;系统会传递一个Bundle对象保存信息。
- onRestoreInstanceState():在Activity确定被销毁以后,重建Activity时调用,在onStart()之后执行;系统保存的Bundle对象会传递到onRestoreInstanceState()和onCreate()中,可以根据实际情况来选择在那个方法中恢复数据。
- 屏幕进行横竖屏切换时。(系统默认情况下) 在屏幕切换之前,系统会销毁Activity A,在屏幕切换之后系统又会自动地创建Activity A,所以onSaveInstanceState一定会被执行。
并且在默认情况下,切换横竖屏会调用各个生命周期方法,横屏调一次,竖屏调用2次;我们可以清单文件manifest file中声明Activity时配置configChanges属性来改变生命周期的调用次数:
(1)设置android:configChanges="orientation"
(2)设置android:configChanges="orientation|keyboardHidden"(3.2系统之前的系统不会执行生命周期方法了)
(3)设置android:configChanges="orientation|keyboardHidden|screenSize"可以控制Activity在横竖屏切换时不销毁重建,只会执行onConfigurationChanged方法。
2. Activity四种启动模式
每个应用程序都有自己的Activity任务栈,用来存放所有启动的Activity,任务栈使用先入后出的操作方式,并且每次启动的Activity都会实例化并放入任务栈,会出现同一个Activity存在多个实例的情况,Android提供了四种启动模式,我们可以根据实际情况选择不同的启动模式。
在清单文件manifest file中声明Activity时,可以使用<activity> 元素的launchMode属性指定Activity在启动时应如何与任务关联:
Modes | Description |
---|---|
"standard" | 默认模式。系统在启动它的任务中创建Activity的新实例,并将意图路由到该实例。Activity可以多次实例化,每个实例可以属于不同的任务,一个任务可以有多个实例。 |
"singleTop" | 如果任务栈的栈顶存在这个要开启的Activity,不会重新的创建Activity,而是复用已经存在的Activity。保证栈顶如果存在,不会重复创建。 |
"singleTask" | 当开启Activity的时候,就去检查在任务栈里面是否有实例已经存在,如果有实例存在就复用这个已经存在的Activity,并且把这个Activity上面的所有的别的Activity都清空,复用这个已经存在的Activity。保证整个任务栈里面只有一个实例存在。 |
"singleInstance" | 相同"singleTask",区别在于:系统不启动任何其他Activity纳入控制实例的任务。Activity始终是其任务的唯一成员; 任何由此开始的Activity都在一个单独的任务中打开。 |
3. Activity的两种启动方式
显示启动
常用的启动方式,在AndroidManifest.xml里配置activity,然后进行如下操作:
Intent intent = new Intent(MainActivity.this,CloneActivity.class);
startActivity(intent);
隐式启动
它与显示启动最大区别就是不需要在Intent的实例化中传参, 但是需要在AndroidManifest.xml给该activity配置intentfilter属性,启动代码如下
<intent-filter>
<action android:name="com.mangoer.activityreview"></action>
<category android:name="android.intent.category.DEFAULT"></category>
</intent-filter>
Intent intent = new Intent();
intent.setAction("com.mangoer.activityreview");
startActivity(intent);
二、四大组件之Service
1. Service介绍
Service是一种可以在后台长期运行的组件,没有UI界面;适合执行不需要交互、不需要界面的任务;需要注意的是,Service默认是运行在Main线程中,如果在Service中进行耗时操作,也会造成程序无响应(ANR)的问题。
2. 启动方式
Service有两种启动方式:
- start启动:组件通过startService()启动,启动后,服务会在后台一直运行,及时调用组件被销毁也不会影响到服务的运行,服务也不会将执行的结果返回给组件;当手动停止服务或者执行的任务完成后服务会停止。
- bind启动:组件通过bindService()启动,组件和服务进行绑定,可以进行数据交互;一个服务可以绑定多个组件,只有每个组件都与服务进行解绑后,服务才会停止。
不管哪种启动方式,都先要在清单(AndroidManifest.xml)中进行声明
<service android:name=".myservice"
android:enabled="true"
android:exported="true"
android:icon="@drawable/background"
android:label="string"
android:process="string"
android:permission="string"/>
属性 | 说明 |
---|---|
android:exported | 表示是否允许除了当前程序之外的其他程序访问这个服务 |
android:enabled | 表示是否能启用这个服务 |
android:permission | 权限声明,组件在启动这个服务的时候必须要有的权限 |
android:process | 是否需要在单独的进程中运行,当设置为android:process=”:remote”时,代表Service在单独的进程中运行。注意“:”很重要,它的意思是指要在当前进程名称前面附加上当前的包名,所以“remote”和”:remote”不是同一个意思,前者的进程名称为:remote,而后者的进程名称为:App-packageName:remote |
android:isolatedProcess | 设置 true 意味着,服务会在一个特殊的进程下运行,这个进程与系统其他进程分开且没有自己的权限。与其通信的唯一途径是通过服务的API(bind and start) |
3. 生命周期
(1)启动Service服务
单次:startService() —> onCreate() —> onStartCommand()
多次:startService() —> onCreate() —> onStartCommand() —> onStartCommand()
(2)停止Service服务
stopService() —> onDestroy()
(3)绑定Service服务
bindService() —> onCreate() —> onBind()
(4)解绑Service服务
unbindService() —> onUnbind() —> onDestroy()
(5)先启动再绑定Service服务
startService() —> onCreate() —> onStartCommand() —> bindService() —> onBind()
(6)解绑后停止Service服务
unbindService() —> onUnbind() —> stopService() —> onDestroy()
(7)解绑后再绑定Service服务
unbindService() —> onUnbind(ture) —> bindService() —> onRebind()
- 两种启动方式中onCreate()都只调用一次,多次startService()时,onStartCommand()会多次调用,而多次bindService()时,onBind()方法只调用一次。
- 当同时使用startService()和bindService()时,需同时调用unbindService()和stopService()才能使服务停止,单独调用其中一个,服务都不会停止运行。
三、四大组件之BroadcastReceiver
BroadcastReceiver广播接收器,用来监听广播的组件,Android内置了一套广播通信机制,通过广播可以在不同APP、不同组件、不同线程之间进行通信。
Android中的广播机制采用了观察者模式,需要有广播发送者和广播接收者参与其中
1. 如何使用BroadcastReceiver
1.1、创建BroadcastReceiver
// 继承BroadcastReceivre基类
public class mBroadcastReceiver extends BroadcastReceiver {
// 复写onReceive()方法
// 接收到广播后,则自动调用该方法
@Override
public void onReceive(Context context, Intent intent) {
//写入接收广播后的操作
}
}
1.2、注册BroadcastReceiver
- 静态注册:直接在AndroidManifest.xml清单文件中通过标签来注册广播,此方式注册的广播不受任何组件的生命周期影响,及时在程序关闭后,仍然可以接受广播。
<receiver
android:enabled=["true" | "false"]
//此broadcastReceiver能否接收其他App的发出的广播
//默认值是由receiver中有无intent-filter决定的:如果有intent-filter,默认值为true,否则为false
android:exported=["true" | "false"]
android:icon="drawable resource"
android:label="string resource"
//继承BroadcastReceiver子类的类名
android:name=".mBroadcastReceiver"
//具有相应权限的广播发送者发送的广播才能被此BroadcastReceiver所接收;
android:permission="string"
//BroadcastReceiver运行所处的进程
//默认为app的进程,可以指定独立的进程
//注:Android四大基本组件都可以通过此属性指定自己的独立进程
android:process="string" >
//用于指定此广播接收器将接收的广播类型
//本示例中给出的是用于接收网络状态改变时发出的广播
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter>
</receiver>
- 动态注册:在代码注册广播,注册后必须注销,否则容易出现内存泄露
// 1. 实例化BroadcastReceiver子类 & IntentFilter
mBroadcastReceiver mBroadcastReceiver = new mBroadcastReceiver();
IntentFilter intentFilter = new IntentFilter();
// 2. 设置接收广播的类型
intentFilter.addAction(android.net.conn.CONNECTIVITY_CHANGE);
// 3. 动态注册:调用Context的registerReceiver()方法
registerReceiver(mBroadcastReceiver, intentFilter);
// 注册广播后,要在相应位置记得销毁广播
// 即在onPause() 中unregisterReceiver(mBroadcastReceiver)
// 当此Activity实例化时,会动态将MyBroadcastReceiver注册到系统中
// 当此Activity销毁时,动态注册的MyBroadcastReceiver将不再接收到相应的广播。
@Override
protected void onPause() {
super.onPause();
//销毁在onResume()方法中的广播
unregisterReceiver(mBroadcastReceiver);
}
2. 广播的分类
2.1、普通广播(Normal Broadcast)
是一种无序广播,所有跟广播匹配的接收器都能接收到该广播,并且没有先后顺序,广播效率高,但是广播接收器无法停止该广播,无法将内容传递到其他接收器
2.2、有序广播(Ordered Broadcast)
是一种同步广播,根据广播接收器属性intent-filter中的android:priority属性来设置优先级,从-1000~1000数值越大优先级越高,广播根据优先级依次传递广播,每个广播接收器都可以终止广播,并且可以传递内容到下一个接收器
2.3、粘性广播(Sticky Broadcast)
该广播发送后会一直滞留在消息容器中,直到有匹配的广播接收器接受该广播,在Android5.0/API 21之后失效,不推荐使用
2.4、系统广播(System Broadcast)
Android系统内置的广播,涉及到手机的基本操作都会自动发出对应的广播,每个广播都有特定的action
系统操作 | action |
---|---|
监听网络变化 | android.net.conn.CONNECTIVITY_CHANGE |
关闭或打开飞行模式 | Intent.ACTION_AIRPLANE_MODE_CHANGED |
充电时或电量发生变化 | Intent.ACTION_BATTERY_CHANGED |
电池电量低 | Intent.ACTION_BATTERY_LOW |
电池电量充足(即从电量低变化到饱满时会发出广播 | Intent.ACTION_BATTERY_OKAY |
系统启动完成后(仅广播一次) | Intent.ACTION_BOOT_COMPLETED |
按下照相时的拍照按键(硬件按键)时 | Intent.ACTION_CAMERA_BUTTON |
屏幕锁屏 | Intent.ACTION_CLOSE_SYSTEM_DIALOGS |
设备当前设置被改变时(界面语言、设备方向等) | Intent.ACTION_CONFIGURATION_CHANGED |
插入耳机时 | Intent.ACTION_HEADSET_PLUG |
未正确移除SD卡但已取出来时(正确移除方法:设置--SD卡和设备内存--卸载SD卡) | Intent.ACTION_MEDIA_BAD_REMOVAL |
插入外部储存装置(如SD卡) | Intent.ACTION_MEDIA_CHECKING |
成功安装APK | Intent.ACTION_PACKAGE_ADDED |
成功删除APK | Intent.ACTION_PACKAGE_REMOVED |
重启设备 | Intent.ACTION_REBOOT |
屏幕被关闭 | Intent.ACTION_SCREEN_OFF |
屏幕被打开 | Intent.ACTION_SCREEN_ON |
关闭系统时 | Intent.ACTION_SHUTDOWN |
重启设备 | Intent.ACTION_REBOOT |
2.5、本地广播(Local Broadcast)
以上四类广播都是全局广播,只要有匹配的接收器,都可以接收到广播,对性能及安全性都有一定的影响,于是有了本地广播,此广播只可通过代码动态注册,并且只有同一个APP内的广播接收器接收的到
四、四大组件之ContentProvider
ContentProvider为不同的应用之间实现数据共享,提供统一的接口,可以实现进程间的数据共享,实现跨进程通信,ContentProvider其底层使用了Binder来完成APP进程之间的通信,同时使用匿名共享内存来作为共享数据的载体。ContentProvider支持访问权限管理机制,以控制数据的访问者及访问方式,保证数据访问的安全性。1. 三大类
- ContentProvider(内容提供者)
- ContentResolver(内容解析者)
- ContentObserver(内容观察者)
假如我们现在有个应用A 提供了数据 ,应用B要操作应用A的数据,那么
应用A使用ContentProvider(内容提供者)去共享自己数据
应用B使用ContentResolver(内容解析者)去操作应用A的数据
应用B通过ContentObserver(内容观察者)去监听应用A的数据变化
当应用A的数据发送改变时,通知ContentObserver去告诉应用B数据变化了及时更新
2. URI
URI(Uniform Resource Identifier)即统一资源标识符,是一个用于标识某一互联网资源名称的字符串。以联系人Contacts的Uri为例,其结构如下所示:
- schema: Android中固定为content://。
- authority: 用于唯一标识一个ContentProvider。
- path: ContentProvider中数据表的表名。
- id: 数据表中数据的标识,可选字段。
3. UriMatcher类
在ContentProvider 中注册URI,根据 URI 匹配 ContentProvider 中对应的数据表,只提供了两个方法——addURI和match方法。
4. ContentUris类
操作 URI,核心方法有两个:withAppendedId()&parseId()
// withAppendedId()作用:向URI追加一个id
Uri uri = Uri.parse("content://cn.scu.myprovider/user")
Uri resultUri = ContentUris.withAppendedId(uri, 7);
// 最终生成后的Uri为:content://cn.scu.myprovider/user/7
// parseId()作用:从URL中获取ID
Uri uri = Uri.parse("content://cn.scu.myprovider/user/7")
long personid = ContentUris.parseId(uri);
//获取的结果为:7
5. ContentProvider的使用
ContentProvider 的使用可以简单的归纳为以下几步:
1、创建自己的数据列表;
2、自定义ContentProvider实现相关的抽象方法;
3、在AndroidManifest中声明provider以及定义相关访问权限;
4 、通过ContentResolver根据URI进行增删改查。