Android基础汇总之四大组件

任何技能的熟练都离不开扎实的基础知识,时不时的回顾这些基础内容会对开发技能的理解有很好的帮助。
随着学习的进度随时更新基础知识

一、前言

本章内容甚多,参考了许多大能的文章,感谢各位大能的分享。
Android之Activity全面解析
Android Activity 你所需要知道的一切
务必知道的Android service的完全详解
Android Service一些知识与理解
Android BroadcastReceiver详解
Android四大组件:BroadcastReceiver史上最全面解析
Android四大组件——ContentProvider(基础篇)
Android ContentProvider详解

二、四大组件之Activity

1. Activity生命周期
image.png

完整生命周期
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. 生命周期
image.png

(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支持访问权限管理机制,以控制数据的访问者及访问方式,保证数据访问的安全性。
image.png
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为例,其结构如下所示:

image

  • 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进行增删改查。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容