Android根本知识点

1、惯例知识点

1、 Android类加载器

在Android开发中,不论是插件化还是组件化,都是基于Android零碎的类加载器ClassLoader来设计的。只不过Android平台上虚拟机运行的是Dex字节码,一种对class文件优化的产物,传统Class文件是一个Java源码文件会生成一个.class文件,而Android是把所有Class文件进行合并、优化,而后再生成一个最终的class.dex,目标是把不同class文件反复的货色只需保留一份,在晚期的Android利用开发中,如果不对Android利用进行分dex解决,那么最初一个利用的apk只会有一个dex文件。

Android中罕用的类加载器有两种,DexClassLoader和PathClassLoader,它们都继承于BaseDexClassLoader。区别在于调用父类结构器时,DexClassLoader多传了一个optimizedDirectory参数,这个目录必须是外部存储门路,用来缓存零碎创立的Dex文件。而PathClassLoader该参数为null,只能加载外部存储目录的Dex文件。所以咱们能够用DexClassLoader去加载内部的apk文件,这也是很多插件化技术的根底。

2、 Service

了解Android的Service,能够从以下几个方面来了解:

  • Service是在main Thread中执行,Service中不能执行耗时操作(网络申请,拷贝数据库,大文件)。
  • 能够在xml中设置Service所在的过程,让Service在另外的过程中执行。
  • Service执行的操作最多是20s,BroadcastReceiver是10s,Activity是5s。
  • Activity通过bindService(Intent,ServiceConnection,flag)与Service绑定。
  • Activity能够通过startService和bindService启动Service。

IntentService

IntentService是一个抽象类,继承自Service,外部存在一个ServiceHandler(Handler)和HandlerThread(Thread)。IntentService是解决异步申请的一个类,在IntentService中有一个工作线程(HandlerThread)来解决耗时操作,启动IntentService的形式和一般的一样,不过当执行完工作之后,IntentService会主动进行。另外能够屡次启动IntentService,每一个耗时操作都会以工作队列的模式在IntentService的onHandleIntent回调中执行,并且每次执行一个工作线程。IntentService的实质是:封装了一个HandlerThread和Handler的异步框架。

2.1、生命周期示意图

Service 作为 Android四大组件之一,利用十分宽泛。和Activity一样,Service 也有一系列的生命周期回调函数,具体如下图。

通常,启动Service有两种形式,startService和bindService形式。

2.2、startService生命周期

当咱们通过调用了Context的startService办法后,咱们便启动了Service,通过startService办法启动的Service会始终无限期地运行上来,只有在内部调用Context的stopService或Service外部调用Service的stopSelf办法时,该Service才会进行运行并销毁。

onCreate

onCreate: 执行startService办法时,如果Service没有运行的时候会创立该Service并执行Service的onCreate回调办法;如果Service曾经处于运行中,那么执行startService办法不会执行Service的onCreate办法。也就是说如果屡次执行了Context的startService办法启动Service,Service办法的onCreate办法只会在第一次创立Service的时候调用一次,当前均不会再次调用。咱们能够在onCreate办法中实现一些Service初始化相干的操作。

onStartCommand

onStartCommand: 在执行了startService办法之后,有可能会调用Service的onCreate办法,在这之后肯定会执行Service的onStartCommand回调办法。也就是说,如果屡次执行了Context的startService办法,那么Service的onStartCommand办法也会相应的屡次调用。onStartCommand办法很重要,咱们在该办法中依据传入的Intent参数进行理论的操作,比方会在此处创立一个线程用于下载数据或播放音乐等。

public @StartResult int onStartCommand(Intent intent, @StartArgFlags int flags, int startId) {}

当Android面临内存匮乏的时候,可能会销毁掉你以后运行的Service,而后待内存短缺的时候能够从新创立Service,Service被Android零碎强制销毁并再次重建的行为依赖于Service中onStartCommand办法的返回值。咱们罕用的返回值有三种值,START_NOT_STICKYSTART_STICKYSTART_REDELIVER_INTENT,这三个值都是Service中的动态常量。

START_NOT_STICKY

如果返回START\_NOT\_STICKY,示意当Service运行的过程被Android零碎强制杀掉之后,不会从新创立该Service,当然如果在其被杀掉之后一段时间又调用了startService,那么该Service又将被实例化。那什么情境下返回该值比拟失当呢?如果咱们某个Service执行的工作被中断几次无关紧要或者对Android内存缓和的状况下须要被杀掉且不会立刻从新创立这种行为也可承受,那么咱们便可将 onStartCommand的返回值设置为START\_NOT\_STICKY。举个例子,某个Service须要定时从服务器获取最新数据:通过一个定时器每隔指定的N分钟让定时器启动Service去获取服务端的最新数据。当执行到Service的onStartCommand时,在该办法内再布局一个N分钟后的定时器用于再次启动该Service并开拓一个新的线程去执行网络操作。假如Service在从服务器获取最新数据的过程中被Android零碎强制杀掉,Service不会再从新创立,这也没关系,因为再过N分钟定时器就会再次启动该Service并从新获取数据。

START_STICKY

如果返回START\_STICKY,示意Service运行的过程被Android零碎强制杀掉之后,Android零碎会将该Service仍然设置为started状态(即运行状态),然而不再保留onStartCommand办法传入的intent对象,而后Android零碎会尝试再次从新创立该Service,并执行onStartCommand回调办法,然而onStartCommand回调办法的Intent参数为null,也就是onStartCommand办法尽管会执行然而获取不到intent信息。如果你的Service能够在任意时刻运行或完结都没什么问题,而且不须要intent信息,那么就能够在onStartCommand办法中返回START\_STICKY,比方一个用来播放背景音乐性能的Service就适宜返回该值。

START_REDELIVER_INTENT

如果返回START\_REDELIVER\_INTENT,示意Service运行的过程被Android零碎强制杀掉之后,与返回START\_STICKY的状况相似,Android零碎会将再次从新创立该Service,并执行onStartCommand回调办法,然而不同的是,Android零碎会再次将Service在被杀掉之前最初一次传入onStartCommand办法中的Intent再次保留下来并再次传入到从新创立后的Service的onStartCommand办法中,这样咱们就能读取到intent参数。只有返回START\_REDELIVER\_INTENT,那么onStartCommand重的intent肯定不是null。如果咱们的Service须要依赖具体的Intent能力运行(须要从Intent中读取相干数据信息等),并且在强制销毁后有必要从新创立运行,那么这样的Service就适宜返回START\_REDELIVER\_INTENT。

onBind

Service中的onBind办法是形象办法,所以Service类自身就是抽象类,也就是onBind办法是必须重写的,即便咱们用不到。在通过startService应用Service时,咱们在重写onBind办法时,只须要将其返回null即可。onBind办法次要是用于给bindService办法调用Service时才会应用到。

onDestroy

onDestroy: 通过startService办法启动的Service会无限期运行,只有当调用了Context的stopService或在Service外部调用stopSelf办法时,Service才会进行运行并销毁,在销毁的时候会执行Service回调函数。

2.3、bindService生命周期

bindService形式启动Service次要有以下几个生命周期函数:

onCreate():

首次创立服务时,零碎将调用此办法。如果服务已在运行,则不会调用此办法,该办法只调用一次。

onStartCommand():

当另一个组件通过调用startService()申请启动服务时,零碎将调用此办法。

onDestroy():

当服务不再应用且将被销毁时,零碎将调用此办法。

onBind():

当另一个组件通过调用bindService()与服务绑定时,零碎将调用此办法。

onUnbind():

当另一个组件通过调用unbindService()与服务解绑时,零碎将调用此办法。

onRebind():

当旧的组件与服务解绑后,另一个新的组件与服务绑定,onUnbind()返回true时,零碎将调用此办法。

3、fragemnt

3.1、创立形式

(1)动态创立

首先咱们须要创立一个xml文件,而后创立与之对应的java文件,通过onCreatView()的返回办法进行关联,最初咱们须要在Activity中进行配置相干参数即在Activity的xml文件中放上fragment的地位。

 <fragment        android:name="xxx.BlankFragment"        android:layout_width="match_parent"        android:layout_height="match_parent">    </fragment>
(2)动态创建

动态创建Fragment次要有以下几个步骤:

  1. 创立待增加的fragment实例。
  2. 获取FragmentManager,在Activity中能够间接通过调用 getSupportFragmentManager()办法失去。
  3. 开启一个事务,通过调用beginTransaction()办法开启。
  4. 向容器内增加或替换fragment,个别应用repalce()办法实现,须要传入容器的id和待增加的fragment实例。
  5. 提交事务,调用commit()办法来实现。

3.2、Adapter比照

FragmnetPageAdapter在每次切换页面时,只是将Fragment进行拆散,适宜页面较少的Fragment应用以保留一些内存,对系统内存不会多大影响。

FragmentPageStateAdapter在每次切换页面的时候,是将Fragment进行回收,适宜页面较多的Fragment应用,这样就不会耗费更多的内存

3.3、Activity生命周期

Activity的生命周期如下图:

(1)动静加载:

动静加载时,Activity的onCreate()调用完,才开始加载fragment并调用其生命周期办法,所以在第一个生命周期办法onAttach()中便能获取Activity以及Activity的布局的组件;

(2)动态加载:

1.动态加载时,Activity的onCreate()调用过程中,fragment也在加载,所以fragment无奈获取到Activity的布局中的组件,但为什么能获取到Activity呢?

2.原来在fragment调用onAttach()之前其实还调用了一个办法onInflate(),该办法被调用时fragment曾经是和Activity互相联合了,所以能够获取到对方,然而Activity的onCreate()调用还未实现,故无奈获取Activity的组件;

3.Activity的onCreate()调用实现是,fragment会调用onActivityCreated()生命周期办法,因而在这儿开始便能获取到Activity的布局的组件;

3.4、与Activity通信

fragment不通过构造函数进行传值的起因是因为横屏切换的时候获取不到值。

Activity向Fragment传值:

Activity向Fragment传值,要传的值放到bundle对象里; 在Activity中创立该Fragment的对象fragment,通过调用setArguments()传递到fragment中; 在该Fragment中通过调用getArguments()失去bundle对象,就能失去外面的值。

Fragment向Activity传值:
第一种:

在Activity中调用getFragmentManager()失去fragmentManager,,调用findFragmentByTag(tag)或者通过findFragmentById(id),例如:

FragmentManager fragmentManager = getFragmentManager();Fragment fragment = fragmentManager.findFragmentByTag(tag);
第二种:

通过回调的形式,定义一个接口(能够在Fragment类中定义),接口中有一个空的办法,在fragment中须要的时候调用接口的办法,值能够作为参数放在这个办法中,而后让Activity实现这个接口,必然会重写这个办法,这样值就传到了Activity中

Fragment与Fragment之间是如何传值的:
第一种:

通过findFragmentByTag失去另一个的Fragment的对象,这样就能够调用另一个的办法了。

第二种:

通过接口回调的形式。

第三种:

通过setArguments,getArguments的形式。

3.5、api区别

add

一种是add形式来进行show和add,这种形式你切换fragment不会让fragment从新刷新,只会调用onHiddenChanged(boolean isHidden)。

replace

而用replace形式会使fragment从新刷新,因为add形式是将fragment暗藏了而不是销毁再创立,replace形式每次都是从新创立。

commit/commitAllowingStateLoss

两者都能够提交fragment的操作,惟一的不同是第二种办法,容许失落一些界面的状态和信息,简直所有的开发者都遇到过这样的谬误:无奈在activity调用了onSaveInstanceState之后再执行commit(),这种异样时能够了解的,界面被零碎回收(界面曾经不存在),为了在下次关上的时候复原原来的样子,零碎为咱们保留界面的所有状态,这个时候咱们再去批改界面实践上必定是不容许的,所以为了防止这种异样,要应用第二种办法。

3.懒加载

咱们常常在应用fragment时,经常会联合着viewpager应用,那么咱们就会遇到一个问题,就是初始化fragment的时候,会连同咱们写的网络申请一起执行,这样十分耗费性能,最现实的形式是,只有用户点开或滑动到以后fragment时,才进行申请网络的操作。因而,咱们就产生了懒加载这样一个说法。

Viewpager配合fragment应用,默认加载前两个fragment。很容易造成网络丢包、阻塞等问题。

在Fragment中有一个setUserVisibleHint这个办法,而且这个办法是优于onCreate()办法的,它会通过isVisibleToUser通知咱们以后Fragment咱们是否可见,咱们能够在可见的时候再进行网络加载。

从log上看setUserVisibleHint()的调用早于onCreateView,所以如果在setUserVisibleHint()要实现懒加载的话,就必须要确保View以及其余变量都曾经初始化完结,防止空指针。

应用步骤:

申明一个变量isPrepare=false,isVisible=false,表明以后页面是否被创立了 在onViewCreated周期内设置isPrepare=true 在setUserVisibleHint(boolean isVisible)判断是否显示,设置isVisible=true 判断isPrepare和isVisible,都为true开始加载数据,而后复原isPrepare和isVisible为false,避免反复加载。

4、Activity

4.1、 Activity启动流程

用户从Launcher程序点击利用图标可启动利用的入口Activity,Activity启动时须要多个过程之间的交互,Android零碎中有一个zygote过程专用于孵化Android框架层和应用层程序的过程。还有一个system\_server过程,该过程里运行了很多binder service。例如ActivityManagerService,PackageManagerService,WindowManagerService,这些binder service别离运行在不同的线程中,其中ActivityManagerService负责管理Activity栈,利用过程,task。

点击Launcher图标来启动Activity

用户在Launcher程序里点击利用图标时,会告诉ActivityManagerService启动利用的入口Activity,ActivityManagerService发现这个利用还未启动,则会告诉Zygote过程孵化出利用过程,而后在这个dalvik利用过程里执行ActivityThread的main办法。利用过程接下来告诉ActivityManagerService利用过程已启动,ActivityManagerService保留利用过程的一个代理对象,这样ActivityManagerService能够通过这个代理对象管制利用过程,而后ActivityManagerService告诉利用过程创立入口Activity的实例,并执行它的生命周期办法。

4.2、Activity生命周期

(1)Activity的状态

Active/Running:

Activity处于活动状态,此时Activity处于栈顶,是可见状态,可与用户进行交互。

Paused:

当Activity失去焦点时,或被一个新的非全屏的Activity,或被一个通明的Activity搁置在栈顶时,Activity就转化为Paused状态。但咱们须要明确,此时Activity只是失去了与用户交互的能力,其所有的状态信息及其成员变量都还存在,只有在零碎内存缓和的状况下,才有可能被零碎回收掉。

Stopped:

当一个Activity被另一个Activity齐全笼罩时,被笼罩的Activity就会进入Stopped状态,此时它不再可见,然而跟Paused状态一样放弃着其所有状态信息及其成员变量。

Killed:

当Activity被零碎回收掉时,Activity就处于Killed状态。

Activity会在以上四种状态中互相切换,至于如何切换,这因用户的操作不同而异。理解了Activity的4种状态后,咱们就来聊聊Activity的生命周期。

Activity的生命周期

所谓的典型的生命周期就是在有用户参加的状况下,Activity经验从创立,运行,进行,销毁等失常的生命周期过程。

onCreate

该办法是在Activity被创立时回调,它是生命周期第一个调用的办法,咱们在创立Activity时个别都须要重写该办法,而后在该办法中做一些初始化的操作,如通过setContentView设置界面布局的资源,初始化所须要的组件信息等。

onStart

此办法被回调时示意Activity正在启动,此时Activity已处于可见状态,只是还没有在前台显示,因而无奈与用户进行交互。能够简略了解为Activity已显示而咱们无奈看见摆了。

onResume

当此办法回调时,则阐明Activity已在前台可见,可与用户交互了(处于后面所说的Active/Running状态),onResume办法与onStart的相同点是两者都示意Activity可见,只不过onStart回调时Activity还是后盾无奈与用户交互,而onResume则已显示在前台,可与用户交互。当然从流程图,咱们也能够看出当Activity进行后(onPause办法和onStop办法被调用),从新回到前台时也会调用onResume办法,因而咱们也能够在onResume办法中初始化一些资源,比方从新初始化在onPause或者onStop办法中开释的资源。

onPause

此办法被回调时则示意Activity正在进行(Paused状态),个别状况下onStop办法会紧接着被回调。但通过流程图咱们还能够看到一种状况是onPause办法执行后间接执行了onResume办法,这属于比拟极其的景象了,这可能是用户操作使以后Activity退居后盾后又迅速地再回到到以后的Activity,此时onResume办法就会被回调。当然,在onPause办法中咱们能够做一些数据存储或者动画进行或者资源回收的操作,然而不能太耗时,因为这可能会影响到新的Activity的显示——onPause办法执行实现后,新Activity的onResume办法才会被执行。

onStop

个别在onPause办法执行实现间接执行,示意Activity行将进行或者齐全被笼罩(Stopped状态),此时Activity不可见,仅在后盾运行。同样地,在onStop办法能够做一些资源开释的操作(不能太耗时)。

onRestart

示意Activity正在重新启动,当Activity由不可见变为可见状态时,该办法被回调。这种状况个别是用户关上了一个新的Activity时,以后的Activity就会被暂停(onPause和onStop被执行了),接着又回到以后Activity页面时,onRestart办法就会被回调。

onDestroy

此时Activity正在被销毁,也是生命周期最初一个执行的办法,个别咱们能够在此办法中做一些回收工作和最终的资源开释。

小结

到这里咱们来个小结,当Activity启动时,顺次会调用onCreate(),onStart(),onResume(),而当Activity退居后盾时(不可见,点击Home或者被新的Activity齐全笼罩),onPause()和onStop()会顺次被调用。当Activity从新回到前台(从桌面回到原Activity或者被笼罩后又回到原Activity)时,onRestart(),onStart(),onResume()会顺次被调用。当Activity退出销毁时(点击back键),onPause(),onStop(),onDestroy()会顺次被调用,到此Activity的整个生命周期办法回调实现。当初咱们再回头看看之前的流程图,应该是相当清晰了吧。嗯,这就是Activity整个典型的生命周期过程。

2、 View局部知识点

Android的Activity、PhoneWindow和DecorView的关系能够用上面的图示意:

2.1、DecorView浅析

例如,有上面一个视图,DecorView为整个Window界面的最顶层View,它只有一个子元素LinearLayout。代表整个Window界面,蕴含告诉栏、标题栏、内容显示栏三块区域。其中LinearLayout中有两个FrameLayout子元素。

DecorView的作用

DecorView是顶级View,实质是一个FrameLayout它蕴含两局部,标题栏和内容栏,都是FrameLayout。内容栏id是content,也就是activity中设置setContentView的局部,最终将布局增加到id为content的FrameLayout中。 获取content:ViewGroup content=findViewById(android.id.content) 获取设置的View:getChildAt(0).

应用总结

每个Activity都蕴含一个Window对象,Window对象通常是由PhoneWindow实现的。 PhoneWindow:将DecorView设置为整个利用窗口的根View,是Window的实现类。它是Android中的最根本的窗口零碎,每个Activity均会创立一个PhoneWindow对象,是Activity和整个View零碎交互的接口。 DecorView:是顶层视图,将要显示的具体内容出现在PhoneWindow上,DecorView是以后Activity所有View的先人,它并不会向用户出现任何货色。

2.2、View的事件散发

View的事件散发机制能够应用下图示意:

如上图,图分为3层,从上往下顺次是Activity、ViewGroup、View。

  1. 事件从左上角那个红色箭头开始,由Activity的dispatchTouchEvent做散发
  2. 箭头的下面字代表办法返回值,(return true、return false、return super.xxxxx(),super 的意思是调用父类实现。
  3. dispatchTouchEvent和 onTouchEvent的框里有个【true---->生产】的字,示意的意思是如果办法返回true,那么代表事件就此生产,不会持续往别的中央传了,事件终止。
  4. 目前所有的图的事件是针对ACTION\_DOWN的,对于ACTION\_MOVE和ACTION\_UP咱们最初做剖析。
  5. 之前图中的Activity 的dispatchTouchEvent 有误(图已修复),只有return super.dispatchTouchEvent(ev) 才是往下走,返回true 或者 false 事件就被生产了(终止传递)。

ViewGroup事件散发

当一个点击事件产生后,它的传递过程将遵循如下程序:

Activity -> Window -> View

事件总是会传递给Activity,之后Activity再传递给Window,最初Window再传递给顶级的View,顶级的View在接管到事件后就会依照事件散发机制去散发事件。如果一个View的onTouchEvent返回了FALSE,那么它的父容器的onTouchEvent将会被调用,顺次类推,如果所有都不解决这个事件的话,那么Activity将会解决这个事件。

对于ViewGroup的事件散发过程,大略是这样的:如果顶级的ViewGroup拦挡事件即onInterceptTouchEvent返回true的话,则事件会交给ViewGroup解决,如果ViewGroup的onTouchListener被设置的话,则onTouch将会被调用,否则的话onTouchEvent将会被调用,也就是说:两者都设置的话,onTouch将会屏蔽掉onTouchEvent,在onTouchEvent中,如果设置了onClickerListener的话,那么onClick将会被调用。如果顶级ViewGroup不拦挡的话,那么事件将会被传递给它所在的点击事件的子view,这时候子view的dispatchTouchEvent将会被调用

View的事件散发

dispatchTouchEvent -> onTouch(setOnTouchListener) -> onTouchEvent -> onClick

onTouch和onTouchEvent的区别 两者都是在dispatchTouchEvent中调用的,onTouch优先于onTouchEvent,如果onTouch返回true,那么onTouchEvent则不执行,及onClick也不执行。

2.3、View的绘制

在xml布局文件中,咱们的layout\_width和layout\_height参数能够不必写具体的尺寸,而是wrap\_content或者是match\_parent。这两个设置并没有指定真正的大小,可是咱们绘制到屏幕上的View必须是要有具体的宽高的,正是因为这个起因,咱们必须本人去解决和设置尺寸。当然了,View类给了默认的解决,然而如果View类的默认解决不满足咱们的要求,咱们就得重写onMeasure函数啦~。

onMeasure函数是一个int整数,外面放了测量模式和尺寸大小。int型数据占用32个bit,而google实现的是,将int数据的后面2个bit用于辨别不同的布局模式,前面30个bit寄存的是尺寸的数据。 onMeasure函数的应用如下图:

MeasureSpec有三种测量模式:

match\_parent—>EXACTLY。怎么了解呢?match\_parent就是要利用父View给咱们提供的所有残余空间,而父View残余空间是确定的,也就是这个测量模式的整数外面寄存的尺寸。

wrap\_content—>AT\_MOST。怎么了解:就是咱们想要将大小设置为包裹咱们的view内容,那么尺寸大小就是父View给咱们作为参考的尺寸,只有不超过这个尺寸就能够啦,具体尺寸就依据咱们的需要去设定。

固定尺寸(如100dp)—>EXACTLY。用户本人指定了尺寸大小,咱们就不必再去干预了,当然是以指定的大小为主啦。

2.4、ViewGroup的绘制

自定义ViewGroup可就没那么简略啦~,因为它不仅要管好本人的,还要兼顾它的子View。咱们都晓得ViewGroup是个View容器,它装纳child View并且负责把child View放入指定的地位。

  1. 首先,咱们得晓得各个子View的大小吧,只有先晓得子View的大小,咱们才晓得以后的ViewGroup该设置为多大去包容它们。
  2. 依据子View的大小,以及咱们的ViewGroup要实现的性能,决定出ViewGroup的大小
  3. ViewGroup和子View的大小算进去了之后,接下来就是去摆放了吧,具体怎么去摆放呢?这得依据你定制的需要去摆放了,比方,你想让子View依照垂直程序一个挨着一个放,或者是依照先后顺序一个叠一个去放,这是你本人决定的。
  4. 曾经晓得怎么去摆放还不行啊,决定了怎么摆放就是相当于把已有的空间”宰割”成大大小小的空间,每个空间对应一个子View,咱们接下来就是把子View对号入座了,把它们放进它们该放的中央去。
![image](https://upload-images.jianshu.io/upload_images/27242968-7977eef94a34965f.image?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)![image](https://upload-images.jianshu.io/upload_images/27242968-cd5ac364ad49b141.image?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

自定义ViewGroup能够参考:Android自定义ViewGroup

3、零碎原理

3.1、打包原理

Android的包文件APK分为两个局部:代码和资源,所以打包方面也分为资源打包和代码打包两个方面,这篇文章就来剖析资源和代码的编译打包原理。

具体说来:

  1. 通过AAPT工具进行资源文件(包含AndroidManifest.xml、布局文件、各种xml资源等)的打包,生成R.java文件。
  2. 通过AIDL工具解决AIDL文件,生成相应的Java文件。
  3. 通过Javac工具编译我的项目源码,生成Class文件。
  4. 通过DX工具将所有的Class文件转换成DEX文件,该过程次要实现Java字节码转换成Dalvik字节码,压缩常量池以及革除冗余信息等工作。
  5. 通过ApkBuilder工具将资源文件、DEX文件打包生成APK文件。
  6. 利用KeyStore对生成的APK文件进行签名。
  7. 如果是正式版的APK,还会利用ZipAlign工具进行对齐解决,对齐的过程就是将APK文件中所有的资源文件举例文件的起始间隔都偏移4字节的整数倍,这样通过内存映射拜访APK文件的速度会更快。

3.2、装置流程

Android apk的装置过程次要气氛以下几步:

  1. 复制APK到/data/app目录下,解压并扫描安装包。
  2. 资源管理器解析APK里的资源文件。
  3. 解析AndroidManifest文件,并在/data/data/目录下创立对应的利用数据目录。
  4. 而后对dex文件进行优化,并保留在dalvik-cache目录下。
  5. 将AndroidManifest文件解析出的四大组件信息注册到PackageManagerService中。
  6. 装置实现后,发送播送。

能够应用上面的图示意:

4、 第三方库解析

4.1、Retrofit网络申请框架

概念:Retrofit是一个基于RESTful的HTTP网络申请框架的封装,其中网络申请的实质是由OKHttp实现的,而Retrofit仅仅负责网络申请接口的封装。

原理:App应用程序通过Retrofit申请网络,实际上是应用Retrofit接口层封装申请参数,Header、URL等信息,之后由OKHttp实现后续的申请,在服务器返回数据之后,OKHttp将原始的后果交给Retrofit,最初依据用户的需要对后果进行解析。

retrofit应用

1.在retrofit中通过一个接口作为http申请的api接口

public interface NetApi {    @GET("repos/{owner}/{repo}/contributors")    Call<ResponseBody> contributorsBySimpleGetCall(@Path("owner") String owner, @Path("repo") String repo);}

2.创立一个Retrofit实例

Retrofit retrofit = new Retrofit.Builder()        .baseUrl("https://api.github.com/")        .build();

3.调用api接口

NetApi repo = retrofit.create(NetApi.class);//第三步:调用网络申请的接口获取网络申请retrofit2.Call<ResponseBody> call = repo.contributorsBySimpleGetCall("username", "path");call.enqueue(new Callback<ResponseBody>() { //进行异步申请    @Override    public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {        //进行异步操作    }    @Override    public void onFailure(Call<ResponseBody> call, Throwable t) {        //执行谬误回调办法    }});

retrofit动静代理

retrofit执行的原理如下: 1.首先,通过method把它转换成ServiceMethod。 2.而后,通过serviceMethod,args获取到okHttpCall对象。 3.最初,再把okHttpCall进一步封装并返回Call对象。 首先,创立retrofit对象的办法如下:

Retrofit retrofit = new Retrofit.Builder()        .baseUrl("https://api.github.com/")        .build();

在创立retrofit对象的时候用到了build()办法,该办法的实现如下:

public Retrofit build() {  if (baseUrl == null) {    throw new IllegalStateException("Base URL required.");  }  okhttp3.Call.Factory callFactory = this.callFactory;  if (callFactory == null) {    callFactory = new OkHttpClient(); //设置kHttpClient  }  Executor callbackExecutor = this.callbackExecutor;  if (callbackExecutor == null) {    callbackExecutor = platform.defaultCallbackExecutor(); //设置默认回调执行器  }  // Make a defensive copy of the adapters and add the default Call adapter.  List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);  adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));  // Make a defensive copy of the converters.  List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);  return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,      callbackExecutor, validateEagerly); //返回新建的Retrofit对象}

该办法返回了一个Retrofit对象,通过retrofit对象创立网络申请的接口的形式如下:

NetApi repo = retrofit.create(NetApi.class);

retrofit对象的create()办法的实现如下:‘

public <T> T create(final Class<T> service) {  Utils.validateServiceInterface(service);  if (validateEagerly) {    eagerlyValidateMethods(service);  }  return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },      new InvocationHandler() {        private final Platform platform = Platform.get();        @Override public Object invoke(Object proxy, Method method, Object... args)            throws Throwable {          // If the method is a method from Object then defer to normal invocation.          if (method.getDeclaringClass() == Object.class) {            return method.invoke(this, args); //间接调用该办法          }          if (platform.isDefaultMethod(method)) {            return platform.invokeDefaultMethod(method, service, proxy, args); //通过平台对象调用该办法          }          ServiceMethod serviceMethod = loadServiceMethod(method); //获取ServiceMethod对象          OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args); //传入参数生成okHttpCall对象          return serviceMethod.callAdapter.adapt(okHttpCall); //执行okHttpCall        }      });}

4.2、图片加载库比照

Picasso:120K

Glide:475K

Fresco:3.4M

Android-Universal-Image-Loader:162K

图片函数库的抉择须要依据APP的具体情况而定,对于重大依赖图片缓存的APP,例如壁纸类,图片社交类APP来说,能够抉择最业余的Fresco。对于个别的APP,抉择Fresco会显得比拟重,毕竟Fresco3.4M的体量摆在这。依据APP对图片的显示和缓存的需要从低到高,咱们能够对以上函数库做一个排序。

Picasso < Android-Universal-Image-Loader < Glide < Fresco

2.介绍:

Picasso :和Square的网络库一起能施展最大作用,因为Picasso能够抉择将网络申请的缓存局部交给了okhttp实现。

Glide:模拟了Picasso的API,而且在他的根底上加了很多的扩大(比方gif等反对),Glide默认的Bitmap格局是RGB\_565,比 Picasso默认的ARGB\_8888格局的内存开销要小一半;Picasso缓存的是全尺寸的(只缓存一种),而Glide缓存的是跟ImageView尺寸雷同的(即56_56和128_128是两个缓存) 。

FB的图片加载框架Fresco:最大的劣势在于5.0以下(最低2.3)的bitmap加载。在5.0以下零碎,Fresco将图片放到一个特地的内存区域(Ashmem区)。当然,在图片不显示的时候,占用的内存会主动被开释。这会使得APP更加晦涩,缩小因图片内存占用而引发的OOM。为什么说是5.0以下,因为在5.0当前零碎默认就是存储在Ashmem区了。

3.总结:

Picasso所能实现的性能,Glide都能做,无非是所需的设置不同。然而Picasso体积比起Glide小太多如果我的项目中网络申请自身用的就是okhttp或者retrofit(实质还是okhttp),那么倡议用Picasso,体积会小很多(Square全家桶的干活)。Glide的益处是大型的图片流,比方gif、Video,如果你们是做美拍、爱拍这种视频类利用,倡议应用。

Fresco在5.0以下的内存优化十分好,代价就是体积也十分的大,按体积算Fresco>Glide>Picasso

不过在应用起来也有些不便(小倡议:他只能用内置的一个ImageView来实现这些性能,用起来比拟麻烦,咱们通常是依据Fresco本人改改,间接应用他的Bitmap层)

4.3、各种json解析库应用

参考链接:www.cnblogs.com/kunpengit/p…

(1)Google的Gson

Gson是目前性能最全的Json解析神器,Gson当初是为因应Google公司外部需要而由Google自行研发而来,但自从在2008年五月公开公布第一版后已被许多公司或用户利用。Gson的利用次要为toJson与fromJson两个转换函数,无依赖,不须要例外额定的jar,可能间接跑在JDK上。而在应用这种对象转换之前需先创立好对象的类型以及其成员能力胜利的将JSON字符串胜利转换成绝对应的对象。类外面只有有get和set办法,Gson齐全能够将简单类型的json到bean或bean到json的转换,是JSON解析的神器。Gson在性能下面无可挑剔,然而性能下面比FastJson有所差距。

(2)阿里巴巴的FastJson

Fastjson是一个Java语言编写的高性能的JSON处理器,由阿里巴巴公司开发。

无依赖,不须要例外额定的jar,可能间接跑在JDK上。FastJson在简单类型的Bean转换Json上会呈现一些问题,可能会呈现援用的类型,导致Json转换出错,须要制订援用。FastJson采纳独创的算法,将parse的速度晋升到极致,超过所有json库。

综上Json技术的比拟,在我的项目选型的时候能够应用Google的Gson和阿里巴巴的FastJson两种并行应用,如果只是性能要求,没有性能要求,能够应用google的Gson,如果有性能下面的要求能够应用Gson将bean转换json确保数据的正确,应用FastJson将Json转换Bean

5、热点技术

参考链接- Android组件化计划

5.1、组件化

(1)概念:

组件化:是将一个APP分成多个module,每个module都是一个组件,也能够是一个根底库供组件依赖,开发中能够独自调试局部组件,组件中不须要相互依赖然而能够互相调用,最终公布的时候所有组件以lib的模式被主APP工程依赖打包成一个apk。

(2)由来:

  1. APP版本迭代,新性能一直减少,业务变得复杂,保护老本高
  2. 业务耦合度高,代码臃肿,团队外部多人合作开发艰难
  3. Android编译代码卡顿,繁多工程下代码耦合重大,批改一处须要从新编译打包,耗时耗力。
  4. 不便单元测试,独自改一个业务模块,不须要着重关注其余模块。

(3)劣势:

  1. 组件化将通用模块独立进去,对立治理,以进步复用,将页面拆分为粒度更小的组件,组件外部出了蕴含UI实现,还能够蕴含数据层和逻辑层
  2. 每个组件度能够独立编译、放慢编译速度、独立打包。
  3. 每个工程外部的批改,不会影响其余工程。
  4. 业务库工程能够疾速拆分进去,集成到其余App中。
  5. 迭代频繁的业务模块采纳组件形式,业务线研发能够互不烦扰、晋升合作效率,并管制产品质量,增强稳定性。
  6. 并行开发,团队成员只关注本人的开发的小模块,升高耦合性,前期保护不便等。

(4)思考问题:

模式切换:如何使得APP在独自调试跟整体调试自在切换

组件化后的每一个业务的module都能够是一个独自的APP(isModuleRun=false), release 包的时候各个业务module作为lib依赖,这里齐全由一个变量管制,在根我的项目 gradle.properties外面isModuleRun=true。isModuleRun状态不同,加载application和AndroidManifest都不一样,以此来辨别是独立的APK还是lib。

在build.grade外面配置:

资源抵触

当咱们创立了多个Module的时候,如何解决雷同资源文件名合并的抵触,业务Module和BaseModule资源文件名称反复会产生抵触,解决方案在于:

每个 module 都有 app\_name,为了不让资源名重名,在每个组件的 build.gradle 中减少 resourcePrefix “xxx\_强行查看资源名称前缀。固定每个组件的资源前缀。然而 resourcePrefix 这个值只能限定 xml 外面的资源,并不能限定图片资源。

依赖关系

多个Module之间如何援用一些独特的library以及工具类

组件通信

组件化之后,Module之间是互相隔离的,如何进行UI跳转以及办法调用,具体能够应用阿里巴巴ARouter或者美团的WMRouter等路由框架。

各业务Module之前不须要任何依赖能够通过路由跳转,完满解决业务之间耦合。

入口参数

咱们晓得组件之间是有分割的,所以在独自调试的时候如何拿到其它的Module传递过去的参数

Application

当组件独自运行的时候,每个Module自成一个APK,那么就意味着会有多个Application,很显然咱们不违心反复写这么多代码,所以咱们只须要定义一个BaseApplication即可,其它的Application间接继承此BaseApplication就OK了,BaseApplication外面还可定义专用的参数。

对于如何进行组件化,能够参考:安居客Android我的项目架构演进

5.2、插件化

参考链接- 插件化入门

(1)概述

提到插件化,就不得不提起办法数超过65535的问题,咱们能够通过Dex分包来解决,同时也能够通过应用插件化开发来解决。插件化的概念就是由宿主APP去加载以及运行插件APP。

(2长处)

在一个大的我的项目外面,为了明确的分工,往往不同的团队负责不同的插件APP,这样分工更加明确。各个模块封装成不同的插件APK,不同模块能够独自编译,进步了开发效率。 解决了上述的办法数超过限度的问题。能够通过上线新的插件来解决线上的BUG,达到“热修复”的成果。 减小了宿主APK的体积。

(3毛病)

插件化开发的APP不能在Google Play上线,也就是没有海内市场。

6、屏幕适配

6.1、基本概念

屏幕尺寸

含意:手机对角线的物理尺寸 单位:英寸(inch),1英寸=2.54cm

Android手机常见的尺寸有5寸、5.5寸、6寸,6.5寸等等

屏幕分辨率

含意:手机在横向、纵向上的像素点数总和

个别形容成屏幕的”宽x高”=AxB 含意:屏幕在横向方向(宽度)上有A个像素点,在纵向方向

(高)有B个像素点 例子:1080x1920,即宽度方向上有1080个像素点,在高度方向上有1920个像素点

单位:px(pixel),1px=1像素点

UI设计师的设计图会以px作为对立的计量单位

Android手机常见的分辨率:320x480、480x800、720x1280、1080x1920

屏幕像素密度

含意:每英寸的像素点数 单位:dpi(dots per ich)

假如设施内每英寸有160个像素,那么该设施的屏幕像素密度=160dpi

6.2、适配办法

1.反对各种屏幕尺寸: 应用wrap\_content, match\_parent, weight.要确保布局的灵活性并适应各种尺寸的屏幕,应应用 “wrap\_content”、“match\_parent” 管制某些视图组件的宽度和高度。

2.应用绝对布局,禁用相对布局。

3.应用LinearLayout的weight属性

如果咱们的宽度不是0dp(wrap\_content和0dp的成果雷同),则是match\_parent呢?

android:layout\_weight的实在含意是:如果View设置了该属性并且无效,那么该 View的宽度等于原有宽度(android:layout\_width)加上残余空间的占比。

从这个角度咱们来解释一下下面的景象。在下面的代码中,咱们设置每个Button的宽度都是match\_parent,假如屏幕宽度为L,那么每个Button的宽度也应该都为L,残余宽度就等于L-(L+L)= -L。

Button1的weight=1,残余宽度占比为1/(1+2)= 1/3,所以最终宽度为L+1/3*(-L)=2/3L,Button2的计算相似,最终宽度为L+2/3(-L)=1/3L。

4.应用.9图片

6.3、今日头条屏幕适配

参考链接:今日头条屏幕适配计划终极版

7、性能优化

参考链接:Android 性能监测工具,优化内存、卡顿、耗电、APK大小的办法 Android的性能优化,次要是从以下几个方面进行优化的: 稳固(内存溢出、解体) 晦涩(卡顿) 耗损(耗电、流量) 安装包(APK瘦身) 影响稳定性的起因很多,比方内存应用不合理、代码异样场景考虑不周全、代码逻辑不合理等,都会对利用的稳定性造成影响。其中最常见的两个场景是:Crash 和 ANR,这两个谬误将会使得程序无奈应用。所以做好Crash全局监控,解决闪退同时把解体信息、异样信息收集记录起来,以便后续剖析;正当应用主线程解决业务,不要在主线程中做耗时操作,避免ANR程序无响应产生。

(一)稳固——内存优化

(1)Memory Monitor 工具:

它是Android Studio自带的一个内存监督工具,它能够很好地帮忙咱们进行内存实时剖析。通过点击Android Studio右下角的Memory Monitor标签,关上工具能够看见较浅蓝色代表free的内存,而深色的局部代表应用的内存从内存变换的走势图变换,能够判断对于内存的应用状态,例如当内存继续增高时,可能产生内存透露;当内存忽然缩小时,可能产生GC等,如下图所示。

LeakCanary工具: LeakCanary是Square公司基于MAT开发的一款监控Android内存透露的开源框架。其工作的原理是: 监测机制利用了Java的WeakReference和ReferenceQueue,通过将Activity包装到WeakReference中,被WeakReference包装过的Activity对象如果被回收,该WeakReference援用会被放到ReferenceQueue中,通过监测ReferenceQueue外面的内容就能查看到Activity是否可能被回收(在ReferenceQueue中阐明能够被回收,不存在透露;否则,可能存在透露,LeakCanary是执行一遍GC,若还未在ReferenceQueue中,就会认定为透露)。

如果Activity被认定为泄露了,就抓取内存dump文件(Debug.dumpHprofData);之后通过HeapAnalyzerService.runAnalysis进行剖析内存文件剖析;接着通过HeapAnalyzer (checkForLeak—findLeakingReference---findLeakTrace)来进行内存透露剖析。最初通过DisplayLeakService进行内存透露的展现。

(3)Android Lint 工具:

Android Lint Tool 是Android Sutido种集成的一个Android代码提醒工具,它能够给你布局、代码提供十分弱小的帮忙。硬编码会提醒以级别正告,例如:在布局文件中写了三层冗余的LinearLayout布局、间接在TextView中写要显示的文字、字体大小应用dp而不是sp为单位,就会在编辑器左边看到提醒。

(二)晦涩——卡顿优化

卡顿的场景通常是产生在用户交互体验最间接的方面。影响卡顿的两大因素,别离是界面绘制和数据处理。

界面绘制:次要起因是绘制的层级深、页面简单、刷新不合理,因为这些起因导致卡顿的场景更多呈现在 UI 和启动后的初始界面以及跳转到页面的绘制上。

数据处理:导致这种卡顿场景的起因是数据处理量太大,个别分为三种状况,一是数据在解决 UI 线程,二是数据处理占用 CPU 高,导致主线程拿不到工夫片,三是内存减少导致 GC 频繁,从而引起卡顿。

(1)布局优化

在Android种系统对View进行测量、布局和绘制时,都是通过对View数的遍从来进行操作的。如果一个View数的高度太高就会重大影响测量、布局和绘制的速度。Google也在其API文档中倡议View高度不宜哦过10层。当初版本种Google应用RelativeLayout代替LineraLayout作为默认根布局,目标就是升高LineraLayout嵌套产生布局树的高度,从而进步UI渲染的效率。

布局复用,应用标签重用layout; 进步显示速度,应用提早View加载; 缩小层级,应用标签替换父级布局; 留神应用wrap\_content,会减少measure计算成本; 删除控件中无用属性;

(2)绘制优化

适度绘制是指在屏幕上的某个像素在同一帧的工夫内被绘制了屡次。在多层次重叠的 UI 构造中,如果不可见的 UI 也在做绘制的操作,就会导致某些像素区域被绘制了屡次,从而节约了多余的 CPU 以及 GPU 资源。如何防止适度绘制?

布局上的优化。移除 XML 中非必须的背景,移除 Window 默认的背景、按需显示占位背景图片

自定义View优化。应用 canvas.clipRect() 帮忙零碎辨认那些可见的区域,只有在这个区域内才会被绘制。

(3)启动优化

利用个别都有闪屏页SplashActivity,优化闪屏页的 UI 布局,能够通过 Profile GPU Rendering 检测丢帧状况。

(三)节俭——耗电优化

在 Android5.0 以前,对于应用电量耗费的测试即麻烦又不精确,而5.0 之后Google专门引入了一个获取设施上电量耗费信息的API—— Battery Historian。Battery Historian 是一款由 Google 提供的 Android 零碎电量剖析工具,直观地展现出手机的电量耗费过程,通过输出电量剖析文件,显示耗费状况。

最初提供一些可供参考耗电优化的办法:

(1)计算优化。算法、for循环优化、Switch..case代替if..else、避开浮点运算。

浮点运算:计算机里整数和小数模式就是按一般格局进行存储,例如1024、3.1415926等等,这个没什么特点,然而这样的数精度不高,表白也不够全面,为了可能有一种数的通用表示法,就创造了浮点数。浮点数的示意模式有点像迷信计数法(_._×10^_),它的示意模式是0._×10^_,在计算机中的模式为 .*** e ±_**),其中后面的星号代表定点小数,也就是整数局部为0的纯小数,前面的指数局部是定点整数。利用这样的模式就能示意出任意一个整数和小数,例如1024就能示意成0.1024×10^4,也就是 .1024e+004,3.1415926就能示意成0.31415926×10^1,也就是 .31415926e+001,这就是浮点数。浮点数进行的运算就是浮点运算。浮点运算比惯例运算更简单,因而计算机进行浮点运算速度要比进行惯例运算慢得多。

(2)防止 Wake Lock 使用不当。

Wake Lock是一种锁的机制,次要是绝对零碎的休眠而言的,,只有有人拿着这个锁,零碎就无奈进入休眠意思就是我的程序给CPU加了这个锁那零碎就不会休眠了,这样做的目标是为了全力配合咱们程序的运行。有的状况如果不这么做就会呈现一些问题,比方微信等及时通信的心跳包会在熄屏不久后进行网络拜访等问题。所以微信外面是有大量应用到了Wake\_Lock锁。零碎为了节俭电量,CPU在没有工作忙的时候就会主动进入休眠。有工作须要唤醒CPU高效执行的时候,就会给CPU加Wake\_Lock锁。大家常常犯的谬误,咱们很容易去唤醒CPU来工作,然而很容易遗记开释Wake\_Lock。

(3)应用 Job Scheduler 治理后台任务。

在Android 5.0 API 21 中,google提供了一个叫做JobScheduler API的组件,来解决当某个工夫点或者当满足某个特定的条件时执行一个工作的场景,例如当用户在夜间劳动时或设施接通电源适配器连贯WiFi启动下载更新的工作。这样能够在缩小资源耗费的同时晋升利用的效率。

(四)安装包——APK瘦身

(1)安装包的组成构造

assets文件夹。寄存一些配置文件、资源文件,assets不会主动生成对应的 ID,而是通过 AssetManager 类的接口获取。

res。res 是 resource 的缩写,这个目录寄存资源文件,会主动生成对应的 ID 并映射到 .R 文件中,拜访间接应用资源 ID。

META-INF。保留利用的签名信息,签名信息能够验证 APK 文件的完整性。

AndroidManifest.xml。这个文件用来形容 Android 利用的配置信息,一些组件的注册信息、可应用权限等。

classes.dex。Dalvik 字节码程序,让 Dalvik 虚拟机可执行,个别状况下,Android 利用在打包时通过 Android SDK 中的 dx 工具将 Java 字节码转换为 Dalvik 字节码。

resources.arsc。记录着资源文件和资源 ID 之间的映射关系,用来依据资源 ID 寻找资源。

(2)缩小安装包大小

代码混同。应用IDE 自带的 proGuard 代码混同器工具 ,它包含压缩、优化、混同等性能。 资源优化。比方应用 Android Lint 删除冗余资源,资源文件起码化等。 图片优化。比方利用 PNG优化工具 对图片做压缩解决。举荐目前最先进的压缩工具Googlek开源库zopfli。如果利用在0版本以上,举荐应用 WebP图片格式。 防止反复或无用性能的第三方库。例如,百度地图接入根底地图即可、讯飞语音无需接入离线、图片库Glide\Picasso等。 插件化开发。比方功能模块放在服务器上,按需下载,能够缩小安装包大小。 能够应用微信开源资源文件混同工具——AndResGuard。个别能够压缩apk的1M左右大。

7.1、冷启动与热启动

参考链接:www.jianshu.com/p/03c0fd3fc…

冷启动 在启动利用时,零碎中没有该利用的过程,这时零碎会创立一个新的过程调配给该利用;

热启动 在启动利用时,零碎中已有该利用的过程(例:按back键、home键,利用尽管会退出,然而该利用的过程还是保留在后盾);

区别 冷启动:零碎没有该利用的过程,须要创立一个新的过程调配给利用,所以会先创立和初始化Application类,再创立和初始化MainActivity类(包含一系列的测量、布局、绘制),最初显示在界面上。 热启动: 从已有的过程中来启动,不会创立和初始化Application类,间接创立和初始化MainActivity类(包含一系列的测量、布局、绘制),最初显示在界面上。

冷启动流程 Zygote过程中fork创立出一个新的过程; 创立和初始化Application类、创立MainActivity; inflate布局、当onCreate/onStart/onResume办法都走完; contentView的measure/layout/draw显示在界面上。

冷启动优化 缩小在Application和第一个Activity的onCreate()办法的工作量; 不要让Application参加业务的操作; 不要在Application进行耗时操作; 不要以动态变量的形式在Application中保留数据; 缩小布局的复杂性和深度;

8、MVP模式架构

8.1、MVP模式

MVP架构由MVC倒退而来。在MVP中,M代表Model,V代表View,P代表Presenter。

模型层(Model):次要是获取数据性能,业务逻辑和实体模型。

视图层(View):对应于Activity或Fragment,负责视图的局部展现和业务逻辑用户交互

管制层(Presenter):负责实现View层与Model层间的交互,通过P层来获取M层中数据后返回给V层,使得V层与M层间没有耦合。

在MVP中 ,Presenter层齐全将View层和Model层进行了拆散,把次要程序逻辑放在Presenter层实现,Presenter与具体的View层(Activity)是没有间接的关联,是通过定义接口来进行交互的,从而使得当View层(Activity)产生扭转时,Persenter仍然能够放弃不变。View层接口类只应该只有set/get办法,及一些界面显示内容和用户输出,除此之外不应该有多余的内容。绝不允许View层间接拜访Model层,这是与MVC最大区别之处,也是MVP外围长处。

9、虚拟机

9.1、Android Dalvik虚拟机和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冷启动速度。

ART长处:

放慢APP冷启动速度

晋升GC速度

提供性能全面的Debug个性

ART毛病:

APP装置速度慢,因为在APK装置的时候要生成可运行.oat文件

APK占用空间大,因为在APK装置的时候要生成可运行.oat文件

arm处理器

对于ART更具体的介绍,能够参考Android ART详解

总结

相熟Android性能剖析工具、UI卡顿、APP启动、包瘦身和内存性能优化

相熟Android APP架构设计,模块化、组件化、插件化开发

熟练掌握Java、设计模式、网络、多线程技术

Java根本知识点

1、Java的类加载过程

jvm将.class类文件信息加载到内存并解析成对应的class对象的过程,留神:jvm并不是一开始就把所有的类加载进内存中,只是在第一次遇到某个须要运行的类才会加载,并且只加载一次

次要分为三局部:1、加载,2、链接(1.验证,2.筹备,3.解析),3、初始化

1:加载

类加载器包含 BootClassLoader、ExtClassLoader、APPClassLoader

2:链接

验证:(验证class文件的字节流是否合乎jvm标准)

筹备:为类变量分配内存,并且进行赋初值

解析:将常量池外面的符号援用(变量名)替换成间接援用(内存地址)过程,在解析阶段,jvm会把所有的类名、办法名、字段名、这些符号援用替换成具体的内存地址或者偏移量。

3:初始化

次要对类变量进行初始化,执行类结构器的过程,换句话说,只对static修试的变量或者语句进行初始化。

范例:Person person = new Person();为例进行阐明。

Java编程思维中的类的初始化过程次要有以下几点:

  1. 找到class文件,将它加载到内存
  2. 在堆内存中调配内存地址
  3. 初始化
  4. 将堆内存地址指给栈内存中的p变量

2、String、StringBuilder、StringBuffer

StringBuffer外面的很多办法增加了synchronized关键字,是能够表征线程平安的,所以多线程状况下应用它。

执行速度:

StringBuilder > StringBuffer > String

StringBuilder就义了性能来换取速度的,这两个是能够间接在原对象下面进行批改,省去了创立新对象和回收老对象的过程,而String是字符串常量(final)修试,另外两个是字符串变量,常量对象一旦创立就不能够批改,变量是能够进行批改的,所以对于String字符串的操作蕴含上面三个步骤:

  1. 创立一个新对象,名字和原来的一样
  2. 在新对象下面进行批改
  3. 原对象被垃圾回收掉

3、JVM内存构造

Java对象实例化过程中,次要应用到虚拟机栈、Java堆和办法区。Java文件通过编译之后首先会被加载到jvm办法区中,jvm办法区中很重的一个局部是运行时常量池,用以存储class文件类的版本、字段、办法、接口等形容信息和编译期间的常量和动态常量。

3.1、JVM根本构造

类加载器classLoader,在JVM启动时或者类运行时将须要的.class文件加载到内存中。 执行引擎,负责执行class文件中蕴含的字节码指令。 本地办法接口,次要是调用C/C++实现的本地办法及返回后果。 内存区域(运行时数据区),是在JVM运行的时候操作所调配的内存区, 次要分为以下五个局部,如下图:

  • 办法区:用于存储类构造信息的中央,包含常量池、动态变量、构造函数等。
  • Java堆(heap):存储Java实例或者对象的中央。这块是gc的次要区域。
  • Java栈(stack):Java栈总是和线程关联的,每当创立一个线程时,JVM就会为这个线程创立一个对应的Java栈。在这个java栈中又会蕴含多个栈帧,每运行一个办法就创立一个栈帧,用于存储局部变量表、操作栈、办法返回值等。每一个办法从调用直至执行实现的过程,就对应一个栈帧在java栈中入栈到出栈的过程。所以java栈是线程公有的。
  • 程序计数器:用于保留以后线程执行的内存地址,因为JVM是多线程执行的,所以为了保障线程切换回来后还能复原到原先状态,就须要一个独立的计数器,记录之前中断的中央,可见程序计数器也是线程公有的。
  • 本地办法栈:和Java栈的作用差不多,只不过是为JVM应用到的native办法服务的。

3.2、JVM源码剖析

www.jianshu.com/nb/12554212

4、GC机制

垃圾收集器个别实现两件事

  1. 检测出垃圾;
  2. 回收垃圾;

4.1 Java对象援用

通常,Java对象的援用能够分为4类:强援用、软援用、弱援用和虚援用。 强援用:通常能够认为是通过new进去的对象,即便内存不足,GC进行垃圾收集的时候也不会被动回收。

Object obj = new Object();

软援用:在内存不足的时候,GC进行垃圾收集的时候会被GC回收。

Object obj = new Object();SoftReference<Object> softReference = new SoftReference<>(obj);

弱援用:无论内存是否短缺,GC进行垃圾收集的时候都会回收。

Object obj = new Object();WeakReference<Object> weakReference = new WeakReference<>(obj);

虚援用:和弱援用相似,次要区别在于虚援用必须和援用队列一起应用。

Object obj = new Object();ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();PhantomReference<Object> phantomReference = new PhantomReference<>(obj, referenceQueue);

援用队列:如果软援用和弱援用被GC回收,JVM就会把这个援用加到援用队列里,如果是虚援用,在回收前就会被加到援用队列里。

垃圾检测办法:

援用计数法:给每个对象增加援用计数器,每个中央援用它,计数器就+1,生效时-1。如果两个对象相互援用时,就导致无奈回收。 可达性剖析算法:以根集对象为起始点进行搜寻,如果对象不可达的话就是垃圾对象。根集(Java栈中援用的对象、办法区中常量池中援用的对象、本地办法中援用的对象等。JVM在垃圾回收的时候,会查看堆中所有对象是否被这些根集对象援用,不可能被援用的对象就会被垃圾回收器回收。)

垃圾回收算法:

常见的垃圾回收算法有:

标记-革除

标记:首先标记所有须要回收的对象,在标记实现之后统计回收所有被标记的对象,它的标记过程即为下面的可达性剖析算法。 革除:革除所有被标记的对象 毛病: 效率有余,标记和革除效率都不高 空间问题,标记革除之后会产生大量不间断的内存碎片,导致大对象调配无奈找到足够的空间,提前进行垃圾回收。

复制回收算法 将可用的内存按容量划分为大小相等的2块,每次只用一块,当这一块的内存用完了,就将存活的对象复制到另外一块下面,而后把已应用过的内存空间一次清理掉。

毛病:

将内存放大了本来的个别,代价比拟高 大部分对象是“朝生夕灭”的,所以不用依照1:1的比例划分。 当初商业虚拟机采纳这种算法回收新生代,但不是按1:1的比例,而是将内存区域划分为eden 空间、from 空间、to 空间 3 个局部。 其中 from 空间和 to 空间能够视为用于复制的两块大小雷同、位置相等,且可进行角色调换的空间块。from 和 to 空间也称为 survivor 空间,即幸存者空间,用于寄存未被回收的对象。

在垃圾回收时,eden 空间中的存活对象会被复制到未应用的 survivor 空间中 (假如是 to),正在应用的 survivor 空间 (假如是 from) 中的年老对象也会被复制到 to 空间中 (大对象,或者老年对象会间接进入老年带,如果 to 空间已满,则对象也会间接进入老年代)。此时,eden 空间和 from 空间中的残余对象就是垃圾对象,能够间接清空,to 空间则寄存此次回收后的存活对象。这种改良的复制算法既保证了空间的连续性,又防止了大量的内存空间节约。

标记-整顿

在老年代的对象大都是存活对象,复制算法在对象存活率教高的时候,效率就会变得比拟低。依据老年代的特点,有人提出了“标记-压缩算法(Mark-Compact)”

标记过程与标记-革除的标记一样,但后续不是对可回收对象进行清理,而是让所有的对象都向一端挪动,而后间接清理掉端边界以外的内存。

这种办法既防止了碎片的产生,又不须要两块雷同的内存空间,因而,其性价比比拟高。

分带收集算法

依据对象存活的周期不同将内存划分为几块,个别是把Java堆分为老年代和新生代,这样依据各个年代的特点采纳适当的收集算法。

新生代每次收集都有大量对象死去,只有大量存活,那就选用复制算法,复制的对象数较少就可实现收集。 老年代对象存活率高,应用标记-压缩算法,以进步垃圾回收效率。

5、类加载器

程序在启动的时候,并不会一次性加载程序所要用的所有class文件,而是依据程序的须要,通过Java的类加载机制(ClassLoader)来动静加载某个class文件到内存当中的,从而只有class文件被载入到了内存之后,能力被其它class所援用。所以ClassLoader就是用来动静加载class文件到内存当中用的。

5.1、双亲委派原理

每个ClassLoader实例都有一个父类加载器的援用(不是继承关系,是一个蕴含的关系),虚拟机内置的类加载器(Bootstrap ClassLoader)自身没有父类加载器,然而能够用做其余ClassLoader实例的父类加载器。

当一个ClassLoader 实例须要加载某个类时,它会试图在亲自搜寻这个类之前先把这个工作委托给它的父类加载器,这个过程是由上而下顺次查看的,首先由顶层的类加载器Bootstrap CLassLoader进行加载,如果没有加载到,则把工作转交给Extension CLassLoader视图加载,如果也没有找到,则转交给AppCLassLoader进行加载,还是没有的话,则交给委托的发起者,由它到指定的文件系统或者网络等URL中进行加载类。还没有找到的话,则会抛出CLassNotFoundException异样。否则将这个类生成一个类的定义,并将它加载到内存中,最初返回这个类在内存中的Class实例对象。

5.2、 为什么应用双亲委托模型

JVM在判断两个class是否雷同时,不仅要判断两个类名是否雷同,还要判断是否是同一个类加载器加载的。

防止反复加载,父类曾经加载了,则子CLassLoader没有必要再次加载。 思考平安因素,假如自定义一个String类,除非扭转JDK中CLassLoader的搜寻类的默认算法,否则用户自定义的CLassLoader如法加载一个本人写的String类,因为String类在启动时就被疏导类加载器Bootstrap CLassLoader加载了。

对于Android的双亲委托机制,能够参考android classloader双亲委托模式

6、汇合

Java汇合类次要由两个接口派生出:Collection和Map,这两个接口是Java汇合的根接口。

Collection接口是汇合类的根接口,Java中没有提供这个接口的间接的实现类。然而却让其被继承产生了两个接口,就是 Set和List。Set中不能蕴含反复的元素。List是一个有序的汇合,能够蕴含反复的元素,提供了按索引拜访的形式。

Map是Java.util包中的另一个接口,它和Collection接口没有关系,是互相独立的,然而都属于汇合类的一部分。Map蕴含了key-value对。Map不能蕴含反复的key,然而能够蕴含雷同的value。

6.1、区别

List,Set都是继承自Collection接口,Map则不是; List特点:元素有放入程序,元素可反复; Set特点:元素无放入程序,元素不可反复,反复元素会笼罩掉,(留神:元素尽管无放入程序,然而元素在set中的地位是有该元素的HashCode决定的,其地位其实是固定的,退出Set 的Object必须定义equals()办法; LinkedList、ArrayList、HashSet是非线程平安的,Vector是线程平安的; HashMap是非线程平安的,HashTable是线程平安的;

6.2、List和Vector比拟

Vector是多线程平安的,线程平安就是说多线程拜访同一代码,不会产生不确定的后果。而ArrayList不是,这个能够从源码中看出,Vector类中的办法很多有synchronized进行润饰,这样就导致了Vector在效率上无奈与ArrayList相比; 两个都是采纳的线性间断空间存储元素,然而当空间有余的时候,两个类的减少形式是不同。 Vector能够设置增长因子,而ArrayList不能够。 Vector是一种老的动静数组,是线程同步的,效率很低,个别不赞成应用。

6.3、HashSet如何保障不反复

HashSet底层通过HashMap来实现的,在往HashSet中增加元素是

public boolean add(E e) {return map.put(e, PRESENT)==null;}// Dummy value to associate with an Object in the backing Mapprivate static final Object PRESENT = new Object();

在HashMap中进行查找是否存在这个key,value始终是一样的,次要有以下几种状况:

  • 如果hash码值不雷同,阐明是一个新元素,存;
  • 如果hash码值雷同,且equles判断相等,阐明元素曾经存在,不存;
  • 如果hash码值雷同,且equles判断不相等,阐明元素不存在,存;
  • 如果有元素和传入对象的hash值相等,那么,持续进行equles()判断,如果依然相等,那么就认为传入元素曾经存在,不再增加,完结,否则依然增加;

6.4、HashSet与Treeset的实用场景

  • HashSet是基于Hash算法实现的,其性能通常都优于TreeSet。为疾速查找而设计的Set,咱们通常都应该应用HashSet,在咱们须要排序的性能时,咱们才应用TreeSet。
  • TreeSet 是二叉树(红黑树的树据构造)实现的,Treeset中的数据是主动排好序的,不容许放入null值
  • HashSet是哈希表实现的,HashSet中的数据是无序的,能够放入null,但只能放入一个null,两者中的值都不能反复,就如数据库中惟一束缚。
  • HashSet是基于Hash算法实现的,其性能通常都优于TreeSet。为疾速查找而设计的Set,咱们通常都应该应用HashSet,在咱们须要排序的性能时,咱们才应用TreeSet。

6.5、HashMap与TreeMap、HashTable的区别及实用场景

HashMap 非线程平安,基于哈希表(散列表)实现。应用HashMap要求增加的键类明确定义了hashCode()和equals()[能够重写hashCode()和equals()],为了优化HashMap空间的应用,您能够调优初始容量和负载因子。其中散列表的抵触解决次要分两种,一种是凋谢定址法,另一种是链表法。HashMap的实现中采纳的是链表法。 TreeMap:非线程平安基于红黑树实现,TreeMap没有调优选项,因为该树总处于均衡状态

7、 常量池

7.1、Interger中的128(-128~127)

当数值范畴为-128~127时:如果两个new进去Integer对象,即便值雷同,通过“==”比拟后果为false,但两个对象间接赋值,则通过“==”比拟后果为“true,这一点与String十分类似。 当数值不在-128~127时,无论通过哪种形式,即便两个对象的值相等,通过“==”比拟,其后果为false; 当一个Integer对象间接与一个int根本数据类型通过“==”比拟,其后果与第一点雷同; Integer对象的hash值为数值自身;

@Overridepublic int hashCode() {return Integer.hashCode(value);}

7.2、为什么是-128-127?

在Integer类中有一个动态外部类IntegerCache,在IntegerCache类中有一个Integer数组,用以缓存当数值范畴为-128~127时的Integer对象。

8、泛型

泛型是Java SE 1.5的新个性,泛型的实质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型能够用在类、接口和办法的创立中,别离称为泛型类、泛型接口、泛型办法。 Java语言引入泛型的益处是平安简略。

泛型的益处是在编译的时候查看类型平安,并且所有的强制转换都是主动和隐式的,进步代码的重用率。

它提供了编译期的类型平安,确保你只能把正确类型的对象放入 汇合中,防止了在运行时呈现ClassCastException。

应用Java的泛型时应留神以下几点:

  • 泛型的类型参数只能是类类型(包含自定义类),不能是简略类型。
  • 同一种泛型能够对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的。
  • 泛型的类型参数能够有多个。
  • 泛型的参数类型能够应用extends语句,例如。习惯上称为“有界类型”。
  • 泛型的参数类型还能够是通配符类型。例如Class<?> classType = Class.forName("java.lang.String");

8.1 T泛型和通配符泛型

  • ? 示意不确定的java类型。
  • T 示意java类型。
  • K V 别离代表java键值中的Key Value。
  • E 代表Element。

8.2 泛型擦除

Java中的泛型基本上都是在编译器这个档次来实现的。在生成的Java字节码中是不蕴含泛型中的类型信息的。应用泛型的时候加上的类型参数,会在编译器在编译的时候去掉。这个过程就称为类型擦除。

泛型是通过类型擦除来实现的,编译器在编译时擦除了所有类型相干的信息,所以在运行时不存在任何类型相干的信息。例如 List在运行时仅用一个List来示意。这样做的目标,是确保能和Java 5之前的版本开发二进制类库进行兼容。你无奈在运行时拜访到类型参数,因为编译器曾经把泛型类型转换成了原始类型。

8.3 限定通配符

一种是<? extends T>它通过确保类型必须是T的子类来设定类型的上界, 另一种是<? super T>它通过确保类型必须是T的父类来设定类型的下界。 另一方面表 示了非限定通配符,因为能够用任意类型来代替。 例如List<? extends Number>能够承受List或List。

8.4 泛型面试题

你能够把List传递给一个承受List参数的办法吗?

对任何一个不太熟悉泛型的人来说,这个Java泛型题目看起来令人纳闷,因为乍看起来String是一种Object,所以 List该当能够用在须要List的中央,然而事实并非如此。真这样做的话会导致编译谬误。如 果你再深一步思考,你会发现Java这样做是有意义的,因为List能够存储任何类型的对象包含String, Integer等等,而List却只能用来存储Strings。

Array中能够用泛型吗?

Array事实上并不反对泛型,这也是为什么Joshua Bloch在Effective Java一书中倡议应用List来代替Array,因为List能够提供编译期的类型平安保障,而Array却不能。

9、反射

9.1、概念

JAVA反射机制是在运行状态中,对于任意一个类,都可能晓得这个类的所有属性和办法;对于任意一个对象,都可能调用它的任意一个办法;这种动静获取的信息以及动静调用对象的办法的性能称为java语言的反射机制。

9.2、作用

Java反射机制次要提供了以下性能: 在运行时判断任意一个对象所属的类;在运行时结构任意一个类的对象;在运行时判断任意一个类所具备的成员变量和办法;在运行时调用任意一个对象的办法;生成动静代理。

数据结构与算法

1、排序

排序有外部排序和内部排序,外部排序是数据记录在内存中进行排序,而内部排序是因排序的数据很大,一次不能包容全副的排序记录,在排序过程中须要拜访外存。

1.1、 间接插入排序

思维:

将第一个数和第二个数排序,而后形成一个有序序列 将第三个数插入进去,形成一个新的有序序列。 对第四个数、第五个数……直到最初一个数,反复第二步。 代码:

首先设定插入次数,即循环次数,for(int i=1;i<length;i++),1个数的那次不必插入。 设定插入数和失去曾经排好序列的最初一个数的位数。insertNum和j=i-1。

2、设计模式

参考:Android开发中的一些设计模式

2.1、单例设计模式

单例次要分为:懒汉式单例、饿汉式单例、注销式单例。

特点:

  1. 单例类只有一个实例
  2. 单例类必须本人创立本人的惟一实例
  3. 单例类必须给所有其余对象提供这一实例。

在计算机系统中,像线程池,缓存、日志对象、对话框、打印机等常被设计成单例。

懒汉式单例:

Singleton通过将构造方法限定为private防止了类在内部被实例化,在同一个虚拟机范畴内,Singleton的惟一实例只能通过getInstance()办法拜访。(事实上,通过Java反射机制是可能实例化构造方法为private的类的,那基本上会使所有的Java单例实现生效。

它是线程不平安的,并发状况下很有可能呈现多个Singleton实例,要实现线程平安,有以下三种形式: 1.在getInstance办法上加上同步

2.双重查看锁定

3.动态外部类

这种形式比照前两种,既实现了线程平安,又防止了同步带来的性能影响。

饿汉式单例:

饿汉式在创立类的同时就曾经创立好了一个动态的对象供零碎应用,当前不再扭转,所以天生是系统安全。

相干视频:

面试官的家常菜——Android事件抵触起因与解决方案大解密!_哔哩哔哩_bilibili

金三银四面试高峰期必问MVVM技术之databinding_哔哩哔哩_bilibili

金三银四大厂面试JVM必问技术_哔哩哔哩_bilibili

本文转自 https://juejin.cn/post/6844903891625050119,如有侵权,请分割删除。