Android开发进阶第一章—Android的构成基石读书笔记

图1

1.Activity

ActivityAndroid中负责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的启动模式主要分为standardSingTopSingTaskSingInstance四种启动模式。
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.调用了startForegroundService所在线程

2.可见线程(Visible process)
这里主要有:

1.调用了onPause(),但没有执行onStop处于暂停状态的Activity
2.绑定到处于暂停状态的ActivityService

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分别代表BinderProxyProxy;而电话通信方式就代表了我们的Binder

3.BroadCast(广播)

BroadCastAndroid中是进行跨进程通信的主要手段,它的实现其实就是一个订阅模式–也就是我们通常所说的观察者模式。它可以在应用程序内部甚至可以在应用程序之间来进行通信。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连接到了一起,但彼此之间却几乎没有耦合。组件式开发的设计模式,是非常灵活且可复用的。