(一)Java虚拟机调用方法的过程;
如上图:先介绍下JVM几大模块
1.1 class文件生成模块:通过jdk自带的javac编译命令生成,详细可查看(六)JVM笔记:class文件
1.2 类加载器子系统模块:JVM运行时自动创建,作用是将class字节码加载到jvm对应的内存中。类加载的核心就是classLoader详细可查看(八)java虚拟机结构详解(JVM)
1.3 内存空间模块:分为四个部分,分别存储class字节码不同的部分。(九)JVM内存管理和垃圾回收
1.4 垃圾回收模块
1.4.1 强引用(必不可少) 垃圾回收器绝对不会回收它。如Object obj = new Object(); obj便
是内存不足时,java虚拟机宁愿抛出OutofMemorryError错误导致程序崩溃异常终止,也 不
会回收强引用的对象。
1.4.2 软引用(可有可无)如果内存空间足够,垃圾回收就不会回收它,如果内存空间不足
了,就会回收这些对象的内存
1.4.3 弱引用(可有可无)垃圾回收器一旦发现了只具有弱引用的对象,不管当前内存空间
足够与否,都会回收它的内存,当发生GC的时候,弱引用的对象总是被回收
1.4.4虚引用 当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象
的内存之前把这个虚引用加入到与之前关联的引用队列中。与弱引用不同点就是虚引用必须
和引用队列联合使用。
1.5 其它模块:如指令集、执行引擎、本地方法接口等等,都属于jvm底层与CPU打交道的
基本结构介绍完下面介绍下执行过程:
首先Java虚拟机在执行方法涉及三块区域,堆区,栈区,方法区
public class Foo{
public int add(int a,int b){
return a + b;
}
}
Foo foo = new Foo();
foo.add(1,3);
以上面的例子来说,Java虚拟机会首先将这个Foo类的字节码加载到方法区还会在栈区开辟一个栈帧,然后new关键字将在堆区实例化Foo的实例,类的实例方法存在一个专门的区叫方法区,事实上类刚装载的时候就被装载好了,不过它们在"睡眠",只是这些方法必须当有对象产生的时候才会"苏醒"。这个实例是方法区Foo类字节码的引用,然后调用add方法。在方法区里面有一个方法表,里面存放了该类的方法及方法的字节码。在调用方法时,查找方法表,将对应的方法加载到栈区中的对应的栈帧中(这里需要提一个概念,栈区里会为类生成对应的栈帧),至此完成了一次JVM调用方法的过程。
需要注意的是,在堆区实例化的类(也就是创建的对象)对应的都是同一个方法区的类字节码,这是非常重要的一点!
以上就是JVM方法执行的一个主要流程。
(二)java中集合,set,List,Map,讲下ArrayList和HashMap的底层实现;
1 List存储特点:有序可重复;Set存储特点:不可重复;Map 是一种把键对象和值对象映射的集合,它的每一个元素都包含一对键对象和值对象。 Map没有继承于Collection接口 从Map集合中检索元素时,只要给出键对象,就会返回对应的值对象。
1.1 List中常见的ArrayList和LinkList和vector
ArrayList:采用的是数组的形式来保存对象的,这种方式将对象放在连续的位置上,数组结构的优点是便于对集合进行快速的随机访问,所以最大的缺点就是插入删除时非常麻烦。原因是当向指定的索引位置插入对象时,会同时将指定索引位置及之后的所有对象相应的向后移动一位.
LinkList:采用的是将对象放在独立的空间中,而且在每个空间中还保存下一个链接的索引,但缺点是查找非常麻烦,要从第一个索引开始。
vector类称作向量类,它实现了动态数组,用于元素数量变化的对象数组。像数组一样,vector类也用从0开始的下标表示元素的位置;但和数组不同的是,当vector对象创建后,数组的元素个数会随着vector对象元素个数的增大和缩小而自动变化。
1.2 Set存储 :
hashSet 无序不可重复无下标;
元素的哈希值是通过元素的hashcode方法 来获取的, HashSet首先判断两个元素的哈希值,如果哈希值一样,接着会比较equals方法 如果 equls结果为true ,HashSet就视为同一个元素。如果equals 为false就不是同一个元素。
TreeSet 有序不可重复无下标,是用来排序的, 可以指定一个顺序, 对象存入之后会按照指定的顺序排列TreeSet底层是二叉树结构是用来排序的, 且每插入一个新元素(第一个除外)都会调用compareTo()方法去和上一个插入的元素作比较,并按二叉树的结构进行排列可以指定一个顺序, 对象存入之后会按照指定的顺序排列.比如贷款产品列表,按收益多少排序要实现Comparable接口
1.3 Map存储
Map 是一种把键对象和值对象映射的集合,它的每一个元素都包含一对键对象和值对象。 Map没有继承于Collection接口 从Map集合中检索元素时,只要给出键对象,就会返回对应的值对象。
hashMap,hashTable 都实现了map接口,底层是哈希表数据结构; 继承的类不同
hashMap中的方法是非同步的(多线程中需要同步处理),内部通过一个Entry数组来实现;
HashMap不是线程安全的类,要想保证线程安全,可以使用
Map m = Collections.synchronizedMap(new HashMap(...));
hashTable的方法是同步的(多线程应用程序中不用专门的操作就可以安全的使用)
hashMap允许null键和null值;
比较:HashMap比HashTable功能更多,而且它不是基于一些陈旧的类所以一般情况下HashMap优于HashTable
ConcurrentHashMap和Hashtable主要区别就是围绕着锁的粒度以及如何锁,可以简单理解成把一个大的HashTable分解成多个,形成了锁分离。如图:
而Hashtable的实现方式是—锁整个hash表
其实不止用于线程,当设计数据表的事务时(事务某种意义上也是同步机制的体现),可以把一个表看成一个需要同步的数组,如果操作的表数据太多时就可以考虑事务分离了(这也是为什么要避免大表的出现),比如把数据进行字段拆分,水平分表等.
HashMap的工作原理
HashMap基于hashing原理,我们通过put()和get()方法储存和获取对象。当我们将键值对传递给put()方法时,它调用键对象的hashCode()方法来计算hashcode,让后找到bucket位置来储存值对象。当获取对象时,通过键对象的equals()方法找到正确的键值对,然后返回值对象。HashMap使用链表来解决碰撞问题,当发生碰撞了,对象将会储存在链表的下一个节点中。 HashMap在每个链表节点中储存键值对对象。
当两个不同的键对象的hashcode相同时会发生什么? 它们会储存在同一个bucket位置的链表中。键对象的equals()方法用来找到键值对。
因为HashMap的好处非常多,我曾经在电子商务的应用中使用HashMap作为缓存。因为金融领域非常多的运用Java,也出于性能的考虑,我们会经常用到HashMap和ConcurrentHashMap。你可以查看更多的关于HashMap的文章:
HashMap和Hashtable的区别
HashMap和HashSet的区别
详情可查看http://www.importnew.com/7099.html
TreeMap是二叉树数据结构,线程不同步,可用于给Map集合中的键进行排序
各自旗下的子类关系
Collection
--List:将以特定次序存储元素。
--ArrayList / LinkedList / Vector
--Set : 不能含有重复的元素,自行排序
--HashSet / TreeSet
Map
--HashMap
--HashTable
--TreeMap
hashmap和hashtable相比区别,hashmap为什么效率高
hashmap和hashtable相比效率差不太多
hashmap为什么效率高其实很简单hashmap底层维护了一个数组,当多线程的时候对这个数组操作是不安全的。 下面就说说数组和hashmap谁的速度快,这其实是个坑,我其实当时回答其实对了一半,我说hashmap比数组快,
因为hashmap底层是使用一个链表实现的,所以插入删除速度快,
(三)应用在远程端出现问题了,你该怎么解决?
用户反馈或者是异常bug收集发现问题,之前可能会等下一个版本,但是这样就太影响用户体验。
解决办法
1.根据业务可能的话,添加开关。界面的显示或隐藏,通过后台返回的标志来判断。
2.强制用户更新弹窗提示,用户体验不太好
3.使用动态加载技术,比如热修复技术腾讯微信的Tinker或阿里的AndFix
AndFix 修复一些方法级的一些bug,不可新增类和方法 原理比如方法A出现问题,Andfix会用patch文件生成器对比两个apk包生成一个patch文件,patch文件包含被修复的方法B,使用替换的选择,是bug代码永远执行不到。通过向用户推送或者是用户主动拉取的方式来修复线上bug
Tinker 腾讯的,不仅可以修复bug还可以进行小版本的发布,可以用gradle依赖和Tinker的核心库来实现,支持类替换,so替换,资源替换等等。
原理是1基于android原生的classloader
2基于dex文件格式,使用DexDiff算法
(四)项目中有什么难点?
热修复,动态加载技术,kotlin
(五)设计模式:单例,工厂设计模式
单例模式有以下特点:
1、单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。
5.1 饿汉式单例类
//饿汉式单例类.在类初始化时,已经自行实例化
public class Singleton1{
//私有的默认构造子
private Singleton1(){}
//已经自行实例化
private static final Singleton1 single=new Singleton1();
//静态工厂方法
public static Singleton1 getInstance(){
return single;
}
}
特点:此类加载时就初始化单例对象,较大时会影响系统加载速度
5.2 懒汉式单例类
//懒汉式单例类.在第一次调用的时候实例化
public class Singleton2{
//私有的默认构造子
private Singleton2(){}
//注意,这里没有final
private static Singleton2 single=null;
//静态工厂方法
public synchronized static Singleton2 getInstance(){
if(single==null){
single=new Singleton2();
}
return single;
}
}
特点:只有访问到单例对象的时候才去检查和实例化单例对象,多线程访问
需要加同步锁影响访问效率
5.3 使用静态内部类作为Singleton容器(登记式单例类)
public class Singleton{
private Singleton(){ }
private static class SingletonHolder{
// 私有内部类在Singleton加载时不初始化
private static Singleton instance = new Singleton();
}
public static Singleton getInstance(){
return SingletonHolder.instance;
}
}
特点:能延迟加载,又能保证线程安全 原理是直接用classLoader(jvm类加载机制) 进行异步加锁的问题,并减少了内存消耗保证了初始化instance时只有一个线程,所以是线程安全的
工厂设计模式
1.介绍
工厂方法模式(Factory Pattern),是创建型设计模式之一。工厂方法模式是一种结构简单的模式,其在我们平时开发中应用很广泛,也许你并不知道,但是你已经使用了无数次该模式了,如Android中的Activity里的各个生命周期方法,以onCreate方法为例,它就可以看作是一个工厂方法,我们在其中可以构造我们的View并通过setContentView返回给framework处理等,相关内容我们下面再讲,先看看工厂方法模式定义。
2.定义
定义一个用于创建对象的接口,让子类决定实例化哪个类。
3.使用场景
在任何需要生成复杂对象的地方,都可以使用工厂方法模式。复杂对象适合使用工厂模式,用new就可以完成创建的对象无需使用工厂模式。
4.模式的简单实现
https://www.cnblogs.com/ouyangduoduo/p/6652789.html
(六)Dagger2使用详解
https://www.jianshu.com/p/22c397354997
(七)
. Android activity四种启动模式以及应用场
7.1 standard
默认模式,可以不用写配置。在这个模式下,每次激活Activity都会默认
创建一个新的Activity实例并放入栈中,可以有多个相同的实例,也允许
多个 相同Activity叠加。
应用场景:Activity可以多次入栈
7.2 singleTop
可以有多个实例,但是不允许多个相同Activity叠加。如果Activity在栈
顶的时候,启动相同的Activity,不会创建新的实例,而会调用其onNewIntent方法,否则会创建新的实例并放入栈顶,即使栈中存在该
Activity的实例,只要是不在栈顶都会创建新的实例
应用场景:栈顶存在则复用,不存在则创建;浏览器的书签界面,
接受通知启动的内容显示界面
7.3 singleTask
若栈中存在activity实例,就会重用该实例(调用其onNewIntent
方法),并将其上的实例移出栈,如果栈中不存在该实例,将会创
建新的实例放入栈中。
应用场景:如果一个activity创建需要占用系统大量的资源(如CPU
, 内存。。。) 就用singleTask;浏览器的Activity不管从多个浏览
器只会启动主界面一次,其余情况都会走onNewIntent方法并会清
空主界面上的其它界面
7.4 singleInstance
单一实例启动模式 Activity会运行在自己的任务栈中,并且这个任
务栈中只有一个实例存在,不允许其它activity在本任务栈中。
应用场景:如果activity在整个手机系统中只允许一个实例存在,
如电话拨打界面。
(八)service两种启动方法及区别,如果一个service同时被两种方法启动如何执行
8.1 首先服务的特点:1 没有界面用户不可见 2 程序在后台运行做耗时操作
8.2 按照服务的启动方式 将服务划分为两类
启动服务 startService() 绑定服务 bindService()
8.3.1 启动式服务:应用程序组件(Activity)调用startService()方法启动服务时称为启动时服务
特点:
1)调用startService()方法一旦启动服务就会一直运行在后台 直到服务被杀死(自杀、他杀、系统回收)
2)启动服务的组件(Activity)被销毁后 启动的服务会一直运行 不受影响
3)启动式服务在后台执行单一的操作 不会将服务的操作结果返回给启动它的组件
8.3.2启动式服务的生命周期
onCreate()—>onStartCommand()—>StopService()
- 当应用程序组件(Activity)调用startService()启动服务先回调onCreate()方法创建和初始化service
- 接着回调onStartCommand()方法接收Intent意图请求并且开启工作线程执行耗时操作—-
- 当service中的操作执行完毕后调用StopSelf()或者时其它组件调用 stopService()就会回调onDestory()释放资源
context.startService() ->onCreate()- >onStartCommand()->Service running--调用context.stopService() ->onDestroy()
8.3.3 代码实例
Intent intent =new Intent(DemoActivity.this, MyService.class);
startService(intent);
public class MyService extends Service{
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
/**
* 表示当service第一次创建时回调的函数 初始化 注意:service只能被创建一次
*/
@Override
public void onCreate() {
super.onCreate();
Log.i("tag","--------onCreate-----");
}
/**
* 表示当应用程序组件调用startService()方法启动服务时 服务就会启动并且回调该方法
* @param intent 应用程序组件调用startService()启动服务时传递的Intent对象
* @param flags 表示开启服务是够需要传递额外的数据
* @param startId 用来唯一标示start请求
* @return 表示当系统回调完onStartCommand()方法后 service被系统意外杀死时 是否能够重新启动服务以及
* 是否可以继续执行请求操作
* 根据返回值将service划分为粘性service和非粘性service
*
* START_STICKY(常量1) STICKY粘性 当应用程序执行完onStartCommand()方法后 service被异常kill
* 系统会自动重启服务 但是在重启服务时传入的intent为null 车祸苏醒失忆
*
* START_NOT_STICKY(常量2) 非粘性 当应用程序执行完onStartCommand()方法后 service被异常kill
* 系统不会自动重启服务 车祸死亡
*
* START_REDELIVER_INTENT(常量3) 当应用程序执行完onStartCommand()方法后 service被异常kill
* 系统会自动的重启服务并且将Intent重新传入 车祸苏醒正常
*/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("tag","--------onStartCommand-----"+intent.getStringExtra("str"));
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// stopSelf();//自杀
return START_REDELIVER_INTENT;
}
/**
* 标示当service被销毁时回调的函数 资源释放
* stopService();
*/
@Override
public void onDestroy() {
super.onDestroy();
Log.i("tag","--------onDestroy-----");
}
}
8.4 绑定服务 bindService()
1)特点:onBind()只有采用Context.bindService()方法启动服务时才会回调该方法。该方法在调用者与服务绑定时被调用,
当调用者与服务已经绑定,多次调用Context.bindService()方法并不会导致该方法被多次调用。
onUnbind()只有采用Context.unbindService()方法启动服务时才会回调该方法。该方法在调用者与服务解除绑定时被调用。
2)调用过程:
当我们bind到一个service的时候,回调用的onBind方法,这时会返回一个IBinder类,这个类个人觉得很想代理模式,通过它来调用service中的方法,在我们bindservice的时候,需要传入一个参数,ServiceConnection,在这个对象里面有两个回调方法,一个是ublic void onServiceConnected(ComponentName name, IBinder service)一个是public void onServiceDisconnected(ComponentName name),在onServiceConnected中的参数service就是我们在onBind方法中返回的IBinder,通过对它的转型,我们就可以调用相应的service中的方法了。
3)生命周期:
context.bindService()->onCreate()->onBind()->Service running--调用>onUnbind() -> onDestroy()
- Context.bindService()启动
Intent intent =new Intent(DemoActivity.this, DemoService.class);
bindService(intent, conn, Context.BIND_AUTO_CREATE);
//unbindService(conn);//解除绑定
startservice启动服务后,程序退出stopservice,服务依旧存在,而bindservice启动服务后程序退出unbindservice,服务就会销毁,而同时调用两种方法启动同一个方法,只会启动一个服务,先后启动。但是其生命周期有所不同,取决于两种方法启动服务的先后顺序详细可查看:https://blog.csdn.net/wuyouagd/article/details/53378589
(九)Binder
Binder是Android系统中进程间通讯(IPC)的一种方式,也是Android系统中最重要的特性之一。Android中的四大组件Activity,Service,Broadcast,ContentProvider,不同的App等都运行在不同的进程中,它是这些进程间通讯的桥梁。正如其名“粘合剂”一样,它把系统中各个组件粘合到了一起,是各个组件的桥梁。
探究与 Binder 相关的 Java 类
要说这些类,我们首先要用到它们,最简单的方式就是去创建一个 Service,让它运行在单独的进程中,然后编写 AIDL,实现一个接口,再到 Activity 中去使用这个接口。
aidl跨进程通信 AIDL的英文全称是Android Interface Define Language 当B进程要去调用A进程中的service时,并实现通信,通过AIDL实现Service的跨进程通信(IPC),其实是通过Binder机制来实现的。我们通常都是通过AIDL来操作的之前整理过的一博客做了详细的介绍:http://blog.csdn.net/yshr1991/article/details/51568752
(十)打包后文档结构
https://www.baidu.com/from=844b/bd_page_type=1/ssid=0/uid=0/pu=usm%401%2Csz%401320_2001%2Cta%40iphone_1_11.3_3_605/baiduid=AB5A7A074C09C3CEB17B7467A0F706D1/w=0_10_/t=iphone/l=3/tc?ref=www_iphone&lid=8487901873809066870&order=9&fm=alop&tj=www_normal_9_0_10_title&vit=osres&m=8&srd=1&cltj=cloud_title&asres=1&title=Android%3Aapk文件结构及打包技巧-ThinkDifferent-...&dict=32&wd=&eqid=75cb15c88cc30c00100000035adcad3d&w_qd=IlPT2AEptyoA_yi9IkOvJCowOSwot9-HoyNvshO5dA6NGw694CgQpme3&tcplug=1&sec=29247&di=a3ebee3e494539a7&bdenc=1&tch=124.399.88.1973.3.4507&nsrc=IlPT2AEptyoA_yixCFOxXnANedT62v3IHBO1NilVLD3amFSyxP4kHREsRDHzL8zTUS3cdT0KtRsI&clk_info=%7B%22srcid%22%3A1599%2C%22tplname%22%3A%22www_normal%22%2C%22t%22%3A1524411749604%2C%22xpath%22%3A%22div-a-h3%22%7D&sfOpen=1
你对网络了解吗,你们项目几个人做的,简单介绍你们项目。
网络处理Volley和okHttp为什么结合使用
volley重点在于request的队列的管理,其他的地方都很不强,一般
都是volley+okhttp接合使用,不过如果没有特别多的网络性能要求(一般而言,比如你们用户量没有超百万)还是默认使用volley那套网络连接方式就够了。
OkHttp引擎在Android 4.4上是基于HttpURLConnection的。 Twitter, Facebook 和 Snapch都采用了它。
Volley的工作方式是创建不同的request,然后把它们添加到队列中(queue)。一个项目只需要一个queue就足够了,每次你想创建一个request的时候你都只需要获得这个唯一的queue来添加。
Volley可以轻松设置OkHttp作为其传输层,Request参数中设置HttpStack
volley 只是适合小数据的传输,上传文件下载文件这些都支持的不是很好
Volley/Gson的解决方案比较成熟,因为这是谷歌的解决方案,同时也因为出现在安卓开发者网站上,因此在2013到2014年都非常流行。到目前为止,这仍然是一个很好的选择,它是简单有效的。不过需要考虑一下的是Volley和Gson现在不怎么更新了。
okhttp还比volley有更多的选择。文中说的组合是说替换volley的HttpStack为okhttp的stack。并不是说okhttp只做了传输层的封装……volley的网络请求过程还就直接封装成异步的了……
拓展性高的是okhttp,数据流还是用okio来优化的,请求过程有同步,也有异步方法,响应体保持同步阻塞,大大的方便了有进行连续多次网络请求的需求。
相关链接:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0720/3209.html
Retrofit 和rxjava的结合
网友评论