最近金三银四, 相信不少朋友都在跃跃欲动, 看看市场机会, 连夜整理了一波 Android 高级开发面试题目, 帮你助力!
Java 基础
1 内部类的作用
内部类可以用多个实例, 每个实例都有自己的状态信息, 并且与其他外围对象的信息相互独立
在单个外部类中, 可以让多个内部类以不同的方式实现同一个接口, 或者继承同一个类
创建内部类对象的时刻并不依赖于外围类对象的创建
内部类并没有令人迷惑的 is-a 关系, 他就是一个独立的实体
内部类提供了更好的封装, 除了该外围类, 其他类都不能访问
2 父类的静态方法能否被子类重写, 为什么?
不能
子类继承父类后, 用相同的静态方法和非静态方法, 这时非静态方法覆盖父类中的方法(即方法重写), 父类的该静态方法被隐藏(如果对象是父类则调用该隐藏的方法), 另外子类可继承父类的静态与非静态方法, 至于方法重载我觉得它其中一要素就是在同一类中, 不能说父类中的什么方法与子类里的什么方法是方法重载的体现
3 哪些情况下的对象会被垃圾回收机制处理掉
Java 垃圾回收机制最基本的做法是分代回收内存中的区域被划分成不同的世代, 对象根据其存活的时间被保存在对应世代的区域中一般的实现是划分成 3 个世代: 年轻年老和永久内存的分配是发生在年轻世代中的当一个对象存活时间足够长的时候, 它就会被复制到年老世代中对于不同的世代可以使用不同的垃圾回收算法进行世代划分的出发点是对应用中对象存活时间进行研究之后得出的统计规律一般来说, 一个应用中的大部分对象的存活时间都很短比如局部变量的存活时间就只在方法的执行过程中基于这一点, 对于年轻世代的垃圾回收算法就可以很有针对性
4 进程和线程的区别
简而言之, 一个程序至少有一个进程, 一个进程至少有一个线程
线程的划分尺度小于进程, 使得多线程程序的并发性高
另外, 进程在执行过程中拥有独立的内存单元, 而多个线程共享内存, 从而极大地提高了程序的运行效率
线程在执行过程中与进程还是有区别的每个独立的线程有一个程序运行的入口顺序执行序列和程序的出口但是线程不能够独立执行, 必须依存在应用程序中, 由应用程序提供多个线程执行控制
从逻辑角度来看, 多线程的意义在于一个应用程序中, 有多个执行部分可以同时执行但操作系统并没有将多个线程看做多个独立的应用, 来实现进程的调度和管理以及资源分配这就是进程和线程的重要区别
进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动, 进程是系统进行资源分配和调度的一个独立单位.
线程是进程的一个实体, 是 CPU 调度和分派的基本单位, 它是比进程更小的能独立运行的基本单位. 线程自己基本上不拥有系统资源, 只拥有一点在运行中必不可少的资源(如程序计数器, 一组寄存器和栈), 但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源.
一个线程可以创建和撤销另一个线程; 同一个进程中的多个线程之间可以并发执行.
进程和线程的主要差别在于它们是不同的操作系统资源管理方式进程有独立的地址空间, 一个进程崩溃后, 在保护模式下不会对其它进程产生影响, 而线程只是一个进程中的不同执行路径线程有自己的堆栈和局部变量, 但线程之间没有单独的地址空间, 一个线程死掉就等于整个进程死掉, 所以多进程的程序要比多线程的程序健壮, 但在进程切换时, 耗费资源较大, 效率要差一些但对于一些要求同时进行并且又要共享某些变量的并发操作, 只能用线程, 不能用进程如果有兴趣深入的话, 我建议你们看看现代操作系统或者操作系统的设计与实现对就个问题说得比较清楚
5HashMap 的实现原理
HashMap 概述: HashMap 是基于哈希表的 Map 接口的非同步实现此实现提供所有可选的映射操作, 并允许使用 null 值和 null 键此类不保证映射的顺序, 特别是它不保证该顺序恒久不变
HashMap 的数据结构: 在 java 编程语言中, 最基本的结构就是两种, 一个是数组, 另外一个是模拟指针(引用), 所有的数据结构都可以用这两个基本结构来构造的, HashMap 也不例外 HashMap 实际上是一个链表散列的数据结构, 即数组和链表的结合体
HashMap 底层就是一个数组结构, 数组中的每一项又是一个链表当新建一个 HashMap 的时候, 就会初始化一个数组
6StringStringBufferStringBuilder 区别
String 字符串常量
StringBuffer 字符串变量(线程安全)
StringBuilder 字符串变量(非线程安全)
简要的说, String 类型和 StringBuffer 类型的主要性能区别其实在于 String 是不可变的对象, 因此在每次对 String 类型进行改变的时候其实都等同于生成了一个新的 String 对象, 然后将指针指向新的 String 对象, 所以经常改变内容的字符串最好不要用 String , 因为每次生成对象都会对系统性能产生影响, 特别当内存中无引用对象多了以后, JVM 的 GC 就会开始工作, 那速度是一定会相当慢的
而如果是使用 StringBuffer 类则结果就不一样了, 每次结果都会对 StringBuffer 对象本身进行操作, 而不是生成新的对象, 再改变对象引用所以在一般情况下我们推荐使用 StringBuffer , 特别是字符串对象经常改变的情况下而在某些特别情况下, String 对象的字符串拼接其实是被 JVM 解释成了 StringBuffer 对象的拼接, 所以这些时候 String 对象的速度并不会比 StringBuffer 对象慢, 而特别是以下的字符串对象生成中, String 效率是远要比 StringBuffer 快的:
在大部分情况下 StringBuffer > String
Java.lang.StringBuffer 线程安全的可变字符序列一个类似于 String 的字符串缓冲区, 但不能修改虽然在任意时间点上它都包含某种特定的字符序列, 但通过某些方法调用可以改变该序列的长度和内容
可将字符串缓冲区安全地用于多个线程可以在必要时对这些方法进行同步, 因此任意特定实例上的所有操作就好像是以串行顺序发生的, 该顺序与所涉及的每个线程进行的方法调用顺序一致
StringBuffer 上的主要操作是 append 和 insert 方法, 可重载这些方法, 以接受任意类型的数据每个方法都能有效地将给定的数据转换成字符串, 然后将该字符串的字符追加或插入到字符串缓冲区中 append 方法始终将这些字符添加到缓冲区的末端; 而 insert 方法则在指定的点添加字符
在大部分情况下 StringBuilder > StringBuffer
java.lang.StringBuilder 一个可变的字符序列是 5.0 新增的此类提供一个与 StringBuffer 兼容的 API, 但不保证同步该类被设计用作 StringBuffer 的一个简易替换, 用在字符串缓冲区被单个线程使用的时候 (这种情况很普遍) 如果可能, 建议优先采用该类, 因为在大多数实现中, 它比 StringBuffer 要快两者的方法基本相同
7 什么导致线程阻塞
线程的阻塞
为了解决对共享存储区的访问冲突, Java 引入了同步机制, 现在让我们来考察多个线程对共享资源的访问, 显然同步机制已经不够了, 因为在任意时刻所要求的资源不一定已经准备好了被访问, 反过来, 同一时刻准备好了的资源也可能不止一个为了解决这种情况下的访问控制问题, Java 引入了对阻塞机制的支持.
阻塞指的是暂停一个线程的执行以等待某个条件发生(如某资源就绪), 学过操作系统的同学对它一定已经很熟悉了 Java 提供了大量方法来支持阻塞, 下面让我们逐一分析
sleep() 方法: sleep() 允许 指定以毫秒为单位的一段时间作为参数, 它使得线程在指定的时间内进入阻塞状态, 不能得到 CPU 时间, 指定的时间一过, 线程重新进入可执行状态 典型地, sleep() 被用在等待某个资源就绪的情形: 测试发现条件不满足后, 让线程阻塞一段时间后重新测试, 直到条件满足为止
suspend() 和 resume() 方法: 两个方法配套使用, suspend()使得线程进入阻塞状态, 并且不会自动恢复, 必须其对应的 resume() 被调用, 才能使得线程重新进入可执行状态典型地, suspend() 和 resume() 被用在等待另一个线程产生的结果的情形: 测试发现结果还没有产生后, 让线程阻塞, 另一个线程产生了结果后, 调用 resume() 使其恢复
yield() 方法: yield() 使得线程放弃当前分得的 CPU 时间, 但是不使线程阻塞, 即线程仍处于可执行状态, 随时可能再次分得 CPU 时间调用 yield() 的效果等价于调度程序认为该线程已执行了足够的时间从而转到另一个线程.
wait() 和 notify() 方法: 两个方法配套使用, wait() 使得线程进入阻塞状态, 它有两种形式, 一种允许 指定以毫秒为单位的一段时间作为参数, 另一种没有参数, 前者当对应的 notify() 被调用或者超出指定时间时线程重新进入可执行状态, 后者则必须对应的 notify() 被调用.
初看起来它们与 suspend() 和 resume() 方法对没有什么分别, 但是事实上它们是截然不同的区别的核心在于, 前面叙述的所有方法, 阻塞时都不会释放占用的锁(如果占用了的话), 而这一对方法则相反
首先, 前面叙述的所有方法都隶属于 Thread 类, 但是这一对却直接隶属于 Object 类, 也就是说, 所有对象都拥有这一对方法初看起来这十分不可思议, 但是实际上却是很自然的, 因为这一对方法阻塞时要释放占用的锁, 而锁是任何对象都具有的, 调用任意对象的 wait() 方法导致线程阻塞, 并且该对象上的锁被释放而调用 任意对象的 notify()方法则导致因调用该对象的 wait() 方法而阻塞的线程中随机选择的一个解除阻塞(但要等到获得锁后才真正可执行)
其次, 前面叙述的所有方法都可在任何位置调用, 但是这一对方法却必须在 synchronized 方法或块中调用, 理由也很简单, 只有在 synchronized 方法或块中当前线程才占有锁, 才有锁可以释放同样的道理, 调用这一对方法的对象上的锁必须为当前线程所拥有, 这样才有锁可以释放因此, 这一对方法调用必须放置在这样的 synchronized 方法或块中, 该方法或块的上锁对象就是调用这一对方法的对象若不满足这一条件, 则程序虽然仍能编译, 但在运行时会出现 IllegalMonitorStateException 异常
wait() 和 notify() 方法的上述特性决定了它们经常和 synchronized 方法或块一起使用, 将它们和操作系统的进程间通信机制作一个比较就会发现它们的相似性: synchronized 方法或块提供了类似于操作系统原语的功能, 它们的执行不会受到多线程机制的干扰, 而这一对方法则相当于 block 和 wakeup 原语 (这一对方法均声明为 synchronized) 它们的结合使得我们可以实现操作系统上一系列精妙的进程间通信的算法(如信号量算法), 并用于解决各种复杂的线程间通信问题
关于 wait() 和 notify() 方法最后再说明两点:
a 调用 notify() 方法导致解除阻塞的线程是从因调用该对象的 wait() 方法而阻塞的线程中随机选取的, 我们无法预料哪一个线程将会被选择, 所以编程时要特别小心, 避免因这种不确定性而产生问题
b 除了 notify(), 还有一个方法 notifyAll() 也可起到类似作用, 唯一的区别在于, 调用 notifyAll() 方法将把因调用该对象的 wait() 方法而阻塞的所有线程一次性全部解除阻塞当然, 只有获得锁的那一个线程才能进入可执行状态
Android 基础
1 是否使用过 IntentService, 作用是什么, AIDL 解决了什么问题?
生成一个默认的且与主线程互相独立的工作者线程来执行所有传送至 onStartCommand() 方法的 Intetnt
生成一个工作队列来传送 Intent 对象给你的 onHandleIntent()方法, 同一时刻只传送一个 Intent 对象, 这样一来, 你就不必担心多线程的问题在所有的请求 (Intent) 都被执行完以后会自动停止服务, 所以, 你不需要自己去调用 stopSelf()方法来停止
该服务提供了一个 onBind()方法的默认实现, 它返回 null
提供了一个 onStartCommand()方法的默认实现, 它将 Intent 先传送至工作队列, 然后从工作队列中每次取出一个传送至 onHandleIntent()方法, 在该方法中对 Intent 对相应的处理
AIDL (Android Interface Definition Language) 是一种 IDL 语言, 用于生成可以在 Android 设备上两个进程之间进行进程间通信 (interprocess communication, IPC) 的代码如果在一个进程中 (例如 Activity) 要调用另一个进程中 (例如 Service) 对象的操作, 就可以使用 AIDL 生成可序列化的参数 AIDL IPC 机制是面向接口的, 像 COM 或 Corba 一样, 但是更加轻量级它是使用代理类在客户端和实现端传递数据
2ActivityWindowView 三者的差别?
Activity 像一个工匠(控制单元),Window 像窗户(承载模型),View 像窗花(显示视图) LayoutInflater 像剪刀, Xml 配置像窗花图纸
在 Activity 中调用 attach, 创建了一个 Window
创建的 window 是其子类 PhoneWindow, 在 attach 中创建 PhoneWindow
在 Activity 中调用 setContentView(R.layout.xxx)
其中实际上是调用的 getWindow().setContentView()
调用 PhoneWindow 中的 setContentView 方法
创建 ParentView: 作为 ViewGroup 的子类, 实际是创建的 DecorView(作为 FramLayout 的子类)
将指定的 R.layout.xxx 进行填充通过布局填充器进行填充其中的 parent 指的就是 DecorView
调用到 ViewGroup
调用 ViewGroup 的 removeAllView(), 先将所有的 view 移除掉
添加新的 view:addView()
3Fragment 特点
Fragment 可以作为 Activity 界面的一部分组成出现;
可以在一个 Activity 中同时出现多个 Fragment, 并且一个 Fragment 也可以在多个 Activity 中使用;
在 Activity 运行过程中, 可以添加移除或者替换 Fragment;
Fragment 可以响应自己的输入事件, 并且有自己的生命周期, 它们的生命周期会受宿主 Activity 的生命周期影响
4HandlerThread 和 HandlerThread 的差别?
- http://blog.csdn.net/guolin_blog/article/details/9991569
- http://droidyue.com/blog/2015/11/08/make-use-of-handlerthread/
从 Android 中 Thread(java.lang.Thread -> java.lang.Object)描述可以看出, Android 的 Thread 没有对 Java 的 Thread 做任何封装, 但是 Android 提供了一个继承自 Thread 的类 HandlerThread(android.os.HandlerThread -> java.lang.Thread), 这个类对 Java 的 Thread 做了很多便利 Android 系统的封装
android.os.Handler 可以通过 Looper 对象实例化, 并运行于另外的线程中, Android 提供了让 Handler 运行于其它线程的线程实现, 也就是 HandlerThreadHandlerThread 对象 start 后可以获得其 Looper 对象, 并且使用这个 Looper 对象实例 Handler
5Launch mode 应用场景
Standard, 创建一个新的 Activity
SingleTop, 栈顶不是该类型的 Activity, 创建一个新的 Activity 否则, onNewIntent
SingleTask, 回退栈中没有该类型的 Activity, 创建 Activity, 否则, onNewIntent+ClearTop
注意:
设置了 "singleTask" 启动模式的 Activity, 它在启动的时候, 会先在系统中查找属性值 affinity 等于它的属性值 taskAffinity 的 Task 存在; 如果存在这样的 Task, 它就会在这个 Task 中启动, 否则就会在新的任务栈中启动因此, 如果我们想要设置了 "singleTask" 启动模式的 Activity 在新的任务中启动, 就要为它设置一个独立的 taskAffinity 属性值
如果设置了 "singleTask" 启动模式的 Activity 不是在新的任务中启动时, 它会在已有的任务中查看是否已经存在相应的 Activity 实例, 如果存在, 就会把位于这个 Activity 实例上面的 Activity 全部结束掉, 即最终这个 Activity 实例会位于任务的 Stack 顶端中
在一个任务栈中只有一个 singleTask 启动模式的 Activity 存在他的上面可以有其他的 Activity 这点与 singleInstance 是有区别的
singleInstance, 回退栈中, 只有这一个 Activity, 没有其他 Activity
SingleTop 适合接收通知启动的内容显示页面
例如, 某个新闻客户端的新闻内容页面, 如果收到 10 个新闻推送, 每次都打开一个新闻内容页面是很烦人的
singleTask 适合作为程序入口点
例如浏览器的主界面不管从多少个应用启动浏览器, 只会启动主界面一次, 其余情况都会走 onNewIntent, 并且会清空主界面上面的其他页面
singleInstance 应用场景:
闹铃的响铃界面 你以前设置了一个闹铃: 上午 6 点在上午 5 点 58 分, 你启动了闹铃设置界面, 并按 Home 键回桌面; 在上午 5 点 59 分时, 你在微信和朋友聊天; 在 6 点时, 闹铃响了, 并且弹出了一个对话框形式的 Activity(名为 AlarmAlertActivity) 提示你到 6 点了(这个 Activity 就是以 SingleInstance 加载模式打开的), 你按返回键, 回到的是微信的聊天界面, 这是因为 AlarmAlertActivity 所在的 Task 的栈只有他一个元素, 因此退出之后这个 Task 的栈空了如果是以 SingleTask 打开 AlarmAlertActivity, 那么当闹铃响了的时候, 按返回键应该进入闹铃设置界面
6Android 事件分发机制
https://www.jianshu.com/p/38015afcdb58
7view 绘制流程
https://www.jianshu.com/p/bb7977990baa
8post 和 postDelay
9 线程同步
- http://www.itzhai.com/java-based-notebook-thread-synchronization-problem-solving-synchronization-problems-synchronized-block-synchronized-methods.html#read-more
- http://www.juwends.com/tech/android/android-inter-thread-comm.html
10 单例模式
- public class Singleton{
- private volatile static Singleton mSingleton;
- private Singleton(){
- }
- public static Singleton getInstance(){
- if(mSingleton == null){
- synchronized(Singleton.class){
- if(mSingleton == null)
- mSingleton = new Singleton();
- }
- }
- return mSingleton;
- }
- }
11 什么情况导致内存泄漏
a 资源对象没关闭造成的内存泄漏
描述: 资源性对象比如 (Cursor,File 文件等) 往往都用了一些缓冲, 我们在不使用的时候, 应该及时关闭它们, 以便它们的缓冲及时回收内存它们的缓冲不仅存在于 java 虚拟机内, 还存在于 java 虚拟机外如果我们仅仅是把它的引用设置为 null, 而不关闭它们, 往往会造成内存泄漏因为有些资源性对象, 比如 SQLiteCursor(在析构函数 finalize(), 如果我们没有关闭它, 它自己会调 close()关闭), 如果我们没有关闭它, 系统在回收它时也会关闭它, 但是这样的效率太低了因此对于资源性对象在不使用的时候, 应该调用它的 close()函数, 将其关闭掉, 然后才置为 null. 在我们的程序退出时一定要确保我们的资源性对象已经关闭 程序中经常会进行查询数据库的操作, 但是经常会有使用完毕 Cursor 后没有关闭的情况如果我们的查询结果集比较小, 对内存的消耗不容易被发现, 只有在常时间大量操作的情况下才会复现内存问题, 这样就会给以后的测试和问题排查带来困难和风险
b 构造 Adapter 时, 没有使用缓存的 convertView
以构造 ListView 的 BaseAdapter 为例, 在 BaseAdapter 中提供了方法: public View getView(int position, ViewconvertView, ViewGroup parent) 来向 ListView 提供每一个 item 所需要的 view 对象初始时 ListView 会从 BaseAdapter 中根据当前的屏幕布局实例化一定数量的 view 对象, 同时 ListView 会将这些 view 对象缓存起来当向上滚动 ListView 时, 原先位于最上面的 list item 的 view 对象会被回收, 然后被用来构造新出现的最下面的 list item 这个构造过程就是由 getView()方法完成的, getView()的第二个形参 View convertView 就是被缓存起来的 list item 的 view 对象 (初始化时缓存中没有 view 对象则 convertView 是 null) 由此可以看出, 如果我们不去使用 convertView, 而是每次都在 getView()中重新实例化一个 View 对象的话, 即浪费资源也浪费时间, 也会使得内存占用越来越大 ListView 回收 list item 的 view 对象的过程可以查看: android.widget.AbsListView.java --> voidaddScrapView(View scrap) 方法 示例代码:
- public View getView(int position, ViewconvertView, ViewGroup parent) {
- View view = new Xxx(...);
- ... ...
- return view;
- }
修正示例代码:
- public View getView(int position, ViewconvertView, ViewGroup parent) {
- View view = null;
- if (convertView != null) {
- view = convertView;
- populate(view, getItem(position));
- ...
- } else {
- view = new Xxx(...);
- ...
- }
- return view;
- }
cBitmap 对象不在使用时调用 recycle()释放内存
有时我们会手工的操作 Bitmap 对象, 如果一个 Bitmap 对象比较占内存, 当它不在被使用的时候, 可以调用 Bitmap.recycle()方法回收此对象的像素所占用的内存, 但这不是必须的, 视情况而定
d 试着使用关于 application 的 context 来替代和 activity 相关的 context
这是一个很隐晦的内存泄漏的情况有一种简单的方法来避免 context 相关的内存泄漏最显著地一个是避免 context 逃出他自己的范围之外使用 Application context 这个 context 的生存周期和你的应用的生存周期一样长, 而不是取决于 activity 的生存周期如果你想保持一个长期生存的对象, 并且这个对象需要一个 context, 记得使用 application 对象你可以通过调用 Context.getApplicationContext() or Activity.getApplication()来获得更多的请看这篇文章如何避免 Android 内存泄漏
e 注册没反注册造成的内存泄漏
一些 Android 程序可能引用我们的 Anroid 程序的对象 (比如注册机制) 即使我们的 Android 程序已经结束了, 但是别的引用程序仍然还有对我们的 Android 程序的某个对象的引用, 泄漏的内存依然不能被垃圾回收调用 registerReceiver 后未调用 unregisterReceiver 比如: 假设我们希望在锁屏界面 (LockScreen) 中, 监听系统中的电话服务以获取一些信息(如信号强度等), 则可以在 LockScreen 中定义一个 PhoneStateListener 的对象, 同时将它注册到 TelephonyManager 服务中对于 LockScreen 对象, 当需要显示锁屏界面的时候就会创建一个 LockScreen 对象, 而当锁屏界面消失的时候 LockScreen 对象就会被释放掉 但是如果在释放 LockScreen 对象的时候忘记取消我们之前注册的 PhoneStateListener 对象, 则会导致 LockScreen 无法被垃圾回收如果不断的使锁屏界面显示和消失, 则最终会由于大量的 LockScreen 对象没有办法被回收而引起 OutOfMemory, 使得 system_process 进程挂掉 虽然有些系统程序, 它本身好像是可以自动取消注册的(当然不及时), 但是我们还是应该在我们的程序中明确的取消注册, 程序结束时应该把所有的注册都取消掉
f 集合中对象没清理造成的内存泄漏
我们通常把一些对象的引用加入到了集合中, 当我们不需要该对象时, 并没有把它的引用从集合中清理掉, 这样这个集合就会越来越大如果这个集合是 static 的话, 那情况就更严重了
12ANR 定位和如何避免?
如果开发机器上出现问题, 我们可以通过查看 / data/anr/traces.txt 即可, 最新的 ANR 信息在最开始部分
主线程被 IO 操作 (从 4.0 之后网络 IO 不允许在主线程中) 阻塞
主线程中存在耗时的计算
主线程中错误的操作, 比如 Thread.wait 或者 Thread.sleep 等 Android 系统会监控程序的响应状况, 一旦出现下面两种情况, 则弹出 ANR 对话框
应用在 5 秒内未响应用户的输入事件(如按键或者触摸)
BroadcastReceiver 未在 10 秒内完成相关的处理
Service 在特定的时间内无法处理完成 20 秒
避免
使用 AsyncTask 处理耗时 IO 操作
使用 Thread 或者 HandlerThread 时, 调用 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND)设置优先级, 否则仍然会降低程序响应, 因为默认 Thread 的优先级和主线程相同
使用 Handler 处理工作线程结果, 而不是使用 Thread.wait()或者 Thread.sleep()来阻塞主线程
Activity 的 onCreate 和 onResume 回调中尽量避免耗时的代码
BroadcastReceiver 中 onReceive 代码也要尽量减少耗时, 建议使用 IntentService 处理
13 什么情况导致 OOM 以及如何避免?
- http://blog.csdn.net/yangxuehui1990/article/details/44994763
- http://blog.csdn.net/ljx19900116/article/details/50037627
14Android Service 与 Activity 之间通信的几种方式?
通过 Binder 对象
通过 Broadcast(广播)的形式
15 如何保证 service 在后台尽量不被 kill
onStartCommand 方法, 返回 START_STICKY
START_STICKY 在运行 onStartCommand 后 service 进程被 kill 后, 那将保留在开始状态, 但是不保留那些传入的 intent 不久后 service 就会再次尝试重新创建, 因为保留在开始状态, 在创建 service 后将保证调用 onstartCommand 如果没有传递任何开始命令给 service, 那将获取到 null 的 intent
START_NOT_STICKY 在运行 onStartCommand 后 service 进程被 kill 后, 并且没有新的 intent 传递给它 Service 将移出开始状态, 并且直到新的明显的方法 (startService) 调用才重新创建因为如果没有传递任何未决定的 intent 那么 service 是不会启动, 也就是期间 onstartCommand 不会接收到任何 null 的 intent
START_REDELIVER_INTENT 在运行 onStartCommand 后 service 进程被 kill 后, 系统将会再次启动 service, 并传入最后一个 intent 给 onstartCommand 直到调用 stopSelf(int)才停止传递 intent 如果在被 kill 后还有未处理好的 intent, 那被 kill 后服务还是会自动启动因此 onstartCommand 不会接收到任何 null 的 intent
提升 service 优先级
在 AndroidManifest.xml 文件中对于 intent-filter 可以通过 android:priority="1000" 这个属性设置最高优先级, 1000 是最高值, 如果数字越小则优先级越低, 同时适用于广播
提升 service 进程优先级
Android 中的进程是托管的, 当系统进程空间紧张的时候, 会依照优先级自动进行进程的回收 Android 将进程分为 6 个等级, 它们按优先级顺序由高到低依次是:
前台进程( FOREGROUND_APP)
可视进程(VISIBLE_APP )
次要服务进程(SECONDARY_SERVER )
后台进程 (HIDDEN_APP)
内容供应节点(CONTENT_PROVIDER)
空进程(EMPTY_APP)
当 service 运行在低内存的环境时, 将会 kill 掉一些存在的进程因此进程的优先级将会很重要, 可以使用 startForeground 将 service 放到前台状态这样在低内存时被 kill 的几率会低一些
onDestroy 方法里重启 service
service+broadcast 方式, 就是当 service 走 ondestory 的时候, 发送一个自定义的广播, 当收到广播的时候, 重新启动 service;
Application 加上 Persistent 属性
监听系统广播判断 Service 状态
16RequestLayout,onLayout,onDraw,DrawChild 区别与联系
requestLayout()方法 : 会导致调用 measure()过程 和 layout()过程 说明: 只是对 View 树重新布局 layout 过程包括 measure()和 layout()过程, 不会调用 draw()过程, 但不会重新绘制 任何视图包括该调用者本身
onLayout()方法(如果该 View 是 ViewGroup 对象, 需要实现该方法, 对每个子视图进行布局)
调用 onDraw()方法绘制视图本身(每个 View 都需要重载该方法, ViewGroup 不需要实现该方法)
drawChild()去重新回调每个子视图的 draw()方法
17invalidate()和 postInvalidate() 的区别及使用
http://blog.csdn.net/mars2639/article/details/6650876
18LinearLayout 对比 RelativeLayout
RelativeLayout 会让子 View 调用 2 次 onMeasure,LinearLayout 在有 weight 时, 也会调用子 View2 次 onMeasure
RelativeLayout 的子 View 如果高度和 RelativeLayout 不同, 则会引发效率问题, 当子 View 很复杂时, 这个问题会更加严重如果可以, 尽量使用 padding 代替 margin
在不影响层级深度的情况下, 使用 LinearLayout 和 FrameLayout 而不是 RelativeLayout
19 优化自定义 view
为了加速你的 view, 对于频繁调用的方法, 需要尽量减少不必要的代码先从 onDraw 开始, 需要特别注意不应该在这里做内存分配的事情, 因为它会导致 GC, 从而导致卡顿在初始化或者动画间隙期间做分配内存的动作不要在动画正在执行的时候做内存分配的事情
你还需要尽可能的减少 onDraw 被调用的次数, 大多数时候导致 onDraw 都是因为调用了 invalidate(). 因此请尽量减少调用 invaildate()的次数如果可能的话, 尽量调用含有 4 个参数的 invalidate()方法而不是没有参数的 invalidate()没有参数的 invalidate 会强制重绘整个 view
另外一个非常耗时的操作是请求 layout 任何时候执行 requestLayout(), 会使得 Android UI 系统去遍历整个 View 的层级来计算出每一个 view 的大小如果找到有冲突的值, 它会需要重新计算好几次另外需要尽量保持 View 的层级是扁平化的, 这样对提高效率很有帮助
如果你有一个复杂的 UI, 你应该考虑写一个自定义的 ViewGroup 来执行他的 layout 操作与内置的 view 不同, 自定义的 view 可以使得程序仅仅测量这一部分, 这避免了遍历整个 view 的层级结构来计算大小这个 PieChart 例子展示了如何继承 ViewGroup 作为自定义 view 的一部分 PieChart 有子 views, 但是它从来不测量它们而是根据他自身的 layout 法则, 直接设置它们的大小
- 20ContentProvider
- https://www.jianshu.com/p/ea8bc4aaf057
21Android 设计模式
http://blog.csdn.net/bboyfeiyu/article/details/44563871
22MVCMVPMVM 区别?
https://www.cnblogs.com/guwei4037/p/5591183.html
来源: https://mp.weixin.qq.com/s?__biz=MzI2OTQxMTM4OQ==&mid=2247485833&idx=1&sn=6c5b61da38c2b025926e18d42a97035b&chksm=eae1fcdbdd9675cd31f61bb299d2c850aade65277fcfa4d251aa7d111a5f48c8fb5cdd4a390f#rd