专栏首页向治洪Android面试之高级篇

Android面试之高级篇

结合自己之前去很多大公司的面试经历和自己面别人的一些题,这里做一些总结,Android面试中常见的面试题。

1,Android的Handler运行机制

    要解释Handler的运行机制就要讲几个对象:Message、Handler、Message Queue、Looper。Handler获取当前线程中的looper对象,looper用来从存放Message的   MessageQueue中取出Message,再有Handler进行Message的分发和处理。

Message Queue(消息队列):用来存放通过Handler发布的消息,通常附属于某一个创建它的线程,可以通过Looper.myQueue()得到当前线程的消息队列 Handler:可以发布或者处理一个消息或者操作一个Runnable,通过Handler发布消息,消息将只会发送到与它关联的消息队列,然也只能处理该消息队列中的消息 Looper:是Handler和消息队列之间通讯桥梁,程序组件首先通过Handler把消息传递给Looper,Looper把消息放入队列。Looper也把消息队列里的消息广播给所有的 Handler:Handler接受到消息后调用handleMessage进行处理 Message:消息的类型,在Handler类中的handleMessage方法中得到单个的消息进行处理 在单线程模型下,为了线程通信问题,Android设计了一个Message Queue(消息队列), 线程间可以通过该Message Queue并结合Handler和Looper组件进行信息交换。下面将对它们进行分别介绍: 1. Message     Message消息,理解为线程间交流的信息,处理数据后台线程需要更新UI,则发送Message内含一些数据给UI线程。 2. Handler     Handler处理者,是Message的主要处理者,负责Message的发送,Message内容的执行处理。后台线程就是通过传进来的 Handler对象引用来sendMessage(Message)。而使用Handler,需要implement 该类的 handleMessage(Message)方法,它是处理这些Message的操作内容,例如Update UI。通常需要子类化Handler来实现handleMessage方法。 3. Message Queue     Message Queue消息队列,用来存放通过Handler发布的消息,按照先进先出执行。     每个message queue都会有一个对应的Handler。Handler会向message queue通过两种方法发送消息:sendMessage或post。这两种消息都会插在message queue队尾并按先进先出执行。但通过这两种方法发送的消息执行的方式略有不同:通过sendMessage发送的是一个message对象,会被 Handler的handleMessage()函数处理;而通过post方法发送的是一个runnable对象,则会自己执行。 4. Looper     Looper是每条线程里的Message Queue的管家。Android没有Global的Message Queue,而Android会自动替主线程(UI线程)建立Message Queue,但在子线程里并没有建立Message Queue。所以调用Looper.getMainLooper()得到的主线程的Looper不为NULL,但调用Looper.myLooper() 得到当前线程的Looper就有可能为NULL。对于子线程使用Looper,API Doc提供了正确的使用方法:这个Message机制的大概流程:     1. 在Looper.loop()方法运行开始后,循环地按照接收顺序取出Message Queue里面的非NULL的Message。     2. 一开始Message Queue里面的Message都是NULL的。当Handler.sendMessage(Message)到Message Queue,该函数里面设置了那个Message对象的target属性是当前的Handler对象。随后Looper取出了那个Message,则调用 该Message的target指向的Hander的dispatchMessage函数对Message进行处理。在dispatchMessage方法里,如何处理Message则由用户指定,三个判断,优先级从高到低:     1) Message里面的Callback,一个实现了Runnable接口的对象,其中run函数做处理工作;     2) Handler里面的mCallback指向的一个实现了Callback接口的对象,由其handleMessage进行处理;     3) 处理消息Handler对象对应的类继承并实现了其中handleMessage函数,通过这个实现的handleMessage函数处理消息。     由此可见,我们实现的handleMessage方法是优先级最低的!     3. Handler处理完该Message (update UI) 后,Looper则设置该Message为NULL,以便回收!     在网上有很多文章讲述主线程和其他子线程如何交互,传送信息,最终谁来执行处理信息之类的,个人理解是最简单的方法——判断Handler对象里面的Looper对象是属于哪条线程的,则由该线程来执行!     1. 当Handler对象的构造函数的参数为空,则为当前所在线程的Looper; 2,Android的Activity的四种启动模式和用途

standerd

默认模式,可以不用写配置。在这个模式下,都会默认创建一个新的实例。因此,在这种模式下,可以有多个相同的实例,也允许多个相同Activity叠加。应用场景:绝大多数Activity。

singleTop

栈顶复用模式,如果要开启的activity在任务栈的顶部已经存在,就不会创建新的实例,而是调用 onNewIntent() 方法。避免栈顶的activity被重复的创建。

singleInstance

“singleInstance”独占一个task,其它activity不能存在那个task里;如果它启动了一个新的activity,不管新的activity的launch mode 如何,新的activity都将会到别的task里运行(如同加了FLAG_ACTIVITY_NEW_TASK参数)。

singleTask

栈内复用模式, activity只会在任务栈里面存在一个实例。如果要激活的activity,在任务栈里面已经存在,就不会创建新的activity,而是复用这个已经存在的activity,调用 onNewIntent() 方法,并且清空这个activity任务栈上面所有的activity。

3,解释一下activity、 intent 、intent filter、service、Broadcase、BroadcaseReceiver

一个activity呈现了一个用户可以操作的可视化用户界面;一个service不包含可见的用户界面,而是在后台运行,可以与一个activity绑定,通过绑定暴露出来接口并与其进行通信;一个broadcast receiver是一个接收广播消息并做出回应的component,broadcast receiver没有界面;一个intent是一个Intent对象,它保存了消息的内容。对于activity和service来说,它指定了请求的操作名称和待操作数据的URI,Intent对象可以显式的指定一个目标component。如果这样的话,android会找到这个component(基于manifest文件中的声明)并激活它。但如果一个目标不是显式指定的,android必须找到响应intent的最佳component。它是通过将Intent对象和目标的intent filter相比较来完成这一工作的;一个component的intent filter告诉android该component能处理的intent。intent filter也是在manifest文件中声明的。

4,、Serializable和Parcelable的区别

在使用内存的时候,Parcelable 类比Serializable性能高,所以推荐使用Parcelable类。 1.Serializable在序列化的时候会产生大量的临时变量,从而引起频繁的GC。 传智播客武汉校区就业部出品 务实、创新、质量、分享、专注、责任 32 2.Parcelable不能使用在要将数据存储在磁盘上的情况。尽管Serializable效率低点,但在这 种情况下,还是建议你用Serializable 。 实现: 1.Serializable 的实现,只需要继承 Serializable 即可。这只是给对象打了一个标记,系统会 自动将其序列化。 2.Parcelabel 的实现,需要在类中添加一个静态成员变量 CREATOR,这个变量需要继承 Parcelable.Creator 接口。

public class MyParcelable implements Parcelable {
	private int mData;

	public int describeContents() {
		return 0;
	}

	public void writeToParcel(Parcel out, int flags) {
		out.writeInt(mData);
	}

	public static final Parcelable.Creator<MyParcelable> CREATOR = new Parcelable.Creator<MyParcelable>() {
		public MyParcelable createFromParcel(Parcel in) {
			return new MyParcelable(in);
		}

		public MyParcelable[] newArray(int size) {
			return new MyParcelable[size];
		}
	};

	private MyParcelable(Parcel in) {
		mData = in.readInt();
	}
}

5,什么是内存泄漏,android在什么情况下容易产生内存泄漏

说到内存泄漏就不得不提内存溢出。

内存溢出 out of memory,是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请了一个integer,但给它存了long才能存下的数,那就是内存溢出。

内存泄露 memory leak,是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重。内存溢出导致了内存泄漏。

在Android中常见的内存泄漏原因:

1. 资源释放问题 程序代码的问题,长期保持某些资源,如Context、Cursor、IO流的引用,资源得不到释放 造成内存泄露。 2. 对象内存过大问题 保存了多个耗用内存过大的对象(如Bitmap、XML文件),造成内存超出限制。 3. static关键字的使用问题 static是Java中的一个关键字,当用它来修饰成员变量时,那么该变量就属于该类,而不是 该类的实例。所以用static修饰的变量,它的生命周期是很长的,如果用它来引用一些资源耗费 过多的实例(Context的情况最多),这时就要谨慎对待了。 public class ClassName { private static Context mContext; //省略 } 以上的代码是很危险的,如果将 Activity 赋值到 mContext 的话。那么即使该 Activity 已经 onDestroy,但是由于仍有对象保存它的引用,因此该Activity依然不会被释放。

4. 线程导致内存溢出 线程产生内存泄露的主要原因在于线程生命周期的不可控。

5,数据库游标忘记回收等

那么针对上面的问题我们怎么避免呢

1、图片过大导致OOM Android 中用bitmap时很容易内存溢出,比如报如下错误:Java.lang.OutOfMemoryError : bitmap size exceeds VM budget。 解决方法: 方法1: 等比例缩小图片

BitmapFactory.Options options = new BitmapFactory.Options(); 
options.inSampleSize = 2; //Options 只保存图片尺寸大小,不保存图片到内存 
BitmapFactory.Options opts = new BitmapFactory.Options(); 
opts.inSampleSize = 2; 
Bitmap bmp = null; 
bmp = BitmapFactory.decodeResource(getResources(), mImageIds[position],opts); 
//回收
bmp.recycle();//

方法2:对图片采用软引用,及时地进行recyle()操作

SoftReference<Bitmap> bitmap = new SoftReference<Bitmap>(pBitmap); 
if(bitmap != null){ 
	if(bitmap.get() != null && !bitmap.get().isRecycled()){ 
		bitmap.get().recycle(); 
		bitmap = null; } 
	}

2、查询数据库没有关闭游标

程序中经常会进行查询数据库的操作,但是经常会有使用完毕Cursor后没有关闭的情况。如果

我们的查询结果集比较小,对内存的消耗不容易被发现,只有在常时间大量操作的情况下才会出现内

存问题,这样就会给以后的测试和问题排查带来困难和风险。

3、构造Adapter时,没有使用缓存的 convertView

在使用ListView的时候通常会使用Adapter,那么我们应该尽可能的使用ConvertView。

为什么要使用convertView?

当convertView为空时,用setTag()方法为每个View绑定一个存放控件的ViewHolder对象。

当 convertView 不为空,重复利用已经创建的 view 的时候,使用 getTag()方法获取绑定的

ViewHolder对象,这样就避免了findViewById对控件的层层查询,而是快速定位到控件。

4、Bitmap对象不再使用时调用recycle()释放内存

有时我们会手工的操作Bitmap对象,如果一个Bitmap对象比较占内存,当它不再被使用的时

候,可以调用Bitmap.recycle()方法回收此对象的像素所占用的内存,但这不是必须的,视情况而定。

6、 简述下Android JNI调用过程

1)安装和下载Cygwin,下载AndroidNDK

2)在ndk项目中JNI接口的设计

3)使用C/C++实现本地方法

4)JNI生成动态链接库.so文件

5)将动态链接库复制到java工程,在java工程中调用,运行java工程即可

以上就列举这么多了,其他的大家可以自行搜索。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • android 开发Handler源码剖析

    Android的消息机制主要是Handler的运行机制,而讲Handler的机制,又需要和MessageQueue和Looper结合。MessageQueue中...

    xiangzhihong
  • android 线程那点事

    在操作系统中,线程是操作系统调度的最小单元,同时线程又是一种受限的系统资源,即线程不可能无限制的产生,并且线程的创建和销毁都会有相应的开销,当系统中存在大量的线...

    xiangzhihong
  • 责任链模式

    概述 概念:责任链模式是一种对象的行为模式。在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定...

    xiangzhihong
  • 异步线程大师Handler(源码+图+demo+常见问题)

    Handler 机制 源码+图+常见问题+Demo 详细记录(本文内容略长,但内容较为详细,推荐Android开发者可深入观看.如有问题,欢迎指正)

    Anymarvel
  • Android Handler异步通信:深入详解Handler机制源码

    在多线程的应用场景中,将工作线程中需更新UI的操作信息 传递到 UI主线程,从而实现 工作线程对UI的更新处理,最终实现异步消息的处理

    Carson.Ho
  • LVS负载均衡的转发机制

    LVS是Linux Virtual Server的简写,是国人贡献到LINUX内核的第一个重要模块,是负载均衡的主流技术 LVS是如何把用户请求转给应用服务器...

    dys
  • 游戏上了腾讯云,不做爆款都很难

    根据动画电影改编的3DARPG手游《西游记之大圣归来》由腾讯云合作伙伴靠谱网络助其上云

    腾讯云渠道拓展部官方账号
  • Android-ServiceManager

    ServiceManager在init进程启动之后启动,用来管理系统中的service,那么首先理解一下在init进程启动之后启动这句话类:

    233333
  • 非阻塞算法(Lock-Free)的实现

    上篇文章我们讲到了使用锁会带来的各种缺点,本文将会讲解如何使用非阻塞算法。非阻塞算法一般会使用CAS来协调线程的操作。

    程序那些事
  • 一个有意思的分钱模拟问题

    大家好,我是吴师兄,今天来分享一个有意思的分钱模拟问题,为了帮助大家理解,采取了可视化的方式。

    五分钟学算法

扫码关注云+社区

领取腾讯云代金券