Android面试知识点总结(2)——安卓基础知识篇

来源:互联网 发布:农商银行柜员工资知乎 编辑:程序博客网 时间:2024/04/30 03:15

在准备安卓的实习面试,自感自己的安卓基础知识掌握的很不好。偶然在微信上看到一个安卓面试知识点总结的文章,虽然只有问题没有答案,但是还是觉得很受用,于是花点时间把所有的问题都回答出来,然后贴在这里为大家服务。


文章主要分为三个篇章,分别是JAVA基础、Android基础和计算机网络基础。本篇文章是解答关于所有Android基础知识的问题:

1、Activity的生命周期和启动模式。

答:Activity的生命周期是个非常基本的问题,首先需要的就是底下的这幅图:


Acivity实际运行起来,需要经过onCreate()、onStart()、onResume()三个阶段,而彻底的被销毁需要onPause()、onStop()、onDestroy()三个阶段。


按键对生命周期的影响:

1)当我们使用BACK键回退到桌面时,我们的应用程序将结束,这个时候调用的方法就是依次是onPause()、onStop()、onDestroy()。如果我们再次启动APP,则会调用onCreate()、onStart()、onResume()三个函数。

2)当我们使用HOME键回退到桌面时,被调用的方法是onPause()、onStop()。再次启动这个APP,调用顺序就是onRestart()、onStart()、onResume()


一般Activity切换的生命周期:

1)ActivityA启动ActivityB:
ActivityA 的生命周期是onPause()->onStop(),ActivityB的生命周期onCreate()->onStart()->onResume()。
2)ActivityB执行finish返回ActivityA:
ActivityB的生命周期onPause()->onStop()->onDestory(),ActivityA的生命周期了onRestart()->onStart()->onResume()

3)如果ActivityB只是Dialog,ActivityA没有被完全遮挡:

ActivityA只调用onPause(),ActivityB不变。

4)ActivityA调用简单的Dialog,生命周期不变化


关于Activity的启动模式,首先我们要知道,当应用运行起来后就会开启一条线程,线程中会运行一个任务栈,当Activity实例创建后就会放入任务栈中。任务栈的堆叠顺序,和用户看到的activity的顺序是一样的。

1)Standard模式(默认):
standard模式是所启动的Activity都是在同一个task容器栈下,不会重新创建新的task容器栈。按物理返回键,退出当前所处活动状态Activity窗口,这样就会从task容器栈中弹出,显示在手机主屏幕上,从而,有非活动状态转换成活动的状态。其次,standard容器栈可能会存在着相同的Activity实例,只有没调用一次startActivity方法,就会创建目标Activity实例对象压入task容器栈。

2.)SingleTop模式
这种模式会考虑当前要激活的Activity实例在任务栈中是否正处于栈顶,如果处于栈顶则无需重新创建新的实例,会重用已存在的实例,否则会在任务栈中创建新的实例。
SingleTop有个不错的用法是防止多次点击创建多个Activity,无论start几次,SingleTop模式能保证栈顶只有一个实例。 如果Activity启动顺序为A->B->B->A->D,栈中的Acitivy为ABAD(当B位于栈顶时,再次启动B的时候,B不会重新创建)
3)SingleTask模式
如果任务栈中存在该模式的Activity实例,则把栈中该实例以上的Activity实例全部移除,调用该实例的newInstance()方法重用该Activity,使该实例处於栈顶位置,否则就重新创建一个新的Activity实例。
singletask模式,特别需要注意了。启动的目标Activity实例如果已经存在task容器栈中,不管当前实例处于栈的任何位置,只要目标Activity实例处于task容器栈中,都可以重用该Activity实例对象,然后,把处于该Activity实例对象上面全部Activity实例清除掉,并且,task容器栈中永远只有唯一实例对象,不会存在两个相同的实例对象。 如果Activity启动顺序为A->B->B->A->D,栈中的Acitivy为AD(当A再次被启动时,A会被移到栈顶,位于A上面的Acitivity全部会出栈)

4)SingleInstance模式
当该模式Activity实例在任务栈中创建后,只要该实例还在任务栈中,即只要激活的是该类型的Activity,都会通过调用实例的newInstance()方法重用该Activity,此时使用的都是同一个Activity实例,它都会处于任务栈的栈顶。此模式一般用于加载较慢的,比较耗性能且不需要每次都重新创建的Activity。
5)特别需要注意的生命周期onNewIntent
当一个Activity被start,而不需要重新创建时,就会执行onNewIntent生命周期。如果一个Activity的启动模式是SingleTask,我们可以在onNewIntent中执行一些刷新操作等。我们一般会把MainActivity设置为SingleTask,除了保证MainActivity的唯一,还可以利用singleTask的特性做一些清理工作。自动管理栈,销毁无用的Acitivity。

(本题内容大部分转载自)


2、Service的生命周期和两种启动方式。

答:服务可以在后台执行长时间运行操作而不提供用户界面的应用组件。服务可由其他应用组件启动,而且即使用户切换到其他应用,服务仍将在后台继续运行。 此外,组件可以绑定到服务,以与之进行交互,甚至是执行进程间通信 (IPC)。 例如,服务可以处理网络事务、播放音乐,执行文件 I/O 或与内容提供程序交互,而所有这一切均可在后台进行。

服务的两种形式及各自的生命周期:
1)启动
当应用组件(如 Activity)通过调用 startService() 启动服务时,服务即处于“启动”状态。一旦启动,服务即可在后台无限期运行,即使启动服务的组件已被销毁也不受影响。 已启动的服务通常是执行单一操作,而且不会将结果返回给调用方。例如,它可能通过网络下载或上传文件。 操作完成后,服务会自行停止运行。
2)绑定
当应用组件通过调用 bindService() 绑定到服务时,服务即处于“绑定”状态。绑定服务提供了一个客户端-服务器接口,允许组件与服务进行交互、发送请求、获取结果,甚至是利用进程间通信 (IPC) 跨进程执行这些操作。 仅当与另一个应用组件绑定时,绑定服务才会运行。 多个组件可以同时绑定到该服务,但全部取消绑定后(且没有通过 onStartCommand()),该服务即会被销毁。


服务的四种对应回调方法:
1)onStartCommand()
当另一个组件(如 Activity)通过调用 startService() 请求启动服务时,系统将调用此方法。一旦执行此方法,服务即会启动并可在后台无限期运行。 如果您实现此方法,则在服务工作完成后,需要由您通过调用 stopSelf() 或 stopService() 来停止服务。(如果您只想提供绑定,则无需实现此方法。)
2)onBind()
当另一个组件想通过调用 bindService() 与服务绑定时,系统将调用此方法。在此方法的实现中,您必须通过返回 IBinder 提供一个接口,供客户端用来与服务进行通信。
3)onCreate()
首次创建服务时,系统将调用此方法来执行一次性设置程序(在调用 onStartCommand() 或 onBind() 之前)。如果服务已在运行,则不会调用此方法。
4)onDestroy()
当服务不再使用且将被销毁时,系统将调用此方法。服务应该实现此方法来清理所有资源,如线程、注册的侦听器、接收器等。 这是服务接收的最后一个调用。

(本题内容转自安卓开发者手册)


3、Fragement的生命周期和使用场景。

答:Fragement之前我在项目中接触过,主要就是使用Fragment+FragmentAdapter+ViewPager的组合达到微信等应用里面滑动的效果。


Fragement的生命周期:

1)当一个fragment被创建的时候,它会经历以下状态:onAttach()(当被加入到Activity)、onCreate()、onCreateView()(Activity的onCreate函数返回时调用)、onActivityCreated()
2)当这个fragment对用户可见的时候,它会经历以下状态:onStart()、onResume()
3)当这个fragment进入“后台模式”的时候,它会经历以下状态:onPause()、onStop()
4)当这个fragment被销毁了(或者持有它的activity被销毁了),它会经历以下状态:onPause()、onStop()、onDestroyView()、onDestroy() 、onDetach()


Fragment容易出现问题,就是在当我们从Activity传递回的数据,调用onActivityResult返回结果时。如何很好的将数据从宿主Activity传送到Fragment中去。之前我的解决办法就是重写接口,直接传送。今天找资料的时候才知道,原来 Support Library(V4)会修改了requestCode,使其中包含了一个Fragment 16位的索引值。这个索引值是与FragmentManager相关联的,Activity会根据这个索引值在自身的FragmentManager里面搜索Fragment来分发onActivityResult。不过这样会带来一个问题,如果我使用的是嵌套式的Fragment,那么我怎么定位到子Fragment中去呢?所以讲道理,还是必须要重写函数,手动将onActivityResult从宿主的Fragment传递给子Fragment。

不过最恐怖的是,使用系统自带的Fragment不会出现这种问题。


4、BroadCastReceiver的两种注册方法。

答:BroadCastReceiver的内容我在课程上学过专题,算是比较清晰的认识这个问题。一般BroadCastReceiver的程序规则都是,系统唤醒负责接受事件的Receiver,然后处理事件。发送广播主要有两个主要步骤:1)发送广播:把需要发送的信息等,装入Intent里,然后调用Context.sendBroadcast() 发送Intent。2)当Intent发送以后,所有已经注册的BroadcastReceiver会检查注册时的IntentFilter是否与发送的Intent相匹配,若匹配则就会调用BroadcastReceiver的onReceive()方法。


onReceive方法必须在10秒内执行完毕退出,否则会导致FC(Force Close强制关闭)。限制使得我们的BroadcastReceiver只会是用来更新content、启动service、更新UI或者通过notification manager在状态栏中提示。不过,值得注意的是,不要使用新的线程,因为BroadCastReceiver的生命周期很短,可能出现子线程还没有结束,BroadCastReceiver就已经退出了。如果当BroadCastReceiver所在的进程结束,虽然该进程中可能有用户启动的新线程,但是由于该进程内没有活动的组件,系统会在内存紧张的时候,优先结束掉该进程,这就会导致BroadCastReceiver启动的子线程不能执行完。


BroadcastReceiver的注册分为两种,一种是静态注册,一种是动态注册。静态注册在AndroidManifest.xml中注册,只要程序被安装,BroadcastReceiver就处于活动状态。静态注册一般用于对系统的某个事件做出响应。而动态注册,只需要Activity在代码中调用方法注册即可。


5、ContentProvider的基本使用方法和作用。

答:ContentProvider主要用于程序应用间的通讯。许多Android系统的内置数据通过ContentProvider提供给用户使用,例如通讯录、音视频文件和图像文件等。ContentProvider只是封闭了一组接口给开发者,实际上ContentProvider的物理实现可以不同的,比如数据库存储数据、文件系统、SharedPreferences等。


ContentProvider的数据模式类似于数据库的数据表,每行是一条记录,每列具有相同的数据类型,每条记录都包含一个长型的字段_ID,唯一标识该记录。调用者不能够直接调用ContentProvider的接口函数需要使用ContentResolver对象,通过URI间接调用ContentProvider。URI的大致格式是:content://<authority>/<data_path>/<id>。其中如果请求多个数据,<id>是可以省略的。


ContentProvider的创建:

程序开发人员通过继承ContentProvider类可以创建一个新的数据提供者,过程可以分为三步:
第1步:继承ContentProvider,并重载六个函数(初始化、增删改查、返回MIME结果)
第2步:声明CONTENT_URI,实现UriMatcher
第3步:注册ContentProvider(在AndroidManifest.xml文件中注册)


6、SharedPreference三种获得方法和区别,commit和apply的区别。

答:SharedPreference一般保存轻量级的数据,通常是一些属性设置和简单的参数设置。SharedPreference中是通过键值对的形式保存数据,保存在Android的文件系统中(XML格式)。


主要支持三种数据访问模式(读写权限)
1)私有 (MODE_PRIVATE):仅创建程序可读写。
2)全局读 (MODE_WORLD_READABLE): 创建程序可读写,其他程
序可读不可写。
3)全局写 (MODE_WORLD_WRITEABLE):创建程序可读写,其他程
序都可写不可读。


注意:根据官网文档,后两种全局模式在 API 17 之后已被弃用,强烈不推荐使用 SharedPreferences 来实现数据共享。开发者应该选择 ContentProvider, 广播和服务等数据共享方式。至于安全漏洞的具体原因,我找了一篇阿里云的文章,算是比较专业的安全解析了。(传送门)其主要的思想就是,通过伪造“android:sharedUserId”属性值和签名key,达到访问同一份数据的目的。


SharedPreference的两种提交方式:

Editor提供了两个提交的修改的方法:apply和commit。

相同点:二者都是原子过程。
区别:
1.apply没有返回值而commit返回boolean表明修改是否提交成功
2.apply是将修改数据原子提交到内存,而后异步真正提交到硬件磁盘;而commit是同步的提交到硬件磁盘,因此,在多个并发的提交commit的时候,他们会等待正在处理的commit保存到磁盘后在操作,从而降低了效率。而apply只是原子的提交到内容,后面有调用apply的函数的将会直接覆盖前面的内存数据,这样从一定程度上提高了很多效率。

综合上述,由于在一个进程中,sharedPreference是单实例,一般不会出现并发冲突,如果对提交的结果不关心的话,建议使用apply,当然需要确保提交成功且有后续操作的话,还是需要用commit的。


7、SQLite数据库的基本操作API。

答:SQLite是一个轻量级的软件库,特点有原子性、坚固性、独立性、耐久性、体积大小只用几千字节、一些SQL的指令只是部分支持(例如:ALTER TABLE指令受限)


SQLite的数据库使用步骤:

1)创建数据库;2)配置数据库属性;3)使用数据库;4)关闭数据库


8、Android基本网络库OkHttp的使用方法和优缺点。

答:我之前在链接网络的时候,都是用的HttpURLConnection。不过除了这个,还有一个叫HttpClient的东西,是第三方开发的一些集成度很高的客户端。可能 由于是嵌入的客户端,HttpClient本身操作更加简单,开发的API更加高,但是也是因为集成度太高了,所以自己操作时对网络访问的性能的提升空间比较小。


所以这个地方,根据我自己的条件,我主要就记录一些HttpURLConnection的方法。主要的步骤就是:1)检查当前网络是否可用;2)


9、Android执行异步有哪些方法?线程间通讯的方式?

答:主要的Android的异步方式就是Handler加Thread和AsyncTask。Handler加Thread算是我使用的经验比较丰富的一种。其主要的架构就是Thread+Looper+Message+Handler,这一套架构用来解决新线程想要更新UI等跨线程问题。


然而单独地使用Thread的新建是非常浪费资源的,因此在多线程的程序中,可以自己组件类似于AnycTask的多线程池ThreadPoolExecutor。线程池主要的好处是不需要总是重复的新建线程和删除线程。


10、AnycTask的优缺点?串行or并行?内部线程池是怎样的?

答:它是一个抽象的辅助类,用来管理后台操作,并最终返回到UI线程,在使用时我们主要是去继承它创建新的子类。老师上课讲这个环节的时候,本来我就没怎么听,何况他还讲的不清楚。所以这个地方我直接全部把asynctask重新理解一下。Android从1.5版本就引入了AsyncTask类,它可以非常灵活方便地从子线程切换到UI线程。每个asynctask的子类都需要实现四个方法。经常需要去重写的方法有以下四个:


1. onPreExecute()
这个方法会在后台任务开始执行之间调用,用于进行一些界面上的初始化操作,比如显示一个进度条对话框等。
2. doInBackground()
这个方法中的所有代码都会在子线程中运行,我们应该在这里去处理所有的耗时任务。
3. onProgressUpdate()
当在后台任务中调用了publishProgress()方法后,这个方法就很快会被调用,方法中携带的参数就是在后台任务中传递过来的。在这个方法中可以对UI进行操作,利用参数中的数值就可以对界面元素进行相应的更新。
4. onPostExecute()
当后台任务执行完毕并通过return语句进行返回时,这个方法就很快会被调用。返回的数据会作为参数传递到此方法中,可以利用返回的数据来进行一些UI操作,比如说提醒任务执行的结果,以及关闭掉进度条对话框等。

简单的实例:

class DownloadTask extends AsyncTask<Void, Integer, Boolean> {@Overrideprotected void onPreExecute() {progressDialog.show();}@Overrideprotected Boolean doInBackground(Void... params) {try {while (true) {int downloadPercent = doDownload();publishProgress(downloadPercent);if (downloadPercent >= 100) {break;}}} catch (Exception e) {return false;}return true;}@Overrideprotected void onProgressUpdate(Integer... values) {progressDialog.setMessage("当前下载进度:" + values[0] + "%");}@Overrideprotected void onPostExecute(Boolean result) {progressDialog.dismiss();if (result) {Toast.makeText(context, "下载成功", Toast.LENGTH_SHORT).show();} else {Toast.makeText(context, "下载失败", Toast.LENGTH_SHORT).show();}}}

如果AnycTask同时接受很多任务的话,我们就需要考虑,在任务与任务之间,到底是并行还是串行的关系了。在观摩了别人的实验以后,证实,最新版本的AnycTask是默认串行执行,但是可以通过调用其他的方法达到并行的目的。AnycTask总存放的任务队列不能超过128个。


12、View的绘制流程。

答:每一个视图的绘制过程都必须经历三个最主要的阶段,即onMeasure()、onLayout()和onDraw()。这三个方法,都从rootview开始,向下递归调用各自的方法。

1)每个View控件的实际宽高都是由父视图和自身决定的。实际的测量是在onMeasure方法进行,所以在View的子类需要重写onMeasure方法,这是因为measure方法是final的,不允许重载,所以View子类只能通过重载onMeasure来实现自己的测量逻辑。

2)Measure方法之后就是Layout方法,每一个onLayout的目的就是安排其children在父View的具体位置,重载onLayout通常做法就是写一个for循环调用每一个子视图的layout(l, t, r, b)函数,传入不同的参数l, t, r, b来确定每个子视图在父视图中的显示位置。

3)最后一步是绘制,每一个View的绘制流程是:第一步,对View的背景进行绘制;第二步,对View的内容进行绘制;第三步,对当前View的所有子View进行绘制,如果当前的View没有子View就不需要进行绘制;第四步,对View的滚动条进行绘制。


13、View,SurfaceView,GLSurfaceView有什么区别。

答:除了View,其他的两个我并没有接触过,这个答案是摘抄自一个知乎的答案。

View:显示视图,内置画布,提供图形绘制函数、触屏事件、按键事件函数等;必须在UI主线程内更新画面,速度较慢

SurfaceView:基于view视图进行拓展的视图类;是view的子类,类似使用双缓机制,在新的线程中更新画面所以刷新界面速度比view快。

GLSurfaceView:基于SurfaceView视图再次进行拓展的视图类;是SurfaceView的子类,openGL专用。


UI的主线程中更新画面可能会引发问题,比如你更新画面的时间过长,那么你的主UI线程会被你正在画的函数阻塞。那么将无法响应按键,触屏等消息,会提示出ANR。当使用surfaceView 由于是在新的线程中更新画面所以不会阻塞你的UI主线程。但这也带来了另外一个问题,就是事件同步,涉及到线程同步。所以基于以上,根据游戏特点,一般分成两类。

1、被动更新画面的。比如棋类,这种用view就好了。因为画面的更新是依赖于 onTouch 来更新,可以直接使用 invalidate。 因为这种情况下,这一次Touch和下一次的Touch需要的时间比较长些,不会产生影响。

2、主动更新。比如一个人在一直跑动。这就需要一个单独的thread不停的重绘人的状态,避免阻塞main UI thread。所以显然view不合适,需要surfaceView来控制。

而GLSurfaceView只是在SurfaceView上封装了egl等一些配置opengl环境的操作,而取决于用在surfaceview上的图形库。


14、ListView的优化方案。

答:ListView的加载一直是一个非常复杂的问题,优化的方案也有很多角度,这里主要选几个主流的角度。

首先解释convertView概念。

ListView原理

每次ListView在准备绘制新的View时,都会调用getView函数。在方法getView(int position, View convertView, ViewGroup parent)中,第二个参数convertView是代表系统最近回收的View,在上图中,即Item1。因此我们能有优化:

1)在Adapter的代码中,在getView方法里首先判断convertView是否为空,若为空则加载相应布局,若不为空则
直接使用该布局;

2)在Adapter的实现代码中,在实现ViewHolder时,使用静态的ViewHoulder。ViewHoulder是为convertView服务的,ViewHoulder存在帮助新的View能够快速的查找到convertView的资源,省去使用方法查找资源的时间;而将ViewHolder设置为static的目的是指在初始化Adapter时初始化一次这个内部类,否则将会在每次创建Adapter时都要初始化一次,而这是没有必要的。

3)第三个就是关于异步加载图片的优化,主要的思想和代码都是参考这个博客。当ListView需要获取图片资源时,先从内存中加载,没有则开启线程从SD卡或网络中获取,这里注意从SD卡获取图片是放在子线程里执行的,否则快速滑屏的话会不够流畅,这是优化一。于此同时,在adapter里有个busy变量,表示listview是否处于滑动状态,如果是滑动状态则仅从内存中获取图片,没有的话无需再开启线程去外存或网络获取图片,这是优化二。ImageLoader里的线程使用了线程池,从而避免了过多线程频繁创建和销毁,有的童鞋每次总是new一个线程去执行这是非常不可取的,好一点的用的AsyncTask类,其实内部也是用到了线程池。在从网络获取图片时,先是将其保存到sd卡,然后再加载到内存,这么做的好处是在加载到内存时可以做个压缩处理,以减少图片所占内存,这是优化三。 


15、RecyclerView与ListView的区别。RecyclerView的优缺点。

答:RecyclerView是新提供的View控件。在Google官方的定义里,RecyclerView是用来表现大数据的控件。

1)相比于ListView、GridView,RecyclerView提供了更好的自由度。它提供的不同LayoutManager,ItemDecoration,ItemAnimator实现各类的效果。LayoutManager负责管理RecyclerView中每个View的位置,比如其中代表性的LinearLayoutManager可以让View呈现线性分布。ItemDecoration是负责每个Item之间的图形设置。而ItemAnimator则是负责设置Item删除和增加后的整体动画效果。RecyclerView不关心如何将子View放在合适的位置(LayoutManager控制),也不关心如何分割这些子View(ItemDecoration负责),更不关心每个子View各自的外观。更进一步来说就是RecyclerView它只负责回收和重用的工作,这也是它名字的由来。这一切成全了RecyclerView的灵活性。

2)RecyclerView本身的出现就解决了之前在ListView被手动解决的Item复用问题。通过使用官方实现的ViewHolder解决之前我们在ListView中需要自己解决的问题。


RecyclerView的缺点在于,无法设置item点击、长按事件,如OnItemClickListener方法


16、WebView的基本使用方法。

答:WebView是一个基于webkit引擎、展现web页面的控件。也就是我们平时在使用APP时的所谓内置浏览器。WebView本身除了读取网络上的html外,还可以读取本地缓存或者自己编写的html。WebView控件功能强大,除了具有一般View的属性和设置外,还可以对url请求、页面加载、渲染、页面交互进行强大的处理。

WebView的状态:

//激活WebView为活跃状态,能正常执行网页的响应webView.onResume() ;//当页面被失去焦点被切换到后台不可见状态,需要执行onPause//通过onPause动作通知内核暂停所有的动作,比如DOM的解析、plugin的执行、JavaScript执行。webView.onPause();//当应用程序(存在webview)被切换到后台时,这个方法不仅仅针对当前的webview而是全局的全应用程序的webview//它会暂停所有webview的layout,parsing,javascripttimer。降低CPU功耗。webView.pauseTimers()//恢复pauseTimers状态webView.resumeTimers();//销毁Webview//在关闭了Activity时,如果Webview的音乐或视频,还在播放。就必须销毁Webview//但是注意:webview调用destory时,webview仍绑定在Activity上//这是由于自定义webview构建时传入了该Activity的context对象//因此需要先从父容器中移除webview,然后再销毁webview:rootLayout.removeView(webView); webView.destroy();
Back键控制网页后退:
在浏览网页的时候,back键一般都表示回退到上一个网页,但是其实原生的WebView在back键时,都是会调用finish()而结束自身。所以我们需要重写函数,达到返回上一个网页的效果。


WebView辅助类:

除了WebView本身以外,它还有很多辅助类。1)WebSetting主要的功能就是对WebView进行配置和管理。能提供屏幕剪切、读取缓存等功能。2)WebViewClient类提供很多对网页读取状态的监听,比如加载网页前,加载网页完成后或者访问出错等时刻的监听。3)WebChromeClient类可以帮助APP获得网页读取的进度、网页的标题,甚至是读取一些javascript的提问框、警告框等。

(参考资料)


18、Intent的作用。

答:Intent是一种运行时绑定机制,它能在程序运行过程中连接两个不同的组件。通过Intent,你的程序可以向Android表达某种请求或者意愿,Android会根据意愿的内容选择适当的组件来完成请求。比如,有一个Activity希望打开网页浏览器查看某一网页的内容,那么这个Activity只需要发出WEB_SEARCH_ACTION给Android,Android就会根据Intent的请求内容,查询各组件注册时声明的IntentFilter,找到网页浏览器的Activity来浏览网页。 或者,程序也可以通过Intent指定其需要的特定的组件。这两种方式也被称为Intent的显式方式和隐式方式。


除此以外,我们对Intent还有一些更加复杂的要求。比如 ,当我们使用1)notification;2)widget;3)闹钟时。我们需要一些类似于监听一样的机制,就是在到达某些条件后,打开某些组件。这些组件也是用Intent来打开,只不过这样的机制要求我们的Intent不能是及时唤醒目标组件。Android内部使用了一种叫PendingIntent的容器,负责装填context和intent。放置在notification或者widget里后,我们就可以达到以上的目的。


这里有一个Intent和PendingIntent的比较:

a)Intent是立即使用的,而PendingIntent可以等到事件发生后触发,PendingIntent可以cancel
b)Intent在程序结束后即终止,而PendingIntent在程序结束后依然有效
c)
PendingIntent自带Context,而Intent需要在某个Context内运行

d)Intent在原task中运行,PendingIntent在新的task中运行


19、Android的屏幕适配方法有哪些。

答:在了解适配方法的时候,我们首先可以了解Android的屏幕度量单位:

px:是英文单词pixel的缩写,意为像素,屏幕上的点,是物理世界中已知存在的标量。

in:表示英寸,是屏幕的物理尺寸。

dpi:Dots Per Inch的缩写, 每英寸点数,即每英寸包含像素个数。比如320X480分辨率的手机,宽2英寸,高3英寸, 每英寸包含的像素点的数量为320/2=160dpi(横向)或480/3=160dpi(纵向)。

density:屏幕密度,density和dpi的关系为 density = dpi/160。

dip(dp):密度独立像素。是Android独立设计的变量。在一个160dpi的屏幕上,1dp的值等于1pixel。随着屏幕dpi的改变,dp所对应的pixel的值也有相应的改变。密度越高,1dp所含有的pixel就越高。


在设置View的大小时候,比较主流的几个适配方法有:使用wrap_content(或者fill_parent)或者是直接dp。使用的dp操作控件的时候,我们需要清楚的知道我们屏幕的dpi。首先使用这两种方法,android系统都会根据手机目前屏幕大小进行调控的。dp作为密度独立像素,保证不管设备的屏幕显示密度是多少,同样的dp值都可以做到同样的显示效果


在drawable里面,有关于dpi的很多文件夹。包括drawable-ldpi、drawable-mdpi (dpi=160, density=1)、drawable-hdpi (dpi=240, density=1.5)、drawable-xhdpi (dpi=320, density=2)、drawable-xxhdpi (dpi=480, density=3)。之前我不是很理解存在里面的图片具体是什么用处。其实比如我们放置一张160X160的图片在mdpi中,我们这个操作的真实含义是,程序当遇到dpi=160的设备时,使用这张160X160的图片。自然,如果我们在每个文件夹里面的图片都是一个尺寸,那么自然就会产生不同尺寸的设备图片效果不同的尴尬局面了。但是,如果我们在某个文件夹的图片缺失,那么Android会自动从其他的文件夹换算出适应新屏幕的图片。



20、XML加载的几种方式,各自的原理。都有什么优缺点。

答:


22、Android中图片加载和缓存怎么做。

答:android编程中经常需要使用到图片,并且往往图片的分辨率和大小都要比我们展示图片的ImageView要大。但是这样一张大的图片,在视觉效果上没有一点的好处。反而会因为图片过大而产生OOM(OutOfMemory)异常,这个异常是因为手机分配给该程序的内存空间已经用完而导致的。因此,我们在加载图片的时候,最好就是使用一些方法,把图片做适当的瘦身,避免多余的信息占用内存空间。这个时候就需要BitmapFactory。BitmapFactory本身可以加载包括文件系统, 资源文件, 输入流以及字节数组。


如果要用BitmapFactory对图片进行分辨率瘦身的话,那么主要的操作步骤有:

1)将BitmapFactory.Options的inJustDecodeBounds参数设置为true并加载图片。inJustDecodeBounds设置为true时,可以简单的加载图片,获取图片的大小尺寸和MIME类型的数据,但是又不至于直接把Bitmap读入内存,因此不会出现OOM的问题。
2)从BitmapFactory.Options取出图片的原始宽高信息, 他们对应于outWidth和outHeight参数。根据采样率的规则并结合目标View的所需大小计算出采样率inSampleSize。
3)将BitmapFactory.Options的inJustDecodeBounds参数设为false, 然后重新加载。此时加载出的Bitmap就是我们所需要的,采样过后的Bitmap。


至于LruCache,其实原理就是LRU算法的原理,然后它的源码内部实现就是简单的LinkedHashMap实现。LRUCache主要提供的接口就是类似于LinkedHashMap,分别是put、get还有查询操作。


23、


26、有哪些造成内存泄露的原因。

答:Java是垃圾回收语言的一种,其优点是开发者无需特意管理内存分配,降低了应用由于局部故障而导致崩溃,同时防止未释放的内存把堆栈挤爆的可能,所以写出来的代码更为安全。但是,在JAVA中,仍然存在大量的逻辑上导致内存泄露的可能,其主要的包括:

1)一般内存泄漏,原因是:由忘记释放分配的内存导致的。(Cursor忘记关闭等)
2)逻辑内存泄漏,原因是:当应用不再需要这个对象,当仍未释放该对象的所有强引用。


30、什么是OOM。

答:OOM是Linux内核的一种机制,该机制会监控那些占用内存过大,尤其是瞬间很快消耗大量内存的进程,为了防止内存耗尽而内核会把该进程杀掉。android因为是建立在linux内核上,所以其实OOM也是android应用程序需要考虑的问题。

0 0
原创粉丝点击