lru算法:最近最少使用
1.新数据插入到链表头部;
2.每当缓存命中(即缓存数据被访问),则将数据移到链表头部;
3.当链表满的时候,将链表尾部的数据丢弃。
自定义控件:
1.measure操作,主要用于计算视图的大小,并通过setMeasuredDimension(width, height)保存计算结果。
2.layout操作,用于设置视图在屏幕中显示的位置
3.draw操作,draw操作利用前两部得到的参数,将视图显示在屏幕上
rect可以直接获取文字的宽高,获取FontMetrics对象,也可以获得top,bottom,ascent,descent,然后计算宽高
布局渲染原理:https://www.cnblogs.com/LiuZhen/p/10890472.html
java集合类:
java集合类主要由两个接口派生而出:Collection和Map,Map没有继承Collection接口.
list:有序的,可以重复的,使用此接口能够精确的控制每个元素插入的位置。用户能够使用索引(元素在List中的位置,类似于数组下标)来访问List中的元素,List允许有相同的元素,查询速度快。因为往list集合里插入或删除数据时,会伴随着后面数据的移动,所有插入删除数据速度慢。
map:Map提供key到value的映射,键不能重复,值可以重复
set:无序的,不包含重复的元素,当试图把两个相同的对象加入一个Set中时,对象会调用equals方法比较两个对象元素是否相同,相同则不会加入。
Queue(队列):实现了一个FIFO(First In First Out,先进先出)的机制。元素在队列的尾部插入(入队操作),并从队列的头部移出(出队操作)。
ArrayList:
基于数组实现,ArrayList的容量默认为10,是一个动态数组,可以通过下标索引直接查找到指定位置的元素,因此查找效率高,但每次插入或删除元素,就要大量地移动元素,插入删除元素的效率低,自动根据数据去申请内存,不是线程安全,如果需要同步,可以用 Collections的synchronizedList方法,实现List接口、底层使用数组保存所有元素,每次数组容量的增长大约是其原容量的1.5倍,线程安全需要更大的系统开销,每次增加元素,都要将原来的元素拷贝到一个新的数组中,非常之耗时,也因此建议在事先能确定元素数量的情况下,才使用ArrayList,否则建议使用LinkedList,ArrayList中允许元素为null。
HashMap:
实现了map接口,map不允许有重复的key,不是有序的,允许key,value为空,一个数组加一个链表,是数组和链表的结合体,值根据key来分配,不是线程安全,内存超出自动申请2倍的内存,把原来的对象放入新的集合中,HashMap不支持线程的同步,即任一时刻可以有多个线程同时写HashMap;可能会导致数据的不一致。如果需要同步,可以使用ConcurrentHashMap
调整HashMap大小的时候存在条件竞争,多线程同时调整map大小,条件发送竞争会导致死循环
ConcurrentHashMap:
引入了CAS,CAS存在三个操作值,内存值,预期值,要修改的新值,可以用来替代HashTable,HashTable是synchronized的,但是ConcurrentHashMap性能更好,因为它只对一部分进行上锁,HashTable则会对整个map上锁
LinkedList:
基于链表的数据结构,LinkedList也和ArrayList一样实现了List接口,随机访问get和set,linkedlist需要移动指针,所以慢于arraylist,执行插入和删除操作时比ArrayList更加高效,因为它是基于链表的。基于链表也决定了它在随机访问方面要比ArrayList逊色一点。
插入或者是删除元素频繁,或者压根就不需要访问元素的时候,选择LinkedLis;
查询比插入或者是删除元素更加频繁的时候,应该使用ArrayList;
HashSet:
实现了Set接口,它不允许集合中有重复的值,HashSet较HashMap来说较慢,因为map因为是使用唯一的键来获取对象,HashSet查找某个对象时,首先用hashCode()方法计算出这个对象的Hash码,然后再根据Hash码到相应的存储区域用equals()方法查找,从而提高了效率。允许元素为null值
HashSet类源码中有一个HashMap类型的成员变量,其中的成员方法也是调用hashMap的成员方法,只不过存储时,按照key-value对存入HashMap对象中,把值存在key中,Value统一为null。
有时候hashset需要重写equals方法,假如存储同学对象,对象包含同学的信息,equals()方法是根据对象的内存地址来判断两个对象是否相等的,由于两次插入的同学的内存地址肯定不相同,所以判断的结果是不相等,所以两次都插入了。于是,我们需要覆写equals()方法来判断两个对象是否是同一个对象,可以根据地址,类型,然后加上hashcode值(每个对象的code值都不一样,所以需要重写hashcode方法,返回对象的名字的hashcode值来判断)来判断。
数组:寻址容易,插入和删除困难;
链表:寻址困难,插入和删除容易;
哈希表:两者结合.
链表是一种数据的存储方式,保存的数据在内存中不连续的,用指针对数据进行访问
队列是一种数据结构,其特点是先进先出,后进后出
GC工作原理:
参考文章:http://blog.jobbole.com/80499/
http://blog.csdn.net/zhb123gggggg/article/details/40901003
年轻代分为3块,年轻代总共有3块空间,其中2块为Survivor区一块Eden区域,其中一个Survivor区必须保持空。如果数据存在于两个Survivor区,或两个都没使用,你可以将这个情况作为系统错误的一个标志。当老年代数据满时,基本上会执行一次GC。执行程序根据不同GC类型而变化,永久带可以看做是方法区
- 绝大多数新创建的对象分配在Eden区。
- 在Eden区发生一次GC后,存活的对象移到其中一个Survivor区。
- 在Eden区发生一次GC后,对象是存放到Survivor区,这个Survivor区已经存在其他存活的对象。
- 一旦一个Survivor区已满,存活的对象移动到另外一个Survivor区。然后之前那个空间已满Survivor区将置为空,没有任何数据。
- 经过重复多次这样的步骤后依旧存活的对象将被移到老年代。
在jdk7中有5中垃圾回收集器
- Serial收集器(不能用于服务器端。这个收集器类型仅应用于单核CPU桌面电脑。使用serial收集器会显着降低应用程序的性能)
- Parallel收集器
- Parallel Old收集器 (Parallel Compacting GC)收集器
- Concurrent Mark & Sweep GC (or “CMS”)收集器
- Garbage First (G1) 收集器
Serial 收集器 (-XX:+UseSerialGC)
在老年代的GC使用算法被称为“标记-清除-整理”。
- 该算法的第一步是在老年代标记存活的对象。
- 从头开始检查堆内存空间,并且只留下依然幸存的对象(清除)。
- 最后一步,从头开始,顺序地填满堆内存空间,将存活的对象连续存放在一起,这样堆分成两部分:一边有存放的对象,一边没有对象(整理)。
serial收集器应用于小的存储器和少量的CPU。
Parallel收集器(-XX:+UseParallelGC)
serial收集器只使用一个线程来处理的GC,而parallel收集器使用多线程并行处理GC,因此更快。当有足够大的内存和大量芯数时,parallel收集器是有用的。它也被称为“吞吐量优先垃圾收集器。”
Parallel Old 垃圾收集器(-XX:+UseParallelOldGC)
Parallel Old收集器是自JDK 5开始支持的。相比于parallel收集器,他们的唯一区别就是在老年代所执行的GC算法的不同。它执行三个步骤:标记-汇总-压缩(mark – summary – compaction)。汇总步骤与清理的不同之处在于,其将依然幸存的对象分发到GC预先处理好的不同区域,算法相对清理来说略微复杂一点。
CMS GC (-XX:+UseConcMarkSweepGC)
CMS垃圾收集器比上面解释的各种算法都要复杂很多。初始标记(initial mark) 比较简单。这一步骤只是查找距离类加载器最近的幸存对象。所以停顿时间非常短。之后的并发标记步骤,所有被幸存对象引用的对象会被确认是否已经被追踪检查。这一步的不同之处在于,在标记的过程中,其他的线程依然在执行。在重新标记步骤会修正那些在并发标记步骤中,因新增或者删除对象而导致变动的那部分标记记录。最后,在并发清除步骤,垃圾收集器执行。垃圾收集器进行垃圾收集时,其他线程的依旧在工作。一旦采取了这种GC类型,由于垃圾回收导致的停顿时间会极其短暂。CMS 收集器也被称为低延迟垃圾收集器。它经常被用在那些对于响应时间要求十分苛刻的应用上。
缺点就是它会比其他GC类型占用更多的内存和CPU,默认情况下不支持压缩步骤
G1 GC
首先你要忘记你所理解的新生代和老年代。正如你在上图所看到的,每个对象被分配到不同的网格中,随后执行垃圾回收。当一个区域填满之后,对象被转移到另一个区域,并再执行一次垃圾回收。在这种垃圾回收算法中,不再有从新生代移动到老年代的三部曲。这个类型的垃圾收集算法是为了替代CMS 收集器而被创建的,因为CMS 收集器在长时间持续运行时会产生很多问题,G1最大的好处是他的性能,他比我们在上面讨论过的任何一种GC都要快。
Viewpager:
禁止左右滑动,拦截器返回false,和onTouchEvent返回false,消费事件可以达到
切换闪烁问题,ViewPager.setCurrentItem(position,false);
public void setCurrentItem(int item, boolean smoothScroll) { // TODO Auto-generated method stub super.setCurrentItem(item, smoothScroll); } @Override public void setCurrentItem(int item) { // TODO Auto-generated method stub super.setCurrentItem(item, false);//表示切换的时候,不需要切换时间 }
Android 事件分发机制:
参考链接:http://www.jianshu.com/p/e99b5e8bd67b ,很赞,写的很详细
ACTION_DOWN:
由Activity的dispatchTouchEvent做分发,如果没有对方法重写或者更改返回值,那么整个事件流向应该是从Activity---->ViewGroup--->View 从上往下调用dispatchTouchEvent方法,一直到叶子节点(View)的时候,再由View--->ViewGroup--->Activity从下往上调用onTouchEvent方法。
假如方法返回了true,那么事件终止,被消费了,没有谁能收到这个事件了,如果返回false的话,事件都会回传给父控件的onTouchEvent处理
对于onInterceptTouchEvent拦截器,默认返回false,如果返回true,那么事件不再分发,而是分发给自己的onTouchEvent去处理
View 没有拦截器,默认实现(super)就是把事件分发给自己的onTouchEvent
对于ACTION_MOVE、ACTION_UP:
如果在dispatchTouchEvent和onTouchEvent返回false,事件都会正常分发,返回true消费了事件,所有事件都不会分发;
dispatchTouchEvent返回true,onTouchEvent返回false,所有分发传递终止,所有事件都不会分发;
dispatchTouchEvent返回false,onTouchEvent返回true,ACTION_DOWN 事件会分发下去,然后在view中从下往上onTouchEvent事件传递,ACTION_MOVE 和 ACTION_UP事件则会在onTouchEvent事件消费处的控件中直接把事件交给这个控件,而不会向下分发
synchronized和volatile的区别:
一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义:
1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
2)禁止进行指令重排序。volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取;synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
1.volatile仅能使用在变量级别;
synchronized则可以使用在变量、方法、和类级别的
2.volatile仅能实现变量的修改可见性,并不能保证原子性;
synchronized则可以保证变量的修改可见性和原子性
3.volatile不会造成线程的阻塞;
synchronized可能会造成线程的阻塞。
4.volatile标记的变量不会被编译器优化;
synchronized标记的变量可以被编译器优化
编译器优化:
在本次线程内,读取一个变量时,为了提高存取速度,编译器优化的时候可能会先把变量读取到一个寄存器中,再取变量时直接从寄存器中取值,当变量值在本线程被改变时,会同时把变量新值copy到该寄存器中,以保持一致,当变量因为别的线程而改变了值,该寄存器的值不会改变,从而造成应用程序读取的值和实际的变量值不一致(在并行和多线程共享情况下容易触发)
注:寄存器是CPU内部的元件,寄存器有非常高的读写速度,所以在寄存器之间的数据传送非常快
AQS:
并发包的一个核心组件,里面有state变量,加锁线程变量等核心东西,维护加锁状态,ReentrantLock只是外层的API,内核中的锁机制实现都是依赖AQS组件,AQS内部还有一个等待队列,用来放置加锁失败的线程
初始化 state=0(表示没有被加过锁),还有一个变量用来标记当前加锁的是哪个线程,此时线程1调用lock方法加锁,此时state从0变成1,而变量也标记是属于线程1
可重入锁就是一个ReentrantLock对象多次执行lock加锁和unlock释放锁,对一个锁加多次,每次重入锁会判断当前加锁线程是否为自己,如果是,就把state的值累加1,其它不变,假如不是,一个线程2过来加锁,此时state不等于0,那么state从0变成1的过程会失败,因为已经被加锁了,然后会判断是否为自己线程之前加的锁,这里肯定不是,标记变量显示为线程1持有,所以线程2加锁失败,此时线程2会进入等待队列,等线程1释放锁后再重新尝试加锁,假如线程1此时开始释放锁,AQS内的变量state就会递减1,如果state为0的时候,表示彻底释放,然后加锁线程的标识变量也会设置为null,然后会从等待队列头中唤醒线程2重新尝试加锁
多线程如何同步:
介绍:synchronized关键字可以修饰方法,也可以修饰代码块,但不能修饰构造器,属性等,每个JAVA对象都有且只有一个同步锁,在任何时刻,最多只允许一个线程拥有这把锁,我们可以给共享资源加一把锁,这把锁只有一把钥匙。哪个线程获取了这把钥匙,才有权利访问该共享资源,同步锁不是加在共享资源上,而是加在访问共享资源的代码段上,访问同一份共享资源的不同代码段,应该加上同一个同步锁;如果加的是不同的同步锁,那么根本就起不到同步的作用,没有任何意义
1:synchronized:
1、同步代码块:
synchronized(同一个数据){} 同一个数据:就是N条线程同时访问一个数据。
2、同步方法:
public synchronized 数据返回类型方法名(){}
2:AsyncTask:
实际上,doInBackground()调用时机取决于Android版本,Android 1.6之前,任务是串行执行,只需要一个后来线程。从Android 1.6开始,线程池取代了单个的后台线程,线程池允许并行执行多个任务,以提升性能
3:使用局部变量实现线程同步:
如果使用ThreadLocal管理变量,则每一个使用该变量的线程都获得该变量的副本
4:lock:
线程A和B都要获取对象O的锁定,假设A获取了对象O锁,B将等待A释放对O的锁定,
如果使用 synchronized ,如果A不释放,B将一直等下去,不能被中断
如果 使用Lock,如果A不释放,可以使B在等待了足够长的时间以后,中断等待,而干别的事情
synchronized是在JVM层面上实现的,不但可以通过一些监控工具监控synchronized的锁定,而且在代码执行时出现异常,JVM会自动释放锁定,但是使用Lock则不行,lock是通过代码实现的,要保证锁定一定会被释放,就必须将unLock()放到finally{}中
在资源竞争不是很激烈的情况下,Synchronized的性能要优于Lock,但是在资源竞争很激烈,就是并发量高的情况下,Synchronized的性能会下降几十倍,但是Lock的性能能维持常态;
5:ThreadLocal: 参考博客:http://blog.csdn.net/lufeng20/article/details/24314381
同步机制仅提供一份变量,让不同的线程排队访问,而ThreadLocal为每一个线程都提供了一份变量,因此可以同时访问而互不影响。
public class TestNum { // ①通过匿名内部类覆盖ThreadLocal的initialValue()方法,指定初始值 private static ThreadLocal<Integer> seqNum = new ThreadLocal<Integer>() { public Integer initialValue() { return 0; } }; // ②获取下一个序列值 public int getNextNum() { seqNum.set(seqNum.get() + 1); return seqNum.get(); } public static void main(String[] args) { TestNum sn = new TestNum(); // ③ 3个线程共享sn,各自产生序列号 TestClient t1 = new TestClient(sn); TestClient t2 = new TestClient(sn); TestClient t3 = new TestClient(sn); t1.start(); t2.start(); t3.start(); } private static class TestClient extends Thread { private TestNum sn; public TestClient(TestNum sn) { this.sn = sn; } public void run() { for (int i = 0; i < 3; i++) { // ④每个线程打出3个序列值 System.out.println("thread[" + Thread.currentThread().getName() + "] --> sn[" + sn.getNextNum() + "]"); } } } } 输出结果 thread[Thread-0] --> sn[1] thread[Thread-1] --> sn[1] thread[Thread-2] --> sn[1] thread[Thread-1] --> sn[2] thread[Thread-0] --> sn[2] thread[Thread-1] --> sn[3] thread[Thread-2] --> sn[2] thread[Thread-0] --> sn[3] thread[Thread-2] --> sn[3]
前面几种同步方式都是在底层实现的线程同步,但是我们在实际开发当中,应当尽量远离底层结构
如何避免死锁:
一个通用的经验法则是:当几个线程都要访问共享资源A、B、C 时,保证每个线程都按照同样的顺序去访问他们。
线程间通信:
1:Handle:
handler:线程,创建了looper,looper里面拥有message queue,handler有两个工作,一是发送任务或者消息;二是处理消息或者执行任务,handler既能发送message,也能发送runnbale。换句话说,message queue不只是装message的queue(其实是一个单链表),而且还能装runnable,创建handler前,必须先创建looper。handler发送消息到message queue,所以构造一个handler的时候必须知道message queue,才能确定把消息发送到哪里,而message queue是由looper来管理的,message的callback就是runnable。因此,分辨一个message是不是runnable,其实只要看message的callback是否为空,如果为空,就是普通的message,否则,就是一个runnbale,不产生消息也能发送消息,比如sendEmptyMessage,消息的处理时机还是由handler来决定,handler.sendMessage,把message放到message queue的尾部排队(Message里面有一个long型的属性when,是根据when来排序等),looper从前往后一个一个取消息,send一个Message,还是post一个Runnnable,最终都是send一个Message,Message中有Runnnable属性,sendRunnable就是接到Message再拿出来。
另外可以从源码中看到,sendMessage时,message queue队列实例是从loop中拿出来等,然后looper中发现,实例化looper的时候会创建queue和获取线程操作,所以一个loop对应一个queue,然后loop等创建会在ActivityThread的main方法里的,当然这只是主线程的操作,但是也能看出,一个线程可以拥有一个loop,一个loop可以拥有一个queue,多个handler发送等message也只会在一个queue里面
handler.sendMessageAtFrontOfQueue,把message放到message queue的头部,消息可以马上被处理。
handler.sendMessageAtTime,不马上发送消息到message queue,而是在指定的时间点发送。
handler.sendMessageDelayed,不马上发送消息到message queue,而是在指定的时延后再发送。
要知道message应该发送到哪个handler,必须先创建looper,handler不是独立存在的,一个handler,一定有一个专属的线程,一个消息队列,和一个looper与之关联
handler引用context,loop引用handler,而当activity被finished后,延时发送的消息会继续在消息队列中存活,直到被处理,这个消息又持有activity的handler,因此activity不会被回收,从而造成内存泄漏,解决方案如下
@Override public void onDestroy() { // If null, all callbacks and messages will be removed. mHandler.removeCallbacksAndMessages(null); }
优缺点:
1. Handler用法简单明了,可以将多个异步任务更新UI的代码放在一起,清晰明了 2. 处理单个异步任务代码略显多
2:runOnUiThread
3:AsyncTask:
execute(触发异步任务的执行)-->onPreExecute(前者被调用后立即执行)-->doInBackground(前者被调用后立即执行,用于执行较为费时的操作)-->onProgressUpdate(更新操作)-->onPostExecute(当后台操作结束时)
实际上,doInBackground()调用时机取决于Android版本,只需要一个后来线程。线程池允许并行执行多个任务,以提升性能,AsyncTask对象的创建和execute方法必须在主线程中调用,一个AsyncTask实例只能调用一次execute方法,AsyncTask执行execute方法时在Android1.6之前串行;Android1.6之后并行;Android3.0后串行,如果想要并行的也可以,自定义执行器
进程:参考博客: http://blog.csdn.net/wuseyukui/article/details/48004687
一个应用程序就是一个独立的进程(应用运行在一个独立的环境中,可以避免其他应用程序/进程的干扰)。一般来说,当我们启动一个应用程序时,系统会创建一个进程,并为这个进程创建一个主线程(UI线程),然后就可以运行MainActivity了,如果内存不足,可又有其它为用户提供更紧急服务的进程需要更多内存,Android可能会决定关闭一个进程。在此进程中运行着的应用程序组件也会因此被销毁。当需要再次工作时,会为这些组件重新创建一个进程。
一.前台进程(Foreground process)
前台进程是用户当前做的事所必须的进程,如果满足下面各种情况中的一种,一个进程被认为是在前台:
-
进程持有一个正在与用户交互的Activity。
-
进程持有一个Service,这个Service处于这几种状态:①Service与用户正在交互的Activity绑定。②Service是在前台运行的,即它调用了 startForeground()。③Service正在执行它的生命周期回调函数(onCreate(), onStart(), or onDestroy())。
-
进程持有一个BroadcastReceiver,这个BroadcastReceiver正在执行它的 onReceive() 方法。
杀死前台进程需要用户交互,因为前台进程的优先级是最高的。
二.可见进程(Visible process)
如果一个进程不含有任何前台的组件,但仍可被用户在屏幕上所见。当满足如下任一条件时,进程被认为是可见的:
-
进程持有一个Activity,这个Activity不在前台,但是仍然被用户可见(处于onPause()调用后又没有调用onStop()的状态,比如,前台的activity打开了一个对话框,这样activity就会在其后可见)。
-
进程持有一个Service,这个Service和一个可见的(或者前台的)Activity绑定。
可见的进程也被认为是很重要的,一般不会被销毁,除非是为了保证所有前台进程的运行而不得不杀死可见进程的时候。
三.服务进程 (Service process)
如果一个进程中运行着一个service,这个service是通过 startService() 开启的,并且不属于上面两种较高优先级的情况,这个进程就是一个服务进程。
尽管服务进程没有和用户可以看到的东西绑定,但是它们一般在做的事情是用户关心的,比如后台播放音乐,后台下载数据等。所以系统会尽量维持它们的运行,除非系统内存不足以维持前台进程和可见进程的运行需要。
四.后台进程 (Background process)
如果进程不属于上面三种情况,但是进程持有一个用户不可见的activity(activity的onStop()被调用,但是onDestroy()没有调用的状态),就认为进程是一个后台进程。
后台进程不直接影响用户体验,系统会为了前台进程、可见进程、服务进程而任意杀死后台进程。
通常会有很多个后台进程存在,它们会被保存在一个LRU (least recently used)列表中,这样就可以确保用户最近使用的activity最后被销毁,即最先销毁时间最远的activity。
五.空进程
如果一个进程不包含任何活跃的应用组件,则认为是空进程。
例如:一个进程当中已经没有数据在运行了,但是内存当中还为这个应用驻留了一个进程空间。
保存这种进程的唯一理由是为了缓存的需要,为了加快下次要启动这个进程中的组件时的启动时间。系统为了平衡进程缓存和底层内核缓存的资源,经常会杀死空进程。
IPC(Inter-Process Communication,进程间通信):
linux进程给每个应用程序都分配了一个独立的userid,userid对应一个Linux用户,如果多个app签名一样就会起冲突,两个apk使用相同的userID,这样它们就可以看到对方的文件,配置文件设置相同的sharedUserId,然后可以通过 createPackageContext("com.android.test", CONTEXT_INCLUDE_CODE|CONTEXT_IGNORE_SECURITY);来获取对方的context对象,有了该对象就可以获取对方应用的资源文件了,还能跳转activity(前提是需要在调用的应用里声明exported=”true”属性)
//第一个应用程序为的menifest文件代码如下: <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.demo_A" android:versionCode="1" android:versionName="1.0" android:sharedUserId="com.android.test"> //第二个应用程序的menifest文件代码如下: <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.demo_B" android:versionCode="1" android:versionName="1.0" android:sharedUserId="com.android.test">
1.Messenger(handler方式):
Messenger是一种轻量级的IPC方案,它对AIDL进行封装,所以使用起来非常的方便,当然AIDL通信的底层实现也是对Binder的封装,如果由于某些原因被系统杀死回收,连接就会断开。
在onBind时返回Messenger的binder。双方用Messenger来发送数据,用Handler来处理数据。Messenger处理数据依靠Handler,所以是串行的,也就是说,Handler接到多个message时,就要排队依次处理。
注意使用sharedUserId+android:process
Messenger messenger = null; private static class MessengerHandler extends Handler{ @Override public void handleMessage(Message msg) { switch (msg.what){ case 1: L.i("i receive '" + msg.getData().getString("message")+"'"); Messenger client = msg.replyTo; //回应客户端 if (client != null){ Message reply = Message.obtain(); Bundle message = new Bundle(); message.putString("message", "i have received your message"); L.i("i have received your message"); reply.setData(message); try { client.send(reply); } catch (RemoteException e) { e.printStackTrace(); } } break; case 2: L.i("i receive '" + msg.getData().getString("message")+"'"); L.i("client has disconnect this connection, bye~"); break; default: break; } } } @Override public IBinder onBind(Intent intent) { return messenger.getBinder(); } @Override public void onCreate() { super.onCreate(); messenger = new Messenger(new MessengerHandler()); }
private Button connect_handler; private TextView tv_handler; private Button connect_binder; private TextView tv_binder; private ServiceConnection serviceConnection; private Messenger serverMessenger; private Messenger messenger; private boolean hasBindService = false; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.client_handler); connect_handler = (Button) findViewById(R.id.connect_handler); connect_handler.setOnClickListener(this); tv_handler = (TextView) findViewById(R.id.tv_handler); connect_binder = (Button) findViewById(R.id.connect_binder); connect_binder.setOnClickListener(this); tv_binder = (TextView) findViewById(R.id.tv_binder); serviceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { //绑定服务后实例化Messenger对象 serverMessenger = new Messenger(service); communicate(); } @Override public void onServiceDisconnected(ComponentName name) { serverMessenger = null; } }; messenger = new Messenger(new Handler(){ @Override public void handleMessage(Message msg) { L.i("i have received '" + msg.getData().getString("message") + "'"); Message message = Message.obtain(); Bundle bundle = new Bundle(); bundle.putString("message", "OK, bye bye~"); message.setData(bundle); L.i("i have send '" + message.getData().getString("message") + "'"); message.what = 2; if (serverMessenger != null){ try { serverMessenger.send(message); } catch (RemoteException e) { e.printStackTrace(); } } } }); } private void communicate(){ SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd-HH:mm:ss"); Message message = Message.obtain(); Bundle msg = new Bundle(); msg.putString("message", "i have send handler a message at " + simpleDateFormat.format(System.currentTimeMillis())); message.setData(msg); L.i("i have send '" + message.getData().getString("message") + "'"); message.what = 1; message.replyTo = messenger; if (serverMessenger != null){ try { serverMessenger.send(message);//发送 } catch (RemoteException e) { e.printStackTrace(); } } } @Override public void onClick(View v) { switch (v.getId()){ case R.id.connect_handler: if (!hasBindService) { Intent intent = new Intent(); intent.setClassName("com.android.messenger_a", "com.android.messenger_a.ServerWithHandler"); bindService(intent, serviceConnection, BIND_AUTO_CREATE); hasBindService = true; }else{ if (serverMessenger == null){ return; } communicate(); } break; case R.id.connect_binder: startActivity(new Intent(this, ClientForBinder.class)); break; } } @Override protected void onDestroy() { super.onDestroy(); if (serverMessenger != null) unbindService(serviceConnection); }
2.ContentProvider(底层同样是Binder实现):
不只是数据库,还可以操作文件,对象等,android:authorities 是ContentProvider的唯一标识,通过这个属性外部应用就可以访问我们的provider
<provider android:authorities="com.android.StudentProvider" android:name="com.android.contentprovider.StudentProvider" android:permission="com.android.CONTENTPROVIDER_READPERMISSSION" android:process=":provider"/>
3:Socket:
学过计算机网络的对Socket不陌生,所以不需要详细讲述。只需要注意,Android不允许在主线程中请求网络,而且请求网络必须要注意声明相应的permission。然后,在服务器中定义ServerSocket来监听端口,客户端使用Socket来请求端口,连通后就可以进行通信
TCP:
当对网络通讯质量有要求的时候,比如:整个数据要准确无误的传递给对方,这往往用于一些要求可靠的应用,比如HTTP、HTTPS、FTP等传输文件的协议,POP、SMTP等邮件传输的协议。 在日常生活中,常见使用TCP协议的应用如下: 浏览器,用的HTTP FlashFXP,用的FTP Outlook,用的POP、SMTP Putty,用的Telnet、SSH QQ文件传输
TCP的优点: 可靠,稳定 TCP的可靠体现在TCP在传递数据之前,会有三次握手来建立连接,而且在数据传递时,有确认、窗口、重传、拥塞控制机制,在数据传完后,还会断开连接用来节约系统资源。
TCP的缺点: 慢,效率低,占用系统资源高,易被攻击 TCP在传递数据之前,要先建连接,这会消耗时间,而且在数据传递时,确认机制、重传机制、拥塞控制机制等都会消耗大量的时间,而且要在每台设备上维护所有的传输连接,事实上,每个连接都会占用系统的CPU、内存等硬件资源。 而且,因为TCP有确认机制、三次握手机制,这些也导致TCP容易被人利用,实现DOS、DDOS、CC等攻击。
UDP:
当对网络通讯质量要求不高的时候,要求网络通讯速度能尽量的快,这时就可以使用UDP。 比如,日常生活中,常见使用UDP协议的应用如下: QQ语音 QQ视频 TFTP
UDP的优点: 快,比TCP稍安全 UDP没有TCP的握手、确认、窗口、重传、拥塞控制等机制,UDP是一个无状态的传输协议,所以它在传递数据时非常快。没有TCP的这些机制,UDP较TCP被攻击者利用的漏洞就要少一些。但UDP也是无法避免攻击的,比如:UDP Flood攻击
UDP的缺点: 不可靠,不稳定 因为UDP没有TCP那些可靠的机制,在数据传递时,如果网络质量不好,就会很容易丢包。
区别:
1、TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接
2、TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付
3、TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)
4、每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
5、TCP首部开销20字节;UDP的首部开销小,只有8个字节
6、TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道
4:AIDL(Android Interface definition language):
AIDL通过定义服务端暴露的接口,以提供给客户端来调用,AIDL使服务器可以并行处理,而Messenger封装了AIDL之后只能串行运行,所以Messenger一般用作消息传递。通过编写aidl文件来设计想要暴露的接口,编译后会自动生成响应的java文件,服务器将接口的具体实现写在Stub中,用iBinder对象传递给客户端,客户端bindService的时候,用asInterface的形式将iBinder还原成接口,再调用其中的方法。
- 基本数据类型(int,long,char,boolean,double等)
- String和CharSequence
- List:只支持ArrayList,而且list中的元素也必须是 AIDL 支持的类型
- Map:只支持HashMap,里面的key和value也必须是AIDL支持的类型
- Parceable:所有实现了 Parceable 接口的对象
- AIDL:所有的 AIDL 接口本身也可以在 AIDL 文件中使用,所以 IBinder 类型也是支持的。
- 服务端 服务端首先要创建一个 Service 用来监听客户端的请求,然后将在对应AIDL文件中声明的接口实现,并且通过onbind函数返回相应 IBinder 对象即可。
- 客户端 客户端所要做的事情就稍微简单一些,首先需要绑定服务端的Service,绑定成功后,将服务端返回的 IBinder 对象转成AIDL接口所属的类型,接着就可以调用AIDL中的方法了。
- AIDL文件中,并不是所有的数据类型都是可以使用的,它支持的数据类型有:
Binder :
Binder是Android系统进程间通信(IPC)方式之一,Linux则是通过管道(Pipe)、信号(Signal)和跟踪(Trace)、插口(Socket)、报文队列(Message)、共享内存(Share Memory)等来通讯
- Binder Client 只需要知道自己要使用的 Binder 的名字及其在 ServiceManager 中的引用即可获取该 Binder 的引用,得到引用后就可以像普通方法调用一样调用 Binder 实体的方法,实现在用户空间中;
- Binder Server 在生成一个 IBinder 实体时会为其绑定一个名称并传递给 Binder Driver,Binder Driver 会在内核空间中创建相应的 Binder 实体节点和节点引用,并将引用传递给 ServiceManager。ServiceManager 会将该 Binder 的名字和引用插入一张数据表中,这样 Binder Client 就能够获取该 Binder 实体的引用,并调用上面的方法,实现在用户空间中;
- ServiceManager 相当于 DNS服务器,负责映射 Binder 名称及其引用,其本质同样是一个标准的 Binder Server,实现在用户空间中;
- Binder Driver 则相当于一个路由器,实现在内核空间中。
类装载器:
(1) 装载:查找和导入(加载类的二进制数据)Class文件;
(2) 链接:把类的二进制数据合并到JRE中;
(a)校验:检查载入Class文件数据的正确性;
(b)准备:给类的静态变量分配存储空间;
(c)解析:将符号引用转成直接引用;
(3) 初始化:JVM负责对类进行初始化,对类的静态变量,静态代码块执行初始化操作,包括字段的赋值
自底向上顺序检查类是否已经加载,自顶向下顺序加载类
双亲委派机制:(类在系统创建的时候会有一个flag标签属性标识着)
1、当AppClassLoader加载一个class时,它首先不会自己去尝试加载这个类,首先判断该类型是否已经被加载,没有就把类加载请求委派给父类加载器ExtClassLoader去完成。
2、当ExtClassLoader加载一个class时,它首先也不会自己去尝试加载这个类,而是把类加载请求委派给BootStrapClassLoader去完成。
3、如果BootStrapClassLoader加载失败(例如在$JAVA_HOME/jre/lib里未查找到该class),会使用ExtClassLoader来尝试加载;
4、若ExtClassLoader也加载失败,则会使用AppClassLoader来加载,如果AppClassLoader也加载失败,则会报出异常ClassNotFoundException。
类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸载(Unloading)7个阶段。其中准备、验证、解析3个部分统称为连接(Linking)。
双亲委派模型意义:
-系统类防止内存中出现多份同样的字节码
-保证Java程序安全稳定运行
自定义类加载器:
通常情况下,我们都是直接使用系统类加载器。但是,有的时候,我们也需要自定义类加载器。比如应用是通过网络来传输 Java 类的字节码,为保证安全性,这些字节码经过了加密处理,这时系统类加载器就无法对其进行加载,这样则需要自定义类加载器来实现。自定义类加载器一般都是继承自 ClassLoader 类,从上面对 loadClass 方法来分析来看,我们只需要重写 findClass 方法即可,最好不要重写loadClass方法,因为这样容易破坏双亲委托模式
可见性限制:下层的加载器能够看到上层加载器中的类,反之则不行,也就是是说委托只能从下到上。
不允许卸载类:类加载器可以加载一个类,但是它不能卸载一个类。但是类加载器可以被删除或者被创建。
在Android中ClassLoader主要有两个直接子类,叫做 BaseDexClassLoader 和 SecureClassLoader 。而前者有两个直接子类是 PathClassLoader 和 DexClassLoader,现在很流行的插件化/热补丁,其实都是通过DexClassLoader来实现的。具体思路是: 创建一个DexClassLoader,通过反射将前者的DexPathList跟系统的PathClassLoader中的DexPathList合并,就可以实现优先加载我们自己的新类,从而替换旧类中的逻辑了。
JVM工作流程:
Loading:文章前面介绍的类加载,将文件系统中的Class文件载入到JVM内存(运行数据区域)
当运行一个JVM示例时,系统将分配给它一块内存区域(这块内存区域的大小可以设置的),这一内存区域由JVM自己来管理。从这一块内存中分出一块用来存储一些运行数据,例如创建的对象,传递给方法的参数,局部变量,返回值等等。分出来的这一块就称为运行数据区域。运行数据区域可以划分为6大块:Java栈、程序计数寄存器(PC寄存器)、本地方法栈(Native Method Stack)、Java堆、方法区域、运行常量池(Runtime Constant Pool)
Verifying:检查载入的类文件是否符合Java规范和虚拟机规范。
Preparing:为这个类分配所需要的内存,确定这个类的属性、方法等所需的数据结构。
Resolving:将该类常量池中的符号引用都改变为直接引用。(不是很理解)
Initialing:初始化类的局部变量,为静态域赋值,同时执行静态初始化块。
堆中存放的是程序创建的对象或者实例。这个区域对JVM的性能影响很大。垃圾回收机制处理的正是这一块内存区域
静态区:编译时就能确定
栈:运行的时候分配,栈式存储分配按照先进后出的原则进行分配
堆:在编译时或运行时,无法确定存储要求的数据结构的内存分配
5.0
1:Material Design
融合卡片式,立体式的设计风格,强调层次感,动画,阴影等元素
2:新增art虚拟机
Dalvik:Android4.4及以前使用的都是Dalvik虚拟机,我们知道Apk在打包的过程中会先将java等源码通过javac编译成.class文件,但是我们的Dalvik虚拟机只会执行.dex文件,这个时候dx会将.class文件转换成Dalvik虚拟机执行的.dex文件。Dalvik虚拟机在启动的时候会先将.dex文件转换成快速运行的机器码,又因为65535这个问题,导致我们在应用冷启动的时候有一个合包的过程,最后导致的一个结果就是我们的app启动慢,这就是Dalvik虚拟机的JIT特性(Just In Time)
ART:ART虚拟机是在Android5.0才开始使用的Android虚拟机,ART虚拟机必须要兼容Dalvik虚拟机的特性,但是ART有一个很好的特性AOT(ahead of time),这个特性就是我们在安装APK的时候就将dex直接处理成可直接供ART虚拟机使用的机器码,ART虚拟机将.dex文件转换成可直接运行的.oat文件,ART虚拟机天生支持多dex,所以也不会有一个合包的过程,所以ART虚拟机会很大的提升APP冷启动速度,缺点是APP安装速度慢,APK占用空间大,因为在APK安装的时候要生成可运行.oat文件
3:水波纹动画,CardView,RecyclerView,ToolBar
升级修改:1.当前使用 Ringtone、MediaPlayer 或 Vibrator 类向通知中添加声音和振动,则移除此代码,以便系统可以在“优先”模式中正确显示通知。取而代之的是,使用 Notification.Builder 方法添加声音和振动
2.Context.bindService() 方法现在需要显式 Intent,如果提供隐式 intent,将引发异常。为确保应用的安全性,请使用显式 intent 启动或绑定 Service,且不要为服务声明 intent 过滤器
3.默认情况下,系统会阻止混合内容和第三方 Cookie。要允许混合内容和第三方 Cookie,请分别使用 setMixedContentMode() 和 setAcceptThirdPartyCookies() 方法
6.0
1:运行时权限(运行时权限提供给用户关于应用所需权限更多的相关上下文和可视性,这也让开发者帮助用户更好的理解:为什么应用需要所请求的权限,授权将有什么样的好处,拒绝将有何种不便)
安装时权限模型(Android5.1以及更早):用户在应用安装和更新时,对危险权限授权。但是OEM和运行商预装的应用将自动预授权。
运行时权限(Android6.0及以后):用户在应用运行时,对应用授予危险权限。由应用决定何时去申请权限(例如,在应用启动时或者用户访问某个特性时),但必须容许用户来授予或者拒绝应用对特定权限组的访问。OEM和运营商可以预装应用,但是不能对权限进行预授权。
2:指纹识别
之前都是各个厂商自行开发的并没有系统底层的支持,Android 6.0则在系统层面加入指纹识别,能提供原生指纹识别API,这不但降低了厂商开发指纹识别模块的成本,最重要的是原生指纹识别将会大大提升安卓手机的指纹识别支付安全性
升级修改:1.在运行时检查和请求权限。要确定您的应用是否已被授予权限,请调用新增的 checkSelfPermission() 方法
2.Android 6.0 版移除了对 Apache HTTP 客户端的支持。如果您的应用使用该客户端,并以 Android 2.3(API 级别 9)或更高版本为目标平台,请改用 HttpURLConnection 类。此 API 效率更高,因为它可以通过透明压缩和响应缓存减少网络使用,并可最大限度降低耗电量,如果要继续使用可以添加依赖库实现
3.相机服务变更,相机服务中共享资源的访问模式已从之前的“先到先得”访问模式更改为高优先级进程优先的访问模式
7.0
1:多屏多任务
按住其中一个卡片,然后向上拖动至顶部即可开启分屏多任务,支持上下分栏和左右分栏,允许拖动中间的分割线调整两个APP所占的比例
2:通知消息回笼
串行是各数据位按顺序进行,只有前面的程序段执行完了才会执行后面的,这样大量的浪费cpu资源,因为等待时不能做其它事情,并行是多个程序段同时执行,同时执行不是指某个时刻同时执行,除非有多个CPU,而是CPU能把时间分给不同的程序段,一个程序等待时就把资源分配给另外的程序,提高资源利用
3:添加了 Just in Time (JIT) 编译器,对 ART 进行代码分析,让它可以在应用运行时持续提升 Android 应用的性能。 JIT 编译器对 Android 运行组件当前的 Ahead of Time (AOT) 编译器进行了补充,有助于提升运行时性能,节省存储空间,加快应用更新和系统更新速度
4:VR支持,一个新的跨平台图形计算库—Vulkan(Vulkan 是 3D 图形和渲染的一项开放标准)
升级修改:1.为了提高私有文件的安全性,面向 Android 7.0 或更高版本的应用私有目录被限制访问 (0700)。此设置可防止私有文件的元数据泄漏,如尝试传递 file:// URI 会触发 FileUriExposedException。分享私有文件内容的推荐方法是使用 FileProvider
class A {
static{System.out.println("A");}//静态域
}
静态域是指静态域在声明时被赋值
Context的数量等于Activity的个数 + Service的个数 + 1,这个1为Application
四大组件:
默认情况下,同一个应用程序内的所有组件都是运行在同一个进程中的,大部分应用程序也不会去改变它。不过,如果需要指定某个特定组件所属的进程,则可以利用manifest 文件来达到目的,manifest文件中的每种组件元素——<activity>、 <service>、 <receiver>和<provider>——都支持定义android:process属性,用于指定组件运行的进程。<application>元素也支持android:process属性,用于指定所有组件的默认进程。
activity 显示界面(显示的界面都是继承activity完成的)
service 服务(后台运行的,service是运行在主线程上的,可以理解为没有界面的activity)
参考博客:http://www.jianshu.com/p/4c798c91a613
一个原则是Service的onCreate的方法只会被调用一次,就是你无论多少次的 startService 又 bindService,Service只被创建一次,onBind(...)函数是Service基类中的唯一抽象方法,子类都必须重写实现
startService: onCreate -> onStart -> onDestroy
通过startService启动后,service会一直无限期运行下去,只有外部调用了stopService()或stopSelf()方法时,该Service才会停止运行并销毁
bindService:onCreate -> onBind -> onUnbind -> onDestroyed
bindService可以通过IBinder接口获取Service实例,从而实现在client端直接调用Service中的方法以实现灵活交互,这在通过startService方法启动中是无法实现的,
Broadcast Receiver 广播(做广播,广播是运行在主线程上的,通知时候用到)
Content Provider 数据通信(数据之间通信,同个程序间数据,或者是不同程序间通信)
常用加密:
参考博客: http://www.cnblogs.com/whoislcj/p/5885006.html
视频加密:
全部加密耗时长,选择将文件的前面多少个字节进行加密,这样播放器就无法识别这个文件的编码,就无法播放了
des:DES是一种对称加密算法,就是加密和解密使用相同密钥的算法。DES加密算法出自IBM的研究,
后来被美国政府正式采用,之后开始广泛流传,但是近些年使用越来越少,因为DES使用56位密钥,以现代计算能力,
24小时内即可被破解。
aes:安全性要高于DES,AES的出现本身就是为了取代DES的,AES具有比DES更好的安全性、效率、灵活性,所以对称加密优先采用AES。
rsa:公钥密码算法,使用长度可以变化的密钥。RSA是第一个既能用于数据加密也能用于数字签名的算法,由于RSA算法进行的都是大数计算,使得RSA最快的情况也比DES慢上倍,这是RSA最大的缺陷,所以通常只能用于加密少量数据或者加密密钥,登陆,支付等接口采用rsa非对称加密
md5:单向加密算法,是不可逆的一种的加密方式,一般用于验证一致性,数字签名,安全访问认证,网上有关MD5解密的网站数不胜数,破解机制采用穷举法,就是我们平时说的跑字典,可以多次加密,或者加盐的方式(随机生成一串字符串作为盐,然后进行MD5加密,md5+key),一般密码和下载的文件会加密md5
加密后最终都会对加密后的二进制数据进行Base64编码,起到一种二次加密的效果,其实呢Base64从严格意义上来说的话不是一种加密算法,而是一种编码算法
在计算机中任何数据都是按ascii码存储的,而ascii码的128~255之间的值是不可见字符。而在网络上交换数据时,比如说从A地传到B地,往往要经过多个路由设备,由于不同的设备对字符的处理方式有一些不同,这样那些不可见字符就有可能被处理错误,这是不利于传输的。所以就先把数据先做一个Base64编码,统统变成可见字符,这样出错的可能性就大降低了。
HTTPS一般使用的加密与HASH算法如下:
非对称加密算法:RSA,DSA/DSS
对称加密算法:AES,RC4,3DES
HASH算法:MD5,SHA1,SHA256
https:
https协议需要到ca申请证书,一般免费证书很少,需要交费。http是超文本传输协议,信息是明文传输,https 则是具有安全性的ssl加密传输协议,http和https使用的是完全不同的连接方式用的端口也不一样,前者是80,后者是443。http的连接很简单,是无状态的HTTPS协议是由SSL+HTTP协议构建的可进行加密传输,HTTPS协议是在HTTP协议的基础上,添加了SSL/TLS握手以及数据加密传输,也属于应用层协议。
SSL/TLS协议的基本思路是采用公钥加密法,客户端先向服务器端索要公钥,然后用公钥加密信息,服务器收到密文后,用自己的私钥解密,这就是非对称加密。
xmpp:
是一种以 XML 为基础的开放式实时通信协议,是经由互联网工程工作小组( IETF)通过的互联网标准。
优点:
1.开源,容易了解,
2.分布式,XMPP以TCP传递XML数据流,没有中央主服务器。任何人都可以运行自己的XMPP服务器,使个人及组织能够掌控他们的实时传讯体验
3.安全,任何XMPP协议的服务器可以独立于公众XMPP网络
4.基于XML 建立起来的应用具有良好的语义完整性和扩展性,比如将XMPP 绑定到HTTP 而不是TCP,主要用于不能够持久的维持与服务器TCP 连接的设备
缺点:
1.XMPP协议的方式被编码为一个单一的长的XML文件,因此无法提供修改二进制数据,Base64是网络上最常见的用于传输8Bit字节代码的编码方式之一
服务端一般采用Openfire,Openfire为java开源项目,采用开放的 XMPP 协议,使用 Socket 通讯TCP方式进行通信,单台服务器可支持上万并发用户,搭建分布式云服务器可轻松提供大量并发用户,还提供了接口,可以自己开发插件。
域名是一个主要的ID,并且是JID中唯一必须的元素。(一个纯粹的域名也是一个合法的JID)
websocket:
websocket协议是近些年随html5发展而诞生的,主要用于解决web服务器与客户端无法双向交互的问题。如今已经被W3C收入标准协议。
import de.tavendo.autobahn.WebSocketConnection; import de.tavendo.autobahn.WebSocketException; import de.tavendo.autobahn.WebSocketHandler; public class MainActivity extends AppCompatActivity implements View.OnClickListener { private static final String wsurl = "ws://111.11.11.111:8080/websocketServer"; private static final String TAG = "MainActivity"; private WebSocketConnection mConnect = new WebSocketConnection(); private EditText mContent; private Button mSend; private TextView mText; private EditText mUserName; private EditText mToSb; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); bindObject(); connect(); } /** * 绑定控件 */ private void bindObject() { mContent = (EditText) findViewById(R.id.et_content); mSend = (Button) findViewById(R.id.btn_send); mText = (TextView) findViewById(R.id.tv_test); mUserName = (EditText) findViewById(R.id.et_username); mToSb = (EditText) findViewById(R.id.et_to); mSend.setOnClickListener(this); } /** * websocket连接,接收服务器消息 */ private void connect() { Log.i(TAG, "ws connect...."); try { mConnect.connect(wsurl, new WebSocketHandler() { @Override public void onOpen() { Log.i(TAG, "Status:Connect to " + wsurl); sendUsername(); } @Override public void onTextMessage(String payload) { Log.i(TAG, payload); mText.setText(payload != null ? payload : ""); // mConnect.sendTextMessage("I am android client"); } @Override public void onClose(int code, String reason) { Log.i(TAG, "Connection lost.."); } }); } catch (WebSocketException e) { e.printStackTrace(); } } /** * 发送用户名给服务器 */ private void sendUsername() { String user = mUserName.getText().toString(); if (user != null && user.length() != 0) mConnect.sendTextMessage(user); else Toast.makeText(getApplicationContext(), "不能为空", Toast.LENGTH_SHORT).show(); } /** * 发送消息 * * @param msg */ private void sendMessage(String msg) { if (mConnect.isConnected()) { mConnect.sendTextMessage(msg); } else { Log.i(TAG, "no connection!!"); } } @Override protected void onDestroy() { super.onDestroy(); mConnect.disconnect(); } @Override public void onClick(View view) { if (view == mSend) { String content = mToSb.getText().toString() + "@" + mContent.getText().toString(); if (content != null && content.length() != 0) sendMessage(content); else Toast.makeText(getApplicationContext(), "不能为空", Toast.LENGTH_SHORT).show(); } } }
http三次握手四次挥手:(参考博客 -- http://uule.iteye.com/blog/2213562)
(1)第一次握手:Client将标志位SYN置为1,随机产生一个值seq=J,并将该数据包发送给Server,Client进入SYN_SENT状态,等待Server确认。
(2)第二次握手:Server收到数据包后由标志位SYN=1知道Client请求建立连接,Server将标志位SYN和ACK都置为1,ack (number )=J+1,随机产生一个值seq=K,并将该数据包发送给Client以确认连接请求,Server进入SYN_RCVD状态。
(3)第三次握手:Client收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=K+1,并将该数据包发送给Server,Server检查ack是否为K+1,ACK是否为1,如果正确则连接建立成功,Client和Server进入ESTABLISHED状态,完成三次握手,随后Client与Server之间可以开始传输数据了。
SYN攻击:
在三次握手过程中,Server发送SYN-ACK之后,收到Client的ACK之前的TCP连接称为半连接(half-open connect),此时Server处于SYN_RCVD状态,当收到ACK后,Server转入ESTABLISHED状态。SYN攻击就是Client在短时间内伪造大量不存在的IP地址,并向Server不断地发送SYN包,Server回复确认包,并等待Client的确认,由于源地址是不存在的,因此,Server需要不断重发直至超时,这些伪造的SYN包将长时间占用未连接队列,导致正常的SYN请求因为队列满而被丢弃,从而引起网络堵塞甚至系统瘫痪。SYN攻击时一种典型的DDOS攻击,检测SYN攻击的方式非常简单,即当Server上有大量半连接状态且源IP地址是随机的,则可以断定遭到SYN攻击了,使用如下命令可以让之现行:
#netstat -nap | grep SYN_RECV
四次挥手:
(1)第一次挥手:Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。
(2)第二次挥手:Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),Server进入CLOSE_WAIT状态。
(3)第三次挥手:Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态。
(4)第四次挥手:Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1,Server进入CLOSED状态,完成四次挥手。
服务端在LISTEN状态下,收到建立连接请求的SYN报文后,把ACK和SYN放在一个报文里发送给客户端。而关闭连接时,当收到对方的FIN报文时,仅仅表示对方不再发送数据了但是还能接收数据,己方也未必全部数据都发送给对方了,所以己方可以立即close,也可以发送一些数据给对方后,再发送FIN报文给对方来表示同意现在关闭连接,因此,己方ACK和FIN一般都会分开发送。
socket和http的区别:
TCP协议对应于传输层,而HTTP协议对应于应用层,从本质上来说,二者没有可比性。Http协议是建立在TCP协议基础之上的,当浏览器需要从服务器获取网页数据的时候,会发出一次Http请求。Http会通过TCP建立起一个到服务器的连接通道,当本次请求需要的数据完毕后,Http会立即将TCP连接断开,这个过程是很短的。所以Http连接是一种短连接,是一种无状态的连接。Http就是在每次请求完成后就把TCP连接关了,所以是短连接。
TCP/IP和其他的协议在最初OSI模型中的位置
网络访问框架:
volley:
请求网络,缓存队列没有就缓存在队列,然后添加到请求队列请求网络,发送线程请求,创建一条缓存线程和四条网络请求线程(默认)并运行。
retrofit:
使用注意:一般我们下载文件使用okhttp去下载
Call<ResponseBody> call = userBiz.downloadTest(); call.enqueue(new Callback<ResponseBody>() { @Override public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) { //还是要处理io操作,也就是说你在这里还要另外开线程操作 InputStream is = response.body().byteStream(); //save file } @Override public void onFailure(Call<ResponseBody> call, Throwable t) { } });
洪洋的一篇博客:http://blog.csdn.net/lmj623565791/article/details/51304204
使用动态代理模式把API当作一个动态代理对象利用java的proxy类完成创建实例,然后解析注解,构建okhttpclient对象,如果不传入默认直接new一个对象,然后交给okhttp去请求网络,返回解析数据
public <T> T create(final Class<T> service) { return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service }, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object... args) throws Throwable { }); }
版本差异:Retrofit1有默认的转换器gson,Retrofit2没有,需要定义
Retrofit1需要添加okhttp的依赖,Retrofit2中自动添加了,是必须的
baseurl问题:可以直接用url注解传递全地址解决
用拦截器 Interceptor 来实现,请求经过拦截器,可以通过拦截器的回调事件做修改重置操作
public class RetrofitHelper { private static final String BASE_URL_USER = "https://www.111.com/"; private static final String BASE_URL_PAY = "https://www.222.com/"; private static final long TIME_OUT = 5000; private RetrofitService retrofitService; public static RetrofitHelper getInstance() { return SingleHolder.INSTANCE; } private static class SingleHolder { private static final RetrofitHelper INSTANCE = new RetrofitHelper(); } private RetrofitHelper() { OkHttpClient okHttpClient = new OkHttpClient .Builder() .connectTimeout(TIME_OUT, TimeUnit.MILLISECONDS) //添加应用拦截器 .addInterceptor(new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { //获取request Request request = chain.request(); //获取request的创建者builder Request.Builder builder = request.newBuilder(); //从request中获取headers,通过给定的键url_name List<string> headerValues = request.headers("url_name"); if (headerValues != null && headerValues.size() > 0) { //如果有这个header,先将配置的header删除,因此header仅用作app和okhttp之间使用 builder.removeHeader(HttpConfig.HEADER_KEY); //匹配获得新的BaseUrl String headerValue = headerValues.get(0); HttpUrl newBaseUrl = null; if ("user".equals(headerValue)) { newBaseUrl = HttpUrl.parse(BASE_URL_USER); } else if ("pay".equals(headerValue)) { newBaseUrl = HttpUrl.parse(BASE_URL_PAY); } else{ newBaseUrl = oldHttpUrl; } //从request中获取原有的HttpUrl实例oldHttpUrl HttpUrl oldHttpUrl = request.url(); //重建新的HttpUrl,修改需要修改的url部分 HttpUrl newFullUrl = oldHttpUrl .newBuilder() .scheme(newBaseUrl.scheme()) .host(newBaseUrl.host()) .port(newBaseUrl.port()) .build(); //重建这个request,通过builder.url(newFullUrl).build(); //然后返回一个response至此结束修改 return chain.proceed(builder.url(newFullUrl).build()); } else { return chain.proceed(request); } } }) .build(); Retrofit retrofit = new Retrofit .Builder() .client(okHttpClient) //创建retrofit时的baseUrl可以不需担心、随意指定了 .baseUrl(BASE_URL_USER) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .build(); retrofitService = retrofit.create(RetrofitService.class); } }
okhttp:
参考博客:http://www.jianshu.com/p/27c1554b7fee
整体采用采用门面模式(OkHttpClient)+建造者模式,http连接使用了桥接模式(将HttpEngine和HttpConnection连接起来),Platform(Android和JdkWithJettyBootPlatform两个平台的适应)采用了动态代理模式,intercept拦截器使用了责任链设计模式
通过OkHttpClient
构建的Request,不过真正请求执行的是
RealCall(OkHttpClient client, Request originalRequest)
方法,
,然后在RealCall中进行异步或同步任务,返回结果,这个结果会经过拦截器interceptor过程,拦截后返回结果
,各个拦截器会对
respone
进行一些处理,最后会传到RealCall
类中通过execute
来得到response
OkHttp使用Okio来大大简化数据的访问与存储,Okio是一个增强 java.io 和 java.nio的库,支持SPDY(通过复用仅仅一条(或几条)TCP连接,在客户端与服务器间发送几十个请求或回应)
Glide:
流程:Glide 收到加载及显示资源的任务,创建 Request 并将它交给RequestManager(任务管理器),Request 启动 Engine(数据获取引擎) 去数据源获取资源(通过 Fetcher(数据获取器) ),获取到后 Transformation(图片处理) 处理后交给 Target(目标)。相对于UniversalImageLoader,Picasso,它还支持video,Gif,SVG格式,支持缩略图请求,旨在打造更好的列表图片滑动体验。Glide有生命周期的概念(主要是对请求进行pause,resume,clear),而且其生命周期与Activity/Fragment的生命周期绑定,支持Volley,OkHttp
Imageloader:
传入地址,把地址作为key,存入链表,链表采用lru算法,先进后出,判断是否有缓存(网络,磁盘,本地三级缓存),没有就去请求网络下载图片,返回资源
RxJava:
通过观察者模式来实现的异步操作的库,代码简洁,而且随着代码逻辑复杂度增加依然保持简洁,核心功能之一是事件和对象的变换,比如map,将string对象转换成bitmap对象返回,flatmap,把也是变换,但是却是返回observable对象
源码解析:
订阅流程:
注意:RxJava的onComplete();与onError(t);只有一个会被执行,通过传入的parent(也就是执行分发数据的)调用其dispose方法来终止
1、Observable通过调用create创建一个Observable(观察者)对象
2、调用create时需要传入一个ObservableOnSubscribe类型的实例参数
3、最终实例参数作为ObservableCreate构造函数的参数传入,subscribe(订阅者)订阅,开始执行事件分发流程 onSubscribe > onNext > onComplete
线程切换:
subscribeOn切换线程,利用装饰者模式往中间插入包装类实现线程的切换,(线程状态会被存入一个叫scheduler调度器的对象变量中),具体点说就是利用装饰者模式在中间包装了Observable和Observer,,通过中间增加一个Observable和Observer来实现线程的切换,装饰者模式的使用贯穿了RxJava2的各处
常用操作符:
zip:把多个事件整合在一起执行
From :将其它的对象或数据结构转换为Observable
tolist:将一个Observable转换为一个List
subscribeOn(Schedulers.newThread())//子线程执行
observerOn(AndroidSchedulers.mainThread())//取回结果返回主线程
doOnCancel:取消订阅
一般用CompositeSubscription来收集Subscription统一取消订阅
1.0和2.0的区别:
背压的概念:流速控制的一种策略,指在异步场景中,背观察者发送事件速度远快于观察者的处理速度的情况下,告诉上游的被观察者降低发送速度的策略,背压的前提是异步环境,背观察者和观察者处于不同环境中,而因为工作量不一样,事件处理速度也不一样,这会出现两种情况,一种是被观察者速度慢些,观察者很快,此时观察者等待被观察者,没有问题,第二种情况是被观察者速度快,观察者处理慢,处理不过来导致事件堆积,最终挤爆内存,程序奔溃,抛出MissingBackpressureException异常,背压也因此得出,背压是响应式拉取,观察者主动从被观察者那去拉取数据,被观察者被动等待通知再发送数据,观察者可以根据自身情况按需求拉取数据,而不是被动接受,也就相当于告诉上游的被观察者把速度慢下来,最终达到背压的策略
Hot and Cold Observables:
Hot指创建后不管是否订阅就发送事件的Observable,Cold指订阅之后才开始发送事件的Observable取消订阅放到了内部的方法中实现了,function增加了throws exception,这样就不用try catch了
Dialog使用了建造者模式
activity和bind使用了动态代理模式
反射:
Class.forName():将类的.class文件加载到jvm中之外,还会对类进行解释,执行类中的static块;
ClassLoader.loadClass():只干一件事情,就是将.class文件加载到jvm中,不会执行static中的内容,只有在newInstance才会去执行static块。
service弹出dialog在.show() 调用之前需要添加dialog.getWindow().setType((WindowManager.LayoutParams.TYPE_SYSTEM_ALERT)),清单文件添加权限:
android.permission.SYSTEM_ALERT_WINDOW
框架模式:
MVP:
Model:处理数据 View:显示数据 Presenter:M层和V层进行通信,M层在获取到数据之后,把它交给P,P层在交给View层
工作原理:presenter层充当了桥梁的作用,用于操作view层发出的事件传递到presenter层中,presenter层去操作model层,并且将数据返回给view层,整个过程中view层和model层完全没有联系。
缺陷:使用了接口的方式去连接view层和presenter层,这样就导致了一个问题,如果你有一个逻辑很复杂的页面,你的接口会有很多,十几二十个都不足为奇。想象一个app中有很多个这样复杂的页面,维护接口的成本就会非常的大。
View不直接与Model交互,而是通过与Presenter交互来与Model间接交互。
Presenter与View的交互是通过接口来进行的。
通常View与Presenter是一对一的,但复杂的View可能绑定多个Presenter来处理逻辑。
解决MVP的内存泄露:Presenter在Activity的onDestroy方法回调时执行资源释放操作,或者在Presenter引用View对象时使用软引用,弱引用,或者使用第三方库
//Model层抽象接口 public interface IInfoModel { //从数据提供者获取数据方法 InfoBean getInfo(); //存入数据提供者方法 void setInfo(InfoBean info); } //View层的抽象接口 public interface IInfoView { //给UI显示数据的方法 void setInfo(InfoBean info); //从UI取数据的方法 InfoBean getInfo(); } //Presenter public class Presenter { private IInfoModel infoModel; private IInfoView infoView; public Presenter(IInfoView infoView) { this.infoView = infoView; infoModel = new InfoModelImpl(); } //供UI调运 public void saveInfo(InfoBean bean) { infoModel.setInfo(bean); } //供UI调运 public void getInfo() { //通过调用IInfoView的方法来更新显示,设计模式运用 //类似回调监听处理 infoView.setInfo(infoModel.getInfo()); } } public class MainActivity extends ActionBarActivity implements IInfoView, View.OnClickListener{ private EditText inputId, inputName, inputAddr; private Button saveBtn, loadBtn; private TextView infoTxt; private Presenter presenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initData(); } private void initData() { presenter = new Presenter(this); inputId = (EditText) findViewById(R.id.id_input); inputName = (EditText) findViewById(R.id.name_input); inputAddr = (EditText) findViewById(R.id.addr_input); saveBtn = (Button) findViewById(R.id.input_confirm); loadBtn = (Button) findViewById(R.id.get_confirm); infoTxt = (TextView) findViewById(R.id.show); saveBtn.setOnClickListener(this); loadBtn.setOnClickListener(this); } @Override public void setInfo(InfoBean info) { StringBuilder builder = new StringBuilder(""); builder.append(info.getId()); builder.append(" "); builder.append(info.getName()); builder.append(" "); builder.append(info.getAddress()); infoTxt.setText(builder.toString()); } @Override public InfoBean getInfo() { InfoBean info = new InfoBean(); info.setId(Integer.parseInt(inputId.getText().toString())); info.setName(inputName.getText().toString()); info.setAddress(inputAddr.getText().toString()); return info; } @Override public void onClick(View v) { switch (v.getId()) { case R.id.input_confirm: presenter.saveInfo(getInfo()); break; case R.id.get_confirm: presenter.getInfo(); break; } } }
MVC:
Model:获取处理数据 View:XML界面显示 Contronller:Activity,通过接口通信来协同 View(视图)和Model(模型)工作,起到了两者之间的通信作用
工作原理:当用户出发事件的时候,view层会发送指令到controller层,接着controller去通知model层更新数据,model层更新完数据以后直接显示在view层上
缺陷:view层和model层是相互可知的,这意味着两层之间存在耦合,耦合对于一个大型程序来说是非常致命的,因为这表示开发,测试,维护都需要花大量的精力。
View可以与Model直接交互。
Controller是基于行为的,并且可以被多个View共享。
可以负责决定显示哪个View。
public class MainActivity extends ActionBarActivity implements OnWeatherListener, View.OnClickListener { private WeatherModel weatherModel; private Dialog loadingDialog; private EditText cityNOInput; private TextView city; private TextView cityNO; private TextView temp; private TextView wd; private TextView ws; private TextView sd; private TextView wse; private TextView time; private TextView njd; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); weatherModel = new WeatherModelImpl(); initView(); } /** * 初始化View */ private void initView() { cityNOInput = findView(R.id.et_city_no); city = findView(R.id.tv_city); cityNO = findView(R.id.tv_city_no); temp = findView(R.id.tv_temp); wd = findView(R.id.tv_WD); ws = findView(R.id.tv_WS); sd = findView(R.id.tv_SD); wse = findView(R.id.tv_WSE); time = findView(R.id.tv_time); njd = findView(R.id.tv_njd); findView(R.id.btn_go).setOnClickListener(this); loadingDialog = new ProgressDialog(this); loadingDialog.setTitle(加载天气中...); } /** * 显示结果 * * @param weather */ public void displayResult(Weather weather) { WeatherInfo weatherInfo = weather.getWeatherinfo(); city.setText(weatherInfo.getCity()); cityNO.setText(weatherInfo.getCityid()); temp.setText(weatherInfo.getTemp()); wd.setText(weatherInfo.getWD()); ws.setText(weatherInfo.getWS()); sd.setText(weatherInfo.getSD()); wse.setText(weatherInfo.getWSE()); time.setText(weatherInfo.getTime()); njd.setText(weatherInfo.getNjd()); } /** * 隐藏进度对话框 */ public void hideLoadingDialog() { loadingDialog.dismiss(); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.btn_go: loadingDialog.show(); weatherModel.getWeather(cityNOInput.getText().toString().trim(), this); break; } } @Override public void onSuccess(Weather weather) { hideLoadingDialog(); displayResult(weather); } @Override public void onError() { hideLoadingDialog(); Toast.makeText(this, 获取天气信息失败, Toast.LENGTH_SHORT).show(); } private <t extends="" view=""> T findView(int id) { return (T) findViewById(id); } }
public interface WeatherModel { void getWeather(String cityNumber, OnWeatherListener listener); } public class WeatherModelImpl implements WeatherModel { @Override public void getWeather(String cityNumber, final OnWeatherListener listener) { /*数据层操作*/ VolleyRequest.newInstance().newGsonRequest(http://www.weather.com.cn/data/sk/ + cityNumber + .html, Weather.class, new Response.Listener<weather>() { @Override public void onResponse(Weather weather) { if (weather != null) { listener.onSuccess(weather); } else { listener.onError(); } } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { listener.onError(); } }); } }
MVVM:
MVVM最早是由微软提出的,在编译阶段,生成一个ViewDataBinding类的对象,该对象持有Activity要展示的数据和布局中的各个view的引用
Model:数据模型层。包含业务逻辑和校验逻辑 View:屏幕上显示的UI界面(layout、views) ViewModel:View和Model之间的链接桥梁,处理视图逻辑
缺陷:data binding框架解决了数据绑定的问题,但是view层还是会过重
它和MVP的区别貌似不大,只不过是presenter层换成了viewmodel层,还有一点就是view层和viewmodel层是相互绑定的关系,这意味着当你更新viewmodel层的数据的时候,view层会相应的变动ui
view和viewmodel相互绑定在一起,viewmodel的改变会同步到view层,从而view层作出响应
databinding原理:
首先,DataBinding会对根元素为<layout>的布局文件进行预处理,原有的<layout>标签、data标签以及里面的variable标签将会解析成tag属性,然后binding中等BR常量是一种标识符,它对应一个会发生变化的数据,当数据改变后,你可以用该标识符通知DataBinding,DataBinding就会用新的数据去更新UI。
然后每个布局会对应生成Binding对象,这个对象等构造函数中会去找到各个View的Tag或者id,然后生成字段属性,接着把之前加到各个View上的Tags清空,最后构造方法调用,Binding将会计算各个view上的binding表达式,然后赋值给view相应的属性
我们在绑定数据的时候会在布局文件中写入variable标签,标签中name属性是对象标识,比如叫data,此时databinding会根据标识生成一个name叫data的变量,而下面具体的赋值是我们手动去操作等,比如给一个textview的txt设置name,直接用databinding的表达式,data.name,通过这些字符就能检测到,拿到具体的值进行绑定操作
设计模式:
1:装饰者模式:
动态地给一个对象添加一些额外的职责,在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责
/**Component 被装饰对象的基类*/ public interface Person { void eat(); } /**ConcreteComponent 具体被装饰对象*/ public class Man implements Person { public void eat() { System.out.println("吃放"); } } /**Decorator 装饰者*/ public abstract class Decorator implements Person { protected Person person;//把原功能对象添加进来 public void setPerson(Person person) { this.person = person; } public void eat() { person.eat(); } } /**ConcreteDectrator 具体装饰者*/ public class ManDecoratorA extends Decorator { public void eat() { super.eat();//不影响原功能 reEat(); System.out.println("ManDecoratorA类"); } public void reEat() {//添加扩展方法 System.out.println("再吃一顿饭"); } } public class Test { public static void main(String[] args) { Man man = new Man(); ManDecoratorA md1 = new ManDecoratorA(); ... md1.setPerson(man); md1.eat(); } }
2:单例模式:
懒汉式,线程不安全,多线程下不能正常工作
public class Singleton { private volatile static Singleton instance; //声明成 volatile private Singleton (){} public static Singleton getSingleton() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }
懒汉式,线程安全,不高效,每次调用都会
public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; }
双重检验锁
1,给 instance 分配内存
2,调用 Singleton 的构造函数来初始化成员变量
3,将instance对象指向分配的内存空间(执行完这步 instance 就为非 null 了)
但是在 JVM 的即时编译器中存在指令重排序的优化。也就是说上面的第二步和第三步的顺序是不能保证的,最终的执行顺序可能是 1-2-3 也可能是 1-3-2。如果是后者,则在 3 执行完毕、2 未执行之前,被线程二抢占了,这时 instance 已经是非 null 了(但却没有初始化),所以线程二会直接返回 instance,然后使用,然后顺理成章地报错,所以我们还需要将 instance 变量声明成 volatile ,使用 volatile 的主要原因是其另一个特性:禁止指令重排序优化。也就是说,在 volatile 变量的赋值操作后面会有一个内存屏障(生成的汇编代码上),读操作不会被重排序到内存屏障之前。比如上面的例子,取操作必须在执行完 1-2-3 之后或者 1-3-2 之后,不存在执行到 1-3 然后取到值的情况
但是特别注意在 Java 5 以前的版本使用了 volatile 的双检锁还是有问题的。其原因是 java 5 以前的 JMM (Java 内存模型)是存在缺陷的,即时将变量声明成 volatile 也不能完全避免重排序,主要是 volatile 变量前后的代码仍然存在重排序问题。这个 volatile 屏蔽重排序的问题在 Java 5 中才得以修复,所以在这之后才可以放心使用volatile
public class Singleton { private volatile static Singleton instance; //声明成 volatile private Singleton (){} public static Singleton getSingleton() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }
饿汉式 static final field
单例会在加载类后一开始就被初始化,即使客户端没有调用 getInstance()方法。
public class Singleton{ //类加载时就初始化 private static final Singleton instance = new Singleton(); private Singleton(){} public static Singleton getInstance(){ return instance; } }
静态内部类 static nested class,《Effective Java》上所推荐的,SingletonHolder 是私有的,
public class Singleton { private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } private Singleton (){} public static final Singleton getInstance() { return SingletonHolder.INSTANCE; } }
3:代理模式:(我们不希望或是不能直接访问对象 A,而是通过访问一个中介对象 B,由 B 去访问 A 达成目的,这种方式我们就称为代理)
根据程序运行前代理类是否已经存在,可以将代理分为静态代理和动态代理。
使用场景:当无法或者不想直接访问某个对象或者是访问某个对象存在困难时,可以通过一个代理对象来间接地访问,为了保证客户端使用的透明性,委托对象与代理对象需要实现相同的接口
静态代理模式:
//一般会继承同一父类或实现同一接口 //委托类 class ClassA { public void getMethod1() {}; } //代理类 public class ClassB { private ClassA a; public ClassB(ClassA a) { this.a = a; } public void getMethod1() { a.getMethod1(); }; }
动态代理模式:(动态代理其实就是通过反射来生成一个代理)
在Java中java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过使用这个类和接口就可以生成动态代理对象。JDK提供的代理只能针对接口做代理
实现步骤:新建委托类 - 实现InvocationHandler接口,这是负责连接代理类和委托类的中间类必须实现的接口 - 通过Proxy类新建代理类对象
public interface User { public void add(); } public class UserImp implements User { @Override public void add() { System.out.println("添加功能"); } } public class MyInvocationHandler implements InvocationHandler { private Object target; public MyInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args)throws Throwable { method.invoke(target, args); //执行被代理target对象的方法 return null; } } public void add() { UserImp ui = new UserImp(); MyInvocationHandler m = new MyInvocationHandler(ui); User u = (User)Proxy.newProxyInstance(ui.getClass().getClassLoader(), ui.getClass().getInterfaces(), m); u.add(); //权限校验 添加功能 日志记录 }
当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用,在调用前后我们也可以添加一些自己的操作
4:门面模式:
将一些复杂的小系统整合起来,为用户的使用提供一个简单的接口,统一一个高层接口给用户使用,门面模式要求一个子系统的外部与其内部的通信必须通过一个统一的门面(Facade)对象进行。
public interface LetterWriter { //写信的内容 public void writeContent(String content); //写信的地址 public void writeAddress(String address); //将信放入信封 public void inMailer(); //寄信 public void sendMail(); } public class LetterWriterImpl implements LetterWriter { @Override public void inMailer() { // TODO Auto-generated method stub System.out.println("放入信封"); } @Override public void sendMail() { // TODO Auto-generated method stub System.out.println("发信"); } @Override public void writeAddress(String address) { // TODO Auto-generated method stub System.out.println("写地址:"+address); } @Override public void writeContent(String content) { // TODO Auto-generated method stub System.out.println("写内容:"+content); } } //那么此时如果一个用户想发信:非常简单 //office.Send("Hello","Good"); public class PostOffice { private LetterWriter writer=new LetterWriterImpl(); //发送邮件 public void Send(String address,String content) { writer.writeContent(content); writer.writeAddress(address); writer.inMailer(); writer.sendMail(); } }
5:责任链设计模式:
责任链就是从一个起点发起请求,然后沿着任务链依次传递给每一个节点上的对象,直到有一个节点处理这个请求为止。比如员工要申请一笔资金,会先向组长申请,额度如果在组长的范围内,组长就批了,组长权限不够就向主管申请,主管如果也额度不够就向经理申请。这就形成了个责任链。普通员工,就是那个申请的发起者并不需要知道到底最后是谁审批的,他只要拿到钱就行了。这样就造成了请求者和处理者的解耦。
//抽象的领导--Handler public abstract class Leader { protected Leader nextLeader; public final void handleRequest(int money){ if (money<=getLimit()){ handle(money); }else { if (nextLeader!=null){ nextLeader.handleRequest(money); }else { System.out.println(money+"没人能批准"); } } } public abstract int getLimit(); public abstract void handle(int money); } //三个具体的领导--ConcreteHandler public class GroupLeader extends Leader { @Override public int getLimit() { return 5000; } @Override public void handle(int money) { System.out.println(money+"由组长批准"); } } public class Director extends Leader { @Override public int getLimit() { return 10000; } @Override public void handle(int money) { System.out.println(money+"由主管批准"); } } public class Manager extends Leader { @Override public int getLimit() { return 20000; } @Override public void handle(int money) { System.out.println(money+"由经理批准"); } } //员工申请 public class A { public static void main(String[] args) { Leader groupLeader = new GroupLeader(); Leader director = new Director(); Leader manager = new Manager(); groupLeader.nextLeader = director; director.nextLeader = manager; groupLeader.handleRequest(5000); groupLeader.handleRequest(9000); groupLeader.handleRequest(12000); groupLeader.handleRequest(30000); } }
运行结果:
Handler:抽象处理者角色,声明一个处理请求的方法,并保持对下一个处理节点Handler对象的引用。
ConcreteHandler: 具体的处理者,对请求进行处理,如果不处理就讲请求转发给下一个节点上的处理对象。
优点
- 降低耦合度,便于拓展,提高代码灵活性。
- 责任链对象互相链接,只用想头部发起请求。
缺点
- 如果责任链太长,或者每条链判断处理的时间太长会影响性能。特别是递归循环的时候。
- 请求不一定能得到处理,可能会没有对象处理。
7:工厂模式:
简单工厂:
由一个工厂对象决定创建出哪一种产品类的实例。
public interface ICar { void GetCar(); } public enum CarType { SportCarType = 0, JeepCarType = 1, HatchbackCarType = 2 } /// <summary> /// 具体产品类: 跑车 /// </summary> public class SportCar : ICar { public void GetCar() { Console.WriteLine("场务把跑车交给范·迪塞尔"); } } /// <summary> /// 具体产品类: 越野车 /// </summary> public class JeepCar : ICar { public void GetCar() { Console.WriteLine("场务把越野车交给范·迪塞尔"); } } /// <summary> /// 具体产品类: 两箱车 /// </summary> public class HatchbackCar : ICar { public void GetCar() { Console.WriteLine("场务把两箱车交给范·迪塞尔"); } } public class Factory { public ICar GetCar(CarType carType) { switch (carType) { case CarType.SportCarType: return new SportCar(); case CarType.JeepCarType: return new JeepCar(); case CarType.HatchbackCarType: return new HatchbackCar(); default: throw new Exception("爱上一匹野马,可我的家里没有草原. 你走吧!"); } } } class Program { static void Main(string[] args) { ICar car; try { Factory factory = new Factory(); Console.WriteLine("范·迪塞尔下一场戏开跑车。"); car = factory.GetCar(CarType.SportCarType); car.GetCar(); } catch (Exception ex) { Console.WriteLine(ex.Message); } } }
- 优点:简单工厂模式能够根据外界给定的信息,决定究竟应该创建哪个具体类的对象。明确区分了各自的职责和权力,有利于整个软件体系结构的优化。
- 缺点:很明显工厂类集中了所有实例的创建逻辑,容易违反GRASPR的高内聚的责任分配原则
抽象工厂:
抽象工厂模式可以向客户端提供一个接口,使客户端在不必指定产品的具体的情况下,创建多个产品族中的产品对象。
抽象工厂模式符合了六大原则中的开闭原则(对于扩展是开放的,对于修改是关闭的,这意味着模块的行为是可以扩展的)、里氏代换原则(所有引用基类的地方必须能透明地使用其子类的对象)等等
- 优点:
- 抽象工厂模式隔离了具体类的生产,使得客户并不需要知道什么被创建。
- 当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。
- 增加新的具体工厂和产品族很方便,无须修改已有系统,符合“开闭原则”。
- 缺点:增加新的产品等级结构很复杂,需要修改抽象工厂和所有的具体工厂类
//大致是在简单工厂基础上把创建实例这一块抽象了出来 /// 抽象工厂类 public abstract class Factorys { /// 抽象方法 public abstract Car CreateRedCar(); public abstract Car CreateBlueCar(); }
out of memory:
内存溢出 out of memory,是指程序在申请内存时,没有足够的内存空间供其使用
内存泄露 memory leak,是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。
memory leak会最终会导致out of memory!
内存泄漏检测库:leak canary
public class ExampleApplication extends Application { private RefWatcher refWatcher; @Override public void onCreate() { super.onCreate(); refWatcher = LeakCanary.install(this); } } //activity也一样 public class BaseFragment extends Fragment { @Override public void onDestroy() { super.onDestroy(); RefWatcher refWatcher = ExampleApplication.getRefWatcher(getActivity()); refWatcher.watch(this); } }
原理分析:
RefWatcher.watch()
会以监控对象来创建一个KeyedWeakReference
弱引用对象- 在
AndroidWatchExecutor
的后台线程里,来检查弱引用已经被清除了,如果没被清除,则执行一次 GC - 如果弱引用对象仍然没有被清除,说明内存泄漏了,系统就导出 hprof 文件,保存在 app 的文件系统目录下
JS交互:
Android调用JS:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Carson_Ho</title> <script> // Android需要调用的方法 function callJS(){ alert("Android调用了JS的callJS方法"); } </script> </head> </html> public class MainActivity extends AppCompatActivity { WebView mWebView; Button button; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mWebView =(WebView) findViewById(R.id.webview); WebSettings webSettings = mWebView.getSettings(); // 设置与Js交互的权限 webSettings.setJavaScriptEnabled(true); // 设置允许JS弹窗 webSettings.setJavaScriptCanOpenWindowsAutomatically(true); // 先载入JS代码 // 格式规定为:file:///android_asset/文件名.html mWebView.loadUrl("file:///android_asset/javascript.html"); button = (Button) findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // 必须另开线程进行JS方法调用(否则无法调用) mWebView.post(new Runnable() { @Override public void run() { // 注意调用的JS方法名要对应上 // 调用javascript的callJS()方法 mWebView.loadUrl("javascript:callJS()"); } }); } }); // 由于设置了弹窗检验调用结果,所以需要支持js对话框 // webview只是载体,内容的渲染需要使用webviewChromClient类去实现 // 通过设置WebChromeClient对象处理JavaScript的对话框 //设置响应js 的Alert()函数 mWebView.setWebChromeClient(new WebChromeClient() { @Override public boolean onJsAlert(WebView view, String url, String message, final JsResult result) { AlertDialog.Builder b = new AlertDialog.Builder(MainActivity.this); b.setTitle("Alert"); b.setMessage(message); b.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { result.confirm(); } }); b.setCancelable(false); b.create().show(); return true; } }); } } mWebView.evaluateJavascript("javascript:callJS()", new ValueCallback<String>() { @Override public void onReceiveValue(String value) { //此处为 js 返回的结果 } }); } // Android版本变量 final int version = Build.VERSION.SDK_INT; // 因为该方法在 Android 4.4 版本才可使用,所以使用时需进行版本判断 if (version < 18) { mWebView.loadUrl("javascript:callJS()"); } else { mWebView.evaluateJavascript("javascript:callJS()", new ValueCallback<String>() { @Override public void onReceiveValue(String value) { //此处为 js 返回的结果 } }); }
JS调用Android:
方法一:
// 继承自Object类 public class AndroidtoJs extends Object { // 定义JS需要调用的方法 // 被JS调用的方法必须加入@JavascriptInterface注解 @JavascriptInterface public void hello(String msg) { System.out.println("JS调用了Android的hello方法"); } } <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Carson</title> <script> function callAndroid(){ // 由于对象映射,所以调用test对象等于调用Android映射的对象 test.hello("js调用了android中的hello方法"); } </script> </head> <body> //点击按钮则调用callAndroid函数 <button type="button" id="button1" onclick="callAndroid()"></button> </body> </html> public class MainActivity extends AppCompatActivity { WebView mWebView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mWebView = (WebView) findViewById(R.id.webview); WebSettings webSettings = mWebView.getSettings(); // 设置与Js交互的权限 webSettings.setJavaScriptEnabled(true); // 通过addJavascriptInterface()将Java对象映射到JS对象 //参数1:Javascript对象名 //参数2:Java对象名 mWebView.addJavascriptInterface(new AndroidtoJs(), "test");//AndroidtoJS类对象映射到js的test对象 // 加载JS代码 // 格式规定为:file:///android_asset/文件名.html mWebView.loadUrl("file:///android_asset/javascript.html");
方法二:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Carson_Ho</title> <script> function callAndroid(){ /*约定的url协议为:js://webview?arg1=111&arg2=222*/ document.location = "js://webview?arg1=111&arg2=222"; } </script> </head> <!-- 点击按钮则调用callAndroid()方法 --> <body> <button type="button" id="button1" onclick="callAndroid()">点击调用Android代码</button> </body> </html> public class MainActivity extends AppCompatActivity { WebView mWebView; // Button button; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mWebView = (WebView) findViewById(R.id.webview); WebSettings webSettings = mWebView.getSettings(); // 设置与Js交互的权限 webSettings.setJavaScriptEnabled(true); // 设置允许JS弹窗 webSettings.setJavaScriptCanOpenWindowsAutomatically(true); // 步骤1:加载JS代码 // 格式规定为:file:///android_asset/文件名.html mWebView.loadUrl("file:///android_asset/javascript.html"); // 复写WebViewClient类的shouldOverrideUrlLoading方法 mWebView.setWebViewClient(new WebViewClient() { @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { // 步骤2:根据协议的参数,判断是否是所需要的url // 一般根据scheme(协议格式) & authority(协议名)判断(前两个参数) //假定传入进来的 url = "js://webview?arg1=111&arg2=222"(同时也是约定好的需要拦截的) Uri uri = Uri.parse(url); // 如果url的协议 = 预先约定的 js 协议 // 就解析往下解析参数 if ( uri.getScheme().equals("js")) { // 如果 authority = 预先约定协议里的 webview,即代表都符合约定的协议 // 所以拦截url,下面JS开始调用Android需要的方法 if (uri.getAuthority().equals("webview")) { // 步骤3: // 执行JS所需要调用的逻辑 System.out.println("js调用了Android的方法"); // 可以在协议上带有参数并传递到Android上 HashMap<String, String> params = new HashMap<>(); Set<String> collection = uri.getQueryParameterNames(); } return true; } return super.shouldOverrideUrlLoading(view, url); } } ); } }
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Carson_Ho</title> <script> function callAndroid(){ /*约定的url协议为:js://webview?arg1=111&arg2=222*/ document.location = "js://webview?arg1=111&arg2=222"; } </script> </head> <!-- 点击按钮则调用callAndroid()方法 --> <body> <button type="button" id="button1" onclick="callAndroid()">点击调用Android代码</button> </body> </html> public class MainActivity extends AppCompatActivity { WebView mWebView; // Button button; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mWebView = (WebView) findViewById(R.id.webview); WebSettings webSettings = mWebView.getSettings(); // 设置与Js交互的权限 webSettings.setJavaScriptEnabled(true); // 设置允许JS弹窗 webSettings.setJavaScriptCanOpenWindowsAutomatically(true); // 步骤1:加载JS代码 // 格式规定为:file:///android_asset/文件名.html mWebView.loadUrl("file:///android_asset/javascript.html"); // 复写WebViewClient类的shouldOverrideUrlLoading方法 mWebView.setWebViewClient(new WebViewClient() { @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { // 步骤2:根据协议的参数,判断是否是所需要的url // 一般根据scheme(协议格式) & authority(协议名)判断(前两个参数) //假定传入进来的 url = "js://webview?arg1=111&arg2=222"(同时也是约定好的需要拦截的) Uri uri = Uri.parse(url); // 如果url的协议 = 预先约定的 js 协议 // 就解析往下解析参数 if ( uri.getScheme().equals("js")) { // 如果 authority = 预先约定协议里的 webview,即代表都符合约定的协议 // 所以拦截url,下面JS开始调用Android需要的方法 if (uri.getAuthority().equals("webview")) { // 步骤3: // 执行JS所需要调用的逻辑 System.out.println("js调用了Android的方法"); // 可以在协议上带有参数并传递到Android上 HashMap<String, String> params = new HashMap<>(); Set<String> collection = uri.getQueryParameterNames(); } return true; } return super.shouldOverrideUrlLoading(view, url); } } ); } }
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Carson_Ho</title> <script> function clickprompt(){ // 调用prompt() var result=prompt("js://demo?arg1=111&arg2=222"); alert("demo " + result); } </script> </head> <!-- 点击按钮则调用clickprompt() --> <body> <button type="button" id="button1" onclick="clickprompt()">点击调用Android代码</button> </body> </html> public class MainActivity extends AppCompatActivity { WebView mWebView; // Button button; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mWebView = (WebView) findViewById(R.id.webview); WebSettings webSettings = mWebView.getSettings(); // 设置与Js交互的权限 webSettings.setJavaScriptEnabled(true); // 设置允许JS弹窗 webSettings.setJavaScriptCanOpenWindowsAutomatically(true); // 先加载JS代码 // 格式规定为:file:///android_asset/文件名.html mWebView.loadUrl("file:///android_asset/javascript.html"); mWebView.setWebChromeClient(new WebChromeClient() { // 拦截输入框(原理同方式2) // 参数message:代表promt()的内容(不是url) // 参数result:代表输入框的返回值 @Override public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) { // 根据协议的参数,判断是否是所需要的url(原理同方式2) // 一般根据scheme(协议格式) & authority(协议名)判断(前两个参数) //假定传入进来的 url = "js://webview?arg1=111&arg2=222"(同时也是约定好的需要拦截的) Uri uri = Uri.parse(message); // 如果url的协议 = 预先约定的 js 协议 // 就解析往下解析参数 if ( uri.getScheme().equals("js")) { // 如果 authority = 预先约定协议里的 webview,即代表都符合约定的协议 // 所以拦截url,下面JS开始调用Android需要的方法 if (uri.getAuthority().equals("webview")) { // // 执行JS所需要调用的逻辑 System.out.println("js调用了Android的方法"); // 可以在协议上带有参数并传递到Android上 HashMap<String, String> params = new HashMap<>(); Set<String> collection = uri.getQueryParameterNames(); //参数result:代表消息框的返回值(输入值) result.confirm("js调用了Android的方法成功啦"); } return true; } return super.onJsPrompt(view, url, message, defaultValue, result); } // 通过alert()和confirm()拦截的原理相同,此处不作过多讲述 // 拦截JS的警告框 @Override public boolean onJsAlert(WebView view, String url, String message, JsResult result) { return super.onJsAlert(view, url, message, result); } // 拦截JS的确认框 @Override public boolean onJsConfirm(WebView view, String url, String message, JsResult result) { return super.onJsConfirm(view, url, message, result); } } ); } }
activity启动黑屏白屏处理:
参考博客:http://www.cnblogs.com/netcorner/p/4883069.html 和 http://www.jianshu.com/p/0026a01c6811
//1、设置背景图Theme <style name="Theme.AppStartLoad" parent="android:Theme"> <item name="android:windowBackground">@drawable/ipod_bg</item> <item name="android:windowNoTitle">true</item> </style> //2、设置透明Theme <style name="Theme.AppStartLoadTranslucent" parent="android:Theme"> <item name="android:windowIsTranslucent">true</item> <item name="android:windowNoTitle">true</item> </style>
第一种Theme就是设置一张背景图。当程序启动时,首先显示这张背景图,避免出现黑屏。(程序启动快,界面先显示背景图,然后再刷新其他界面控件。给人刷新不同步感觉)
第二种Theme是把样式设置为透明,程序启动后不会黑屏而是整个透明了,等到界面初始化完才一次性显示出来。(给人程序启动慢感觉,界面一次性刷出来,刷新同步)
还有方法也就是禁止此功能的出现,不过不建议这么做,android增加此功能就是为了提升用户体验
给Preview Window设置的背景图如果不做处理,图片就会一直存在于内存中,所以,当我们进入到欢迎页的时候,不要忘了把背景图设置为空
@Override protected void onCreate(@Nullable Bundle savedInstanceState) { //将window的背景图设置为空 getWindow().setBackgroundDrawable(null);
getWindow().setBackgroundDrawableResource(android.R.color.white); super.onCreate(savedInstanceState); }
android抽象布局:
1.<include />标签能够重用布局文件
2.<merge/>删减多余的层级,多用于替换FrameLayout或者当一个布局包含另一个时
3.<ViewStub />需要时加载,是一个不可见的,大小为0的View。
1)ViewStub只能Inflate一次,之后ViewStub对象会被置为空。按句话说,某个被ViewStub指定的布局被Inflate后,就不会够再通过ViewStub来控制它了。
2) ViewStub只能用来Inflate一个布局文件,而不是某个具体的View,当然也可以把View写在某个布局文件中。
<ViewStub android:id="@+id/viewstub_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="5dip" android:layout_marginRight="5dip" android:layout_marginTop="10dip" android:layout="@layout/viewstub_demo_text_layout"/>
ViewStub stub = (ViewStub) findViewById(R.id.viewstub_text); stub.inflate(); TextView text = (TextView) findViewById(R.id.viewstub_demo_textview); text.setText("test");
Fragment:
Fragment生命周期分析:
1. 当一个fragment被创建的时候,它会经历以下状态.
- onAttach()--当fragment被加入到activity时调用(在这个方法中可以获得所在的activity,不过此方法已经标记为过时)
- onCreate()
- onCreateView()
- onActivityCreated()--当activity的onCreated()方法返回后
2. 当这个fragment对用户可见的时候,它会经历以下状态。
- onStart()
- onResume()
3. 当这个fragment进入“后台模式”的时候,它会经历以下状态。
- onPause()
- onStop()
4. 当这个fragment被销毁了(或者持有它的activity被销毁了),它会经历以下状态。
- onPause()
- onStop()
- onDestroyView()--fragment中的视图被移除
- onDestroy()
- onDetach()--fragment和activity分离的时候
addToBackStack(null)表示加入返回栈,这样你点击返回键的时候就会回退到上一个fragment了
activity和fragment之间:
1.可以通过fragment对象直接调用fragment里面的方法
2.通过接口回调传递回activity
3.getActivity直接强制转换activity获取activity的数据
4.通过构造方法传递参数
5.第三方库
fragment和fragment之间传递数据:
1.
// 在Fragment1中创建Fragment2的实例
Fragment2 fragment2 = new Fragment2();
Bundle bundle = new Bundle();
// 传递实体类,需要实体类实现Serializable接口
bundle.putSerializable("key_entity", value_Entity);
//设置数据
fragment2.setArgument(bundle);
//调用上面的方法由 fragment1 跳转到 fragment2
showFragment(Fragment1.this, fragment2);
//在fragment2中onCreateView通过getArgument.getSerializable("key_entity");直接获取数据
2.通过接口回调
3.通过构造方法传递
4.在onActivityCreated方法里通过getActivity().findViewById获取view刷新(不建议此方法刷新)
四大启动模式:
standard(默认):每次重新创建实例
singletop:如果在任务的栈顶正好存在该Activity的实例, 就重用该实例,调用其onNewIntent方法,否者就会创建新的实例并放入栈顶(即使栈中已经存在该Activity实例,只要不在栈顶,都会创建实例)。
singletask:若Activity不存在,则会在当前task创建一个新的实例,若存在,则会把task中在其之上的其它Activity destory掉并调用它的onNewIntent方法。
singleinstance:只有一个实例,并且这个实例独立运行在一个task中,这个task只有这个实例,不允许有别的Activity存在。
singletask传值问题:
@Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); setIntent(intent); }
内存优化:
图片压缩:
照片压缩,并且保证图片不失真
1.获取设备的宽高,根据宽高比例压缩
2.在判断大小进行质量压缩
回收图片资源
处理一些占用内存大而且声明周期较长的对象时候,可以尽量应用软引用和弱引用技术
避免创建不必要的对象
避免经常访问的类中提供get和set字段的访问
使用实体类比接口好,比如:Map map1 = new HashMap(); HashMap map2 = new HashMap();,后者能满足需求尽量使用后者
避免使用枚举类
内存和磁盘的缓存
引用类型:
引用分为四种级别,这四种级别由高到低依次为:强引用>软引用>弱引用>虚引用。
强引用(strong reference)
如:Object object=new Object(),object就是一个强引用了。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足问题。
软引用(SoftReference)
只有内存不够时才回收,常用于缓存;当内存达到一个阀值,GC就会去回收它;
弱引用(WeakReference)
弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它 所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。
虚引用(PhantomReference)
虚引用,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。
如果只是想避免OutOfMemory异常的发生,则可以使用软引用。如果对于应用的性能更在意,想尽快回收一些占用内存比较大的对象,则可以使用弱引用。
netcraft:查询公司服务器系统