Andriod基础
前言
没想到要久违地学习安卓的知识了。
先来复习一下最基础的知识:
安卓的四大组件分别是:
- Activity:类似于WPF中的windows,用来显示界面的视图。
- Service:服务,类似WPF中的后台进程。
- BroadcastReceiver:广播接收器。
- contextProvider:用于在不同进程之间共享数据。
Activity
Activity是安卓的四大组件之一,我们平时使用Activity的时候总是后来的Activity覆盖在先来的上面,撤销的时候会逐级返回,这非常容易让我们想到栈的数据结构,事实也正是如此,这个用于管理Activity的栈被称为返回栈。
生命周期
每个Activity在其生命周期中一共有4中状态:
- 运行状态:处于返回栈顶的Activity。
- 暂停状态:当一个Activity不在栈顶但是可见的时候就会进入暂停状态,常见于弹出对话框的时候,第二个Activity不在栈顶但是可见,这个时候就处于暂停状态,系统不愿意回收这种Activity,但是在内存极低的时候还是会回收这个Activity。
- 停止状态:当一个Activity不在栈顶且不可见的时候就处于停止状态,当其他地方需要内存的时候就会回收这个活动。
- 销毁状态:一个Activity从返回栈移除之后就进入了销毁状态,系统会优先回收这个状态的Activity保持手机内存充足。
生存期
Activity类中定义了7个回调方法,覆盖了Activity生命周期的每一个环节。
- onCreate():Activity的初始化方法,一般用于加载组件等。
- onStart():这个方法在Activity从不可见变为可见时候进行调用。
- onResume():用于Activity需要和用户进行交互的时候调用。
- onPause():当Activity进行切换的时候调用,用于保存当前Activity的上下文,释放部分资源。
- onStop():Activity变为不可用的时候调用和onPause()的区别在于,如果是一个新的Activity那么会同时调用这两个方法,但是如果是对话框那么就只会调用onPause()方法。
- onDestroy():当Activity销毁的时候调用。
- onRestart():当Activity重新启动的时候调用。
Activity被回收了怎么办
上面说到,处于停止状态的Activity是会被系统回收的,那么当我们重新返回上一个Activity的时候,这个被回收的Activity还会显示吗?
答案是会显示,但是这是一个重新调用onCreate的Activity,所以并不会保存之前填入的一些信息,如果想要保存这些信息,可以调用onSaveInstanceState()方法暂时保存一下。
onSaveInstanceState()携带一个Bundle类的参数用于保存一些数据。
启动模式
Activity一共有4种启动模式:
- standard:类似于多例模式,每次启动都会新建一个。
- singleTop:类似于单例模式,但是不同的是只有活动在返回栈顶的时候才会复用,否则还是会新建。
- singleTask:类似于单例模式,只要返回栈中存放了这个Activity就不在新建。
- singleInstance:这个模式就比较特殊了,它会给Activity生成一个新的返回栈。这个模式的使用场景主要是如果你的应用调起了另一个应用,那么这两个应用需要能够同时管理一些共同的Activity,那么新建一个返回栈,然后由二者共同管理就是一个非常好的选择。
布局
安卓的布局分为三种:LinearLayout、RelativeLayout以及FrameLayout。
- LinearLayout:线性布局,可以在水平或者垂直方向上排列组件,可以使用百分比布局调整相对位置和大小。
- RelativeLayout:相对布局,可以指定一个组件在父布局的相对位置,比如左上、右下等等。
- FrameLayout:帧布局,会把所有的组件都默认重叠在左上角,当然也可以字节选择不同的位置,主要用于Fragment相关。
Fragment
Fragment,碎片,主要用于填充Activity。
由于各种手机、平板的尺寸大小各不相同,如果都采用相同的Activity会导致很多机型上的组件和视图被拉长,不太美观,这种情况下,安卓3.0引入了Fragment用于填充不同的屏幕。
生命周期
和Activity一样,Fragment也存在生命周期。
由于Fragment是完全依赖于Activity的,所以生命周期也取决于Activity的生命周期。
- 运行状态:Activity处于运行状态时,Fragment也处于运行状态。
- 暂停状态:Activity处于暂停状态时,Fragment也处于暂停状态。
- 停止状态:Activity处于停止状态时,Fragment也处于停止状态。
- 销毁状态:Activity处于销毁状态时,Fragment也处于销毁状态。
回调函数
Activity有的回调函数,Fragment也都有,并且Fragment增加了额外几种回调函数。
- onAttach():当Fragment和Activity建立关联的时候调用。
- onCreateView():为Fragment创建视图时调用。
- onActivityCreated():确保与Fragment相关联的Activity已经创建完毕时调用。
- onDestroyView():当与Fragment关联的视图被移除时调用。
- onDetach():当Fragment和Activity解除关系时调用。
BroadcastReceiver
安卓中的广播机制是非常灵活的,每个应用都可以对自己关心的广播进行注册,一旦当一个事件发生的时候,就能接收到这个事件的广播。除此之外,应用程序也可以自己发送广播。
安卓中的广播主要分为两种:
- 标准广播:一种完全异步执行的广播,当一条标准广播被发出之后,所有的BroadcastReceiver都会接受到这个广播,毫无顺序可言,所以也是完全不可拦截的。
- 有序广播:一种同步执行的广播,在广播发出之后,同一时间只有一个BroadcastReceiver能收到这条广播,处理完成之后再传递给下个BroadcastReceiver。所以时一种可以拦截的广播。
ContentProvider
ContentProvider主要用于在不同的进程之间实现数据共享的功能,由于是多个进程,所以他也是线程安全的,当然ContentProvider也涉及到一个权限的问题。
安卓有已经实现好的ContentProvider类,可以通过Context中的getContentResolver()
方法来获取该类的实例。ContentProvider提供了update()
、insert()
、delete()
、query()
用于增删改查,这和数据库非常相似(安卓其实也有一个内嵌的轻量级关系型数据库,叫做SQLite,操作方法也与之相似)。
ContentProvider通过URI(资源定位符,我们在http中已经见识过他了)定位想要查询的资源。
除了使用安卓自带的ContentProvider,我们也可以通过继承的方式覆盖他的增删改查和初始化函数,来自定义一个自己的ContentProvider。
Service
终于到了最后一个组件了,Service是一个用于在后台执行任务的组件,所以很显然说到Service离不开多线程。
和我们熟悉的WPF一样,安卓应用虽然是一个支持多线程的进程,但是对于UI的更新操作只能由主线完成。所以子线程想要修改UI,就得异步地委托给主线程进行修改,在安卓中可以通过继承AsyncTask来完成。
说完了子线程更新UI,我们再回到Service上面来,想要实现一个Service也非常简单,只要继承Service类就行了,Service类中有一个抽象方法:onBind()
,这是一个必须由子类实现的方法,这个方法用于Activity和Service之间的通信。
除此之外,一般会覆盖Service的onCreate()
方法、onStartCommand()
方法和onDestory()
方法。第一个是创建Service时调用的方法,第二个是每次启动Service都会调用方法,最后一个则是在销毁的时候调用的方法。
当然Service除了可以在后台执行任务,它也可在前台执行一些任务。
从安卓8.0开始,如果一个应用不在前台的话,它的Service也是可能会被系统回收的,但是这样对于一些比如网易云音乐之类需要在后台运行的程序来说就没有那么友好了。
为了解决这个问题,Service还可以运行在前台,当一个前台Service运行的时候,他会像通知一样显示在状态栏里,可以通过下拉来显示更加详细的信息。
上面说到的Service其实都是在主线程里运行的,如果想要单独起一个线程来执行任务,可以使用IntentService。
Handler
说到多线程,那么不得不提到这些线程是如何通信的了。
在安卓中使用了一个叫做handler的机制进行消息的异步发送和接受,有点类似于消息队列,它一共分为4个部分:
- Message,线程之间传递的消息,用于不同线程之间的数据交互。Message中的what字段用来标记区分多个消息,arg1、arg2 字段用来传递int类型的数据,obj可以传递任意类型的字段。
- Handler,用于发送和处理消息。其中的sendMessage()用来发送消息, handleMessage()用于消息处理,进行相应的UI操作。
- MessageQueue,消息队列(先进先出),用于存放Handler发送的消息,一个线程只有一个消息队列。
- Looper,可以理解为消息队列的管理者,当发现MessageQueue中存在消息,Looper就会将消息传递到handleMessage()方法中,同样,一个线程只有一个Looper。
使用Handler有一个需要注意的地方就是,它需要被声明为静态类,不然会引起OOM,因为非静态内部类和匿名类的Handler会使得引用的类无法被回收,从而导致OOM。
Kotlin
Kotlin是最近几年刚被声称为安卓开发一级语言,Kotlin起源于Java,它的原理就是把自己的代码编译(确切的说是解释)为虚拟机可以识别的Class文件,反正虚拟机并不关心它的Class文件是如何生成的,照着执行就是了。除此之外,Kotlin可以完美支持Java的第三方库,所以这个语言在国外迅速得到了青睐。
当然最重要的一点,Kotlin可以选择一个变量是否能为Null,所以这在一定程度上解决了很多空指针异常。
这边就简单介绍一下基本的语法:
//可变成员变量
//Kotlin不需要以;结尾
var a = 10
//不可变成员变量
val b = 10
//指定类型,并且声明是可以为null的
//在Kotlin不显示指定?的变量都是不能为null的
fun doStudy(study:Study?){
//?.判空工具
//let函数将变量本身传递进函数并且立即执行
//it是lambda表示式在单个变量的时候的替代关键字
study?.let{
it.readBook()
}
}
JetPack
JetPack是一个开发组件工具集,在2018年由谷歌推出,可以帮助程序员编写出更加简洁的代码。
MVVM
MVVM(Model-View-ViewModel)是谷歌官方推荐的架构,他也包含在JetPack中,和MVC类似,主要是对View(视图)和Model(模型)之间相互转化,只不过MVC中使用的是Controller,MVVM中使用的是ViewModel。
Android 虚拟机
现在的Android采用了一个叫做ART的虚拟机,它基于JVM,但是缺不满足JVM的规范,所以算是一个魔改的JVM。
由于JVM中经常需要在栈中读取信息,这在性能较低的移动端上不太合适,所以ART不再采用栈的形式,而是把堆划分为4个不同的区域:
- Image Space
- Zygote Space
- Allocation Space
- Large Object Space
ART的GC也会根据具体情况的不同而选择不同的GC,一共有3种策略:
- 分代垃圾回收Sticky GC :只回收上一次GC到本次GC之间申请的内存。
- 局部垃圾回收Partial GC:回收除了Image Space和Zygote Space空间以外的其他内存垃圾。
- 全局垃圾回收Full GC:除了Image Space之外的空间的内存垃圾。
ART的GC分为3个不同的阶段:
- 阶段一:首先会进行一次轻量级的GC, GC完成后尝试分配。如果分配失败,则选取下一个GC策略,再进行一次轻量级GC。每次GC完成后都尝试分配,直到三种GC策略都被轮询了一遍还是不能完成分配,则进入下一阶段。
- 阶段二:允许堆进行增长的情况下进行对象的分配,如果还是分配失败,则会进行一次允许回收软引用的GC。
- 阶段三:如果对象是不可移动对象,则ART会把它分配在Non-Moving Space。如果对象是可移动的对象,那么就进行一次同构空间压缩(Main Space),压缩后也增加了分配成功的可能性。
除了对GC进行优化之外,ART还对代码编译进行了优化。在JVM中总是由前端编译器Javac编译成Class文件,然后再交由后端编译器转换为机器码,但是在ART中进行了优化。
Java文件在编译成class文件,然后经过Android平台的dx工具转换为Dex文件后,同Native code(JNI)和资源一起打包成apk,apk安装到手机后解压出Dex文件。Dalvik会通过dexopt工具将Dex优化,成为Odex文件,Odex文件的效率比Dex高,但其中大部分代码仍然需要每次执行时编译;而ART则会将Dex通过dex2oat工具编译得到一个ELF文件,它是一个可执行的文件。
后记
安卓基础的部分先补充到这边,感觉网上很多人都瞎讲,根本不知道对不对,之后还是买本书慢慢学吧,这种东西也一口吃不成胖子。