1.Activity
Activity
在Android
中负责ui
处理,所有的页面都需要它来进行展示。当然fragment
也可以用来展示数据,但是它必须要依附于Activity
。
1.1Activity的构成
Activity
的构成从外到内由Activity
-> PhoneWindow
-> DecorView
-> DefaultLayout
(这里指的是布局最外层的FrameLayout
) -> Content title Layout
/mContentParent
-> /Content Layout
(这一部分就是我们通过setContentView来进行设置的部分,添加到mContentParent
)。
从上图可以看出,我们的Activity
在不使用setContentView
方法来填充布局时,其实已经有了默认的布局。当我们创建一个Activity
时,不进行setContentView设置时,打开该Activity
能正常显示,只是显示的是空白页面而已。
1.2Activity启动模式
Activity
的启动模式主要分为standard
、SingTop
、SingTask
、SingInstance
四种启动模式。standard模式
:也就是Activity
默认的启动模式,每打开一个Activity
就在任务栈里创建一个实例。SingTop模式
:栈顶模式,如果当前启动的Activity
已经在栈顶了,就不会重新去创建该实例,反之,就会去创建。SingTask
:单任务模式,这个是说在同一个任务栈里同一个Activity
只会存在一个实例,如果任务栈里已经存在该实例,那么就会把该实例之上的实例全部出栈,让该实例回到栈顶。但是在不同的任务栈可以同时出现。它的范围是在同一任务栈。SingInstance
:单实例模式,属于SingInstance
模式的Activity
在被启动的时候,会单独开一个任务栈来存放该实例。也就是说整个操作系统只会存在这么一份该Activity
的实例,它的范围是整个系统。
1.3关于newIntent什么时候被调用的问题
之前在一家公司面试的时候,面试官就问了这个问题,而我没答上来,今天刚好可以来说说我们的newIntent什么时候会被调用。上面讲了Activity
的启动模式,而这个newIntent()
方法又是和启动模式密不可分的。主要是这样子的。如果在启动Activity
时,不会调用oncreate
方法时,就是调用我们的newIntent
方法,因为要传递Intent
信息嘛。
2.Service和AIDL(Android 接口定义语言)
service
主要是处理非UI界面的业务处理,也就是程序在后台处理的解决方案。它的生命周期比较简单,只有三个方法:onCreate
(主要是在服务启动的时候调用,同一服务多次启动只会被调用一次),onStartCommand
(服务每次启动都会被调用一次),onDestory
(服务被销毁的时候被调用)。要使用服务必须要在Androidmanifest
配置文件里进行注册,然后通过Context.startService
方法来开启服务。当然也可以通过bindService
来启动服务。
2.1 service的运行线程
service
默认是运行在ui
线程的,所以在service
里不能处理耗时的操作,不然会阻塞ui
线程,造成ui
线程卡顿。如果有耗时的操作需要开子线程来处理。当然,我们可以在Androidmanifest
文件注册的时候指定service
运行在单独的线程。
2.2 service线程优先级
这里有个问题就是:我们的四大组件都是运行在主进程里的,那么,很多地方都是说Android进程优先级,那同一进程,何来优先级,所以我把这壮大理解为线程,四大组件运行在默认运行在同一进程的不同线程里,这样理解不知道有没有问题,未验证。
Android中线程优先级为(1-5):
1.前台线程(foreground process)
这里主要有:
1.当前用户操作的
Activity
所在线程
2.和我们Activity
通过bindService
进行绑定的Service
所在线程
3.调用了startForeground
的Service
所在线程
2.可见线程(Visible process)
这里主要有:
1.调用了onPause(),但没有执行
onStop
处于暂停状态的Activity
2.绑定到处于暂停状态的Activity
的Service
3.服务线程(service process)
这就是我们
Service
默认的优先级。
4.后台线程(Background process)
这里主要有:
1.处理停止状态的
Activity
,也就是调用了onStop
方法的Activity
5.空线程(Empty process)
服务的默认优先级为3,也就是说当系统资源不够用的时候会优先kill
掉我们的Service
,当系统资源充足的情况下我们的service
又会自动运行进来。我们可以通过startForeground
来提升我们Service
的优先级。
2.3提升Service
优先级示例
通过startForeground
来调用service
时会在我们通知栏出现一个service
的界面。我们通过仿墨迹天气来简单实现一下通知栏提示的例子。
/**
* Created by allen on 16/3/27.
*/
public class WeatherService extends Service {
private static final int NOTIFY_ID = 222;
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
showNotifycation();
}
private void showNotifycation() {
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.a1)
.setContentTitle(getString(R.string.weather_title))
.setContentText("今天中转阵雨");
//创建通知被点击是触发的Intent
Intent resultIntent = new Intent(this, MainActivity.class);
//创建任务栈Builder
TaskStackBuilder taskStackBuilder = TaskStackBuilder.create(this);
taskStackBuilder.addParentStack(MainActivity.class);
taskStackBuilder.addNextIntent(resultIntent);
//获取延时任务并设置到通知栏
PendingIntent resultPendingIntent = taskStackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
mBuilder.setContentIntent(resultPendingIntent);
//创建通知栏管理器并发送通知
NotificationManager nManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
//构建通知
Notification mNotification = mBuilder.build();
nManager.notify(NOTIFY_ID, mNotification);
startForeground(NOTIFY_ID, mNotification);
}
上面代码运行的样子
点击之后会跳转到我们设置的MainActivity
。
2.4 AIDL(Android 接口定义语言)
AIDL
解决的问题就是在不同的进程之间让应用程序通信,也就是在不同的应用程序之前进行通信。AIDL
主要是一个进程间通信的接口定义规范。
编译器根据AIDL
文件生成一个系列对应的Java类,通过预先定义的接口和Binder来完成进程中的通信,也就是说最终进程间通信实际是通过底层Binder
来完成的。
AIDL运行原理
说白了,AIDL
定义一个接口,客户端(也就是调用端)通过bindService
来与远程服务端建立一个连接,远程服务端返回一个IBinder
对象,该对象其实是服务端Binder
的一个代理BinderProxy
,客户端拿到这个代理之后,通过asInterface
将服务端的BinderProxy
对象包装成本地的Proxy
,并将远程服务端的BinderProxy
对象赋值给了Proxy
对象的mRemote
字段,客户端就是通过这个mRemote
执行远程函数调用的。
2.5关于AIDL现实生活中的例子说明
打个比方,两家公司正在进行一个合作磋商,这次合作已经签署了合同,只是还有些细枝末节的问题还在沟通。大BOSS当然比较忙,这些小事就安排一个手下来处理沟通。由于两家公司比较远,双方代表通过电话来进行沟通。BOSS-A对A职员说,这次支付的酬劳不能低于10W.于是A职员就打电话跟B职员进行电话说明,职员B就去请示BOSS-B,BOSS-B确认后就由职员B回复职员A,最后由职员A将BOSS-B的回复反馈给了BOSS-A.
这里BOSS-A和BOSS-B两者分别代表Binder
通信客户端和服务器;职员A和职员B分别代表BinderProxy
和Proxy
;而电话通信方式就代表了我们的Binder
。
3.BroadCast(广播)
BroadCast
在Android
中是进行跨进程通信的主要手段,它的实现其实就是一个订阅模式–也就是我们通常所说的观察者模式。它可以在应用程序内部甚至可以在应用程序之间来进行通信。BroadCast
最大的特点就是广播的发送者不必知道接收方是否接收数据,以及接收方怎么处理数据,通过这种形式将发送者和接收者完全的解耦,它们之间通过intent
来进行数据的传递。
3.1 BroadCast分类
3.1.1普通广播
普通广播是完全异步的,通过sendBroadCast
来发送广播,消息传递效率比较高。因为是异步的关系,那接收者的接收顺序也是不确定的。缺点:接收者并不能处理广播或者将广播继续传递,并且无法终止广播,直到广播接收者全部接收到广播后,广播才会停止。
3.1.2有序广播
有序广播通过sendOrderBroadCast
来发送广播。广播接收者可以设定接收广播的优先级,优先级越高,就越是先接收到广播;每个广播接收者都可以对接收到的广播进行修改,通过setResult()
方法来将广播传递给下一个接收者,同时通过getResult
来接收上一个广播接收者传递过来的数据,或者直接中断广播的传递。
3.1.3本地广播
在sdk21
的v4包中新增了本地广播,也就是LocalBroadCastManager
.之前的广播都是整个系统级别的,所有的应用程序都可以接收到广播,这样子会存在一些安全隐患。所以就有了本地广播。本地广播主要是将广播的传播范围从全局的缩小到单个应用级别内。它与普通广播的用法基本相同,只是在调用相应方法的时候,使用LocalBroadcastManager.getInstance(Context context)
来调用相应的方法即可。没有特别需求的应用,都应该用本地广播来代替普通广播。
3.1.4 sticky广播sticky
广播通过sendStickyBroadCast
来发送广播,用此函数发送的广播会一直滞留在内存中,当有匹配的广播接收者注册时,该广播接收者就会接收到此广播。使用此广播时要添加一个权限:<uses-permission android:name="android.permission.BROADCAST_STICKY"/>
此广播只保留最后一条广播,并且一直保留下去,需要调用removeStickyBroadCast来移除该广播。
4.ContentProvider(处共享数据)
ContentProvider
主要是用来不同应用程序间进行数据库访问的一种技术。一般情况下用得非常少,我们一般只会以消费者的角色去系统程序里获取一些比如联系人,短信相关的数据。在使用ContentProvider
时,我们通过Uri来对要访问的资源进行定位。
4.1 Uri的组成
uri的组成分为四部分:schema,authority,path,id.
schema:Android固定设置为content://。xxx://这就是schema的格式
authority:用于唯一标识这个ContentProvider
,外部调用者可以通过这个标识来找到它。
path:指定要操作的数据库表
id:可选字段,比如某个联系人:content://contacts/people/5,这个5表示id,也就是查询联系人id为5的数据
关于ContentProvider
就写到这里吧,用得太少了。。。
5.总结
Android
四大组件使用组件化的思想,使得Android
变得非常灵活,Activity
提供UI
界面的管理;Service
提供与UI
无关的服务;ContentProvider
用于在不同应用程序中共享数据;BroadCast
用于完成跨进程的数据通信,而这四者通过Intent
连接到了一起,但彼此之间却几乎没有耦合。组件式开发的设计模式,是非常灵活且可复用的。