Android四大组件的作用及多进程的开启

一、Android四大组件

Android四大组件除了BroadcastReceiver以外,其他三种组件都必须在AndroidManifest中注册,对于BroadcastReceiver来说,既可以在AndroidManifest中注册,也可以通过代码来注册。在调用方式上,Activity、Service和BroadcastReceiver需要借助Intent,而ContentProvider无须借助Intent。

二、Activity

Activity 是一种展示型组件,其作用就在于向用户直接展示一个界面,并且接收用户操作信息从而进行交互。Activity 是唯一一种可以直接感知的组件,可以说,在用户看来 Activity 就是一个Android应用的全部。

1. 启动方式

Activity 启动方式有两种,显式启动和隐式启动

  • 显示启动:明确指定一个 Activity 组件
Intent intent=new Intent(this,SampleActivity.class);
startActivity(intent);
  • 隐式启动:指向一个或多个 Activity 组件,需要我们再 AndroidManifest 中为 Activity 配置 intent-filter
Intent intent=new Intent("com.wuc.sampleActivity");
startActivity(intent);
<activity android:name=".SampleActivity">
    <intent-filter>
        <action android:name="com.wuc.sampleActivity"/>
        <category android:name="android.intent.category.DEFAULT"/>
    </intent-filter>
</activity>
2. 生命周期

(1)正常情况下,Activity 会经历如下生命周期

  • onCreate:表示 Activity 正在被创建。在这个方法中可以做一些初始化操作,比如调用 setContentView 去加载界面布局资源,初始化 Activity 所需数据等。
  • onRestart:表示 Activity 正在重新启动。当当前 Activity 从不可见重新变为可见状态时,onRestart 就会被调用。这种情形一般是用户行为导致,比如用户按 Home 健切换桌面或者用户打开了一个新的 Activity,这时当前 Activity 就会暂停,也就是 onPause 和 onStop 被执行了,接着用户又回到这个 Activity,就会出现这种情况。
  • onStart:表示 Activity 正在被启动,即将开始,这时 Activity 已经可见了,但还没有出现在前台,还无法和用户交互。这个时候可以理解为 Activity 已经显示出来了,但我们还看不到。
  • onResume:表示 Activity 已经可见了,并且出现在前台并开始活动。要注意这个和 onStart 的对比,onStart 和 onResume 都表示 Activity 已经可见,但是 onStart 的时候 Activity 还在后台,onResume 的时候 Activity 才显示到前台。
  • onPause:表示 Activity 正在停止,正常情况下,紧接着 onStop 就会被调用。在特殊情况下,如果这个时候快速的在回到当前 Activity,那么 onResume 会被调用,但这种情况很难重现。此时可以做一些存储数据、停止动画等工作,但不能太耗时,因为这会影响到新 Activity 的显示,onPause 必须先执行完,新 Activity 的 onResume 才会执行。
  • onStop:表示 Activity 即将停止,可以做一些稍微重量级的回收工作,但不能太耗时。
  • onDestroy:表示 Activity 即将被销毁,这是 Activity 生命周期最后一个回调,可以做一些回收工作和最终的资源释放。

下图详细的描述了 Activity 各种生命周期的切换过程:

Activity 的生命周期切换过程.png

三、Service

Service主要用于在后台执行一系列计算任务,耗时的后台任务,需要单独的线程去完成,因为Service本身是运行在主线程的。Service不与用户产生UI交互,其他的应用组件可以启动Service,即便用户切换了其他应用,启动的Service仍可在后台运行。
1. Context.startService()启动
被启动的服务的生命周期:onCreate -> onStartCommand -> onDestroy

  • 如果Service被startService方法启动多次,其中只会调用一次onCreate方法,但是会调用多次onStartCommand,此时系统只会创建一个Service实例,因此只需要调用一次stopService方法就可以停掉服务。
  • 如果Service被某个Activity 调用startService方法启动,不管是否有Activity使用bindService绑定或unbindService解除绑定到该Service,该Service都在后台运行。
  • startService启动的Service将会一直在后台运行,而不管对应程序的Activity是否在运行,直到被调用stopService、或自身的stopSelf方法、或系统资源不足android系统也可能结束该服务。

2.bindService
被绑定的服务的生命周期:onCreate-> onBind-> onUnbind->onDestory()

  • 如果Service被某个Activity调用bindService方法绑定启动,不管调用bindService几次,只会调用一次onCreate方法,同时始终不会调用onStartCommand方法。
  • 当连接建立之后,Service将会一直运行,除非调用unbindService断开连接、或之前调用bindService的Context 不存在了,如Activity被finish的时候,系统将会自动停止Service,对应的将被调用onDestroy方法。

3. startService和bindService混合使用
需求场景:在activity中要得到service对象进而能调用对象的方法,但同时又不希望activity finish的时候service也被destory了。

  • 先startService,后bindService

1、先调用startService,后调用bindService。服务的执行过程为:

onCreate —》onStartCommand —》onStart —》onBind  —》(onServiceConnected)

2、先unBindService,后stopService。服务结束的执行过程:

onUnbind —》onDestroy

注意:unBindService会执行到onUnbind,stopService会执行到onDestroy。
3、先stopService,后unBindService。服务结束的执行过程:

onUnbind —》onDestroy

注意:stopService不会执行任何操作,unBindService会执行到onUnbind—》onDestroy。

  • 先bindService,后startService

1、先调用startService,后调用bindService。服务的执行过程为:

onCreate —》onBind  —》(onServiceConnected) —》onStartCommand —》onStart

2、先unBindService,后stopService。服务结束的执行过程:

onUnbind —》onDestroy

注意:unBindService会执行到onUnbind,stopService会执行到onDestroy。
3、先stopService,后unBindService。服务结束的执行过程:

onUnbind —》onDestroy

注意:stopService不会执行任何操作,unBindService会执行到onUnbind—》onDestroy。

总结:
1、若被停止的服务依然有ServiceConnection 与其绑定,则服务不能销毁,直至我们把所有ServiceConnection 解绑
2、当所有ServiceConnection 解绑后,系统会自动销毁服务。(不包括同时用startService()启动的情况。此时,我们不得不再调用一次stopService来销毁它)
3、多次bindService时,服务本身的onBind不会被多次执行。
4、bind上一个Service后,执行一次unBindService就够了。不然会出错。
5、一个App里,同一个Activity多次bind一个服务,除了第一次,后面的bind不会有任何onBind、onServiceConnected打印。
一个App里,不同的Activity去bind一个服务,第一次bind有onBind、onServiceConnected打印,后面的bind只会有onServiceConnected打印。
6、一个Activity bind上一个Service后,如果Activity finish前没有调用unBind,App会崩溃

四、BroadcastReceiver

主要用于在不同的组件乃至不同的应用之间传递消息,不与用户产生交互,工作在系统内部。
注册的两种方式:
1、静态注册
静态注册是在AndroidManifest.xml文件中配置的

<receiver android:name=".MyReceiver">  
     <intent-filter>  
            <action android:name="android.intent.action.MY_BROADCAST"/>  
            <category android:name="android.intent.category.DEFAULT" />  
     </intent-filter>  
</receiver> 

配置了以上信息之后,只要是android.intent.action.MY_BROADCAST这个地址的广播,MyReceiver都能够接收的到。注意,这种方式的注册是常驻型的,也就是说当应用关闭后,如果有广播信息传来,MyReceiver也会被系统调用而自动运行。
2、动态注册
动态注册需要在代码中动态的指定广播地址并注册,通常我们是在Activity或Service注册一个广播

MyReceiver receiver = new MyReceiver();  
          
IntentFilter filter = new IntentFilter();  
filter.addAction("android.intent.action.MY_BROADCAST");  
          
registerReceiver(receiver, filter); 

注意,registerReceiver是android.content.ContextWrapper类中的方法,Activity和Service都继承了ContextWrapper,所以可以直接调用。在实际应用中,我们在Activity或Service中注册了一个BroadcastReceiver,当这个Activity或Service被销毁时如果没有解除注册,系统会报一个异常,提示我们是否忘记解除注册了。所以,记得在特定的地方执行解除注册操作:

@Override  
protected void onDestroy() {  
    super.onDestroy();  
    unregisterReceiver(receiver);  
}

注意,这种注册方式与静态注册相反,不是常驻型的,也就是说广播会跟随程序的生命周期。
当注册完成之后,这个接收者就可以正常工作了。我们可以用以下方式向其发送一条广播:

Intent intent = new Intent("android.intent.action.MY_BROADCAST");  
intent.putExtra("msg", "hello receiver.");  
sendBroadcast(intent);

注意,sendBroadcast也是android.content.ContextWrapper类中的方法,它可以将一个指定地址和参数信息的Intent对象以广播的形式发送出去。

五、ContentProvider

ContentProvider是一种数据共享型组件,用于向其他组件乃至其他应用共享数据

  • ContentProvider 封装了数据的跨进程传输,我们可以直接使用 getContentResolver() 拿到 ContentResolver 进行增删改查即可。
  • ContentProvider 以一个或多个表(与在关系型数据库中的表类似)的形式将数据呈现给外部应用。 行表示提供程序收集的某种数据类型的实例,行中的每个列表示为实例收集的每条数据。

实现一个 ContentProvider 时需要实现以下几个方法:

  • onCreate():初始化 provider
  • query():查询数据
  • insert():插入数据到 provider
  • update():更新 provider 的数据
  • delete():删除 provider 中的数据
  • getType():返回 provider 中的数据的 MIME 类型

注意:
1、onCreate() 默认执行在主线程,别做耗时操作,query() 也最好异步执行
2、上面的 4 个增删改查操作都可能会被多个线程并发访问,因此需要注意线程安全

ContentProvider 与 URI

ContentProvider 使用 URI 标识要操作的数据,这里的内容 URI 主要包括两部分:
1、authority:整个提供程序的符号名称
2、path:指向表的名称/路径
内容 URI 统一的形式就是:

content://authority/path

如:

content://user_dictionary/words

当调用 ContentResolver 方法来访问 ContentProvider 中的表时,需要传递要操作表的 URI。

在通过 ContentResolver 进行数据请求时(比如 contentResolver.insert(uri, contentValues);), 系统会检查指定 URI 的 authority 信息,然后将请求传递给注册监听这个 authority 的 ContentProvider 。这个 ContentProvider 可以监听 URI 想要操作的内容,Android 中为我们提供了 UriMatcher 来解析 URI。

六、开启多进程

在Android中使用多进程的方法,那就是在AndroidManifest中给四大组件(Activity,Service,Receiver,ContentProvider)指定android:process属性
process属性的值可以随便设置

<activity   
      android:name="com.wuc.sample.SecondActivity"  
      android:process=":second" />  
<activity   
      android:name="com.wuc.sample.ThirdActivity"  
      android:process="com.wuc.sample.third" /> 

假设包名是:com.wuc.sample
1、":"的含义是指要在当前的进程名前面附加上当前的包名,对于SecondActivity来说,它的完整的进程名为"com.wuc.sample:second"。对于ThirdActivity中的声明方式,它是一种完整的命名方式,不会附加包名信息;
2、以":"开头的进程属于当前应用的私有进程,其他应用的组件不可以和它跑在同一个进程中;而进程名不以":"开头的进程属于全局进程,其他应用通过ShareUID方式可以和它跑在同一个进程中。

Android系统为每个应用分配一个唯一的UID,具有相同UID的应用才能共享数据。两个应用通过ShareUID跑在同一个进程中是有要求的,需要这两个应用有相同的ShareUID并且签名相同才可以。在这种情况下,它们可以互相访问对方的私有数据,比如data目录,组件信息等。
如果它们跑在同一个进程中,那么除了能共享data目录,组件信息,还可以共享内存数据等等

多进程模式的运行机制

SecondActivity运行在一个单独的进程中,Android为每一个应用程序分配了一个独立的虚拟机,或者说为每个进程都分配了一个独立的虚拟机,不同的虚拟机在内存分配上 有不同的地址空间,这就会导致在不同的虚拟机中访问同一类的对象会产生多份副本。在一个进程中修改某个值只会影响当前进程,对其他进程不会造成任何影响。
所有运行在不同进程中的四大组件,只要它们之间如要通过内存来共享数据,都会失败,这也是多进程所带来的主要影响。

一般来说,使用多进程会造成如下几方面的问题:

  • 静态成员和单例模式完全失效 (不是同一块内存,会产生不同的副本)
  • 线程同步机制完全失效 (不是同一块内存,所以对象也不是同一个,因此类锁、对象锁也不是同一个,不能保证线程同步)
  • SharedPreferences 的可靠性下降 (SharedPreferences不支持多个进程同时写,会有一定的几率丢失数据)
  • Application会多次创建(Android为每个进程分配独立的虚拟机,这个过程其实就是启动一个应用,所以Application会被创建多次),所以我们不能直接将一些数据保存在Application中。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,547评论 4 374
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,787评论 2 308
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 112,175评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,752评论 0 223
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,169评论 3 297
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 41,056评论 1 226
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,195评论 2 321
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,980评论 0 214
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,724评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,872评论 2 254
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,344评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,667评论 3 264
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,379评论 3 245
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,202评论 0 9
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,992评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,189评论 2 286
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,987评论 2 279

推荐阅读更多精彩内容