Android
Activity
Activity启动模式
- standard
标准启动模式,也是activity的默认启动模式。在这种模式下启动的activity可以被多次实例化,即在同一个任务中可以存在多个activity的实例,每个实例都会处理一个Intent对象。如果Activity A的启动模式为standard,并且A已经启动,在A中再次启动Activity A,即调用startActivity(new Intent(this,A.class)),会在A的上面再次启动一个A的实例,即当前的桟中的状态为A-->A。
- singleTop
如果一个以singleTop模式启动的activity的实例已经存在于任务桟的桟顶,那么再启动这个Activity时,不会创建新的实例,而是重用位于栈顶的那个实例,并且会调用该实例的onNewIntent()方法将Intent对象传递到这个实例中。举例来说,如果A的启动模式为singleTop,并且A的一个实例已经存在于栈顶中,那么再调用startActivity(new Intent(this,A.class))启动A时,不会再次创建A的实例,而是重用原来的实例,并且调用原来实例的onNewIntent()方法。这是任务桟中还是这有一个A的实例。
如果以singleTop模式启动的activity的一个实例已经存在与任务桟中,但是不在桟顶,那么它的行为和standard模式相同,也会创建多个实例。
- singleTask
是栈内复用模式。这是最复杂的一种模式,因为它可能涉及到多个栈。当一个具有 singleTask 模式的 Activity 启动后,比如 Activity A,系统会首先寻找是否存在所需的任务栈,如果不存在,就重新创建一个任务栈,然后创建 A 的实例后把 A 放入到栈中。如果存在 A 所需要的任务栈,这时要看 A 是否在栈中有实例存在,如果有,那么系统就会把它调到栈顶并且调用它的 onNewIntent 方法,如果不存在,就创建 A 的实例并把 A 压入栈中。这里所说的 A 所需要的任务栈是什么意思呢?其实 Activity 是可以指定自己想要的任务栈的名字的,通过一个参数:TaskAffinity,默认情况下,所有的 Activity 所需要的任务栈的名字为应用的包名。使用时需要小心因为会将之前入栈的实例之上的实例全部移除,需要格外小心逻辑。
- singleInstance
总是在新的任务中开启,并且这个新的任务中有且只有这一个实例,也就是说被该实例启动的其他activity会自动运行于另一个任务中。当再次启动该activity的实例时,会重用已存在的任务和实例。并且会调用这个实例的onNewIntent()方法,将Intent实例传递到该实例中。和singleTask相同,同一时刻在系统中只会存在一个这样的Activity实例。
如果要使用这四种启动模式,必须在manifest文件中< activity >标签中的launchMode属性中配置
Activity生命周期
Fragment
- Fragment的产生与介绍
Android运行在各种各样的设备中,有小屏幕的手机,超大屏的平板甚至电视。针对屏幕尺寸的差距,很多情况下,都是先针对手机开发一套App,然后拷贝一份,修改布局以适应平板神马超级大屏的。难道无法做到一个App可以同时适应手机和平板么,当然了,必须有啊。Fragment的出现就是为了解决这样的问题。你可以把Fragment当成Activity的一个界面的一个组成部分,甚至Activity的界面可以完全有不同的Fragment组成,更帅气的是Fragment拥有自己的生命周期和接收、处理用户的事件,这样就不必在Activity写一堆控件的事件处理的代码了。更为重要的是,你可以动态的添加、替换和移除某个Fragment。
- Fragment的生命周期
Fragment必须是依存与Activity而存在的,因此Activity的生命周期会直接影响到Fragment的生命周期。官网这张图很好的说明了两者生命周期的关系:
可以看到Fragment比Activity多了几个额外的生命周期回调方法:
onAttach(Activity)
当Fragment与Activity发生关联时调用。
onCreateView(LayoutInflater, ViewGroup,Bundle)
创建该Fragment的视图
onActivityCreated(Bundle)
当Activity的onCreate方法返回时调用
onDestoryView()
与onCreateView想对应,当该Fragment的视图被移除时调用
onDetach()
与onAttach相对应,当Fragment与Activity关联被取消时调用
注意:除了onCreateView,其他的所有方法如果你重写了,必须调用父类对于该方法的实现
Intent
- 显式(设置Component)
显式,即直接指定需要打开的activity对应的类。
- 隐式
隐式,即不是像显式的那样直接指定需要调用的Activity,隐式不明确指定启动哪个Activity,而是设置Action、Data、Category,让系统来筛选出合适的Activity。筛选是根据所有的 < intent-filter > 来筛选。
Uri uri = Uri.parse("tel:15980665805");
Intent intent = new Intent(Intent.ACTION_CALL, uri);
startActivity(intent);
Android中的MVC
- 视图层(View):
一般采用XML文件进行界面的描述,这些XML可以理解为Android App的View。使用的时候可以非常方便的引入。同时便于后期界面的修改。逻辑中与界面对应的id不变化则代码不用修改,大大增强了代码的可维护性。
- 控制层(Controller):
Android的控制层的重任通常落在了众多的Activity的肩上。这句话也就暗含了不要在Activity中写逻辑代码,要通过Activity交割给Model业务逻辑层处理,这样做的另外一个原因是Android中的Actiivity的响应时间是5s,如果耗时的操作放在这里,程序就很容易被回收掉。
- 模型层(Model):
我们针对业务模型,建立的数据结构和相关的类,就可以理解为AndroidApp的Model,Model是与View无关,而与业务相关的。对数据库的操作、对网络等的操作都应该在Model里面处理,当然对业务计算等操作也是必须放在的该层的。
Service
- Service简介
服务是一个应用程序组件,可以在后台执行长时间运行的操作,不提供用户界面。一个应用程序组件可以启动一个服务,它将继续在后台运行,即使用户切换到另一个应用程序。此外,一个组件可以绑定到一个服务与它交互,甚至执行进程间通信(IPC)。例如,一个服务可能处理网络通信、播放音乐、计时操作或与一个内容提供者交互,都在后台执行。
- 使用方式分类:
类别 | 区别 |
---|---|
startService启动的服务 | 主要用于启动一个服务执行后台任务,不进行通信。停止服务使用stopService。 |
bindService启动的服务 | bindService方法启动的服务要进行通信。停止服务使用unbindService。 |
同时使用startService、bindService 启动的服务 | 停止服务应同时使用stopService与unbindService。 |
- Service的生命周期
通过这个图可以看到,两种启动Service的方式以及他们的生命周期,bindService的不同之处在于当绑定的组件销毁后,对应的service也就被kill了。
- 启动型服务的生命周期
一个Service被使用startService方法启动,不管是否调用了bindService(绑定服务)或unbindService(解除绑定服务)到该Service,该Service都会在后台运行并不受影响。
一个Service被使用startService方法启动多少次,onCreate方法只会调用一次,onStartCommand方法将会被调用多次(与startService的次数一致),且系统只会创建一个Service实例(结束该Service也只需要调用一次stopService),该Service会一直在后台运行,直至调用stopService或调用自身的stopSelf方法。
注:在系统资源不足的情况下,服务有可能被系统结束(kill);
- 绑定的服务的生命周期
当一个Service在被启动(startService)的同时又被绑定(bindService),该Service将会一直在后台运行,并且不管调用几次,onCreate方法始终只会调用一次,onStartCommand的调用次数与startService调用的次数一致(使用bindService方法不会调用onStartCommand)。同时,调用unBindService将不会停止Service,必须调用stopService或Service自身的stopSelf来停止服务。
当手机屏幕发生旋转时,如果Activity设置的是自动旋转的话,在旋转的过程中,Activity会重新创建,那么之前通过bindService建立的连接便会断开(之前的Context不存在了),服务也会被自动停止。
在新建一个Service后,记得在AndroidManifest.xml中注册Service,在application内添加需要注册的Service信息。
Content Provider
ContentProvider一般为存储和获取数据提供统一的接口,可以在不同的应用程序之间共享数据。
BroadcastReceiver
BroadcastReceiver广播接收者用于接收系统或其他程序(包括自己程序)发送的广播。
在android中,我们如果想接收到广播信息,必须自定义我们的广播接收者。要写一个类来继承BroadcastReceiver,并且重写其onReceive()方法,实现接收到特定广播所要做的事情。
- 代码中动态注册
步骤如下:
实例化自定义的广播接收者
实例化意图过滤器,并设置要过滤的广播类型(如,我们接收收到短信系统发出的广播)
使用Context的registerReceiver(BroadcastReceiver, IntentFilter, String, Handler)方法注册广播
//new出上边定义好的BroadcastReceiver
MyBroadCastReceiver yBroadCastReceiver = new MyBroadCastReceiver();
//实例化过滤器并设置要过滤的广播
IntentFilter intentFilter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED");
//注册广播
myContext.registerReceiver(smsBroadCastReceiver,intentFilter,
"android.permission.RECEIVE_SMS", null);
- 在Manifest.xml中静态注册
直接在Manifest.xml文件的< application >节点中配置广播接收者。
<receiver android:name=".MyBroadCastReceiver">
<!-- android:priority属性是设置此接收者的优先级( 从-1000到1000 ) -->
<intent-filter android:priority="20">
<actionandroid:name="android.provider.Telephony.SMS_RECEIVED"/>
</intent-filter>
</receiver>
第一种不是常驻型广播,也就是说广播跟随程序的生命周期。
第二种是常驻型,也就是说当应用程序关闭后,如果有信息广播来,程序也会被系统调用自动运行。
- 发送广播
当我们需要发送一个自定义的广播来通知程序中其他组件一些状态时,就可以使用发送一条广播的方式。
- 有两种方式分别发送两种不同的广播:
通过mContext.sendBroadcast(Intent)或mContext.sendBroadcast(Intent, String)发送的是无序广播(后者加了权限);
通过mContext.sendOrderedBroadcast(Intent, String, BroadCastReceiver, Handler, int, String, Bundle)发送的是有序广播。
- 区别:
无序广播:所有的接收者都会接收事件,不可以被拦截,不可以被修改。
有序广播:按照优先级,一级一级的向下传递,接收者可以修改广播数据,也可以终止广播事件。
Android中的Thread, Looper和Handler机制
接收消息的“消息队列” ——【MessageQueue】
阻塞式地从消息队列中接收消息并进行处理的“线程” ——【Thread+Looper】
可发送的“消息的格式” ——【Message】
“消息发送函数”——【Handler的post和sendMessage】
在线程中使用Handler的步骤如下:
调用Looper.prepare()为当前线程创建Looper对象,这将自动创建与之配套的MessageQueue
创建Handler子类的实例,重写handleMessage(Message msg)分发,该方法负责处理来自其他线程的消息
调用Looper.loop()启动Looper
主UI线程中,系统已经初始化了一个Looper对象,因此程序直接创建Handler即可,然后就可以通过Handler来发送消息和处理消息了。
数据库
- GreenDao 是在 Android 开发中广泛使用的数据库框架,其优点是运行效率高,内存消耗少,性能佳
greenDAO是一个对象关系映射(ORM)的框架,能够提供一个接口通过操作对象的方式去操作关系型数据库,它能够让你操作数据库时更简单、更方便。如下图所示:
GreenDao 3.0最大的变化就是采用注解的方式通过编译方式生成Java数据对象和DAO对象。
性能高,号称Android最快的关系型数据库
内存占用小
库文件比较小,小于100K,编译时间低,而且可以避免65K方法限制
支持数据库加密 greendao支持SQLCipher进行数据库加密
简洁易用的API
View的绘制流程
measure: 判断是否需要重新计算View的大小,需要的话则计算;
layout: 判断是否需要重新计算View的位置,需要的话则计算;
draw: 判断是否需要重新绘制View,需要的话则重绘制。
强引用、弱引用、软引用、虚引用
引用类型 | 被垃圾回收时间 | 用途 | 生存时间 |
---|---|---|---|
强引用 | 从来不会 | 对象的一般状态 | JVM停止运行时终止 |
软引用 | 在内存不足时 | 对象缓存 | 内存不足时终止 |
弱引用 | 在垃圾回收时 | 对象缓存 | gc运行后终止 |
虚引用 | Unknown | Unknown | Unknown |
Android 7.0新特性:
- 分屏多任务支持
- 画中画
- 通知栏快速回复
- OpenJDK替换java API
- Android7.0采用了一项具有实时代码剖析功能的ARI JIT编译器,它能够在安卓应用程序在运行时不断提高自身的性能
Asset目录与res目录的区别
assets目录与res下的raw、drawable目录一样,也可用来存放资源文件,但它们三者有区别,对比总结如下表:
assets | res/raw | res/drawable | |
---|---|---|---|
获取资源方式: | 文件路径+文件名 | R.raw.xxx | R.drawable.xxx |
是否被压缩: | NO | NO | YES(失真压缩) |
能否获取子目录下的资源: | YES | NO | NO |
Android中如何将Activity伪装成Dialog
给AndroidManifest文件中Activity添加了Dialog样式属性:
android:theme="@android:style/Theme.Dialog"
Android 动画分类
- 帧动画
帧动画是最容易实现的一种动画,这种动画更多的依赖于完善的UI资源,他的原理就是将一张张单独的图片连贯的进行播放,
从而在视觉上产生一种动画的效果;有点类似于某些软件制作gif动画的方式。
- 补间动画
补间动画又可以分为四种形式,分别是 alpha(淡入淡出),translate(位移),scale(缩放大小),rotate(旋转)。
补间动画的实现,一般会采用xml 文件的形式;代码会更容易书写和阅读,同时也更容易复用。
- 属性动画
属性动画,顾名思义它是对于对象属性的动画。因此,所有补间动画的内容,都可以通过属性动画实现。
Android五大布局
LinearLayout
FrameLayout
AbsoluteLayout
RelativeLayout
TableLayout
Android新布局-ConstraintLayout
网络框架
- Retrofit
Retrofit的特点我个人认为是简化了网络请求流程,同时自己内部对OkHtttp客户端做了封装,同时2.x把之前1.x版本的部分不恰当职责都转移给OkHttp了(例如Log,目前用OkHttp的Interceptor来实现),这样的好处是职责清晰,Retrofit做自己该做的事儿。
而且Retrofit提供不同的Json Converter实现(也可以自定义),同时提供RxJava支持(返回Observable对象),配合Jackson(或者Gson)和RxJava,效率杠杠。
如果是标准的RESTful API,那么用Retrofit会非常爽!网络交互部分代码量可以减少90%。同时支持Gson,契合度很高。
- 接口缓存
Retrofit只能是Get请求才能缓存,而Post请求的参数是在http的body体里面,是可变的,无法成为唯一的标示,OkHttp有限制。那么我们可以自己手动缓存。sqlite缓存或文件缓存
UI线程 子线程
一个Android 程序默认情况只有一个进程,但是一个进程可以有多个线程。其中有一个UI 线程也称为UI主线程,UI Thread在Android程序运行的时候就被创建,主要是负责控制UI界面的显示、更新和控件交互。所有的Android应用程序组件----包括Activity、Service、Broadcast Receiver都在应用程序的主线程中运行。因此,任何组件中的费时操作处理都可能阻塞所有其他的组件、包括Service和可见的Activity。
在Android 中,对未响应的定义是:Activity对一个输入事件在5s内没有响应,或者Broadcast Receiver在10s内没有完成他的onReceive处理程序。对于任何不用直接和用户界面进行交互的重要处理,使用后台线程技术处理是非常重要的,将文件操作、网络交互、数据库、复杂计算调度到后台线程中完成非常重要,以免阻塞主线程。
注意的内容
equals()比较的是对象的内容(区分字母的大小写格式),但是如果使用“==”比较两个对象时,比较的是两个对象的内存地址,所以不相等。即使它们内容相等,但是不同对象的内存地址也是不相同的。
try catch finally顺序:
- try {}.
- 如果有Error Exception则,执行catch(){}中的代码。
- 无论有没有 Error Exception都要执行finally{}中的代码。
- 执行 try 中的 return
-
简单类型 | boolean | byte | char | short | Int | long | float | double |
---|---|---|---|---|---|---|---|---|
二进制位数 | 1 | 8 | 16 | 16 | 32 | 64 | 32 | 64 |
封装器类 | Boolean | Byte | Character | Short | Integer | Long | Float | Double |
默认值 | false | 0 | \u0000 | 0 | 0 | 0L | 0.0f | 0.0d |
- android分为四个层,从高层到低层分别是应用程序层、应用程序框架层、系统运行库层和linux核心层。