在这篇文章中,我们将带领您了解Android基础知识汇总的全貌,包括android基础知识总结的相关情况。同时,我们还将为您介绍有关android基础知识、android基础知识1、Android基础
在这篇文章中,我们将带领您了解Android基础知识汇总的全貌,包括android基础知识总结的相关情况。同时,我们还将为您介绍有关android 基础知识、android 基础知识 1、Android 基础知识再次理解、Android 基础知识学习的知识,以帮助您更好地理解这个主题。
本文目录一览:Android基础知识汇总(android基础知识总结)
文章目录
- 1.Activity
- 1.Activity启动模式
- 2.IntentFilter的匹配原则
1.Activity
1.Activity启动模式
Activity启动模式
2.IntentFilter的匹配原则
IntentFilter的匹配原则
android 基础知识
2005 年 google 收购成立 22 个月的 android 公司;
2007 年谷歌为首的 34 家公司宣布成立开放手机联盟;
android 建国纲领:
1、随时随地为每个人提供信息;
2、永不作恶
android 的四大天王:
1、Activity:整个应用程序的界面。主要负责应用程序中的数据展示。也就是构建应用程序的界面。
2、Intent:运输大队长。应用程序中所有数据都是通过它来传递,主要是整个应用程序界面和界面之间床传递数据。
3、Service:Android 里面的劳模,虽然看不到它,但是它承担着大部分数据处理的工作。
4、Content Provider:Android 里面的国家档案馆,负责存储数据,并允许有需要的应用程序访问这些数据
Android 开发的工具:
Android 开发之屠龙刀:Android SDK
Android 开发之倚天剑:eclipse
android 基础知识 1
1、java 中 == 和 equals 和 hashCode 的区别
基本数据类型的 == 比较的值相等.
类的 == 比较的内存的地址,即是否是同一个对象,在不覆盖 equals 的情况下,同比较内存地址,原实现也为 == ,如 String 等重写了 equals 方法.
hashCode 也是 Object 类的一个方法。返回一个离散的 int 型整数。在集合类操作中使用,为了提高查询速度。(HashMap,HashSet 等比较是否为同一个)
如果两个对象 equals,Java 运行时环境会认为他们的 hashcode 一定相等。
如果两个对象不 equals,他们的 hashcode 有可能相等。
如果两个对象 hashcode 相等,他们不一定 equals。
如果两个对象 hashcode 不相等,他们一定不 equals。
2、int 与 integer 的区别
int 基本类型
integer 对象 int 的封装类
3、String、StringBuffer、StringBuilder 区别
String: 字符串常量 不适用于经常要改变值得情况,每次改变相当于生成一个新的对象
StringBuffer: 字符串变量 (线程安全)
StringBuilder: 字符串变量(线程不安全) 确保单线程下可用,效率略高于 StringBuffer
4、什么是内部类?内部类的作用
内部类可直接访问外部类的属性
Java 中内部类主要分为成员内部类、局部内部类 (嵌套在方法和作用域内)、匿名内部类(没构造方法)、静态内部类(static 修饰的类,不能使用任何外围类的非 static 成员变量和方法, 不依赖外围类)
5、进程和线程的区别
进程是 cpu 资源分配的最小单位,线程是 cpu 调度的最小单位。
进程之间不能共享资源,而线程共享所在进程的地址空间和其它资源。
一个进程内可拥有多个线程,进程可开启进程,也可开启线程。
一个线程只能属于一个进程,线程可直接使用同进程的资源,线程依赖于进程而存在。
6、final,finally,finalize 的区别
final: 修饰类、成员变量和成员方法,类不可被继承,成员变量不可变,成员方法不可重写
finally: 与 try...catch... 共同使用,确保无论是否出现异常都能被调用到
finalize: 类的方法,垃圾回收之前会调用此方法,子类可以重写 finalize () 方法实现对资源的回收
7、Serializable 和 Parcelable 的区别
Serializable Java 序列化接口 在硬盘上读写 读写过程中有大量临时变量的生成,内部执行大量的 i/o 操作,效率很低。
Parcelable Android 序列化接口 效率高 使用麻烦 在内存中读写(AS 有相关插件 一键生成所需方法) ,对象不能保存到磁盘中
8、静态属性和静态方法是否可以被继承?是否可以被重写?以及原因?
可继承 不可重写 而是被隐藏
如果子类里面定义了静态方法和属性,那么这时候父类的静态方法或属性称之为 "隐藏"。如果你想要调用父类的静态方法和属性,直接通过父类名。方法或变量名完成。
9、成员内部类、静态内部类、局部内部类和匿名内部类的理解,以及项目中的应用
ava 中内部类主要分为成员内部类、局部内部类 (嵌套在方法和作用域内)、匿名内部类(没构造方法)、静态内部类(static 修饰的类,不能使用任何外围类的非 static 成员变量和方法, 不依赖外围类)
使用内部类最吸引人的原因是:每个内部类都能独立地继承一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。
因为 Java 不支持多继承,支持实现多个接口。但有时候会存在一些使用接口很难解决的问题,这个时候我们可以利用内部类提供的、可以继承多个具体的或者抽象的类的能力来解决这些程序设计问题。可以这样说,接口只是解决了部分问题,而内部类使得多重继承的解决方案变得更加完整。
10、string 转换成 integer 的方式及原理
String integer Intrger.parseInt(string);
Integerstring Integer.toString();
11、哪些情况下的对象会被垃圾回收机制处理掉?
1. 所有实例都没有活动线程访问。
2. 没有被其他任何实例访问的循环引用实例。
3.Java 中有不同的引用类型。判断实例是否符合垃圾收集的条件都依赖于它的引用类型。
要判断怎样的对象是没用的对象。这里有 2 种方法:
1. 采用标记计数的方法:
给内存中的对象给打上标记,对象被引用一次,计数就加 1,引用被释放了,计数就减一,当这个计数为 0 的时候,这个对象就可以被回收了。当然,这也就引发了一个问题:循环引用的对象是无法被识别出来并且被回收的。所以就有了第二种方法:
2. 采用根搜索算法:
从一个根出发,搜索所有的可达对象,这样剩下的那些对象就是需要被回收的
12、静态代理和动态代理的区别,什么场景使用?
静态代理类:
由程序员创建或由特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class 文件就已经存在了。动态代理类:在程序运行时,运用反射机制动态创建而成。
14、Java 中实现多态的机制是什么?
答:方法的重写 Overriding 和重载 Overloading 是 Java 多态性的不同表现
重写 Overriding 是父类与子类之间多态性的一种表现
重载 Overloading 是一个类中多态性的一种表现.
16、说说你对 Java 反射的理解
JAVA 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性。 从对象出发,通过反射(Class 类)可以取得取得类的完整信息(类名 Class 类型,所在包、具有的所有方法 Method [] 类型、某个方法的完整信息(包括修饰符、返回值类型、异常、参数类型)、所有属性 Field []、某个属性的完整信息、构造器 Constructors),调用类的属性或方法自己的总结: 在运行过程中获得类、对象、方法的所有信息。
17、说说你对 Java 注解的理解
元注解
元注解的作用就是负责注解其他注解。java5.0 的时候,定义了 4 个标准的 meta-annotation 类型,它们用来提供对其他注解的类型作说明。
1.@Target
2.@Retention
3.@Documented
4.@Inherited
18、Java 中 String 的了解
在源码中 string 是用 final 进行修饰,它是不可更改,不可继承的常量。
19、String 为什么要设计成不可变的?
1、字符串池的需求
字符串池是方法区(Method Area)中的一块特殊的存储区域。当一个字符串已经被创建并且该字符串在 池 中,该字符串的引用会立即返回给变量,而不是重新创建一个字符串再将引用返回给变量。如果字符串不是不可变的,那么改变一个引用(如: string2)的字符串将会导致另一个引用(如: string1)出现脏数据。
2、允许字符串缓存哈希码
在 java 中常常会用到字符串的哈希码,例如: HashMap 。String 的不变性保证哈希码始终一,因此,他可以不用担心变化的出现。 这种方法意味着不必每次使用时都重新计算一次哈希码 —— 这样,效率会高很多。
3、安全
String 广泛的用于 java 类中的参数,如:网络连接(Network connetion),打开文件(opening files )等等。如果 String 不是不可变的,网络连接、文件将会被改变 —— 这将会导致一系列的安全威胁。操作的方法本以为连接上了一台机器,但实际上却不是。由于反射中的参数都是字符串,同样,也会引起一系列的安全问题。
20、Object 类的 equal 和 hashCode 方法重写,为什么?
首先 equals 与 hashcode 间的关系是这样的:
1、如果两个对象相同(即用 equals 比较返回 true),那么它们的 hashCode 值一定要相同;
2、如果两个对象的 hashCode 相同,它们并不一定相同 (即用 equals 比较返回 false)
由于为了提高程序的效率才实现了 hashcode 方法,先进行 hashcode 的比较,如果不同,那没就不必在进行 equals 的比较了,这样就大大减少了 equals 比较的次数,这对比需要比较的数量很大的效率提高是很明显的
21、List,Set,Map 的区别
Set 是最简单的一种集合。集合中的对象不按特定的方式排序,并且没有重复对象。 Set 接口主要实现了两个实现类:HashSet: HashSet 类按照哈希算法来存取集合中的对象,存取速度比较快
TreeSet :TreeSet 类实现了 SortedSet 接口,能够对集合中的对象进行排序。
List 的特征是其元素以线性方式存储,集合中可以存放重复对象。
ArrayList () : 代表长度可以改变得数组。可以对元素进行随机的访问,向 ArrayList () 中插入与删除元素的速度慢。
LinkedList (): 在实现中采用链表数据结构。插入和删除速度快,访问速度慢。
Map 是一种把键对象和值对象映射的集合,它的每一个元素都包含一对键对象和值对象。 Map 没有继承于 Collection 接口 从 Map 集合中检索元素时,只要给出键对象,就会返回对应的值对象。
HashMap:Map 基于散列表的实现。插入和查询 “键值对” 的开销是固定的。可以通过构造器设置容量 capacity 和负载因子 load factor,以调整容器的性能。
LinkedHashMap: 类似于 HashMap,但是迭代遍历它时,取得 “键值对” 的顺序是其插入次序,或者是最近最少使用 (LRU) 的次序。只比 HashMap 慢一点。而在迭代访问时发而更快,因为它使用链表维护内部次序。
TreeMap : 基于红黑树数据结构的实现。查看 “键” 或 “键值对” 时,它们会被排序 (次序由 Comparabel 或 Comparator 决定)。TreeMap 的特点在 于,你得到的结果是经过排序的。TreeMap 是唯一的带有 subMap () 方法的 Map,它可以返回一个子树。
WeakHashMao :弱键 (weak key) Map,Map 中使用的对象也被允许释放:这是为解决特殊问题设计的。如果没有 map 之外的引用指向某个 “键”,则此 “键” 可以被垃圾收集器回收。
26、ArrayMap 和 HashMap 的对比
1、存储方式不同
HashMap 内部有一个 HashMapEntry<K, V>[] 对象,每一个键值对都存储在这个对象里,当使用 put 方法添加键值对时,就会 new 一个 HashMapEntry 对象,
2、添加数据时扩容时的处理不一样,进行了 new 操作,重新创建对象,开销很大。ArrayMap 用的是 copy 数据,所以效率相对要高。
3、ArrayMap 提供了数组收缩的功能,在 clear 或 remove 后,会重新收缩数组,是否空间
4、ArrayMap 采用二分法查找;
29、HashMap 和 HashTable 的区别
1 HashMap 不是线程安全的,效率高一点、方法不是 Synchronize 的要提供外同步,有 containsvalue 和 containsKey 方法。
hashtable 是,线程安全,不允许有 null 的键和值,效率稍低,方法是是 Synchronize 的。有 contains 方法方法。Hashtable 继承于 Dictionary 类
30、HashMap 与 HashSet 的区别
hashMap:HashMap 实现了 Map 接口,HashMap 储存键值对,使用 put () 方法将元素放入 map 中,HashMap 中使用键对象来计算 hashcode 值,HashMap 比较快,因为是使用唯一的键来获取对象。
HashSet 实现了 Set 接口,HashSet 仅仅存储对象,使用 add () 方法将元素放入 set 中,HashSet 使用成员对象来计算 hashcode 值,对于两个对象来说 hashcode 可能相同,所以 equals () 方法用来判断对象的相等性,如果两个对象不同的话,那么返回 false。HashSet 较 HashMap 来说比较慢。
31、HashSet 与 HashMap 怎么判断集合元素重复?
HashSet 不能添加重复的元素,当调用 add(Object)方法时候,
首先会调用 Object 的 hashCode 方法判 hashCode 是否已经存在,如不存在则直接插入元素;如果已存在则调用 Object 对象的 equals 方法判断是否返回 true,如果为 true 则说明元素已经存在,如为 false 则插入元素。
33、ArrayList 和 LinkedList 的区别,以及应用场景
ArrayList 是基于数组实现的,ArrayList 线程不安全。
LinkedList 是基于双链表实现的:
使用场景:
(1)如果应用程序对各个索引位置的元素进行大量的存取或删除操作,ArrayList 对象要远优于 LinkedList 对象;
(2) 如果应用程序主要是对列表进行循环,并且循环时候进行插入或者删除操作,LinkedList 对象要远优于 ArrayList 对象;
34、数组和链表的区别
数组:是将元素在内存中连续存储的;它的优点:因为数据是连续存储的,内存地址连续,所以在查找数据的时候效率比较高;它的缺点:在存储之前,我们需要申请一块连续的内存空间,并且在编译的时候就必须确定好它的空间的大小。在运行的时候空间的大小是无法随着你的需要进行增加和减少而改变的,当数据两比较大的时候,有可能会出现越界的情况,数据比较小的时候,又有可能会浪费掉内存空间。在改变数据个数时,增加、插入、删除数据效率比较低。
链表:是动态申请内存空间,不需要像数组需要提前申请好内存的大小,链表只需在用的时候申请就可以,根据需要来动态申请或者删除内存空间,对于数据增加和删除以及插入比数组灵活。还有就是链表中数据在内存中可以在任意的位置,通过应用来关联数据(就是通过存在元素的指针来联系)
35、开启线程的三种方式?
ava 有三种创建线程的方式,分别是继承 Thread 类、实现 Runable 接口和使用线程池
36、线程和进程的区别?
线程是进程的子集,一个进程可以有很多线程,每条线程并行执行不同的任务。不同的进程使用不同的内存空间,而所有的线程共享一片相同的内存空间。别把它和栈内存搞混,每个线程都拥有单独的栈内存用来存储本地数据。
38、run() 和 start() 方法区别
这个问题经常被问到,但还是能从此区分出面试者对 Java 线程模型的理解程度。start () 方法被用来启动新创建的线程,而且 start () 内部调用了 run () 方法,这和直接调用 run () 方法的效果不一样。当你调用 run () 方法的时候,只会是在原来的线程中调用,没有新的线程启动,start () 方法才会启动新线程。
39、如何控制某个方法允许并发访问线程的个数?
semaphore.acquire () 请求一个信号量,这时候的信号量个数 - 1(一旦没有可使用的信号量,也即信号量个数变为负数时,再次请求的时候就会阻塞,直到其他线程释放了信号量)
semaphore.release () 释放一个信号量,此时信号量个数 + 1
40、在 Java 中 wait 和 seelp 方法的不同;
Java 程序中 wait 和 sleep 都会造成某种形式的暂停,它们可以满足不同的需要。wait () 方法用于线程间通信,如果等待条件为真且其它线程被唤醒时它会释放锁,而 sleep () 方法仅仅释放 CPU 资源或者让当前线程停止执行一段时间,但不会释放锁。
41、谈谈 wait/notify 关键字的理解
等待对象的同步锁,需要获得该对象的同步锁才可以调用这个方法,否则编译可以通过,但运行时会收到一个异常:IllegalMonitorStateException。
调用任意对象的 wait () 方法导致该线程阻塞,该线程不可继续执行,并且该对象上的锁被释放。
唤醒在等待该对象同步锁的线程 (只唤醒一个,如果有多个在等待), 注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由 JVM 确定唤醒哪个线程,而且不是按优先级。
调用任意对象的 notify () 方法则导致因调用该对象的 wait () 方法而阻塞的线程中随机选择的一个解除阻塞(但要等到获得锁后才真正可执行)。
42、什么导致线程阻塞?线程如何关闭?
阻塞式方法是指程序会一直等待该方法完成期间不做其他事情,ServerSocket 的 accept () 方法就是一直等待客户端连接。这里的阻塞是指调用结果返回之前,当前线程会被挂起,直到得到结果之后才会返回。此外,还有异步和非阻塞式方法在任务完成前就返回。
一种是调用它里面的 stop () 方法
另一种就是你自己设置一个停止线程的标记 (推荐这种)
43、如何保证线程安全?
1.synchronized;
2.Object 方法中的 wait,notify;
3.ThreadLocal 机制 来实现的。
44、如何实现线程同步?
1、synchronized 关键字修改的方法。2、synchronized 关键字修饰的语句块 3、使用特殊域变量(volatile)实现线程同步
45、线程间操作 List
List list = Collections.synchronizedList(new ArrayList());
46、谈谈对 Synchronized 关键字,类锁,方法锁,重入锁的理解
java 的对象锁和类锁:java 的对象锁和类锁在锁的概念上基本上和内置锁是一致的,但是,两个锁实际是有很大的区别的,对象锁是用于对象实例方法,或者一个对象实例上的,类锁是用于类的静态方法或者一个类的 class 对象上的。我们知道,类的对象实例可以有很多个,但是每个类只有一个 class 对象,所以不同对象实例的对象锁是互不干扰的,但是每个类只有一个类锁。但是有一点必须注意的是,其实类锁只是一个概念上的东西,并不是真实存在的,它只是用来帮助我们理解锁定实例方法和静态方法的区别的
49、synchronized 和 volatile 关键字的区别
1.volatile 本质是在告诉 jvm 当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取;synchronized 则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
2.volatile 仅能使用在变量级别;synchronized 则可以使用在变量、方法、和类级别的
3.volatile 仅能实现变量的修改可见性,不能保证原子性;而 synchronized 则可以保证变量的修改可见性和原子性
4.volatile 不会造成线程的阻塞;synchronized 可能会造成线程的阻塞。
5.volatile 标记的变量不会被编译器优化;synchronized 标记的变量可以被编译器优化
51、ReentrantLock 、synchronized 和 volatile 比较
ava 在过去很长一段时间只能通过 synchronized 关键字来实现互斥,它有一些缺点。比如你不能扩展锁之外的方法或者块边界,尝试获取锁时不能中途取消等。Java 5 通过 Lock 接口提供了更复杂的控制来解决这些问题。 ReentrantLock 类实现了 Lock,它拥有与 synchronized 相同的并发性和内存语义且它还具有可扩展性。
53、死锁的四个必要条件?
死锁产生的原因
1. 系统资源的竞争
系统资源的竞争导致系统资源不足,以及资源分配不当,导致死锁。
2. 进程运行推进顺序不合适
互斥条件:一个资源每次只能被一个进程使用,即在一段时间内某 资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待。
请求与保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源 已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。
不可剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能 由获得该资源的进程自己来释放(只能是主动释放)。
循环等待条件:若干进程间形成首尾相接循环等待资源的关系
这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。
死锁的避免与预防:
死锁避免的基本思想:
系统对进程发出每一个系统能够满足的资源申请进行动态检查,并根据检查结果决定是否分配资源,如果分配后系统可能发生死锁,则不予分配,否则予以分配。这是一种保证系统不进入死锁状态的动态策略。
理解了死锁的原因,尤其是产生死锁的四个必要条件,就可以最大可能地避免、预防和解除死锁。所以,在系统设计、进程调度等方面注意如何让这四个必要条件不成立,如何确定资源的合理分配算法,避免进程永久占据系统资源。此外,也要防止进程在处于等待状态的情况下占用资源。因此,对资源的分配要给予合理的规划。
死锁避免和死锁预防的区别:
死锁预防是设法至少破坏产生死锁的四个必要条件之一,严格的防止死锁的出现,而死锁避免则不那么严格的限制产生死锁的必要条件的存在,因为即使死锁的必要条件存在,也不一定发生死锁。死锁避免是在系统运行过程中注意避免死锁的最终发生。
56、什么是线程池,如何使用 ?
创建线程要花费昂贵的资源和时间,如果任务来了才创建线程那么响应时间会变长,而且一个进程能创建的线程数有限。为了避免这些问题,在程序启动的时候就创建若干线程来响应处理,它们被称为线程池,里面的线程叫工作线程。从 JDK1.5 开始,Java API 提供了 Executor 框架让你可以创建不同的线程池。比如单线程池,每次处理一个任务;数目固定的线程池或者是缓存线程池(一个适合很多生存期短的任务的程序的可扩展线程池)。
57、Java 中堆和栈有什么不同?
为什么把这个问题归类在多线程和并发面试题里?因为栈是一块和线程紧密相关的内存区域。每个线程都有自己的栈内存,用于存储本地变量,方法参数和栈调用,一个线程中存储的变量对其它线程是不可见的。而堆是所有线程共享的一片公用内存区域。对象都在堆里创建,为了提升效率线程会从堆中弄一个缓存到自己的栈,如果多个线程使用该变量就可能引发问题,这时 volatile 变量就可以发挥作用了,它要求线程从主存中读取变量的值。
58、有三个线程 T1,T2,T3,怎么确保它们按顺序执行?
在多线程中有多种方法让线程按特定顺序执行,你可以用线程类的 join () 方法在一个线程中启动另一个线程,另外一个线程完成该线程继续执行。为了确保三个线程的顺序你应该先启动最后一个 (T3 调用 T2,T2 调用 T1),这样 T1 就会先完成而 T3 最后完成。
线程间通信
我们知道线程是 CPU 调度的最小单位。在 Android 中主线程是不能够做耗时操作的,子线程是不能够更新 UI 的。而线程间通信的方式有很多,比如广播,Eventbus,接口回掉,在 Android 中主要是使用 handler。handler 通过调用 sendmessage 方法,将保存消息的 Message 发送到 Messagequeue 中,而 looper 对象不断的调用 loop 方法,从 messageueue 中取出 message,交给 handler 处理,从而完成线程间通信。
线程池
Android 中常见的线程池有四种,FixedThreadPool、CachedThreadPool、ScheduledThreadPool、SingleThreadExecutor。
FixedThreadPool 线程池是通过 Executors 的 new FixedThreadPool 方法来创建。它的特点是该线程池中的线程数量是固定的。即使线程处于闲置的状态,它们也不会被回收,除非线程池被关闭。当所有的线程都处于活跃状态的时候,新任务就处于队列中等待线程来处理。注意,FixedThreadPool 只有核心线程,没有非核心线程。
CachedThreadPool 线程池是通过 Executors 的 newCachedThreadPool 进行创建的。它是一种线程数目不固定的线程池,它没有核心线程,只有非核心线程,当线程池中的线程都处于活跃状态,就会创建新的线程来处理新的任务。否则就会利用闲置的线程来处理新的任务。线程池中的线程都有超时机制,这个超时机制时长是 60s,超过这个时间,闲置的线程就会被回收。这种线程池适合处理大量并且耗时较少的任务。这里得说一下,CachedThreadPool 的任务队列,基本都是空的。
ScheduledThreadPool 线程池是通过 Executors 的 newScheduledThreadPool 进行创建的,它的核心线程是固定的,但是非核心线程数是不固定的,并且当非核心线程一处于空闲状态,就立即被回收。这种线程适合执行定时任务和具有固定周期的重复任务。
SingleThreadExecutor 线程池是通过 Executors 的 newSingleThreadExecutor 方法来创建的,这类线程池中只有一个核心线程,也没有非核心线程,这就确保了所有任务能够在同一个线程并且按照顺序来执行,这样就不需要考虑线程同步的问题。
AsyncTask 的工作原理
AsyncTask 是 Android 本身提供的一种轻量级的异步任务类。它可以在线程池中执行后台任务,然后把执行的进度和最终的结果传递给主线程更新 UI。实际上,AsyncTask 内部是封装了 Thread 和 Handler。虽然 AsyncTask 很方便的执行后台任务,以及在主线程上更新 UI,但是,AsyncTask 并不合适进行特别耗时的后台操作,对于特别耗时的任务,个人还是建议使用线程池。
AsyncTask 提供有 4 个核心方法:
1、onPreExecute (): 该方法在主线程中执行,在执行异步任务之前会被调用,一般用于一些准备工作。
2、doInBackground (String... params): 这个方法是在线程池中执行,此方法用于执行异步任务。在这个方法中可以通过 publishProgress 方法来更新任务的进度,publishProgress 方法会调用 onProgressUpdate 方法,另外,任务的结果返回给 onPostExecute 方法。
3、onProgressUpdate (Object... values): 该方法在主线程中执行,主要用于任务进度更新的时候,该方法会被调用。
4、onPostExecute (Long aLong):在主线程中执行,在异步任务执行完毕之后,该方法会被调用,该方法的参数及为后台的返回结果。
除了这几个方法之外还有一些不太常用的方法,如 onCancelled (), 在异步任务取消的情况下,该方法会被调用。
源码可以知道从上面的 execute 方法内部调用的是 executeOnExecutor () 方法,即 executeOnExecutor (sDefaultExecutor, params); 而 sDefaultExecutor 实际上是一个串行的线程池。而 onPreExecute () 方法在这里就会被调用了。接着看这个线程池。AsyncTask 的执行是排队执行的,因为有关键字 synchronized,而 AsyncTask 的 Params 参数就封装成为 FutureTask 类,FutureTask 这个类是一个并发类,在这里它充当了 Runnable 的作用。接着 FutureTask 会交给 SerialExecutor 的 execute 方法去处理,而 SerialExecutor 的 executor 方法首先就会将 FutureTask 添加到 mTasks 队列中,如果这个时候没有任务,就会调用 scheduleNext () 方法,执行下一个任务。如果有任务的话,则执行完毕后最后在调用 scheduleNext (); 执行下一个任务。直到所有任务被执行完毕。而 AsyncTask 的构造方法中有一个 call () 方法,而这个方法由于会被 FutureTask 的 run 方法执行。所以最终这个 call 方法会在线程池中执行。而 doInBackground 这个方法就是在这里被调用的。我们好好研究一下这个 call () 方法。mTaskInvoked.set (true); 表示当前任务已经执行过了。接着执行 doInBackground 方法,最后将结果通过 postResult (result); 方法进行传递。postResult () 方法中通过 sHandler 来发送消息,sHandler 的中通过消息的类型来判断一个 MESSAGE_POST_RESULT,这种情况就是调用 onPostExecute (result) 方法或者是 onCancelled (result)。另一种消息类型是 MESSAGE_POST_PROGRESS 则调用更新进度 onProgressUpdate。
Binder 的工作机制
直观来说,Binder 是 Android 中的一个类,它实现了 IBinder 接口,从 IPC 的角度来说,Binder 是 Android 中的一种跨进程通信的一种方式,同时还可以理解为是一种虚拟的物理设备,它的设备驱动是/dev/binder/。从 Framework 角度来说,Binder 是 ServiceManager 的桥梁。从应用层来说,Binder 是客户端和服务端进行通信的媒介。
我们先来了解一下这个类中每个方法的含义:
DESCRIPTOR:Binder 的唯一标识,一般用于当前 Binder 的类名表示。
asInterface (android.os.IBinder obj):用于将服务端的 Binder 对象转换成客户端所需的 AIDL 接口类型的对象,这种转化过程是区分进程的,如果客户端和服务端位于同一个进程,那么这个方法返回的是服务端的 stub 对象本身,否则返回的是系统封装后的 Stub.proxy 对象。
asBinder ():用于返回当前 Binder 对象。
onTransact:该方法运行在服务端的 Binder 线程池中,当客户端发起跨进程通信请求的时候,远程请求通过系统底层封装后交给该方法处理。注意这个方法 public boolean onTransact (int code, android.os.Parcel data, android.os.Parcel reply, int flags),服务端通过 code 可以确定客户端所请求的目标方法是什么,接着从 data 中取出目标方法所需的参数,然后执行目标方法。当目标方法执行完毕后,就像 reply 中写入返回值。这个方法的执行过程就是这样的。如果这个方法返回 false,客户端是会请求失败的,所以我们可以在这个方法中做一些安全验证。
Binder 的工作机制但是要注意一些问题:1、当客户端发起请求时,由于当前线程会被挂起,直到服务端返回数据,如果这个远程方法很耗时的话,那么是不能够在 UI 线程,也就是主线程中发起这个远程请求的。
2、由于 Service 的 Binder 方法运行在线程池中,所以 Binder 方法不管是耗时还是不耗时都应该采用同步的方式,因为它已经运行在一个线程中了。
view 的事件分发和 view 的工作原理
Android 自定义 view,我们都知道实现有三部曲,onMeasure (),onLayout (),onDraw ()。View 的绘制流程是从 viewRoot 的 perfromTraversal 方法开始的。它经过 measure,layout,draw 方法才能够将 view 绘制出来。其中 measure 是测量宽高的,layout 是确定 view 在父容器上的摆布位置的,draw 是将 view 绘制到屏幕上的。
Measure:
view 的测量是需要 MeasureSpc (测量规格),它代表一个 32 位 int 值,高 2 位代表 SpecMode (测量模式),低(30)位的代表 SpecSize (某种测量模式下的规格大小)。而一组 SpecMode 和 SpeSize 可以打包为一个 MeasureSpec, 反之,MeasureSpec 可以解包得到 SpecMode 和 SpeSize 的值。SpecMode 有三类:
unSpecified: 父容器不对 view 有任何限制,要多大有多大。一般系统用这个多。
Exactly: 父容器已经检测出 view 所需要的精确大小,这个时候,view 的大小就是 SpecSize 所指定的值,它对应者 layout 布局中的 math_parent 或者是具体的数值
At_most: 父容器指定了一个可用大小的 SpecSize,view 的大小不能够大于这个值,它对应这布局中的 wrao_content.
对于普通的 view,它的 MeasureSpec 是由父容器的 MeasureSpec 和自身的 layoutParam 共同决定的,一旦 MeasureSpec 确定后,onMeasure 就可以确定 view 的宽高了。
View 的 measure 过程:
onMeasure 方法中有个 setMeasureDimenSion 方法来设置 view 的宽高测量值,而 setMeasureDimenSion 有个 getDefaultSize () 方法作为参数。一般情况下,我们只需要关注 at_most 和 exactly 两种情况,getDefaultSize 的返回值就是 measureSpec 中的 SpecSize, 而这个值基本就是 view 测量后的大小。而 UnSpecified 这种情况,一般是系统内部的测量过程,它是需要考虑 view 的背景这些因素的。
前面说的是 view 的测量过程,而 viewGroup 的 measure 过程:
对于 viewGroup 来说,除了完成自己的 measure 过程以外,还要遍历去调用子类的 measure 方法,各个子元素在递归执行这个过程,viewGroup 是一个抽象的类,没有提供有 onMeasure 方法,但是提供了一个 measureChildren 的方法。measureChild 方法的思想就是取出子元素的 layoutParams, 然后通过 getChildMeasureSpec 来常见子元素的 MeasureSpec, 然后子元素在电泳 measure 方法进行测量。由于 viewGroup 子类有不同的布局方式,导致他们的测量细节不一样,所以 viewGroup 不能象 view 一样调用 onMeasure 方法进行测量。
注意:在 activity 的生命周期中是没有办法正确的获取 view 的宽高的,原因就是 view 没有测量完。
- 在 onWindowFocuschanged 方法中获取 ---- 改方法含义是 view 已经初始化完毕
- View.post () 方法,将润那边了投递到消息队列的尾部。
- 使用 viewTreeObserver 的回调来完成。
- 通过 view.measure 方式手动测量。
onLayout
普通的 view 的话,可以通过 setFrame 方法来的到 view 四个顶点的位置,也就确定了 view 在父容器的位置,接着就调用 onLayout 方法,该方法是父容器确定子元素的位置。
onDraw
该方法就是将 view 绘制到屏幕上。分以下几步
- 绘制背景,
- 绘制自己,
- 绘制 child,
- 绘制装饰。
Android 中性能优化
由于手机硬件的限制,内存和 CPU 都无法像 pc 一样具有超大的内存,Android 手机上,过多的使用内存,会容易导致 oom,过多的使用 CPU 资源,会导致手机卡顿,甚至导致 anr。我主要是从一下几部分进行优化:
布局优化,绘制优化,内存泄漏优化,响应速度优化,listview 优化,bitmap 优化,线程优化
布局优化:工具 hierarchyviewer,解决方式:
1、删除无用的空间和层级。
2、选择性能较低的 viewgroup,如 Relativelayout,如果可以选择 Relativelayout 也可以使用 LinearLayout, 就优先使用 LinearLayout,因为相对来说 Relativelayout 功能较为复杂,会占用更多的 CPU 资源。
3、使用标签 <include/> 重用布局,<Merge/> 减少层级,<viewStub/> 进行预加载,使用的时候才加载。
绘制优化
绘制优化指 view 在 ondraw 方法中避免大量的耗时操作,由于 ondraw 方法可能会被频繁的调用。
1、ondraw 方法中不要创建新的局部变量,ondraw 方法被频繁的调用,很容易引起 GC。
2、ondraw 方法不要做耗时操作。
内存优化:参考内存泄漏。
响应优化
主线程不能做耗时操作,触摸事件 5s, 广播 10s,service20s。
listview 优化:
1、getview 方法中避免耗时操作。
2、view 的复用和 viewholder 的使用。
3、滑动不适合开启异步加载。
4、分页处理数据。
5、图片使用三级缓存。
Bitmap 优化:
1、等比例压缩图片。
2、不用的图片,及时 recycler 掉
线程优化
线程优化的思想是使用线程池来管理和复用线程,避免程序中有大量的 Thread,同时可以控制线程的并发数,避免相互抢占资源而导致线程阻塞。
其他优化
1、少用枚举,枚举占用空间大。
2、使用 Android 特有的数据结构,如 SparseArray 来代替 hashMap。
3、适当的使用软引用和弱引用。
加密算法(base64、MD5、对称加密和非对称加密)和使用场景。
什么是 Rsa 加密?
RSA 算法是最流行的公钥密码算法,使用长度可以变化的密钥。RSA 是第一个既能用于数据加密也能用于数字签名的算法。
RSA 算法原理如下:
1. 随机选择两个大质数 p 和 q,p 不等于 q,计算 N=pq;
2. 选择一个大于 1 小于 N 的自然数 e,e 必须与 (p-1)(q-1) 互素。
3. 用公式计算出 d:d×e = 1 (mod (p-1)(q-1)) 。
4. 销毁 p 和 q。
最终得到的 N 和 e 就是 “公钥”,d 就是 “私钥”,发送方使用 N 去加密数据,接收方只有使用 d 才能解开数据内容。
RSA 的安全性依赖于大数分解,小于 1024 位的 N 已经被证明是不安全的,而且由于 RSA 算法进行的都是大数计算,使得 RSA 最快的情况也比 DES 慢上倍,这是 RSA 最大的缺陷,因此通常只能用于加密少量数据或者加密密钥,但 RSA 仍然不失为一种高强度的算法。
使用场景:项目中除了登陆,支付等接口采用 rsa 非对称加密,之外的采用 aes 对称加密,今天我们来认识一下 aes 加密。
什么是 MD5 加密?
MD5 英文全称 “Message-Digest Algorithm 5”,翻译过来是 “消息摘要算法 5”,由 MD2、MD3、MD4 演变过来的,是一种单向加密算法,是不可逆的一种的加密方式。
MD5 加密有哪些特点?
压缩性:任意长度的数据,算出的 MD5 值长度都是固定的。
容易计算:从原数据计算出 MD5 值很容易。
抗修改性:对原数据进行任何改动,哪怕只修改 1 个字节,所得到的 MD5 值都有很大区别。
强抗碰撞:已知原数据和其 MD5 值,想找到一个具有相同 MD5 值的数据(即伪造数据)是非常困难的。
MD5 应用场景:
一致性验证
数字签名
安全访问认证
什么是 aes 加密?
高级加密标准(英语:Advanced Encryption Standard,缩写:AES),在密码学中又称 Rijndael 加密法,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的 DES,已经被多方分析且广为全世界所使用。
HashMap 的实现原理:
HashMap 是基于哈希表的 map 接口的非同步实现,它允许使用 null 值作为 key 和 value。在 Java 编程语言中最基本的结构就是两种,一种是数组,另一种是模拟指针(引用)。所有的数据结构都可以用这两个基本的结构来构造,HashMap 也不例外。HashMap 实际上是一个 “链表散列” 的数据结构。即数组和链表的结合体。
从图上可以看出,HashMap 底层就是一个数据结构,数组中的每一项又是一个链表。
冲突:
HashMap 中调用 Hashcode () 方法计算 Hashclde 值,由于 Java 中两个不同的对象可能有一样的 Hashcode。就导致了冲突的产生。
解决:
HashMap 在 put 时候,底层源码可以看出,当程序试图将一个 key-value 对象放入到 HashMap 中,首先根据该 key 的 hashCode () 返回值决定该 Entry 的存储位置,如果两个 Entry 的 key 的 hashCode () 方法返回值相同,那他们的存储位置相同,如果这两个 Entry 的 key 通过 equals 比较返回 true,新添加的 Entry 的 value 将会覆盖原来的 Entry 的 value,但是 key 不会被覆盖,反之,如果返回 false,新添加的 Entry 将与集合中原有的 Entry 形成 Entry 链,新添加的位于头部,旧的位于尾部。
存:
取:
HashMap 的实现原理:
- 利用 key 的 hashCode 重新 hash 计算出当前对象的元素在数组中的下标。
- 存储时如果出现 hash 值相同的 key,分两种情况:1、如果 key 相同,则覆盖原来的值。2、如果 key 不同(出现冲突),则放在链表中。
- 获取时,直接找到 hash 值对应的下标,再进一步判断 key 是否相同,从而拿到对应的值。
- Hashmap 的核心就是使用数组进行存储,出现 key 冲突的时候,就存放在链表中。
Android 基础知识再次理解
一、Activity的重新认识
1.Intent的使用
补充:FlAG_ACTIVITY_CLEAN_TOP : 类似于singal task的使用
Android 基础知识学习
一.Android 编译过程
- 初始化参数设置
- 检查环境变量与目标环境
- 选择 lunch 并读取目标配置和平台信息
- 清空输出目录
- 编译
- 生成升级包
二. ./build/envsetup.sh 分析
- 加载编译命令:source ./build/envsetup.sh
- 加载平台信息:lunch 平台信息;export 查看环境变量
三.Android.mk
- native 层 (可执行文件、动态库、静态库);Java 层 (Jar 包、Apk);组成 android 编译基本语法规则
Android.mk简例
LOCAL_PATH := $(call my-dir) #LOCAL_PATH定义在build/core/definitons.mk,定义当前模块相对路径
include $(CLEAR_VARS) #CLEAR_VAR定义在build/core/cler_vars.mk,表示清空除loal_path外的环境变量
LOCAL_MODULE := test #编译生成名称
LOCAL_SRC_FILES := test.c #源文件
LOCAL_MODULE_PATH := $(LOCAL_PATH) #指定输出路径
include $(BUILD_EXECUTABLE) #生成格式,这里是编程生成二进制文件
- Android.mk 深度学习 1:多源码文件编译、一个.mk 中编译多个目标文件、编译动态库、编译静态库
Android.mk 简例:增加源文件__将每个文件添加到.mk中
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := test
LOCAL_SRC_FILES := src/main.c \ #增加多源文件,将每个文件添加到.mk中
src/test1.c \
src/test2.c
LOCAL_MODULE_PATH := $(LOCAL_PATH)/bin
include $(BUILD_EXECUTABLE)
Android.mk 简例:增加源文件__使用系统提供的函数处理
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := test
LOCAL_C_ALL_FILES := $(call all-c-files-under) #取所有.c文件
LOCAL_SRC_FILES := $(LOCAL_C_ALL_FILES)
LOCAL_MODULE_PATH := $(LOCAL_PATH)/bin
include $(BUILD_EXECUTABLE)
Android.mk 简例:一个mk文件编译多个目标文件
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := test #目标文件test
LOCAL_C_ALL_FILES := $(call all-c-files-under) #取所有.c文件
LOCAL_SRC_FILES := $(LOCAL_C_ALL_FILES)
LOCAL_MODULE_PATH := $(LOCAL_PATH)/bin
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_MODULE := test1 #目标文件test1
LOCAL_C_ALL_FILES := $(call all-c-files-under) #取所有.c文件
LOCAL_SRC_FILES := $(LOCAL_C_ALL_FILES)
LOCAL_MODULE_PATH := $(LOCAL_PATH)/bin
include $(BUILD_EXECUTABLE)
Android.mk 简例:编译生成动态库
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := libtest
LOCAL_C_ALL_FILES := src/test1.c \
src/test2.c
LOCAL_SRC_FILES := $(LOCAL_C_ALL_FILES)
LOCAL_MODULE_PATH := $(LOCAL_PATH)/bin
include $(BUILD_SHARED_LIBRARY) #编译生成动态库
Android.mk 简例:编译生成静态库
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := libtest
LOCAL_C_ALL_FILES := src/test1.c \
src/test2.c
LOCAL_SRC_FILES := $(LOCAL_C_ALL_FILES)
LOCAL_MODULE_PATH := $(LOCAL_PATH)/bin
include $(BUILD_STATIC_LIBRARY) #编译生成静态库
- Android.mk 深度学习 2:如何引入系统库、如何引入第三方库、如何引入静态库、如何进入头文件
Android.mk 简例:项目中引入系统库
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := test
LOCAL_SRC_FILES := src/main.cpp
LOCAL_SHARED_LIBRARIES += liblog #链入系统log库
LOCAL_MODULE_PATH := $(LOCAL_PATH)/bin
include $(BUILD_EXECUTABLE)
Android.mk 简例:加载第三方库
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := libtest
LOCAL_SRC_FILES := src/test.cpp
LOCAL_MODULE_PATH := $(LOCAL_PATH)/lib
include $(BUILD_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := test
LOCAL_LDFLAGS :=-L./lib -ltest #加载当前目录下的第三方库
LOCAL_SRC_FILES := src/main.cpp #调用了test.cpp接口
LOCAL_SHARED_LIBRARIES += liblog
LOCAL_MODULE_PATH := $(LOCAL_PATH)/bin
include $(BUILD_EXECUTABLE)
Android.mk 简例:引入第三方头文件
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := libtest
LOCAL_SRC_FILES := src/test.cpp
LOCAL_MODULE_PATH := $(LOCAL_PATH)/lib
include $(BUILD_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := test
LOCAL_C_INCLUDES :=$(LOCAL_PATH)/inc
LOCAL_LDFLAGS :=-L./lib -ltest #加载当前目录下的第三方库
LOCAL_SRC_FILES := src/main.cpp #调用了test.cpp接口
LOCAL_SHARED_LIBRARIES += liblog
LOCAL_MODULE_PATH := $(LOCAL_PATH)/bin
include $(BUILD_EXECUTABLE)
Android.mk 简例:引入静态库
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := libtest
LOCAL_SRC_FILES := src/test.cpp
LOCAL_MODULE_PATH := $(LOCAL_PATH)/lib
include $(BUILD_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := test
LOCAL_C_INCLUDES :=$(LOCAL_PATH)/inc
LOCAL_LDFLAGS := $(LOCAL_PATH)/lib/libtest1.a
LOCAL_SRC_FILES := src/main.cpp #调用了test.cpp接口
LOCAL_SHARED_LIBRARIES += liblog
LOCAL_MODULE_PATH := $(LOCAL_PATH)/bin
include $(BUILD_EXECUTABLE)
- Android.mk 深度学习 3:如何编译 jar 包、如何编译 Apk、Apk 中导入 jar 包和库文件、mk 中如何加判断语句
Android.mk编译生成Apk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_STATIC_JAVA_LIBRARIES := libarity android-support-v4 guava
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_SDK_VERSION := current
LOCAL_PACKAGE_NAME := Calculator #编译生成apk名字
include $(BUILD_PACKAGE) #编译生成apk
Android.mk编译生成jar
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_MODULE := com.test.myjar
include $(BUILD_STATIC_JAVA_LIBRARY) #编译生成静态jar包
若是-》include $(BUILD_JAVA_LIBRARY) #编译生成共享jar包
判断
ifeq/ifneq:根据判断条件执行相关编译
四.Android 启动过程
五.Android 启动模式
- recovery 升级模式:启动 recovery 分区内核和文件系统;
- 正常启动模式:引导内核和启动 Android 系统;
六.init 启动分析
- Android init 进程:init 进程源码位置 system/core/init/init.c,init 系统中位置 /init;
七。走进 init 启动脚本
八.Android 启动脚本分析
九.Android 脚本执行和进程守护
- init 进程处理消息事件:(1) 根据 shell 或系统中消息设置系统 Prop;(2) 守护系统服务,如果服务退出,重启退出的服务;
学习视频路径:https://www.bilibili.com/video/BV1mb411872F?from=search&seid=13706101650228050770
关于Android基础知识汇总和android基础知识总结的介绍已经告一段落,感谢您的耐心阅读,如果想了解更多关于android 基础知识、android 基础知识 1、Android 基础知识再次理解、Android 基础知识学习的相关信息,请在本站寻找。
本文标签: