Android面试题集锦

面试笔试题 同时被 2 个专栏收录
8 篇文章 0 订阅
207 篇文章 0 订阅

拼接的,不给出具体出处了。

1, 谈谈你对Activity的理解?

  对于这种“你对什么什么怎么理解”的面试题,最好的回到方式就是用你自己的理解说出来,通俗中夹杂些专业术语,偶尔再蹦出几个英文。当然如果你很牛X,你可以往深了说,只要别漫无目的的说就行。围绕一条主线。这其中如果你对某一方面比较精通,你就应该往你精通的方向引。面试官可能就会顺着你的思路走了。不过现在有很多面试问Android方面的知识不多,问java方面的比较多。

  通俗的讲,Activity就是一个应用程序的门面,也可以理解成就是WEB程序中一个页面,当然与web程序中的页面不同的是,web中的一个页面可能只是一个纯粹的展示页面不与用户进行任何交互,而几乎所有的Activity都会与用户交互。当然两者在架构上也有本质区别,Activity与用户的交互通过触发UI的不同事件完成的。而Web程序是通过请求,响应来完成交互的。还有在android中颠覆了很多常规想法,比如在一个Activity中可以打开另一个不在同一应用的Activity。这在其他程序是不可想象的。当然这种设计的出发点也是为了节省系统资源。从View层的角度来看,Activity承载了与用户交互的不同控件。从控制层看,也就是内部逻辑,Activity需要保持各个界面的状态,背后会做很多持久化的操作。包括妥善管理生命周期的各个阶段。(把生命周期引出来了,没准就会问你这方面的)。

  下面是文档式的回答:(最好的回答方式两者结合)

  首先Activity是android四大组件之一。它是单独的,作为用户与程序交互的一个载体。几乎所有的Activity都与用户交互。Activity创建了一个窗口,你可以通过setContentView这个方法将需要的UI放置在窗口。任何一个应用程序都可以调用单独的一个Activity。

  其次,Activity的继承关系 Activity→ContextThemeWrapper→ContextWrapper→Context

  最后大部分的Activity的子类都需要实现以下两个接口:

  onCreate(Bundle savedInstanceState)接口是初始化activity的地方. 在这儿通常可以调用setContentView(int)设置在资源文件中定义的UI, 使用findViewById(int) 可以获得UI中定义的窗口.

  onPause()接口是使用者准备离开activity的地方,在这儿,任何的修改都应该被提交(通常用于ContentProvider保存数据).

  所有Activity必须在清单文件里注册一下才能使用。

  2, Activity的生命周期?

  在系统中的Activity被一个Activity栈所管理。当一个新的Activity启动时,将被放置到栈顶,成为运行中的Activity,前一个Activity保留在栈中,不再放到前台,直到新的Activity退出为止。

  Activity有四种本质区别的状态:

  1)在屏幕的前台(Activity栈顶),叫做活动状态或者运行状态(active or running)

  2)如果一个Activity失去焦点,但是依然可见(一个新的非全屏的Activity 或者一个透明的Activity 被放置在栈顶),叫做暂停状态(Paused)。一个暂停状态的Activity依然保持活力(保持所有的状态,成员信息,和窗口管理器保持连接),但是在系统内存极端低下的时候将被杀掉。

  3)如果一个Activity被另外的Activity完全覆盖掉,叫做停止状态(Stopped)。它依然保持所有状态和成员信息,但是它不再可见,所以它的窗口被隐藏,当系统内存需要被用在其他地方的时候,Stopped的Activity将被杀掉。

  4)如果一个Activity是Paused或者Stopped状态,系统可以将该Activity从内存中删除,Android系统采用两种方式进行删除,要么要求该Activity结束,要么直接杀掉它的进程。当该Activity再次显示给用户时,它必须重新开始和重置前面的状态。

  Activity提供了7个生命周期方法:

  onCreate(),onStart(),onResume(),onPause(),onStop(),onDestory(),onRestart()。在这七个生命周期方法中有三个关键的循环。其实官方给的那张图画得很明白,但估计很少人能有耐心去揣摩。那就换种方式展现。看图吧:

  1)以下该图呈现了一个Activity完整的生命周期,从onCreate(Bundle)开始到onDestroy()结束。Activity在onCreate()设置所有的“全局”状态,在 onDestory()释放所有的资源。

  首先我们运行程序,这是打开MainActivity依次调用onCreate->onStart->onResume,这时MainActivity在栈顶,与我们交互的Activity都在栈顶。然后我按下返回键,则会依次调用onPause->onStop->onDestory。这属于一个完整的生命周期。

  



  2)可见的生命周期,从onStart()开始到onStop()结束。说白了就是一个Activity被另一个activity完全覆盖掉,然后又重新回到前台这个一个过程称之为可见生命周期。

  首先我们打开OtherActivity这个窗口。此时MainActivity将被覆盖掉。则会依次调用onPause->onStop。在内存不足的时候,系统也会杀死MainActivity进程。

  然后,按下返回键,MainActivity又回到前台此时会调用 onRestart->onStart->onResume。

  



  3)前台的生命周期,从onResume()开始到onPause()结束。在这段时间里,该Activity处于所有Activity的最前面,和用户进行交互。Activity可以经常性地在resumed和paused状态之间切换。说白了就是一个Activity覆盖到另一个Activity上面,但是并没有完全覆盖掉。

  首先,我们将OtherActivity打开,这是OtherActivity以对话框模式打开,悬浮在MainActivity上面。直接调用的onPause()。

  然后 我们按下返回键,则调用的onResume。对于这种没用完全覆盖的状态只会在onPause与onResume两个方法之间切换。

  



  注:还有一种特殊情况,当屏幕发生改变的时候生命周期是怎样的呢?比如由竖屏调整为横屏。生命周期的方法依次用:onPause->onStop->onDestroy->onCreate->onStart->onResume。这么一个过程。它会销毁掉原先的activity,重新创建。

  3, Acitvity启动模式?

  对于这种题,最后就是自己写个例子,观察一下,才能真正明白。

  Activty启动提供了四种启动模式。launchMode:

  standard:每次启动新的活动窗口(new操作)

  singleTop:如果在栈顶是目标活动,则直接打开.否则开启新的活动窗口(new).

  singleTask和singleInstance基本上相同.差别在于若根活动设置为singleTask时,则由此所开启的活动也在同一任务中,即taskId相同。.而若根活动设置为singleInstance时,由此开启的活动在新的任务中.即栈中只有一个活动,taskid不同。.其余情况相同.

  4, 在onCreate方法中Bundle savedInstanceState 这个参数有什么作用?

  在onCreate方法中有saveInstanceState这个参数,其实这个参数对应两个方法。

  void onSaveInstanceState(Bundle outState);void onRestoreInstanceState(Bundle savedInstanceState)。当某个activity变得“容易”被系统销毁时,说白了就是系统在内存不足或者其他异常情况下把你的Activity销毁时,将调用这个方法。需要注意的是它是系统调用的,并且你的Activity是被动得被销毁。你可以在销毁的时候保存一下数据。然后在onCreate方法中拿出来。那什么情况下能触发这两个方法呢?

  1)、当用户按下HOME键时。

  这是显而易见的,系统不知道你按下HOME后要运行多少其他的程序,自然也不知道activity A是否会被销毁,故系统会调用onSaveInstanceState,让用户有机会保存某些非永久性的数据。以下几种情况的分析都遵循该原则

  2)、长按HOME键,选择运行其他的程序时。

  3)、按下电源按键(关闭屏幕显示)时。

  4)、从activity A中启动一个新的activity时。

  5)、屏幕方向切换时,例如从竖屏切换到横屏时。

  在屏幕切换之前,系统会销毁activity A,在屏幕切换之后系统又会自动地创建activity A,所以onSaveInstanceState一定会被执行

  总而言之,onSaveInstanceState的调用遵循一个重要原则,即当系统“未经你许可”时销毁了你的activity,则onSaveInstanceState会被系统调用,这是系统的责任,因为它必须要提供一个机会让你保存你的数据(当然你不保存那就随便你了)。

  至于onRestoreInstanceState方法,需要注意的是,onSaveInstanceState方法和onRestoreInstanceState方法“不一定”是成对的被调用的,onRestoreInstanceState被调用的前提是,activity A“确实”被系统销毁了,而如果仅仅是停留在有这种可能性的情况下,则该方法不会被调用,例如,当正在显示activity A的时候,用户按下HOME键回到主界面,然后用户紧接着又返回到activity A,这种情况下activity A一般不会因为内存的原因被系统销毁,故activity A的onRestoreInstanceState方法不会被执行

  另外,onRestoreInstanceState的bundle参数也会传递到onCreate方法中,你也可以选择在onCreate方法中做数据还原。

  5, 谈谈你对BroadCastRceiver的理解?

  广播接收者,android四大组件之一,也是唯一一个能动态注册的组件。

  1)广播接收者是一个专注于接收广播通知信息,并做出对应处理的组件。很多广播是源自于系统──比如,通知时区改变、电池电量低、拍摄了一张照片或者用户改变了语言选项。应用程序也可以进行广播──比如说,通知其它应用程序一些数据下载完成并处于可用状态。

  2)应用程序可以拥有任意数量的广播接收者以对所有它感兴趣的通知信息予以响应。所有的接收器均继承自BroadcastReceiver基类。

  3)广播接收者没有用户界面。然而,它们可以启动一个activity来响应它们收到的信息,或者用NotificationManager来通知用户。通知可以用很多种方式来吸引用户的注意力──闪动背灯、震动、播放声音等等。一般来说是在状态栏上放一个持久的图标,用户可以打开它并获取消息。

  6, 广播分几种?他们有什么区别?

  广播被分为两种不同的类型:“普通广播(Normal broadcasts)”和“有序广播(Ordered broadcasts)”。普通广播是完全异步的,可以在同一时刻(逻辑上)被所有接收者接收到,消息传递的效率比较高,但缺点是:接收者不能将处理结果传递给下一个接收者,并且无法终止广播Intent的传播。

  然而有序广播是按照接收者声明的优先级别,被接收者依次接收广播。如:A的级别高于B,B的级别高于C,那么,广播先传给A,再传给B,最后传给C 。优先级别声明在 intent-filter 元素的 android:priority 属性中,数越大优先级别越高,取值范围:-1000到1000,优先级别也可以调用IntentFilter对象的setPriority()进行设置 。有序广播的接收者可以终止广播Intent的传播,广播Intent的传播一旦终止,后面的接收者就无法接收到广播。

  另外,有序广播的接收者可以将数据传递给下一个接收者,如:A得到广播后,可以往它的结果对象中存入数据,当广播传给B时,B可以从A的结果对象中得到A存入的数据。

  Context.sendBroadcast()

  发送的是普通广播,所有订阅者都有机会获得并进行处理。

  Context.sendOrderedBroadcast()

  发送的是有序广播,系统会根据接收者声明的优先级别按顺序逐个执行接收者,

  前面的接收者有权终止广播(BroadcastReceiver.abortBroadcast()),如果广播被前面的接收者终止,后面的接收者就再也无法获取到广播。对于有序广播,前面的接收者可以将数据通过setResultExtras(Bundle)方法存放进结果对象,然后传给下一个接收者,下一个接收者通过代码:Bundle bundle = getResultExtras(true))可以获取上一个接收者存入在结果对象中的数据。

  7, 广播的生命周期?

  广播的生命周期是非常短的,当发送之后intent会到AndroidManifest.xml文件中找是不是有匹配的action,如果有就会调用Receiver ,然后获得Receiver 对象,再执行onReceive方法,这时候Receiver对象就没有用了,当我们再次点击按钮的时候就会重新获得对象,这就是BroadcastReceiver的生命周期.

  在BroadcastReceiver里不能做一些比较耗时的操作,否侧会弹出ANR(Application No

  Response)的对话框.

  如果需要完成一项比较耗时的工作,应该通过发送Intent给Service,由Service来完成.这里不能使用子线程来解决,因为BroadcastReceiver的生命周期很短,子线程可能还没有结束,BroadcastReceiver就先结束了.BroadcastReceiver一旦结束,此时BroadcastReceiver的所在进程很容易在系统需要内存时被优先杀死,因为它属于空进程(没有任何活动组件的进程).如果它的宿主进程被杀死,那么正在工作的子线程也会被杀死.所以采用子线程来解决是不可靠的.

  8, 两种注册BroadcastReceiver的方法?

  使用在manifest中注册的方法注册BroadcastReceiver,即使你的应用程序没有启动,或者已经被关闭,这个BroadcastReceiver依然会继续运行,这样的运行机制可能会给软件的用户造成困扰。所以作为程序的开发者,我们希望能够有一种灵活的机制完成BroadcastReceiver的绑定和解除绑定操作。Android当然也考虑到了这些问题,所以在Context这个类当中提供了如下两个函数可以在代码中注册:

  i. registerReceiver(receiver,filter);

  这个函数的作用就是将一个BroadcastReceiver注册到应用程序当中,这个函数接收两个参数,第一个参数是需要注册的BroadcastReceiver对象,第二个是一个IntentFilter。第一个参数是非常容易理解的,第二个参数的作用是定义了哪些Intent才能触发这个注册的BroadcastReceiver对象。

  ii. unregisterReceiver(receiver);

  这个方法就非常的简单了,用于解除BroadcastReceiver的绑定状态。一旦解除完成,响应的BroadcastReceiver就不会再接收系统所广播的Intent了。

  两种注册BroadcastReceiver的方法

  1.在应用程序的代码中进行注册

  注册BroadcastReceiver

  registerReceiver(receiver,filter);

  取消注册BroadcastReceiver

  unregisterReceiver(receiver);

  2.在AndroidManifest.xml当中进行注册

  两种注册BroadcastReceiver方法的比较:

  现在我们了解了两种注册BroadcastReceiver的方法之后,需要考虑一下这两种方法适用的场合:

  i. 第一种注册的方法可以保证在应用程序安装之后,BroadcastReceiver始终处于活动状态,通常用于监听系统状态的改变,比如说手机的电量,wifi网卡的状态(当然,监视这些东西也是取决于软件的需求)。对于这样的BroadcastReceiver,通常是在产生某个特定的系统事件之后,进行相应的操作,比如说wifi网卡打开时,给用户一个提示;

  ii. 第二种注册方法相对第一种要灵活的多,这样注册的BroadcastReceiver通常用于更新UI的状态。一般来说,都是在一个Activity启动的时候使用这样的方法注册BroadcastReceiver,一旦接收到广播的事件,就可以在onReceive方法当中更新当前的这个Activity当中的控件。但是需要注意的是如果这个Activity不可见了,就应该调用unregisterReceiver方法来解除注册

  9, 粘性广播有什么作用?怎么使用?

  粘性广播主要为了解决,在发送完广播之后,动态注册的接收者,也能够收到广播。举个例子首先发送一广播,我的接收者是通过程序中的某个按钮动态注册的。如果不是粘性广播,我注册完接收者肯定无法收到广播了。这是通过发送粘性广播就能够在我动态注册接收者后也能收到广播。

  用法:

  //发送粘性广播

  Public void sendStickyBroadCast(){

  Intent intent=new Intent();

  intent.setAction(“com.iteye.myreceiver.action”);

  intent.putExtra(“name”,”tom”);

  this.sendStickyBroadCast(intent);

  }

  发送粘性广播还需要发送粘性广播的权限:

  //动态注册广播接收者

  MyReceiver mr = new MyReceiver ();

  IntentFilter filter = new IntentFilter();

  filter.addAction("com.iteye.myreceiver.action ");

  filter.addCategory(Intent.CATEGORY_DEFAULT);

  Intent ii = this.registerReceiver(mr, filter);

  String n = ii.getStringExtra("name");

  Toast.makeText(this, "动态注册接收者完成,收到粘性广播,name=" + n, 1).show();

  10,如何控制接收者或者发送方得权限?

  1)如果广播发送方要求接收方必须有某个权限才能收到广播怎么做呢?

  /**

  * 发送广播,指定接收者权限

  * sendBroadcast(i, "com.iteye.permission.receiver");//指定接收者权限

  */

  public void sendBroadcast() {

  //隐式意图,发送广播

  Intent i = new Intent();

  i.setAction("com.iteye.receiver.action");

  i.putExtra("name", "tom");

  this.sendBroadcast(i, " com.iteye.permission.receiver ");

  Log.i("Other",".send ok!");

  }

  在清单文件里receiver必须有这个权限才能收到广播。

  首先,需要定义权限:

  然后,声明权限:

  这时接收者就能收到发送的广播。

  2)反过来,如果接收者要求发送方必须拥有某个权限,我才接收你的广播怎么办呢?

  即使过滤器匹配,如果发送方没有相应权限,接收者也不会接收其广播。



11, 谈谈对service的理解?
  Service,服务。幕后工作者,甘心做绿叶。默默为前台提供支持。Android的核心三组件可以通过电视台进行比喻,电视播放画面可以看作Activity,用户可以通过遥控器切换不同的电视台可以看做是不同Activity之间的切换。电视播放的新闻可以看做是广播发送方,每个观众都是接收者。而电视幕后工作者可以看做是Service,从不抛头露面。默默为Activity提供所需要的内容。
  说到Service,我们最熟悉莫过于windows中的服务。他们都是在后台长时间运行,接受上层指令,完成相关操作。但android中的 service与其他的服务还是有所差别的,service与其他三个组件一样,其进程模型都是可以通过XML进行配置的。调用方和发布方都可以有权利来选择是把这个组件运行在同一个进程下,还是不同的进程下。如果一个Service,是有期望运行在于调用方不同进程的时候,就需要利用Android提供的RPC(Remote Procedure Call Protocol)机制,为其部署一套进程间通信的策略。当然这些策略android已经为你封装好了。你需要了解底层。直接拿来使用即可。RPC其实就是代理模式的一种实现,在调用端和服务端都去生成一个代理类,做一些序列化和反序列化的事情,使得调用端和服务器端都可以像调用一个本地接口一样使用 RPC接口。
  Android中用来做数据序列化的类是Parcel,封装了序列化的细节,向外提供了足够对象化的访问接口
  AIDL (Android Interface Definition Language) ,一种接口定义的语言,服务的RPC接口,可以用AIDL来描述, ADT帮助你自动生成一整套的代理模式需要用到的类,不要要你自己写。下面会给个例子。
  12, service的生命周期?
  在有生命周期的这几个组件中,Service的生命周期最长,其次是Activity最后Receiver。
  Service的生命周期相对Activity简单一些。Service提供了两种启动service的方式,每一种方式对应一套生命周期方法。以下是android提供的图片:
  
  1) 以startService()方式启动的服务,它的生命周期如下:
  onCreate()->onStartCommand() 这时服务已经开启,如果再点击一下启动则只会调用onStartCommand()。这说明service是单例的,只实例化一次。点击停止按钮调用 onDestroy()。销毁。生命周期完成。以下为图示:
  
  2) 以bindService()方式启动服务。调用方式如下:
  onCreate()->onBind() 此时服务绑定。再次绑定则无响应。解除绑定:onUnbind()->onDestroy()。生命周期完成。如图所示:
  
  13, 启动service的两种方法?有什么区别?
  一种是startService(), 另一种是bindService()。这两者的区别是第一种方式调用者开启了服务,即会与服务失去联系,两者没有关联。即使访问者退出了,服务仍在运行。如需解除服务必须显式的调用stopService方法。主要用于调用者与服务没有交互的情况下,也就是调用者不需要获取服务里的业务方法。比如电话录音。而后者调用者与服务绑定在一起的。当调用者退出的时候,服务也随之退出。用于需要与服务交互。
  14, 实现进程内与进程间通信是怎么实现的?
  在linux中进程通信机制有很多种,比如:消息队列(message queue),socket,共享内存(share mermory)等等。但是在Android中,无论是进程内还是进程间,所采用的通信机制,主要是以Binder为核心。Android之所以选择是 Binder作为进程的通信机制,主要是Binder与其他通信方式相比更加快速和简洁,所消耗的内存也是相对较少。当然也有其他方面的原因,比如传统进程通信有可能会增加进程开销,也有安全方面的风险。Binder能解决这些问题。故成为首选的通信方式。
  1) 进程内的通信,与进程间的通信相比,相对容易些。进程内通信肯定要采用绑定的方式开启服务。这样才能与服务进行交互。绑定服务的方法:
  bindService(Intent service, ServiceConnection conn, int flags)
  这个方法的第一个参数很好理解。用于激活哪个服务,第二个参数是关键,服务的连接,这是一个接口,用于接收服务开启或者关闭时返回的数据。实现这个接口要重写两个方法:
  onServiceConnected(ComponentName name, IBinder service):一旦客户端与服务端绑定成功,将有这个方法的第二个参数接收服务端返回的IBinder对象。得到服务端的引用就可以在客户端调用服务里的一些业务方法。这也是进程内通信的主要部分。
  onServiceDisconnected(ComponentName name):这个方法在服务断开是调用,服务被销毁或者被干掉时调用。附件处有例子。
  2) 进程之间的通信,其主要的核心机制同样是Binder。其实Binder底层就是通过Parcel(邮包)来完成数据传输的。进程内通信可以通过IBinder对象来实现业务共享的,但是进程间的通信这种方式就肯定不行了。这时就用到了AIDL。
  AIDL是一种接口定义语言,用于约束两个进程间的通讯规则,供编译器生成代码,实现Android设备上的两个进程间通信(IPC).AIDL的IPC机制和EJB所采用的CORBA很类似,进程之间的通信信息,首先会被转换成AIDL协议消息,然后发送给对方,对方收到AIDL协议消息后再转换成相应的对象.由于进程之间的通信信息需要双向转换,所以android采用代理类在背后实现了信息的双向转换,代理类由android编译器生成,对开发人员来说是透明的.
  使用方式如下:
  定义AIDL. //XXXService.aidl,注意扩展名
  package com.iteye.androidtoast;
  interface XXXService {
  void sayHello(in/out/inout String name);//in|out|inout是参数的方向
  }
  ide会自动在gen包下生成对应的 java类,接口文件中生成一个Stub的抽象类,里面包括aidl定义的方法,还包括一些其它辅助方法.值得关注的是 asInterface(IBinder iBinder),它返回接口类型的实例,对于远程服务调用,远程服务返回给客户端的对象为代理对象,客户端 onServiceConnected(ComponentName name, IBinder service)方法引用该对象时不能直接强转成接口类型的实例,而应该使用asInterface(IBinder iBinder)进行类型转换.
  AIDL定义注意事项:
  (1)接口名和aidl文件名相同.
  (2)接口和方法前不用加访问权限修饰符public,private,protected等,也不能用final,static.
  (3)AIDL默认支持的类型包话java基本类型(int,long,boolean等)和(String,List,Map,
  CharSequence),使用这些类型时不需要import声明.对于List和Map中的元素类型必须是AIDL支持的类型.如果使用自定义类型作为参数或返回值,自定义类型必须实现Parcelable接口.
  (4)自定义类型和AIDL生成的其它接口类型在aidl描述文件中,应该显式import,即便在该类和定义的包在同一个包中.
  (5)在aidl文件中所有非Java基本类型参数必须加上in、out、inout标记,以指明参数是输入参数、输出参数还是输入输出参数.
  (6)Java原始类型默认的标记为in,不能为其它标记.
  具体步骤:
  1.创建服务器android项目(server)
  2.创建服务类.CustomerService
  /**
  * 服务类
  */
  public class CustomerService extends Service {
  ...
  }
  3.配置清单注册服务类
  
  
  4.定义aidl文件.
  ICustomerService.aidl
  package com.iteye.androidtoast.aidl;
  interface ICustomerService {
  String sayHello(String name);
  }
  5.ide在gen目录下自动生成java接口.
  6.需要实现CustomerService.onBind()方法的返回值.
  public IBinder onBind(Intent intent) {
  return new ICustomerService.Stub() {
  public String sayHello(String name) throws RemoteException {
  return "hello " + name;
  }
  };
  }
  7.创建客户端项目(android项目)
  8.复制服务端项目aidl文件到客户端(连同包结构一同复制),同样在客户端项目中
  也会生成对应的接口类.同步骤(5).
  9.绑定服务.
  class CustomerConnection implements ServiceConnection{
  public void onServiceConnected(ComponentName name, IBinder service) {
  is = ICustomerService.Stub.asInterface(service);
  }
  public void onServiceDisconnected(ComponentName name) {
  }
  }
  10.绑定服务
  Intent i = new Intent();
  i.setAction(“”)
  this.bindService(i, conn, BIND_AUTO_CREATE);
  11,调用远程服务方法



1:android中notifyDataSetChanged()的作用?
答:当修改已经生成的列表,添加或者修改数据,notifyDataSetChanged()可以在修改适配器绑定的数据后,通知Activity更新ListView(不是刷新Activity)。

2:这样动态刷新ListView中的数据?
答:通过notifyDataSetChanged()强制调用getView来刷新每个Item的内容。

3:两个Activity跳转必须执行的方法,依次写出。
答:onPause()、onStop()

4:Handler机制的原理。
答:android提供了handler和looper来满足线程间的通信。handler先进先出原则,looper类
用来管理特定线程内对象之间的消息交换。
1).looper:一个线程可以产生一个looper对象,由它来管理次线程里的message queue(
消息队列)
2):handler:可以构造handler对象来与looper沟通,以便push新消息到message queue
里;或者接受looper从message queue取出或送出来的消息
3):message queue(消息队列):用来存放线程放入的消息
4):线程:UI thread通常是mian thread,儿android启动程序时会替它建立一个message 
queue.

5:如何退出已调用多个Activity的Application?
答:在2.1之前,使用ActivityManager的restartPackage方法可以直接结束整个应用。在使用时需要权限android.permission.RESTART_PACKAGES。
2.2之后没有办法直接结束一个应用,只能通过间接的方法,个人认为最好的方法是记录每一个打开的Activity,当需要推出应用时关闭每一个Activity,具体方法可仿照新浪微博的做法,定义一个ArrayList,将每个打开的Activity添加到集合中。

6:怎样使用Intent进行数据通信?
答:使用Intent进行数据传递有两种方法:
1):putExtras(key,values),代码如下
         Intent intent=new Intent(this,XXXAct);
         intent.putExtras(key,values);
         startActivity(intent);
2):     Intent intent=new Intent(this,XXXAct);
         Bunlde bundle=new Bunlde();
         bundle.putSerializable(key,value);
         intent.putExtras(bundle);
         startActivity(intent);

7:怎样动态取出HashMap中的key-value?
这个不明白所谓动态取出是什么意思,有知道请指点下。

以下面试题都是在网上找的总结出来的,谢谢大家的分享!希望,我们共同进步,找到自己梦想的公司:


1. android dvm 的进程和Linux的进程,应用程序的进程是否为同一个概念:

答:dvm是dalivk虚拟机。每一个android应用程序都在自己的进程中运行,都拥有一个dalivk虚拟机实例。而每一个dvm都是在linux的一个进程。所以说可以认为是同一个概念。

2.android的动画有哪几种?他们的特点和区别是什么?

答:两种,一种是tween动画,一种是frame动画。tween动画,这种实现方式可以使视图组件移动,放大或缩小以及产生透明度的变化。frame动画,传统的动画方法,通过顺序的播放排列好的图片来实现,类似电影。

3.handler进制的原理:

答:android提供了handler和looper来满足线程间的通信。Handler先进先出原则。looper用来管理特定线程内对象之间的消息交换(message Exchange).

    1)looper:一个线程可以产生一个looper对象,由它来管理此线程里的message queue(消息队列)

   2)handler:你可以构造一个handler对象来与looper沟通,以便push新消息到messagequeue里;或者接收looper(从messagequeue里取出)所送来的消息。

    3)messagequeue:用来存放线程放入的消息。

    4)线程:UI thread 通常就是main thread,而android启动程序时会为它建立一个message queue.

4.android view的刷新:

答:android中对View的更新有很多种方式,使用时要区分不同的应用场合。我感觉最要紧的是分清:多线程和双缓冲的使用情况。

    1).不使用多线程和双缓冲

   这种情况最简单了,一般只是希望在View发生改变时对UI进行重绘。你只需在Activity中显式地调用View对象中的invalidate()方法即可。系统会自动调用 View的onDraw()方法。

    2).使用多线程和不使用双缓冲

    这种情况需要开启新的线程,新开的线程就不好访问View对象了。强行访问的话会报:android.view.ViewRoot$CalledFromWrongThreadException:Only the originalthread that created a view hierarchy can touch its views.

    这时候你需要创建一个继承了android.os.Handler的子类,并重写handleMessage(Messagemsg)方法。android.os.Handler是能发送和处理消息的,你需要在Activity中发出更新UI的消息,然后再你的Handler(可以使用匿名内部类)中处理消息(因为匿名内部类可以访问父类变量,你可以直接调用View对象中的invalidate()方法 )。也就是说:在新线程创建并发送一个Message,然后再主线程中捕获、处理该消息。

    3).使用多线程和双缓冲

    Android中SurfaceView是View的子类,她同时也实现了双缓冲。你可以定义一个她的子类并实现SurfaceHolder.Callback接口。由于实现SurfaceHolder.Callback接口,新线程就不需要android.os.Handler帮忙了。SurfaceHolder中lockCanvas()方法可以锁定画布,绘制玩新的图像后调用unlockCanvasAndPost(canvas)解锁(显示),还是比较方便得。


5.说说mvc模式的原理,它在android中的运用:

答:android的官方建议应用程序的开发采用mvc模式。何谓mvc?

mvc是model,view,controller的缩写,mvc包含三个部分:
l模型(model)对象:是应用程序的主体部分,所有的业务逻辑都应该写在该层。
2视图(view)对象:是应用程序中负责生成用户界面的部分。也是在整个mvc架构中用户唯一可以看到的一层,接收用户的输入,显示处理结果。

3控制器(control)对象:是根据用户的输入,控制用户界面数据显示及更新model对象状态的部分,控制器更重要的一种导航功能,想用用户出发的相关事件,交给m哦得了处理。


 android鼓励弱耦合和组件的重用,在android中mvc的具体体现如下:

    1)视图层(view):一般采用xml文件进行界面的描述,使用的时候可以非常方便的引入,当然,如何你对android了解的比较的多了话,就一定 可以想到在android中也可以使用javascript+html等的方式作为view层,当然这里需要进行java和javascript之间的通 信,幸运的是,android提供了它们之间非常方便的通信实现。

 2)控制层(controller):android的控制层的重 任通常落在了众多的acitvity的肩上,这句话也就暗含了不要在acitivity中写代码,要通过activity交割model业务逻辑层处理, 这样做的另外一个原因是android中的acitivity的响应时间是5s,如果耗时的操作放在这里,程序就很容易被回收掉。

 3)模型层(model):对数据库的操作、对网络等的操作都应该在model里面处理,当然对业务计算等操作也是必须放在的该层的。


6.Activity的生命周期:

答:onCreate: 在这里创建界面,做一些数据 的初始化工作


  onStart: 到这一步变成用户可见不可交互的

    onResume:变成和用户可交互 的,(在activity 栈系统通过栈的方式管理这些个Activity的最上面,运行完弹出栈,则回到上一个Activity)

  onPause: 到这一步是可见但不可交互的,系统会停止动画 等消耗CPU 的事情从上文的描述已经知道,应该在这里保存你的一些数据,因为这个时候你的程序的优先级降低,有可能被系统收回。在这里保存的数据,应该在

  onstop: 变得不可见,被下一个activity覆盖了

onDestroy: 这是activity被干掉前最后一个被调用方法了,可能是外面类调用finish方法或者是系统为了节省空间将它暂时性的干掉


7.让Activity变成一个窗口:

答:Activity属性设定:有时候会做个应用程序是漂浮在手机主界面的。这个只需要在设置下Activity的主题theme,即在Manifest.xml定义Activity的地方加一句:

android :theme="@android:style/Theme.Dialog"
如果是作半透明的效果:

android:theme="@android:style/Theme.Translucent"


8.Android中常用的五种布局:

答:LinearLayout线性布局;AbsoluteLayout绝对布局;TableLayout表格布局;RelativeLayout相对布局;FrameLayout帧布局;


9.Android的五种数据存储方式:

答:sharedPreferences;文件;SQLite;contentProvider;网络


10.请解释下在单线程模型中Message、Handler、Message Queue、Looper之间的关系:

答:Handler获取当前线程中的looper对象,looper用来从存有Message的Message Queue里取出message,再由Handler进行message的分发和处理。


11.AIDL的全称是什么?如何工作?能处理哪些类型的数据?

答:AIDL(AndroidInterface Definition Language)android接口描述语言


12.系统上安装了多种浏览器,能否指定某浏览器访问指定页面?请说明原由:

答:通过直接发送Uri把参数带过去,或者通过manifest里的intentfilter里的data属性。代码如下:
    Intent intent = new Intent();

Intent.setAction(“android.intent.action.View”);

Uri uriBrowsers = Uri.parse(“http://www.sina.com.cn”);

Intent.setData(uriBrowsers);

//包名、要打开的activity
    intent.setClassName(“com.android.browser”,”com.android.browser.BrowserActivity”);

startActivity(intent);


13.什么是ANR,如何避免?

答:ANR的定义:

在android上,如果你的应用程序有一段时间响应不移灵敏,系统会向用户提示“应用程序无响应”(ANR:application Not Responding)对话框。因此,在程序里对响应性能的设计很重要,这样,系统不会显示ANR给用户。

如何避免:

首先来研究下为什么它会在android的应用程序里发生和如何最佳构建应用程序来避免ANR.
    android应用程序通常是运行在一个单独的线程(例如:main)里,这就意味你的应用程序所做的事情如果在主线程里占用了大长时间的话,就会引发ANR对话框,因为你的应用程序并没有给自己机会来处理输入事件或者Intent广播。

    因此,运行在主线程里的任何访求都尽可能少做事情。特别是,activity应该在它的关键生命周期方法(onCreate()和onResume())里尽可能少的去作创建操作。潜在的耗时操作,例如网络或数据库操作,或者高耗时的计算如改变位图尺寸,应该在子线程里(或者以数据库操作为例,通过异步请求的方式)来完成。然而,不是说你的主线程阻塞在那里等待子线程的完成---也不是调用Thread.wait()或者Thread.sleep()。替代的方法是:主线程应该为子线程提供一个Handler,以便完成时能够提交给主线程。以这种方式设计你的应用程序,将能保证你的主线程保持对输入的响应性并能避免由5秒输入事件的超时引发的ANR对话框。这种做法应该在其它显示UI的线程里效仿,因为它们都受相同的超时影响。

    IntentReceiver执行时间的特殊限制意味着它应该做:在后台里做小的、琐碎的工作,如保存设定或注册一个Notification。和在主线程里调用的其它方法一样,应用程序应该避免在BroadcastReceiver里做耗时的操作或计算,但也不是在子线程里做这些任务(因为BroadcastReceiver的生命周期短),替代的是,如果响应Intent广播需要执行一个耗时的动作的话,应用程序应该启动一个Service。顺便提及一句,你也应该避免在Intent Receiver里启动一个Activity,因为它会创建一个新的画面,并从当前用户正在运行的程序上抢夺焦点。如果你的应用程序在响应Intent广播时需要向用户展示什么,你应该使用Notification Manager来实现。

    一般来说,在应用程序里,100到200ms是用户能感知阻滞的时间阈值,下面总结了一些技巧来避免ANR,并有助于让你的应用程序看起来有响应性。

    如果你的应用程序为响应用户输入正在后台工作的话,可以显示工作的进度(ProgressBar和ProgressDialog对这种情况来说很有用)。特别是游戏,在子线程里做移动的计算。如果你的程序有一个耗时的初始化过程的话,考虑可以显示一个Splash Screen或者快速显示主画面并异步来填充这些信息。在这两种情况下,你都应该显示正在进行的进度,以免用户认为程序被冻结了。

14.什么情况会导致Force Close?如何避免?能否捕获导致其的异常?

答:如空指针等可以导致ForceClose;可以看Logcat,然后找到对应的程序代码来解决错误。


15.横竖屏切换时候的activity的生命周期:

答:

1) 新建一个activity,并把各个生命周期打印出来

2) 运行activity,得到如下信息:

onCreate()à

onStart()à

onResume()à

    3)  按ctrl+F12切换成横屏时

        onSaveInstanceState()à

        onPause()à

        onStop()à

        onDestroy()à

        onCreate()à

        onStart()à

        onRestoreInstanceState()à

        onResume()à

    4)  再按ctrl+f12切换成竖屏时,发现打印了两次相同的Log

        onSaveInstanceState()à

        onPause()à

        onStop()à

        onDestroyà

        onCreate()à

        onStart()à

        onRestoreInstanceState()à

        onResume()à


        onSaveInstanceState()à

        onPause()à

        onStop()à

        onDestroyà

        onCreate()à

        onStart()à

        onRestoreInstanceState()à

        onResume()à

    5)  修改AndroidManifest.xml,把该Activity添加android:configChanges=“orientation”,执行步骤3

        onSaveInstanceState()

        onPause()

        onStop()

        onDestroy()

        onCreate()

        onStart()

        onRestoreInstanceState()

        onResume()

    6)  修改AndroidManifest.xml,把该Activity添加android:configChanges=“orientation”,执行步骤4,发现不会再打印相同信息,但多打印了一行onConfigChanged

        onSaveInstanceState()

        onPause()

        onStop()

        onDestroy()

        onCreate()

        onStart()

        onRestoreInstanceState()

        onResume()

        onConfigurationChanged()

    7)  把步骤5的android:configChanges=“orientation”改成

android:configChanges=“orientation|keyboradHidden”,执行步骤3,就只打印onConfigChanged

        onConfigurationChanged()

    8)  把步骤5的android:configChanges=“orientation”改成

android:configChanges=“orientation|keyboradHidden”,执行步骤4

        onConfigurationChanged()

        onConfigurationChanged()

    总结:

1) 不设置activity的android:configChanges时,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次。

2) 设置activity的android:configChanges=“orientation”时, 切屏会重新调用各个生命周期,切横屏、竖屏时都只会执行一次,但是竖屏最后多打印一条onConfigurationChanged()

3) 设置activity的android:configChanges=“orientation|keyboardHidden”时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged(),横屏一次,竖屏两次

再总结下整个activity的生命周期:

1)  当前activity产生事件弹出Toast和AlertDialog的时候Activity的生命周期不会有改变

2)  Activity运行时按下HOME键(跟被完全覆盖一样的)

onSavaInstanceState

onPause

onStop


onRestart

onStart

onResume

    3)  未被完全覆盖,只是失去焦点:

        onPause

        onResume


16.如何将SQLite数据库(.db文件)与apk文件一起发布?

答:可以将.db文件复制到Eclipse Android工程中的res aw目录中。所有在res aw目录中的文件不会被压缩,这样可以直接提取该目录中的文件。可以将.db文件复制到res aw目录中


17.如何将打开res aw目录中的数据库文件?

答:在Android中不能直接打开res aw目录中的数据库文件,而需要在程序第一次启动时将该文件复制到手机内存或SD卡的某个目录中,然后再打开该数据库文件。复制的基本方法是使用getResources().openRawResource方法获得res aw目录中资源的 InputStream对象,然后将该InputStream对象中的数据写入其他的目录中相应文件中。在Android SDK中可以使用SQLiteDatabase.openOrCreateDatabase方法来打开任意目录中的SQLite数据库文件。


18.android 中有哪几种解析xml的类?官方推荐哪种?以及它们的原理和区别:

答:XML解析主要有三种方式,SAX、DOM、PULL。常规在PC上开发我们使用Dom相对轻松些,但一些性能敏感的数据库或手机上还是主要采用SAX方 式,SAX读取是单向的,优点:不占内存空间、解析属性方便,但缺点就是对于套嵌多个分支来说处理不是很方便。而DOM方式会把整个XML文件加载到内存 中去,这里Android开发网提醒大家该方法在查找方面可以和XPath很好的结合如果数据量不是很大推荐使用,而PULL常常用在J2ME对于节点处 理比较好,类似SAX方式,同样很节省内存,在J2ME中我们经常使用的KXML库来解析。


19.DDMS和TraceView的区别?

答:DDMS是一个程序执行查看器,在里面可以看见线程和堆栈等信息,TraceView是程序性能分析器


20.谈谈Android的IPC机制:

答:IPC是内部进程通信的简称,是共享"命名管道"的资源。Android中的IPC机制是为了让Activity和Service之间可以随时的进行交互,故在Android中该机制,只适用于Activity和Service之间的通信,类似于远程方法调用,类似于C/S模式的访问。通过定义AIDL接口文件来定义IPC接口。Servier端实现IPC接口,Client端调用IPC接口本地代理。


21.NDK是什么:

答:NDK是一系列工具的集合

    NDK提供了一系列的工具,帮助开发者迅速的开发C/C++的动态库,并能自动将so和java应用打成apk包

    NDK集成了交叉编译器,并提供了相应的mk文件和隔离cpu,平台等的差异,开发人员只需简单的修改mk文件就可以创建出so


22.描述一下android的系统架构:

答:android系统架构分从下往上为Linux内核层、运行库、应用程序框架层和应用程序层。

    Linux内核层:负责硬件的驱动程序、网络、电源、系统安全以及内存管理等功能。

运行库和androidruntion:运行库:即c/c++函数库部分,大多数都是开放源代码的函数库,例如webkit,该函数库负责android网页浏览器的运行;例如标准的c函数库libc、openssl、sqlite等,当然也包括支持游戏开发的2dsgl和3dopengles,在多媒体方面有mediaframework框架来支持各种影音和图形文件的播放与显示,如mpeg4、h.264、mp3、aac、amr、jpg和png等众多的多媒体文件格式。Androidruntion负责解释和执行生成的dalvik格式的字节码

应用软件架构:java应用程序开发人员主要是使用该层封装好的api进行快速开发的。

应用程序层:该层是java的应用程序层,android内置的googlemaps、email、IM、浏览器等,都处于该层,java开发人员工发的程序也处于该层,而且和内置的应用程序具有平等的地位,可以调用内置的应用程序,也可以替换内置的应用程序


23.Activity 与 Task的启动模式有哪些,它们含义具体是什么?

答:在一个activity中,有多次调用startActivity来启动另一个activity,要想只生成一个activity实例,可以设置启动模式。

    一个activity有四种启动模式:standed,signleTop,singleTask,singleInstance

    Standed:标准模式,一调用startActivity()方法就会产生一个新的实例。

    SingleTop:如果已经有一个实例位于activity栈顶,就不产生新的实例,而只是调用activity中的newInstance()方法。如果不位于栈顶,会产生一个新的实例。

    singleTask:会在一个新的task中产生这个实例,以后每次调用都会使用这个,不会去产生新的实例了。

    SingleInstance:这个和singleTask基本一样,只有一个区别:在这个模式下的activity实例所处的task中,只能有这个activity实例,不能有其他实例

24.Application类的作用:

答:API里的第一句是:

Base class for those who need to maintain global application state

如果想在整个应用中使用全局变量,在java中一般是使用静态变量,public类型;而在android中如果使用这样的全局变量就不符合Android的框架架构,但是可以使用一种更优雅的方式就是使用Application context。 
  首先需要重写Application,主要重写里面的onCreate方法,就是创建的时候,初始化变量的值。然后在整个应用中的各个文件中就可以对该变量进行操作了。 
  启动Application时,系统会创建一个PID,即进程ID,所有的Activity就会在此进程上运行。那么我们在Application创建的时候初始化全局变量,同一个应用的所有Activity都可以取到这些全局变量的值,换句话说,我们在某一个Activity中改变了这些全局变量的值,那么在同一个应用的其他Activity中值就会改变

25.说明onSaveInstanceState() 和 onRestoreInstanceState()在什么时候被调用:

答:Activity的 onSaveInstanceState() 和 onRestoreInstanceState()并不是生命周期方法,它们不同于 onCreate()、onPause()等生命周期方法,它们并不一定会被触发。当应用遇到意外情况(如:内存不足、用户直接按Home键)由系统销毁一个Activity时,onSaveInstanceState()才会被调用。但是当用户主动去销毁一个Activity时,例如在应用中按返回键,onSaveInstanceState()就不会被调用。因为在这种情况下,用户的行为决定了不需要保存Activity的状态。通常onSaveInstanceState()只适合用于保存一些临时性的状态,而onPause()适合用于数据的持久化保存。

另外,当屏幕的方向发生了改变, Activity会被摧毁并且被重新创建,如果你想在Activity被摧毁前缓存一些数据,并且在Activity被重新创建后恢复缓存的数据。可以重写Activity的 onSaveInstanceState() 和 onRestoreInstanceState()方法。

26.android的service的生命周期?哪个方法可以多次被调用:

答:1)与采用Context.startService()方法启动服务有关的生命周期方法

onCreate() -> onStart() -> onDestroy()

onCreate()该方法在服务被创建时调用,该方法只会被调用一次,无论调用多少次startService()或bindService()方法,服务也只被创建一次。
onStart() 只有采用Context.startService()方法启动服务时才会回调该方法。该方法在服务开始运行时被调用。多次调用startService()方法尽管不会多次创建服务,但onStart() 方法会被多次调用。
onDestroy()该方法在服务被终止时调用。


2)与采用Context.bindService()方法启动服务有关的生命周期方法
onCreate() -> onBind() -> onUnbind() -> onDestroy()

onBind()只有采用Context.bindService()方法启动服务时才会回调该方法。该方法在调用者与服务绑定时被调用,当调用者与服务已经绑定,多次调用Context.bindService()方法并不会导致该方法被多次调用。
onUnbind()只有采用Context.bindService()方法启动服务时才会回调该方法。该方法在调用者与服务解除绑定时被调用。
如果先采用startService()方法启动服务,然后调用bindService()方法绑定到服务,再调用unbindService()方法解除绑定,最后调用bindService()方法再次绑定到服务,触发的生命周期方法如下:
onCreate() ->onStart() ->onBind() ->onUnbind()[重载后的方法需返回true] ->onRebind()

27.android的broadcast的生命周期:

答:1)Broadcast receiver生命周期中仅有一个回调方法: 
void onReceive(Context curContext, Intent broadcastMsg) 
当接收器接收到一条broadcast消息,Android就会调用onReceiver(),并传递给它一个Intent对象,这个对象携带着那条broadcast消息。我们认为仅当执行这个方式时,Broadcast receiver是活动的;这个方法返回时,它就终止了。这就是Broadcast receiver的生命周期。


2)由于Broadcast receiver的生命周期很短,一个带有活动的Broadcast receiver的进程是受保护的,以避免被干掉;但是别忘了有一点,Android会在任意时刻干掉那些携带不再活动的组件的进程,所以很可能会造成这个问题。


3)解决上述问题的方案采用一个Service来完成这项工作,Android会认为那个进程中(Service所在的进程)仍然有在活动的组件。


28.android view,surfaceview,glsurfaceview的区别:

答:SurfaceView是从View基类中派生出来的显示类,直接子类有GLSurfaceView和VideoView,可以看出GL和视频播放以及Camera摄像头一般均使用SurfaceView
SurfaceView和View最本质的区别在于,surfaceView是在一个新起的单独线程中可以重新绘制画面而View必须在UI的主线程中更新画面。 
那么在UI的主线程中更新画面 可能会引发问题,比如你更新画面的时间过长,那么你的主UI线程会被你正在画的函数阻塞。那么将无法响应按键,触屏等消息。 
当使用surfaceView 由于是在新的线程中更新画面所以不会阻塞你的UI主线程。但这也带来了另外一个问题,就是事件同步。比如你触屏了一下,你需要surfaceView中thread处理,一般就需要有一个event queue的设计来保存touch event,这会稍稍复杂一点,因为涉及到线程同步。 

所以基于以上,根据游戏特点,一般分成两类。 

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

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



  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 扫一扫,分享海报

参与评论 您还未登录,请先 登录 后发表或查看评论
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值