Android--面试中遇到的问题总结(一)

一、handler

一、主要涉及到的类有Handler、Thread、Message、Looper、MessageQueue;

二、.异步消息处理机制的作用主要有刷新UI和线程间通信

三、    .Handler主要是发送消息(sendMessage),处理消息(handlerMessage)的类;

             Message就是在线程之间传递的消息,它可以携带少量信息,在线程间进行信息交换;

             Looper主要是管理消息队列的,一旦调用Loop()方法之后就会进入到一个无线循环中去,每当发现 MessageQueue 中存在一条消息,就会将其取出,并传递到 handleMessage()方法当中,每个线程中也 只会 有一个Looper对象;

             MessageQueue消息队列用来存储handler传过来的消息的,每个线程只有一个消息队列。

二、手机屏幕适配

1.布局文件适配:多用match_parent、wrap_content,weight;多套布局文件

2.图片资源适配:根据手机屏幕分辨率的不同,制作几套图片资源;使用自动拉伸位图:Nine-Patch的图片类

3.布局控件尺寸适配:尽量使用密度无关像素 dp 或独立比例像素 sp 单位指定尺寸

4.自定义控件,利用代码根据屏幕的大小动态设置界面的显示大小

使用 “wrap_content”,系统就会将视图的宽度或高度设置成所需的最小尺寸以适应视图中的内容,而 “match_parent”(在低于 API 级别 8 的级别中称为 “fill_parent”)则会展开组件以匹配其父视图的尺寸。

如果使用 “wrap_content” 和 “match_parent” 尺寸值而不是硬编码的尺寸,视图就会相应地仅使用自身所需的空间或展开以填满可用空间。 此方法可让布局正确适应各种屏幕尺寸和屏幕方向。

weight是线性布局的一个独特的属性,我们可以使用这个属性来按照比例对界面进行分配,完成一些特殊的需求。

三、内存泄漏的原因

1.资源对象没关闭造成的内存泄漏:资源性对象比如(Cursor,File文件等)往往都用了一些缓冲,我们在不使用的时候,应该及时关闭它们,以便它们的缓冲及时回收内存。它们的缓冲不仅存在于 java虚拟机内,还存在于java虚拟机外。如果我们仅仅是把它的引用设置为null,而不关闭它们,往往会造成内存泄漏。因为有些资源性对象,比如 SQLiteCursor(在析构函数finalize(),如果我们没有关闭它,它自己会调close()关闭),如果我们没有关闭它,系统在回收它时也会关闭它,但是这样的效率太低了。因此对于资源性对象在不使用的时候,应该调用它的close()函数,将其关闭掉,然后才置为null.在我们的程序退出时一定要确保我们的资源性对象已经关闭。

2.构造Adapter时,没有使用缓存的convertView:public View getView(int position, ViewconvertView, ViewGroup parent)

来向ListView提供每一个item所需要的view对象。初始时ListView会从BaseAdapter中根据当前的屏幕布局实例化一定数量的 view对象,同时ListView会将这些view对象缓存起来。当向上滚动ListView时,原先位于最上面的list item的view对象会被回收,然后被用来构造新出现的最下面的list item。这个构造过程就是由getView()方法完成的,getView()的第二个形参View convertView就是被缓存起来的list item的view对象(初始化时缓存中没有view对象则convertView是null)。由此可以看出,如果我们不去使用 convertView,而是每次都在getView()中重新实例化一个View对象的话。

3.Bitmap对象不在使用时调用recycle()释放内存

4.试着使用关于application的context来替代和activity相关的context:有一种简单的方法来避免context相关的内存泄漏。最显著地一个是避免context逃出他自己的范围之外。使用Application context。这个context的生存周期和你的应用的生存周期一样长,而不是取决于activity的生存周期。如果你想保持一个长期生存的对象,并且这个对象需要一个context,记得使用application对象。你可以通过调用 Context.getApplicationContext() or Activity.getApplication()来获得。

5.注册没取消造成的内存泄漏

一些Android程序可能引用我们的Anroid程序的对象(比如注册机制)。即使我们的Android程序已经结束了,但是别的引用程序仍然还有对我们的Android程序的某个对象的引用,泄漏的内存依然不能被垃圾回收。调用registerReceiver后未调用unregisterReceiver。

比如:假设我们希望在锁屏界面(LockScreen)中,监听系统中的电话服务以获取一些信息(如信号强度等),则可以在LockScreen中定义一个 PhoneStateListener的对象,同时将它注册到TelephonyManager服务中。对于LockScreen对象,当需要显示锁屏界面的时候就会创建一个LockScreen对象,而当锁屏界面消失的时候LockScreen对象就会被释放掉。

但是如果在释放 LockScreen对象的时候忘记取消我们之前注册的PhoneStateListener对象,则会导致LockScreen无法被垃圾回收。如果不断的使锁屏界面显示和消失,则最终会由于大量的LockScreen对象没有办法被回收而引起OutOfMemory,使得system_process 进程挂掉。

6.集合中对象没清理造成的内存泄漏

我们通常把一些对象的引用加入到了集合中,当我们不需要该对象时,并没有把它的引用从集合中清理掉,这样这个集合就会越来越大。如果这个集合是static的话,那情况就更严重了。

四、Android中的动画

Android中动画分别帧动画、补间动画和属性动画(Android 3.0以后的)

帧动画

帧动画是最容易实现的一种动画,这种动画更多的依赖于完善的UI资源,他的原理就是将一张张单独的图片连贯的进行播放,从而在视觉上产生一种动画的效果;有点类似于某些软件制作gif动画的方式。在有些代码中,我们还会看到android:oneshot=”false” ,这个oneshot 的含义就是动画执行一次(true)还是循环执行多次。



    
    
    

补间动画

补间动画又可以分为四种形式,分别是 alpha(淡入淡出),translate(位移),scale(缩放大小),rotate(旋转)。 
补间动画的实现,一般会采用xml 文件的形式;代码会更容易书写和阅读,同时也更容易复用。Interpolator 主要作用是可以控制动画的变化速率 ,就是动画进行的快慢节奏。pivot 决定了当前动画执行的参考位置



    
    
    
    
    
        ...
    

属性动画

属性动画,顾名思义它是对于对象属性的动画。因此,所有补间动画的内容,都可以通过属性动画实现。属性动画的运行机制是通过不断地对值进行操作来实现的,而初始值和结束值之间的动画过渡就是由ValueAnimator这个类来负责计算的。它的内部使用一种时间循环的机制来计算值与值之间的动画过渡,我们只需要将初始值和结束值提供给ValueAnimator,并且告诉它动画所需运行的时长,那么ValueAnimator就会自动帮我们完成从初始值平滑地过渡到结束值这样的效果。除此之外,ValueAnimator还负责管理动画的播放次数、播放模式、以及对动画设置监听器等。

五、Android中常用的布局

常用的布局:

FrameLayout(帧布局):所有东西依次都放在左上角,会重叠
LinearLayout(线性布局):按照水平和垂直进行数据展示
RelativeLayout(相对布局):以某一个元素为参照物,来定位的布局方式

不常用的布局:

TableLayout(表格布局): 每一个TableLayout里面有表格行TableRow,TableRow里面可以具体定义每一个元素(Android TV上使用)
AbsoluteLayout(绝对布局):用X,Y坐标来指定元素的位置,元素多就不适用。(机顶盒上使用)

新增布局:

PercentRelativeLayout(百分比相对布局)可以通过百分比控制控件的大小。
PercentFrameLayout(百分比帧布局)可以通过百分比控制控件的大小。
六、Android数据存储

  1. 使用SharedPreferences存储数据;它是Android提供的用来存储一些简单配置信息的一种机制,采用了XML格式将数据存储到设备中。只能在同一个包内使用,不能在不同的包之间使用。
  2. 文件存储数据;文件存储方式是一种较常用的方法,在Android中读取/写入文件的方法,与Java中实现I/O的程序是完全一样的,提供了openFileInput()和openFileOutput()方法来读取设备上的文件。
  3. SQLite数据库存储数据;SQLite是Android所带的一个标准的数据库,它支持SQL语句,它是一个轻量级的嵌入式数据库。
  4. 使用ContentProvider存储数据;主要用于应用程序之间进行数据交换,从而能够让其他的应用保存或读取此Content Provider的各种数据类型。
  5. 网络存储数据;通过网络上提供给我们的存储空间来上传(存储)和下载(获取)我们存储在网络空间中的数据信息。
七、Android中的ANR

ANR的全称application not responding 应用程序未响应。

在android中Activity的最长执行时间是5秒。
BroadcastReceiver的最长执行时间则是10秒。
Service的最长执行时间则是20秒。

超出执行时间就会产生ANR。注意:ANR是系统抛出的异常,程序是捕捉不了这个异常的。

解决方法: 
1. 运行在主线程里的任何方法都尽可能少做事情。特别是,Activity应该在它的关键生命周期方法 (如onCreate()和onResume())里尽可能少的去做创建操作。(可以采用重新开启子线程的方式,然后使用Handler+Message 的方式做一些操作,比如更新主线程中的ui等) 
2. 应用程序应该避免在BroadcastReceiver里做耗时的操作或计算。但不再是在子线程里做这些任务(因为 BroadcastReceiver的生命周期短),替代的是,如果响应Intent广播需要执行一个耗时的动作的话,应用程序应该启动一个 Service。

八、 AsyncTask

AsyncTask的三个泛型参数说明

1.第一个参数:传入doInBackground()方法的参数类型

2.第二个参数:传入onProgressUpdate()方法的参数类型

3.第三个参数:传入onPostExecute()方法的参数类型,也是doInBackground()方法返回的类型

运行在主线程的方法:

 
   
onPostExecute()
onPreExecute()
onProgressUpdate(Progress...)

运行在子线程的方法:

doInBackground()

控制AsyncTask停止的方法:

cancel(boolean mayInterruptIfRunning)

AsyncTask的执行分为四个步骤

1.继承AsyncTask。

2.实现AsyncTask中定义的下面一个或几个方法onPreExecute()、doInBackground(Params…)、onProgressUpdate(Progress…)、onPostExecute(Result)。

3.调用execute方法必须在UI thread中调用。

4.该task只能被执行一次,否则多次调用时将会出现异常,取消任务可调用cancel。

九、Android进程间的通讯

1.activity

Activity的跨进程访问与进程内访问略有不同。虽然它们都需要Intent对象,但跨进程访问并不需要指定Context对象和Activity的 Class对象,而需要指定的是要访问的Activity所对应的Action(一个字符串)。有些Activity还需要指定一个Uri(通过 Intent构造方法的第2个参数指定)。

       在android系统中有很多应用程序提供了可以跨进程访问的Activity,例如,下面的代码可以直接调用拨打电话的Activity。

  1. Intent callIntent = new  Intent(Intent.ACTION_CALL, Uri.parse("tel:12345678" );  
  2. startActivity(callIntent);
2. Content Provider  

      Android应用程序可以使用文件或SqlLite数据库来存储数据。Content Provider提供了一种在多个应用程序之间数据共享的方式(跨进程共享数据)。应用程序可以利用Content Provider完成下面的工作

1. 查询数据

2. 修改数据

3. 添加数据

4. 删除数据


3.广播(Broadcast) 
      广播是一种被动跨进程通讯的方式。当某个程序向系统发送广播时,其他的应用程序只能被动地接收广播数据。这就象电台进行广播一样,听众只能被动地收听,而不能主动与电台进行沟通。
在应用程序中发送广播比较简单。只需要调用sendBroadcast方法即可。该方法需要一个Intent对象。通过Intent对象可以发送需要广播的数据。

4.Service

1.利用AIDL Service实现跨进程通信

        这是我个人比较推崇的方式,因为它相比Broadcast而言,虽然实现上稍微麻烦了一点,但是它的优势就是不会像广播那样在手机中的广播较多时会有明显的时延,甚至有广播发送不成功的情况出现。 

       注意普通的Service并不能实现跨进程操作,实际上普通的Service和它所在的应用处于同一个进程中,而且它也不会专门开一条新的线程,因此如果在普通的Service中实现在耗时的任务,需要新开线程。

       要实现跨进程通信,需要借助AIDL(Android Interface Definition Language)。Android中的跨进程服务其实是采用C/S的架构,因而AIDL的目的就是实现通信接口。




转载于:https://www.cnblogs.com/chaoyu/p/6436774.html

你可能感兴趣的:(Android--面试中遇到的问题总结(一))