Android面试攻略(1)——Android基础

字数 7809阅读 595

最近刚从旧公司离职,为面试在做准备,因为平时开发CV大法用得比较多,很多基础知识掌握得不是很牢靠以及很多工具框架只会用并不知道内部实现。所以正好趁这个机会开个坑,复习下Android相关的基础和一些面试需要掌握的知识点,内容都是基于我在书本及网上查阅的资料整合,笔记性质。

系列文章

 Android面试攻略(1)——Android基础

Android面试攻略(2)——异步消息处理机制

Android面试攻略(3)——View相关

这篇主要涉及Activity、Fragment、Service、BroadcastReceiver、WebView、Binder。

Activity

Android四大组件中,Activity是我们用的最多也是最基本的组件,因为应用的几乎所有操作都与用户相关,Activity 提供窗口来和用户进行交互。


一、Activity的生命周期

Activity生命周期是个老生常谈的问题,既然是从基础开始,那也不能放过。

(1)Activity的四种状态

简单粗暴直接上图

activity状态关系图

Running(运行):在屏幕前台(位于当前任务堆栈的顶部)

Paused(暂停):失去焦点但仍然对用户可见(覆盖Activity可能是透明或未完全遮挡)

Stopped(停止):完全被另一个Activity覆盖

Destroyed(销毁):退出,完全销毁

(2)Activity生命周期分析

关于生命周期的文章网上是一搜一大把,这里贴一下《深入理解Activity的生命周期》这篇文章讲得很详细透彻。

场景分析:

Activity启动 →onCreate()→onStart()→onResume()

点击Home键回到主界面(Activity不可见)→onPause()→onStop()

当我们再次回到原Activity时→onRestart()→onStart()→onResume()

退出当前Activity时→onPasue()→onStop()→onDestory()

(3)Activity进程优先级

1.Foreground processes 前台进程

    a. 进程中包含处于前台的正与用户交互的activity;

    b. 进程中包含与前台activity绑定的service;

    c. 进程中包含调用了startForground()方法的service;

    d. 进程中包含正在执行onCreate(), onStart(), 或onDestroy()方法的service;

    e. 进程中包含正在执行onReceive()方法的BroadcastReceiver.

2.Visible processes 可视进程

    a. 进程中包含未处于前台但仍然可见的activity(调用了activity的onPause()方法, 但没有调用onStop()方法). 典型的情况是:运行activity时弹出对话框(类似对话框,将activity遮挡), 此时的activity虽然不是前台activity, 但其仍然可见.

    b. 进程中包含与可见activity绑定的service.可视进程不会被系统杀死, 除非为了保证前台进程的运行而不得已为之.

3.Service processes 服务进程

正在运行的Service(不在create(),start(),destory()状态中)

4.background processes 后台进程

如:不可见状态的activity

5.Empty processes 空进程

不包含任何处于活动状态的进程是一个空进程. 系统经常杀死空进程, 这不会造成任何影响. 空进程存在的唯一理由是为了缓存一些启动数据, 以便下次可以更快的启动.

补充:Android的进程的销毁不需要人工干预,由系统控制


二、Activity的任务栈

Android中的activity全都归属于task管理 。task 是多个 activity 的集合,这些 activity 按照启动顺序排队存入一个栈(即“back stack”)。android默认会为每个App维持一个task来存放该app的所有activity,task的默认name为该app的packagename。

当然我们也可以在AndroidMainfest.xml中申明activity的taskAffinity属性来自定义task,但不建议使用,如果其他app也申明相同的task,它就有可能启动到你的activity,带来各种安全问题(比如拿到你的Intent)。

我们知道,当我们退出一个应用程序的时候,必须把所有Activity清除出栈,这时候你才能安全的退出程序,任务栈被销毁了,数据才是处于最安全的状态。当然,如果你不想销毁任务栈,那你要合理的去保存这个任务栈,这时候这个任务栈会保存有所有Activity的状态,以及Activity包含的信息 。任务栈并不是唯一的,一个APP当中可能不止一个任务栈,但是,在某些情况下,一个Activity可以独享一个任务栈,这就是我们接下来会说到的启动模式中的singleInstance。


三、Activity的启动模式

首先,要知道为什么Android要给我们提供Activity的启动模式。我们在做app开发时,不可避免的需要在不同的页面之间进行跳转,也就是Activity之间的跳转,所以说我们肯定需要复用某些Activity。如果我们跳转到某个以前的Activity的时候,我们希望这个Activity是复用的,而不是被重新创建的,这样可以避免资源的浪费。所以Android为了应对开发时对Activity跳转的不同需求,提供了4种启动模式。

standard (默认模式)

当通过这种模式来启动Activity时, Android总会为目标 Activity创建一个新的实例,并将该Activity添加到当前Task栈中。这种方式不会启动新的Task,只是将新的 Activity添加到原有的Task中。

singleTop

该模式和standard模式基本一致,但有一点不同:当将要被启动的Activity已经位于Task栈顶时,系统不会重新创建目标Activity实例,而是直接复用Task栈顶的Activity。

singleTask

Activity在同一个Task内只有一个实例。如果将要启动的Activity不存在,那么系统将会创建该实例,并将其加入Task栈顶;

如果将要启动的Activity已存在,且存在栈顶,直接复用Task栈顶的Activity。

如果Activity存在但是没有位于栈顶,那么此时系统会把位于该Activity上面的所有其他Activity全部移出Task,从而使得该目标Activity位于栈顶,此时会回调onNewIntent()方法。

singleInstance

无论从哪个Task中启动目标Activity,只会创建一个目标Activity实例且会用一个全新的Task栈来装载该Activity实例(全局单例).

如果将要启动的Activity不存在,那么系统将会先创建一个全新的Task,再创建目标Activity实例并将该Activity实例放入此全新的Task中。

如果将要启动的Activity已存在,那么无论它位于哪个应用程序,哪个Task中;系统都会把该Activity所在的Task转到前台,从而使该Activity显示出来。


四、scheme跳转协议

android中的scheme是一种页面内跳转协议,是一种非常好的实现机制。通过定义自己的scheme,可以非常方便的跳转app中的各个页面。通过scheme协议,服务器可以定制化告诉app跳转到那个页面,可以通过通知栏消息定制化跳转页面,可以通过H5页面跳转页面等。



Fragment

Fragment是Activity中用户界面的一个行为或者是一部分。主要是支持在大屏幕上动态和更为灵活的去组合或是交换UI组件,通过将activity的布局分割成若干个fragment,可以在运行时编辑activity的呈现,并且那些变化会被保存在由activity管理的后台栈里面。

Fragment必须总是被嵌入到一个activity之中,并且fragment的生命周期直接受其宿主activity的生命周期的影响。你可以认为fragment是activity的一个模块零件,它有自己的生命周期,接收它自己的输入事件,并且可以在activity运行时添加或者删除。

应该将每一个fragment设计为模块化的和可复用化的activity组件。也就是说,你可以在多个activity中引用同一个fragment,因为fragment定义了它自己的布局,并且使用它本身生命周期回调的行为。


一、Fragment加载中到Activity中的两种方式

(1)静态添加:将fragment写到Activity的布局文件中

(2)动态添加:动态在Activity中通过代码添加fragment

网络截图


二、FragmentStatePagerAdapter与FragmentPagerAdapter的区别

首先上结论:FragmentStatePagerAdapter相比于FragmentPagerAdapter,更节省资源。若与ViewPager配合使用,在页面比较多的情况下,适合使用FragmentStatePagerAdapter,而如果页面较少,则适合使用FragmentPagerAdapter。

why?先来看一段源码

FragmentStatePagerAdapter源码
FragmentPagerAdapter源码

第一张图是FragmentStatePagerAdapter中的destroyItem方法,方法里最后对fragment执行了remove操作,也就是将fragment所占用的资源释放掉了,而第二张FragmentPagerAdapter中的destroyItem方法里,最后只是将fragment与activity进行UI分离,并没有销毁回收资源,所以在页面较少的情况下,使用FragmentPagerAdapter可以保证资源的重复利用,当页面较多时,使用FragmentStatePagerAdapter保证系统资源的及时回收,以免造成内存溢出。


三、Fragment的生命周期

还是先上图

图片来自网络

再来看看fragment和activity两者之间的生命周期关系图

图片来自网络

fragment所生存的activity生命周期直接影响着fragment的生命周期,由此针对activity的每一个生命周期回调都会引发一个fragment类似的回调。例如,当activity接收到onPause()时,这个activity之中的每个fragment都会接收到onPause()。

fragment有一些额外的生命周期回调方法(创建和销毁fragment界面).

onAttach()

当fragment被绑定到activity时调用(Activity会被传入)。

onCreateView()

将本身的布局构建到activity中去(fragment作为activity界面的一部分)

onActivityCreated()

当activity的onCreate()函数返回时被调用。

onDestroyView()

当与fragment关联的视图体系正被移除时被调用。

onDetach()

当fragment正与activity解除关联时被调用。

当activity接收到它的onCreate()回调时,activity之中的fragment接收到onActivityCreated()回调。

一旦activity处于resumed状态,则可以在activity中自由的添加或者移除fragment。因此,只有当activity处于resumed状态时,fragment的生命周期才可以独立变化。    fragment会在 activity离开恢复状态时 再一次被activity推入它的生命周期中。

管理fragment生命周期与管理activity生命周期很相像。像activity一样,fragment也有三种状态:

Resumed

fragment在运行中的activity可见。

Paused

另一个activity处于前台且得到焦点,但是这个fragment所在的activity仍然可见(前台activity部分透明,或者没有覆盖全屏)。

Stopped

fragment不可见。要么宿主activity已经停止,要么fragment已经从activity上移除,但已被添加到后台栈中。一个停止的fragment仍然活着(所有状态和成员信息仍然由系统保留着)。但是,它对用户来讲已经不再可见,并且如果activity被杀掉,它也将被杀掉。

如果activity的进程被杀掉了,在activity被重新创建时,你需要恢复fragment状态。可以执行fragment的onSaveInstanceState()来保存状态(注意在fragment是在onCreate(),onCreateView(),或onActvityCreate()中进行恢复)。

在生命周期方面,activity与fragment之间一个很重要的不同,就是在各自的后台栈中是如何存储的。当activity停止时,默认情况下activity被安置在由系统管理的activity后台栈中;    fragment仅当在一个事务被移除时,通过显式调用addToBackStack()请求保存的实例,该fragment才被置于由宿主activity管理的后台栈。


四、Fragment通信

1.在Fragment中调用Activity中的方法:getActivity()。

2.在Activity中调用Fragment中的方法:接口回调。

3.在Fragment中调用另一个Fragment中的方法:findFragmentById();



Service

一、什么是Service

Service是一个应用程序组件,它能够在后台执行一些耗时较长的操作,并且不提供用户界面。服务能被其它应用程序的组件启动,即使用户切换到另外的应用时还能保持后台运行。此外,应用程序组件还能与服务绑定,并与服务进行交互,甚至能进行进程间通信(IPC)。 比如,服务可以处理网络传输、音乐播放、执行文件I/O、或者与content provider进行交互,所有这些都是后台进行的。


二、Service 与 Thread 的区别

服务仅仅是一个组件,即使用户不再与你的应用程序发生交互,它仍然能在后台运行。因此,应该只在需要时才创建一个服务。

如果你需要在主线程之外执行一些工作,但仅当用户与你的应用程序交互时才会用到,那你应该创建一个新的线程而不是创建服务。 比如,如果你需要播放一些音乐,但只是当你的activity在运行时才需要播放,你可以在onCreate()中创建一个线程,在onStart()中开始运行,然后在onStop()中终止运行。还可以考虑使用AsyncTask或HandlerThread来取代传统的Thread类。

由于无法在不同的 Activity 中对同一 Thread 进行控制,这个时候就要考虑用服务实现。如果你使用了服务,它默认就运行于应用程序的主线程中。因此,如果服务执行密集计算或者阻塞操作,你仍然应该在服务中创建一个新的线程来完成(避免ANR)。


三、Service的生命周期

图片来自网络

onCreate():服务正在被创建。

onStartCommand():服务正在启动,由startService()调用触发,该方法必须返回一个整数。这个整数是描述系统在杀死服务之后应该如何继续运行。onStartCommand()的返回值必须是以下常量之一:

START_NOT_STICKY:如果系统在onStartCommand()返回后杀死了服务,则不会重建服务了,除非还存在未发送的intent。 当服务不再是必需的,并且应用程序能够简单地重启那些未完成的工作时,这是避免服务运行的最安全的选项。

START_STICKY 如果系统在onStartCommand()返回后杀死了服务,则将重建服务并调用onStartCommand(),但不会再次送入上一个intent, 而是用null intent来调用onStartCommand() 。除非还有启动服务的intent未发送完,那么这些剩下的intent会继续发送。 这适用于媒体播放器(或类似服务),它们不执行命令,但需要一直运行并随时待命。

START_REDELIVER_INTENT:如果系统在onStartCommand()返回后杀死了服务,则将重建服务并用上一个已送过的intent调用onStartCommand()。任何未发送完的intent也都会依次送入。这适用于那些需要立即恢复工作的活跃服务,比如下载文件。

onBind():客户端用bindService()绑定服务。

onUnbind():所有的客户端都用unbindService()解除了绑定

onRebind():某客户端正用bindService()绑定到服务,而onUnbind()已经被调用过了

onDestroy():服务被销毁。

服务的生命周期与activity的非常类似。不过,更重要的是你需密切关注服务的创建和销毁环节,因为后台运行的服务是不会引起用户注意的。

服务的生命周期——从创建到销毁——可以有两种路径:

一个started服务

这类服务由其它组件调用startService()来创建。然后保持运行,且必须通过调用stopSelf()自行终止。其它组件也可通过调用stopService() 终止这类服务。服务终止后,系统会把它销毁。

如果一个Service被startService 方法多次启动,那么onCreate方法只会调用一次,onStart将会被调用多次(对应调用startService的次数),并且系统只会创建Service的一个实例(因此你应该知道只需要一次stopService调用)。该Service将会一直在后台运行,而不管对应程序的Activity是否在运行,直到被调用stopService,或自身的stopSelf方法。当然如果系统资源不足,android系统也可能结束服务。

一个bound服务

服务由其它组件(客户端)调用bindService()来创建。然后客户端通过一个IBinder接口与服务进行通信。客户端可以通过调用unbindService()来关闭联接。多个客户端可以绑定到同一个服务上,当所有的客户端都解除绑定后,系统会销毁服务。(服务不需要自行终止。)

如果一个Service被某个Activity 调用 Context.bindService 方法绑定启动,不管调用 bindService 调用几次,onCreate方法都只会调用一次,同时onStart方法始终不会被调用。当连接建立之后,Service将会一直运行,除非调用Context.unbindService 断开连接或者之前调用bindService 的 Context 不存在了(如Activity被finish的时候),系统将会自动停止Service,对应onDestroy将被调用。

这两条路径并不是完全隔离的。也就是说,你可以绑定到一个已经用startService()启动的服务上。例如,一个后台音乐服务可以通过调用startService()来启动,传入一个指明所需播放音乐的 Intent。 之后,用户也许需要用播放器进行一些控制,或者需要查看当前歌曲的信息,这时一个activity可以通过调用bindService()与此服务绑定。在类似这种情况下,stopService()或stopSelf()不会真的终止服务,除非所有的客户端都解除了绑定。

当在旋转手机屏幕的时候,当手机屏幕在“横”“竖”变换时,此时如果你的 Activity 如果会自动旋转的话,旋转其实是 Activity 的重新创建,因此旋转之前的使用 bindService 建立的连接便会断开(Context 不存在了)。


四、使用Service

(1)startService

1.定义一个类继承Service。

2.在AndroidMainfest.xml文件中声明该Service。

<mainfest ...>

...

<application...>

<service android:name=".服务类名">

</application>

</mainfest>

3.使用Context中的startService(Intent)方法启动该Service。

4.不再使用时,使用stopService(Intent)方法停止该Service。

(2)bindService

1.创建bindService服务端,继承自Service并在类中创建一个实现IBinder接口的实例对象并提供公共方法给客户端调用。

public class LocalBinder extends Binder  {

        // 声明一个方法,getService() 提供给客户端调用

        BindService getService() {

            //返回当前对象LocalService,这样我们就可以在客户端调用Service的公共方法了

            return BindService.this;

      }

}

2.从onBind()方法返回此IBinder实例。

/**

 *  把Binder类返回给客户端

**/

@Override                                                                                                                   public IBinder onBind (Intent intent) {

        return new LocalBinder();

}

3.在客户端中,从onServiceConnected()回调方法接收Binder,并使用提供的方法调用绑定服务。

ServiceConnection conn = new ServiceConnection() {

        // 与服务端交互的接口方法,绑定服务的时候被回调,在这个方法获取绑定Service           // 传递过来的IBinder对象,通过这个对象,实现宿主和Service的交互

        @Override                                                                                                                           public void onServiceConnected (ComponentName name, IBinder service) {

                 //获取IBinder                                                                                                                        BindService.LocalBinder binder = (BindService.LocalBinder) service;

                 mService = binder.getService();

    }

        // 当取消绑定的时候被回调,但正常情况下是不被调用的,它的调用实际是当                   // Service服务被意外销毁时,例如内存的资源不足时这个方法才被自动调用。

        @Override                                                                                                                           public void onServiceDisconnected(ComponentName name) {

                mService = null;

    }

}

ServiceConnection代表与服务的连接,它只有两个方法:onServiceConnected()和onServiceDisconnected(),前者是在操作者连接一个服务成功时回调,而后者是在服务崩溃或被杀死导致服务连接中断时回调。

4.调用bindService(Intent intent,ServiceConnection conn,int flags)方法绑定启动服务。

Intent intent = new Intent(this,BindService.class);

bindService(intent,conn,Service.BIND_AUTO_CREATE);

其中第三个参数在进行服务绑定的时,其flags有:

Context.BIND_AUTO_CREATE

表示收到绑定请求的时候,如果服务尚未创建,则即刻创建,在系统内存不足需要先摧毁优先级组件来释放内存,且只有驻留该服务的进程成为被摧毁对象时,服务才被摧毁

Context.BIND_DEBUG_UNBIND

通常用于调试场景中判断绑定的服务是否正确,但容易引起内存泄漏,因此非调试目的的时候不建议使用

Context.BIND_NOT_FOREGROUND

表示系统将阻止驻留该服务的进程具有前台优先级,仅在后台运行。

5.不使用时,解绑服务。

unbindService(conn);


Broadcast Receiver

广播是一种广泛运用的在应用程序之间传输信息的机制,主要用来监听系统或者应用发出的广播信息,然后根据广播信息作为相应的逻辑处理,也可以用来传输少量、频率低的数据。Android中我们要发送的广播内容是一个Intent,这个Intent中可以携带我们要传送的数据。

一、广播的使用场景

1.同一APP具有多个进程的不同组件之间的消息通信。

2.不同APP之间的组件组件之间消息通信。


二、广播的种类

1.Normal Broadcast(普通广播):应用在需要通知各个广播接收者的情况下使用,如 开机启动。使用方式:sendBroadcast()。

2.System Broadcast(有序广播):应用在需要特定拦截场景下使用,如黑名单短信、电话拦截。使用方式:sendOrderedBroadcast();

3.Local Broadcast(本地广播):只在自身APP内传播。


三、注册Broadcast Receiver

定义自己的BroadcastReceiver 类

public class MyBroadcastReceiver extends BroadcastReceiver {

    // action 名称

    String SMS_RECEIVED = "android.provider.Telephony.SMS_RECEIVED" ;

    public void onReceive(Context context, Intent intent) {

        if (intent.getAction().equals( SMS_RECEIVED )) {

            // 一个receiver可以接收多个action的,即可以有多个intent-filter,需要在                           //onReceive里面对intent.getAction(action name)进行判断。

        }

    }

}

1.静态注册

静态注册的广播接收者是一个常驻在系统中的全局监听器,当你在应用中配置了一个静态的BroadcastReceiver,安装了应用后而无论应用是否处于运行状态,广播接收者都是已经常驻在系统中了。同时应用里的所有receiver都在清单文件里面,方便查看。要销毁掉静态注册的广播接收者,可以通过调用PackageManager将Receiver禁用。

在AndroidManifest.xml的application里面定义receiver并设置要接收的action。

< receiver android:name = ".MyBroadcastReceiver" >

< intent-filter android:priority = "777" >

< action android:name = "android.provider.Telephony.SMS_RECEIVED" />

< /intent-filter >

</ receiver>

这里的priority取值是-1000到1000,值越大优先级越高,同时注意加上系统接收短信的限权。

< uses-permission android:name ="android.permission.RECEIVE_SMS" />

2.动态注册

在Activity中声明BroadcastReceiver的扩展对象,在onResume中注册,onPause中卸载。动态注册的广播跟随activity的生命周期。

public class MainActivity extends Activity {

MyBroadcastReceiver receiver;

@Override

protected void onResume() {

// 动态注册广播 (代码执行到这才会开始监听广播消息,并对广播消息作为相应的处理)

receiver = new MyBroadcastReceiver();

IntentFilter intentFilter = new IntentFilter( "android.provider.Telephony.SMS_RECEIVED" );

registerReceiver( receiver , intentFilter);

super.onResume();

}

@Override

protected void onPause() {

// 撤销注册 (撤销注册后广播接收者将不会再监听系统的广播消息)

unregisterReceiver(receiver);

super.onPause();

}

}

3.静态注册和动态注册的区别

1、静态注册的广播接收者一经安装就常驻在系统之中,不需要重新启动唤醒接收者;动态注册的广播接收者随着应用的生命周期,由registerReceiver开始监听,由unregisterReceiver撤销监听,如果应用退出后,没有撤销已经注册的接收者应用应用将会报错。

2、当广播接收者通过intent启动一个activity或者service时,如果intent中无法匹配到相应的组件。动态注册的广播接收者将会导致应用报错,而静态注册的广播接收者将不会有任何报错,因为自从应用安装完成后,广播接收者跟应用已经脱离了关系。


四、广播的内部实现机制

1.自定义广播接收者BroadcastReceiver,并复写onReceive()方法。

2.通过Binder机制向AMS(Activity Manager Service)进行注册。

3.广播发送者通过Binder机制向AMS发送广播。

4.AMS查找符合相应条件(IntentFilter/Permission)的BroadcastReceiver,将广播发送到BroadcastReceiver(一般情况下是Activity)相应的消息循环队列中。

5.消息循环执行拿到此广播,回调BroadcastReceiver中的onReceive()方法。


五、LocalBroadcastManager

优势

1.使用它发送的广播将只在APP内中传播,因此你不必担心泄漏隐私数据。

2.其他APP无法对你的APP发送此广播,因为你的APP根本就不可能接收到非自身应用发送的该广播,因此你不必担心有安全漏洞可以利用。

3.比系统的全局广播更加高效。

原理

1.LocalBroadcastManager高效的原因主要是它内部是通过Handler实现的,它的sendBroadcast()方法含义并非和我们平时用的一样,因为它的sendBroadcast()其实是通过handler发送一个Massage实现的。

2.既然他的内部是通过Handler来实现广播的发送的,那么相比于系统广播通过Binder实现肯定是更高效了,同时使用Handler实现,别的应用无法向我们的应用发送该广播,而我们应用内发送的广播也不会离开我们的应用。


WebView

一、WebView开发中常见的一些坑

1. webview 在Androidapi16以及之前版本的安全漏洞,该漏洞是因为程序没有正确的限制webview.addjavascriptinterface方法,让远程攻击者可以使用Java的反射机制利用该漏洞执行任意的java对象方法。

2. webview动态添加到其他布局的时候,在activity销毁的生命周期时,需要主动调用webview.removeallviews和webview的ondestory方法释放内存,否则会导致内存泄漏。

3. jsbridge ,这应该不算是坑,只是一个概念吧。js桥可以允许远程网页端与android的native端进行通信,通俗的说就是使用js桥可以在android代码中调用网页的js方法,也可以让js调用原生的代码

4. webview.onpagefinished方法,该方法并不靠谱,按照api上面的说法,在web页面完全加载完成的时候会回调该方法,但在实际应用过程中,该方法在跳转url的时候会被多次调用,更加靠谱的方法是使用onprogresschange方法代替该方法的功能,当newProgress为100的时候,即是页面加载完成。

5. 后台耗电问题,webview加载网页的时候,会自动创建线程,如果如果使用不当,这些线程会永远在后台运行,导致你的应用耗电量居高不下,这个问题的解决方式是在activity的ondetory方法中销毁webview或者简单粗暴的调用System.exit()直接关闭虚拟机。

6. webview硬件加速导致渲染问题,比如加载的时候会有闪屏现象,解决方式就是暂时关闭硬件加速。


二、关于WebView的内存泄漏的问题

为什么WebView会造成内存泄漏呢?根本原因是由于我们的WebView需要关联一个Activity,而WebView内部执行的操作是在一个新的线程中的,它的执行时间我们Activity并不能确定,所以会导致WebView可能会一直持有该Activity的引用,导致无法回收。其原理类似于匿名内部类持有外部类的引用导致外部类无法释放的问题。所以,为了避免内存泄漏问题,我们一般都两个做法:

1.独立进程。开启单独的一个进程给WebView使用,在Activity销毁的同时将进程也销毁。不过可能涉及到进程间通信。

2.动态添加WebView,对传入的WebView中的Context使用弱引用。动态添加WebView的意思是在布局创建个ViewGroup用来放置WebView,Activity创建的时候add进来,在Activity停止的时候remove掉。


Binder

这部分涉及的知识有点多且杂,但却不是所有都是需要我们掌握的,所以我强烈推荐包建强老师的《写给Android App开发人员看的Android底层知识》系列文章。这个系列很全面的总结了与binder相关的各种知识,原理,通俗易懂,没有大篇幅的代码和对我们暂时无意义的深度知识,值得一看。作为应付面试,我这只做简单总结。

什么是Binder

1.通常意义下,Binder指的是一种跨进程的通信机制。

2.对于Server进程来说,Binder指的是Binder本地对象,而对于Client进程来说,Binder指的是Binder的代理对象。

3.对于传输过程而言,Binder是可以进行跨进程传递的对象。


参考资料

全面了解Activity

Service全面总结

BroadcastReceiver使用总结

推荐阅读更多精彩内容