您的位置:首页 > 职场人生

Android面试总结(持续更新修改)

2017-08-27 21:51 489 查看

Android面试总结(持续更新修改)

1.Android 的四大组件是哪些,它们的作用?

①Activity是Android程序与用户交互的窗口,是Android构造块中最基本的一种,它需要为保持各界面的状态,做很多持久化的事情,妥善管理生命周期以及一些跳转逻辑

②service:后台服务于 Activity,封装一个完整的功能逻辑实现,接受上层指令,完成相关的事物

③Content Provider:是 Android 提供的第三方应用数据的访问方案,可以派生 Content Provider 类,对外提供数据,可以像数据库一样进行选择排序,屏蔽内部数据的存储细节,向外提供统一的接口模型,大大简化上层应用,对数据的整合提供了更方便的途径

④BroadCastReceiver:接受一种或者多种Intent作触发事件,接受相关消息,做一些简单处理,转换成一条Notification,统一了 Android 的事件广播模型

2.Activity的生命周期,及每个周期的应用场景

比如说手机卫士每次进入某个界面的时候都要看到最新的数据,这个刷新列表的操作 就放在onStart()的方法里面.这样保证每次用户看到的数据都是最新的.

多媒体播放, 播放来电话.

onStop() 视频,

视频声音设置为0 , 记录视频播放的位置 mediaplayer.pause();

onStart() 根据保存的状态恢复现场. mediaplayer.start()

3.Activity的四种启动模式分别是什么,以及他们的特点

意义在于 : 记录这些activity开启的先后顺序,google引入任务栈(task stack)概念,帮助维护好的用户体验。

①standard 默认标准的启动模式, 每次startActivity都是创建一个新的activity的实例。

适用于绝大大数情况

②singleTop 单一顶部,如果要开启的activity在任务栈的顶部已经存在,就不会创建新的实例,而是调用 onNewIntent() 方法。应用场景: 浏览器书签。 避免栈顶的activity被重复的创建,解决用户体验问题。

③singletask 单一任务栈 ,activity只会在任务栈里面存在一个实例。如果要激活的activity,在任务栈里面已经存在,就不会创建新的activity,而是复用这个已经存在的activity,调用 onNewIntent() 方法,并且清空这个activity任务栈上面所有的activity 应用场景:浏览器activity, 整个任务栈只有一个实例,节约内存和cpu的目的注意: activity还是运行在当前应用程序的任务栈里面的。不会创建新的任务栈。

④singleInstance 单态 单例模式单一实例,整个手机操作系统里面只有一个实例存在。不同的应用去打开这个activity共享 公用的同一个activity。他会运行在自己单独,独立的任务栈里面,并且任务栈里面只有他一个实例存在。

5.你后台的Activity被系统 回收怎么办?

答:重写onSaveInstanceState()方法,在此方法中保存需要保存的数据,该方法将会在activity被回收之前调用。通过重写 onRestoreInstanceState()方法可以从中提取保存好的数据

6.Fragment生命周期是什么?

onAttach() – onCreate() – onCreateView()- onActivitCreated – onStart() – onResume() – onPause()

onStop() – onDestoryView() – onDestory() – onDetach()

7.Activity和Fragment如何进行通信,Activity之间通信有几种方式

①Activity向Fragment传递数据 :一 通过bundle设置参数Bundle bundle = new Bundle();bundle.putString(“name”,”加多宝”);sf.setArguments(bundle);二 通过调用接口设置接口 让被接收数据方 实现该方法让发送方调用该接口

②Fragment 向 A ctivity 传递数据 :一 通过 getActivity 强转成 需要接收数据的Activity 调用其方法 二 通过接口 还可以利用 sp sqLite 等

③Activity之间传递数据:通过全局application类共享数据;通过Intent传值;使用静态变量传递数据;使用剪切板传递数据;

8.Service 的生命周期(两种)?

①onCreate – onStartCommand(每次开启都要执行,可以执行多次) – onDestory()——-只要不执行方法onStop(),在内存中永远驻留

②onCreate – onBind(只能执行一次) – onUnbind() – onDestory()——-依赖于Activity,随着Activity的销毁而关闭

注意:同一个服务可以被开启多次,但是关闭只需要一次

9.如何开机就启服务?

通过广播接收者监听开机广播然后在onReceive(Context context, Intent intent)方法 里开启相应的服务

10.Service是否在mainThread中执行, service里面是否能执行耗时的操作?

是 不能

11.Service和IntentService的区别?

Service主要用于后台服务,当应用程序被挂到后台的时候,为了保证应用某些组件仍然可以工作而引入了Service这个概念,那么这里面要强调的是Service不是独立的进程,也不 是独立的线程,它是依赖于应用程序的主线程的,在更多时候不建议在Service中编写耗时的逻辑和操作,否则会引起ANR。IntentService是继承Service的,那么它包含了Service的全部特性,当然也包含service的生命周期,那么与service不同的是,IntentService在执行onCreate操作的时候,内部开了一个线程,去执行耗时操作

12.广播的两种注册方式?

①清单文件注册

②代码注册

IntentFilter filter = new IntentFilter();

filter.addAction(Intent.ACTION_MEDIA_REMOVED);

filter.addAction(Intent.ACTION_MEDIA_EJECT);

filter.addAction(Intent.ACTION_MEDIA_MOUNTED);

filter.addDataScheme(“file”);

sdcardStateReceiver = new SdcardStateChanageReceiver();

registerReceiver(sdcardStateReceiver,filter);

13.IPC(Inter-Progress-Communication)哪些方式?简述AIDL

Messenger Socket AIDL ContentProvider Intent

14.数据存储几种方式?分别是什么?

Android 提供了 5 种方式存储数据:

①使用 SharedPreferences 存储数据:它是 Android 提供的用来存储一些简单配置信息的一种机制,采用了 XML 格式将数据存储到设备中。只能在同一个包内使用,不能在不同的包之间使用。

②文件存储数据:文件存储方式是一种较常用的方法,在 Android 中读取/写入文件的方法,与 Java 中实现 I/O 的程序是完全一样的,提供了 openFileInput()和

openFileOutput()方法来读取设备上的文件。

③SQLite 数据库存储数据:SQLite 是 Android 所带的一个标准的数据库,它支持SQL 语句,它是一个轻量级的嵌入式数据库。

④使用 ContentProvider 存储数据:主要用于应用程序之间进行数据交换,从而能够让其他的应用保存或读取此 Content Provider 的各种数据类型

⑤网络存储数据:通过网络上提供给我们的存储空间来上传(存储)和下载(获取)我们存储在网络空间中的数据信息。

15.listView的优化

①第一层: 复用converterView

if(converterView==null) {

converterView = View.inflate(R.layout.xxx); 10–>11

}

问题: 每次执行getView()都需要执行converterView.findViewById()得到子View

②第二层: 使用ViewHolder, 减少findViewById()的次数

③第三层: 对数据列表进行分页加载显示

1). 自己做: 通过Scroll监听listView.setonScrollListener(scrollListener), 当到达底部时加载下一页列表数据并显示

2). 使用第方开源框架: Android-PullToRefresh或其它

④第四层优化: 图片级缓存处理 参见图片级缓存机制

⑤Item布局,层级越少越好,使用hierarchyview工具查看优化。

⑥item中图片时,异步加载

⑦快速滑动时,不加载图片、

⑧item中图片时,应对图片进行适当压缩

16.简述三级缓存的实现?

例如: 如何根据url根据图片显示?

①根据url从一级缓存(Map

3.是否存在滚动条

当我们做类似上拉加载下一页这样的功能的时候,页面初始的时候需要知道当前WebView是否存在纵向滚动条,如果有则不加载下一页,如果没有则加载下一页直到其出现纵向滚动条。首先继承WebView类,在子类添加下面的代码:

public boolean existVerticalScrollbar () {

return computeVerticalScrollRange() > computeVerticalScrollExtent();

}

computeVerticalScrollRange得到的是可滑动的最大高度,computeVerticalScrollExtent得到的是滚动把手自身的高,当不存在滚动条时,两者的值是相等的。当有滚动条时前者一定是大于后者的。

是否已滚动到页面底部

同样我们在做上拉加载下一页这样的功能时,也需要知道当前页面滚动条所处的状态,如果快到底部,则要发起网络请求数据更新网页。同样继承WebView类,在子类覆盖onScrollChanged方法,具体如下:

@Override

protected void onScrollChanged(int newX, int newY, int oldX, int oldY) {

super.onScrollChanged(newX, newY, oldX, oldY);

if (newY != oldY) {

float contentHeight = getContentHeight() * getScale();

// 当前内容高度下从未触发过, 浏览器存在滚动条且滑动到将抵底部位置

if (mCurrContentHeight != contentHeight && newY > 0 && contentHeight <= newY + getHeight() + mThreshold) {

// TODO Something…

mCurrContentHeight = contentHeight;

}

}

}

上面mCurrContentHeight用于记录上次触发时的网页高度,用来防止在网页总高度未发生变化而目标区域发生连续滚动时会多次触发TODO,mThreshold是一个阈值,当页面底部距离滚动条底部的高度差<=这个值时会触发TODO。

远程网页需访问本地资源

当我们在WebView中加载出从web服务器上拿取的内容时,是无法访问本地资源的,如assets目录下的图片资源,因为这样的行为属于跨域行为(Cross-Domain),而WebView是禁止的。解决这个问题的方案是把html内容先下载到本地,然后使用loadDataWithBaseURL加载html。这样就可以在html中使用 file:///android_asset/xxx.png 的链接来引用包里面assets下的资源了。示例如下:

private void loadWithAccessLocal(final String htmlUrl) {

new Thread(new Runnable() {

public void run() {

try {

final String htmlStr = NetService.fetchHtml(htmlUrl);

if (htmlStr != null) {

TaskExecutor.runTaskOnUiThread(new Runnable() {

@Override

public void run() {

loadDataWithBaseURL(htmlUrl, htmlStr, “text/html”, “UTF-8”, “”);

}

});

return;

}

} catch (Exception e) {

Log.e(“Exception:” + e.getMessage());

}

TaskExecutor.runTaskOnUiThread(new Runnable() {

@Override

public void run() {

onPageLoadedError(-1, “fetch html failed”);

}

});

}

}).start();

}

上面几点需要注意:

从网络上下载html的过程应放在工作线程中

html下载成功后渲染出html的步骤应放在UI主线程,不然WebView会报错

html下载失败则可以使用我们前面讲述的方法来显示自定义错误界面

ViewPager里非首屏WebView点击事件不响应

如果你的多个WebView是放在ViewPager里一个个加载出来的,那么就会遇到这样的问题。ViewPager首屏WebView的创建是在前台,点击时没有问题;而其他非首屏的WebView是在后台创建,滑动到它后点击页面会出现如下错误日志:

20955-20968/xx.xxx.xxx E/webcoreglue﹕ Should not happen: no rect-based-test nodes found

解决这个问题的办法是继承WebView类,在子类覆盖onTouchEvent方法,填入如下代码:

@Override

public boolean onTouchEvent(MotionEvent ev) {

if (ev.getAction() == MotionEvent.ACTION_DOWN) {

onScrollChanged(getScrollX(), getScrollY(), getScrollX(), getScrollY());

}

return super.onTouchEvent(ev);

}

该方法的最先提出在WebView in ViewPager not receive user inputs。

WebView硬件加速导致页面渲染闪烁

4.0以上的系统我们开启硬件加速后,WebView渲染页面更加快速,拖动也更加顺滑。但有个副作用就是,当WebView视图被整体遮住一块,然后突然恢复时(比如使用SlideMenu将WebView从侧边滑出来时),这个过渡期会出现白块同时界面闪烁。解决这个问题的方法是在过渡期前将WebView的硬件加速临时关闭,过渡期后再开启,代码如下:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {

webview.setLayerType(View.LAYER_TYPE_SOFTWARE, null);

}

避免addJavaScriptInterface带来的安全问题

使用开源项目Safe Java-JS WebView Bridge可以很好替代addJavaScriptInterface方法,同时增加了异步回调等支持,并且不存在了安全风险。

@Override

public boolean onTouchEvent(MotionEvent ev) {

boolean ret = super.onTouchEvent(ev);

if (mPreventParentTouch) {

switch (ev.getAction()) {

case MotionEvent.ACTION_MOVE:

requestDisallowInterceptTouchEvent(true);

ret = true;

break;

case MotionEvent.ACTION_UP:

case MotionEvent.ACTION_CANCEL:

requestDisallowInterceptTouchEvent(false);

mPreventParentTouch = false;

break;

}

}

return ret;

}

public void preventParentTouchEvent () {

mPreventParentTouch = true;

}

代码控制的关键在于mPreventParentTouch这个变量,mPreventParentTouch默认为false,当用户touchdown页面元素时通知该WebView将mPreventParentTouch设置为true。示意代码如下:

document.getElementById("targetEle").addEventListener("touchstart",
function(ev) {
HostApp.preventParentTouchEvent(); // 通知WebView阻止祖先对其Touch事件的拦截
}
);
document.getElementById("targetEle").addEventListener("touchmove",
function(ev) {
// todo something on this page
}
);

关于web页面如何通知WebView(即调用Java方法)请参看第8条。

刚提到了上面是一种简单的做法,并不能很好的解决手指滑动过快带来的误操作问题,即当用户快速地滑动时,还是有一定机率会出现ViewPager拦截TouchMove事件而发生了Tab切换而非页面元素做出了响应。要完美解决此问题,就要用到稍微复杂一点的方法(仅是整体消息传递流程复杂一点)。

首先假设在ViewPager之上还有一个父元素叫做ParentViewOnViewPager,当我们接收到页面preventParentTouchEvent通知时就先于ViewPager而进行拦截。如下:

ParentViewOnViewPager.java

public class ParentViewOnViewPager extends FrameLayout {

private MineWebView mDispatchWebView;

public void preventParentTouchEvent (WebView view) {

mDispatchWebView = (MineWebView)view;

}

@Override

public boolean onInterceptTouchEvent(MotionEvent ev) {

if (ev.getAction() == MotionEvent.ACTION_MOVE && mDispatchWebView != null) {

mDispatchWebView.ignoreTouchCancel(true);

return true;

}

return false;

}

@Override

public boolean onTouchEvent(MotionEvent ev) {

if (mDispatchWebView != null){

switch (ev.getAction()) {

case MotionEvent.ACTION_MOVE:

mDispatchWebView.onTouchEvent(ev);

break;

default:

mDispatchWebView.ignoreTouchCancel(false);

mDispatchWebView.onTouchEvent(ev);

mDispatchWebView = null;

break;

}

return true;

}

return super.onTouchEvent(ev);

}

}

即当ParentViewOnViewPager接收到通知时,发起TouchEvent拦截,将拦截到的Touch事件转嫁到装载页面的mDispatchWebView进行事件派发。这样就直接跳过了ViewPager这一层。这里需要注意的是当ParentViewOnViewPager发起拦截时,WebView会接收到一个TouchCancel事件,WebView应该忽略这个事件,以避免页面接收到这个事件而打断整个处理流程。如下代码所示:

MineWebView.java

public class MineWebView extends WebView {

boolean mIgnoreTouchCancel;

public void ignoreTouchCancel (boolean val) {

mIgnoreTouchCancel = val;

}

@Override

public boolean onTouchEvent(MotionEvent ev) {

return ev.getAction() == MotionEvent.ACTION_CANCEL && mIgnoreTouchCancel || super.onTouchEvent(ev);

}

}

另外针对这种解决方案,页面端的JS脚本不用做任何变动。

42.如何优化布局?

1、场景:些布局会隐藏显布局会展示

把View的初始可见View.GONE但是在Inflate布局的时候View仍然会被Inflate,也就是说仍然会创建对象,会被实例化,会被设置属性。也就是说,会耗费内存等资源。

2、替换方式:

推荐的做法是使用android.view.ViewStub,ViewStub是一个轻量级的View,使用非常简单:

mViewStub = (ViewStub) this.findViewById(R.id.viewstub);

mViewStub.inflate();

它是一个占用资源非常小的控件,相当于一个“占位控件”。使用时可以为ViewStub指定一个布局,在Inflate布局的时候,只有ViewStub会被初始化,然后当ViewStub被设置为可见的时或调用了ViewStub.inflate()的时候,ViewStub所指向的布局就会被inflate实例化,此布局文件直接将当前ViewStub替换掉,然后ViewStub的布局属性都会传给它所指向的布局。这样,就可以使用ViewStub在运行时动态显示布局,节约内存资源。

目的: 正确把握住ViewStub的应用场景非常重要,因为使用ViewStub可以优化布局,一般应用在当前布局或控件在用户使用较少情况下,这样可以提高性能,节约内存,加快界面渲染


3、布局重用可以通过这个标签直接加载外部的xml到当前结构中,是复用UI资源的常用标签

4、减少视图层级

标签在UI的结构优化中起着非常重要的作用,它可以删减多余的层级,优化UI。多用于替换FrameLayout(因为所有的Activity视图的根结点都是FrameLayout,如果当前的布局根结点是Framelayout,那么可以用merge替代,减少多余的层级)或者当一个布局包含另一个时,标签消除视图层次结构中多余的视图组。例如你的主布局文件是垂直布局,又include引入了一个垂直布局,这是如果include布局使用的LinearLayout就没意义了,使用的话反而减慢你的UI渲染。这时可以使用标签进行优化。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: