Android面试题(三)

1、**Handler的使用场景?它有什么做用? **java

在使用线程间通讯的时候会使用到Handler,它的做用就是实现线程间的通信。android

通常来讲,若是在Activity中有一些耗时的操做,好比说访问网络、读写sd卡等,那么此时咱们一般会弹出一个加载框,而后会启动一个子线程,让子线程去作耗时的操做。当子线程任务完成以后,那么此时子线程须要通知主线程,让主线程将加载框消失,这时候就须要使用到Handler了。算法

在咱们使用广告轮播条的时候,咱们要让广告轮播条自动滑动起来,这时候,咱们也会使用到Handler,让它发送一个延时消息。sql

因此总结来讲,Handler的做用有两点:一、发送消息 二、处理消息数据库

还须要注意的是,Handler在哪一个线程中建立,那么这个Handler维护的就是那个线程的消息队列。apache

在回答Handler的使用的时候,咱们也能够结合谷歌电子市场这个项目说说对Handler的使用。咱们会在Application的onCreate方法中把主线程的Handler给建立出来,而后经过封装一些方法让别人能够很方便的往主线程的消息队列发消息,就能够避免出现主线程的Handler建立太多的实例对象,也能够方便代码的书写。api

2、你能说说**Handler的机制吗? **缓存

说到Handler的机制,有几个很是重要的对象,分别是:Handler、Message、MessageQueue和Looper安全

任何一个线程,不管是子线程仍是主线程,都维护着一个消息队列,这个消息队列就是MessageQueue,有了这个消息队列以后,是须要不断的从这个消息队列里面取出消息进行处理的,这个就是Looper作的事情。那么,谁往这个消息队列里面丢消息呢?那就是Handler了。服务器

Looper是用来管理所属线程的消息队列MessageQueue的。

每个线程都须要有一个looper,每个looper管理一个MessageQueue.

Handler.sendMessage的意思是将某一个message放到MessageQueue中去,looper是个死循环,不断的读MessageQueue中的新消息。

要让looper的死循环运行起来,得调用Looper.loop()方法。

咱们一般都会在子线程中,发一个消息到主线程中的MessageQueue中去。Handler究竟是往主线程的MessageQueue发送消息呢仍是往子线程的         MessageQueue发送消息呢?这取决于Handler在哪里建立。若是Handler在主线程中建立,那么这个Handler就会把消息发到主线程的消息队列,若是Handler是在子线程中建立,这个Handler就会把消息发到子线程的消息队列。这里须要注意的是,子线程的Looper须要咱们本身手动启动,要调用Looper.prepare()和Looper.loop()方法,主线程的Looper系统已经帮咱们启动了,所以咱们不须要为主线程的Looper调用loop()方法

** **

3、如何从主线程发消息给子线程?主要注意什么?** **

大多状况下,咱们都是从子线程发送消息到主线程中,让主线程进行一些ui操做,其实主线程也是能够发送消息给子线程的。

若是须要实现主线程发送消息给子线程,那么就须要往子线程的消息队列MessageQueue里面放消息。那么谁能往子线程的MessageQueue放消息呢?答案就是子线程的Handler,因此,咱们要建立出子线程的Handler对象。

这时候须要注意的是,当咱们在子线程建立了Handler对象,子线程内部的Looper对象是不工做,因此不能造成消息循环。要让Looper工做起来,得调用Looper.prepare()和Looper.loop()方法。具体的代码以下:

public Handler mHandler;

class LooperThread extends Thread {

public void run() {

Looper.prepare();

mHandler = new Handler() {

public void handleMessage(Message msg) {

}

};

Looper.loop();

}

}

那么如今有人就有疑问了,咱们绝大多数状况下使用Handler并无调用Looper.prepare()和Looper.loop()方法。其实主线程的这两个方法系统在应用程序一启动的时候就帮咱们调用了。具体代码以下:

在ActivityThread中的main方法:

public static final void main(String[] args) {

...

Looper.prepareMainLooper();

...

Looper.loop();

...

}

4、你有没有使用过**AsyncTask****,怎么使用?**** **

使用过,可是用的很少。AsyncTask是用来处理异步任务的,和咱们本身建立子线程,而后经过handler进行通讯的效果差很少,只不过AsyncTask内部维护了一个线程池,用这个线程池来控制住线程的数量。

使用AsyncTask的话,通常咱们会new出这个类的对象,而后执行execute方法。

new AsyncTask<String, String, String>() {

// 2. 运行在主线程中 , 作一些准备操做 .

public void onPreExecute() {

}

// 3. 运行在子线程中 , 作一些耗时的任务 .

public String doInBackground(String... params) {

return null;

}

// 4. 运行主线程中 , result 就是 doInBackground 方法返回的值 . 作一些收尾操做 .

public void onPostExecute(String result) {

}

}.execute(String... params);    // 1. 开始执行异步任务 .

** **

5**、说说你对Application的理解?你在项目中使用到Application吗?具体拿它作什么?**** **

Application能够理解为是一个程序的入口,运行中的应用程序必然会建立Application的实例。它的特色是:生命周期长,onCreate能够认为是程序第一个执行的方法,而且运行在主线程中。在项目中,会有几种场景使用到Application

a、在onCreate中能够判断软件是否第一次运行

@Override

public void onCreate() {

super.onCreate();

SharedPreferences  sp = getSharedPreferences("setting",                    Context.MODE_WORLD_WRITEABLE);

boolean firstStart = sp.getBoolean("version_1", false);

if(!firstStart) {

// 这里能够作一些相似于初始化内置数据的工做

Editor editor = sp.edit();

editor.putBoolean("version_1", true);

editor.commit();

}

}

b、因为生命周期长,能够存储一些数据,也能够帮忙传递数据

c、捕获全局异常,避免弹出Force Close框

public void onCreate() {

super.onCreate();

Thread.setDefaultUncaughtExceptionHandler(this);

}

@Override

public void uncaughtException(Thread thread, Throwable ex) {

Intent intent = new Intent(getApplicationContext(), MainActivity.class);

PendingIntent restartIntent = PendingIntent.getActivity(

getApplicationContext(), 0, intent,

Intent.FLAG_ACTIVITY_NEW_TASK);

AlarmManager mgr = (AlarmManager) getSystemService(Context.ALARM_SERVICE);

mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 1000,

restartIntent);

}

d、获取context对象,这样的话,别的地方就能够很是方便的建立View了。这里须要注意一点的是,建立dialog是不能使用Application的context的,须要使用activity的context

e、建立主线程的handler,供别人使用

f、获取主线程的线程id

@Override

public void onCreate() {

super.onCreate();

//context 常用到。 Toast 、 new View

context = getApplicationContext();

// 获得主线程的 handler 对象,维护的是主线程的 MessageQueue

handler = new Handler();

// 哪一个方法调用了 myTid , myTid 返回的就是那个方法所在的线程 id

mainThreadId = android.os.Process.myTid();

}

6、**Android 中布局的优化措施都有哪些? **

a、尽量减小布局的嵌套层级

尽可能多用RelativeLayout能够颇有效的减小布局的嵌套层级。也可使用       hierarchyviewer这个工具来检查布局层次

b、不用设置没必要要的背景,避免过分绘制        好比父控件设置了背景色,子控件彻底将父控件给覆盖的状况下,那么父控件就没     有必要设置背景。

c、使用标签复用相同的布局代码 d、使用标签减小视图层次结构

e、经过实现 View 的延迟加载

八、如何使用数据库 传统的方式咱们会使用到SQLiteOpenHelper ,而后会本身写一些sql语句,在真实的开发当中,咱们每每会借助于一些第三方框架帮咱们处理数据相关的逻辑,这样能够帮助咱们帮主要精力花在其余功能实现上。我比较经常使用的是LitePal,它能够帮咱们很轻松的进行数据库操做。

九、图片的优化 对于程序来讲,图片的优化主要分为大图片的优化和多图片优化。 对于单张大图片的优化来讲,通常的思路是这样的:咱们的手机是不须要显示过高分辨率的图片,由于手机的分辨率就那么高,图片的分辨率再高也是没有什么效果的。这种状况下,咱们会选择对图片进行缩放。通常咱们会先获得控件的大小,而后再获得图片的大小。根据他们的值,计算出一个比例进行缩放。具体的代码以下: 仅请求图片的大小,inJustDecodeBounds = true,仅请求图片大小,而不会加载图片到内存; public Bitmap decodeBitmap(){ BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; // 经过这个bitmap获取图片的宽和高 Bitmap bitmap = BitmapFactory.decodeFile("/sdcard/test.jpg", options); float realWidth = options.outWidth; float realHeight = options.outHeight; // 计算缩放比 int scale = (int) ((realHeight > realWidth ? realHeight : realWidth) / 控件的高度或宽度); if (scale <= 0) { scale = 1; } options.inSampleSize = scale; options.inJustDecodeBounds = false; // 注意此次要把options.inJustDecodeBounds 设为 false,此次图片是要读取出来的。 bitmap = BitmapFactory.decodeFile("/sdcard/MTXX/test.jpg", options); return bitmap; } 而图片优化的另一方面则是关于多图片的优化。为了程序的流畅度,咱们每每会把以前建立好图片缓存起来,以便下一次显示这张图片的话能够更快的加载。一般是内存缓存--本地缓存。而咱们应用程序的内存大小是有限的,咱们不能无限制的将全部的图片都缓存到内存中,这就须要控制住内存中图片的缓存数量。一般咱们会使用LRUCache来对内存中的图片数量进行一个控制。其实在xutils中的BitmapUtils它自身就带来LRUCache功能。 使用缓存不是为了解决OOM,偏偏正是由于使用了缓存,才形成了OOM

十、内存泄露和内存溢出的差异 内存泄露和内存溢出是彻底不一样的两个概念。内存泄露指的是因为开发人员的疏忽,使得一些不可能被使用到的对象没法被垃圾回收器回收,随着时间的堆积,内存不断增长,直到内存溢出。 因此内存泄露会形成内存溢出,而内存溢出,不必定是内存泄露形成的。 内存泄漏自己不会产生什么危害,真正有危害的是内存泄漏的堆积。Android应用内存 泄漏的的缘由有如下几个: a、register以后没有unregister,add以后没有remove b、查询数据库后没有关闭游标cursor file没有close c、构造Adapter时,没有使用 convertView 重用 d、Bitmap对象不在使用时调用recycle()释放内存 e、对象被生命周期长的对象引用,如activity被静态集合引用致使activity不能释放

十一、怎么解决OOM异常 分析方向分为两个,一个是分析图片,绝大多数的OOM都是因为图片引发的。二是分析是否存在内存泄露的状况。

12、如何进行内存分析** **

heap工具

Heap 视图中部有一个Type叫作data object,即数据对象,也就是咱们的程序中大量存在的类类型的对象。在data object 一行中有一列是“Total Size”,其值就是当前进程中全部Java 数据对象的内存总量,通常状况下,这个值的大小决定了是否会有内存泄漏。

若是这个值不断的增长,那么就判断可能有内存泄露的状况。若是这个值稳定在必定范围以内,会变大,也会变小,那么就说明此时的内存状态挺好。 **

mat:mat是一个更为强大的内存分析工具,能够经过内存快照分析出内存的信息。

有的公司本身也会开发出专门分析内存的工具。

13、写出几种你认为能够提升**Android程序运行效率的方法? **

一、释放主线程

二、优化界面显示的布局,包括层次、背景

三、尽可能减小对象的建立,好比咱们常常会在getView中new 出一个时间监听者

四、避免内存泄露

能够经过traceview工具来查看方法运行的时间。

14、**Android **中如何访问网络 Android 提供了 org.apache.http.HttpClientConnection 和 java.net.HttpURLConnection 两个链接网络对象。除此 之 外 一 般 我 比 较 喜 欢 使 用 xUtils 中的 HttpUtils 功能 , 该 模 块 底 层 使 用 的 就 是org.apache.http.client.HttpClient,使用起来很是方便。还有一些其余的网络访问框架:

OKhttp、Volley等

15、与服务器接口的访问** **

与服务器访问的方式有HTTP和SOCKET两种。

Http:好比说新闻列表啥的,都是事先和服务器的开发人员肯定好具体的访问地址,而后客户端使用get或者post请求去访问那个地址就能获得具体的数据了。

Socket:,服务端开启ServerSocket,客户端开启 socket,而后客户端跟服务端创建长链接,这样实现了客户端跟服务端数据的即时通讯    保证数据安全。像推送、聊天通常就是使用这种访问方式。

关于数据安全:咱们的数据有些是须要安全设置的有些不须要,咱们的新闻类数据不须要特殊的添加安全设置,而用户注册,用户登陆以及用户隐私数据保存是考虑安全性的。用户的密码等信息确定不能进行明文传输的,咱们将用户的密码在本地进行了MD5 算法的加密,而后再传输。同时保存在本地的时候也是加密后的数据。还有须要安全性更高的数据须要经过咱们自定义协议经过Socket 传输。

16、数据传递** **

a、intent:intent能够传递一些基本的数据类型:String、int、float等,也能够传递对象,这个对象必须实现Parcelable接口。实现Parcelable接口表明这个对象能够被序列化到内存中,和Serializable相似,只不过Serializable是将对象写到文件当中。

b、Application:Application的特色:一个应用程序运行的时候只有一个,它的生命周期是最长的,比Activity、Service都长,只要这个程序在运行,不管是否在前台,它都会有一个Application对象。往Application存某一些对象,在页面跳转的时候会很是方便。

c、能够经过view.setTag来进行数据的传递。

17、如何将一个** java 对象序列化到文件里,Android如何经过intent传递对象? **

在 java 中可以被序列化的类必须先实现 Serializable 接口,该接口没有任何抽象方法只是起到一个标记做用。

// 对象输出流

ObjectOutputStream objectOutputStream = new ObjectOutputStream(new

FileOutputStream(new File("D://obj")));

objectOutputStream.writeObject(new User("zhangsan", 100));

objectOutputStream.close();

// 对象输入流

ObjectInputStream objectInputStream = new ObjectInputStream(new

FileInputStream(new File("D://obj")));

User user = (User)objectInputStream.readObject();

System.out.println(user);

objectInputStream.close();

Activity若是但愿经过Intent来传递一个对象的话,这个对象必须实现Parcelable接口。实现Parcelable接口表明这个对象能够被序列化到内存中

19、在项目中,你是如何作版本适配** **

对于版本适配的话,咱们在应用程序中一般最低只会适配到2.2,不过因为如今市场上绝大多数的手机系统版本已是4.0以上的了,如今也慢慢的愈来愈少的适配4.0如下的系统。

在适配的时候,主要有几个jar咱们会常用到:v4包,v7包,nineoldandroid包。

v4:Fragment、ViewPager

v7:ActionBar的适配

nineoldandroid:属性动画的适配

有时候咱们也会在代码中进行系统版本号的判断。好比更改状态栏颜色。咱们会判断系统版本是否是大于等于4.4,若是是的话才会调用相关的api,不然咱们就不修改状态栏颜色。

** **

20、屏幕适配** **

布局适配:多用相对布局

图片适配:在不一样的文件夹下放下对应的图片。drawable-xhdpi、drawable-xxhdpi

单位适配:在布局文件中尽可能写dp,在java代码中尽可能将dp转化成px

代码适配:有时候须要根据屏幕的宽高动态计算控件的宽高

21、自定义控件,有没有本身写过自定义控件** **

这个能够结合本身开发经从来说,好比下拉刷新ListView、滑动开关都是比较好说的。

说到自定义控件,还有几个知识点可能会被问到。

a、自定义属性

b、view的绘制流程:onMeasure、onLayout、onDraw

22、如何实现**SlidingMenu的效果 **

能够借助ViewDragHelper来帮助咱们完成控件拖动的效果。

相关文章
相关标签/搜索