知识点总结
Android
View
-
View的滑动方式
- layout(left,top,right,bottom):通过修改View四个方向的属性值来修改View的坐标,从而滑动View
- offsetLeftAndRight() offsetTopAndBottom():指定偏移量滑动view
- LayoutParams,改变布局参数:layoutParams中保存了view的布局参数,可以通过修改布局参数的方式滑动view
- 通过动画来移动view:注意安卓的平移动画不能改变view的位置参数,属性动画可以
- scrollTo/scrollBy:注意移动的是view的内容,scrollBy(50,50)你会看到屏幕上的内容向屏幕的左上角移动了,这是参考对象不同导致的,你可以看作是它移动的是手机屏幕,手机屏幕向右下角移动,那么屏幕上的内容就像左上角移动了
- scroller:scroller需要配置computeScroll方法实现view的滑动,scroller本身并不会滑动view,它的作用可以看作一个插值器,它会计算当前时间点view应该滑动到的距离,然后view不断的重绘,不断的调用computeScroll方法,这个方法是个空方法,所以我们重写这个方法,在这个方法中不断的从scroller中获取当前view的位置,调用scrollTo方法实现滑动的效果
-
View的事件分发机制
- 点击事件产生后,首先传递给Activity的dispatchTouchEvent方法,通过PhoneWindow传递给DecorView,然后再传递给根ViewGroup,进入ViewGroup的dispatchTouchEvent方法,执行onInterceptTouchEvent方法判断是否拦截,再不拦截的情况下,此时会遍历ViewGroup的子元素,进入子View的dispatchToucnEvent方法,如果子view设置了onTouchListener,就执行onTouch方法,并根据onTouch的返回值为true还是false来决定是否执行onTouchEvent方法,如果是false则继续执行onTouchEvent,在onTouchEvent的Action Up事件中判断,如果设置了onClickListener ,就执行onClick方法。
-
View的加载流程
- View随着Activity的创建而加载,startActivity启动一个Activity时,在ActivityThread的handleLaunchActivity方法中会执行Activity的onCreate方法,这个时候会调用setContentView加载布局创建出DecorView并将我们的layout加载到DecorView中,当执行到handleResumeActivity时,Activity的onResume方法被调用,然后WindowManager会将DecorView设置给ViewRootImpl,这样,DecorView就被加载到Window中了,此时界面还没有显示出来,还需要经过View的measure,layout和draw方法,才能完成View的工作流程。我们需要知道View的绘制是由ViewRoot来负责的,每一个DecorView都有一个与之关联的ViewRoot,这种关联关系是由WindowManager维护的,将DecorView和ViewRoot关联之后,ViewRootImpl的requestLayout会被调用以完成初步布局,通过scheduleTraversals方法向主线程发送消息请求遍历,最终调用ViewRootImpl的performTraversals方法,这个方法会执行View的measure layout 和draw流程
-
View的measure layout 和 draw流程
- 在上边的分析中我们知道,View绘制流程的入口在ViewRootImpl的performTraversals方法,在方法中首先调用performMeasure方法,传入一个childWidthMeasureSpec和childHeightMeasureSpec参数,这两个参数代表的是DecorView的MeasureSpec值,这个MeasureSpec值由窗口的尺寸和DecorView的LayoutParams决定,最终调用View的measure方法进入测量流程
- measure:
- View的measure过程由ViewGroup传递而来,在调用View.measure方法之前,会首先根据View自身的LayoutParams和父布局的MeasureSpec确定子view的MeasureSpec,然后将view宽高对应的measureSpec传递到measure方法中,那么子view的MeasureSpec获取规则是怎样的?分几种情况进行说明
- 父布局是EXACTLY模式:
- 子view宽或高是个确定值,那么子view的size就是这个确定值,mode是EXACTLY(是不是说子view宽高可以超过父view?见下一个)
- 子view宽或高设置为match_parent,那么子view的size就是占满父容器剩余空间,模式就是EXACTLY
- 子view宽或高设置为wrap_content,那么子view的size就是占满父容器剩余空间,不能超过父容器大小,模式就是AT_MOST
- 父布局是AT_MOST模式:
- 子view宽或高是个确定值,那么子view的size就是这个确定值,mode是EXACTLY
- 子view宽或高设置为match_parent,那么子view的size就是占满父容器剩余空间,不能超过父容器大小,模式就是AT_MOST
- 子view宽或高设置为wrap_content,那么子view的size就是占满父容器剩余空间,不能超过父容器大小,模式就是AT_MOST
- 父布局是UNSPECIFIED模式:
- 子view宽或高是个确定值,那么子view的size就是这个确定值,mode是EXACTLY
- 子view宽或高设置为match_parent,那么子view的size就是0,模式就是UNSPECIFIED
- 子view宽或高设置为wrap_content,那么子view的size就是0,模式就是UNSPECIFIED
- 获取到宽高的MeasureSpec后,传入view的measure方法中来确定view的宽高,这个时候还要分情况
- 1、当MeasureSpec的mode是UNSPECIFIED,此时view的宽或者高要看view有没有设置背景,如果没有设置背景,就返回设置的minWidth或minHeight,这两个值如果没有设置默认就是0,如果view设置了背景,就取minWidth或minHeight和背景这个drawable固有宽或者高中的最大值返回
- 2、当MeasureSpec的mode是AT_MOST和EXACTLY,此时view的宽高都返回从MeasureSpec中获取到的size值,这个值的确定见上边的分析。因此如果要通过继承view实现自定义view,一定要重写onMeasure方法对wrap_conten属性做处理,否则,他的match_parent和wrap_content属性效果就是一样的
- 父布局是EXACTLY模式:
- View的measure过程由ViewGroup传递而来,在调用View.measure方法之前,会首先根据View自身的LayoutParams和父布局的MeasureSpec确定子view的MeasureSpec,然后将view宽高对应的measureSpec传递到measure方法中,那么子view的MeasureSpec获取规则是怎样的?分几种情况进行说明
- layout:
- layout方法的作用是用来确定view本身的位置,onLayout方法用来确定所有子元素的位置,当ViewGroup的位置确定之后,它在onLayout中会遍历所有的子元素并调用其layout方法,在子元素的layout方法中onLayout方法又会被调用。layout方法的流程是,首先通过setFrame方法确定view四个顶点的位置,然后view在父容器中的位置也就确定了,接着会调用onLayout方法,确定子元素的位置,onLayout是个空方法,需要继承者去实现。
-
getMeasuredHeight和getHeight方法有什么区别?
- getMeasuredHeight(测量高度)形成于view的measure过程,
- getHeight(最终高度)形成于layout过程,
- 在有些情况下,view需要measure多次才能确定测量宽高,在前几次的测量过程中,得出的测量宽高有可能和最终宽高不一致,但是最终来说,还是会相同,有一种情况会导致两者值不一样,如下,此代码会导致view的最终宽高比测量宽高大100px
public void layout(int l,int t,int r, int b)
{
super.layout(l,t,r+100,b+100);
}
- View的绘制过程遵循如下几步:
- a.绘制背景 background.draw(canvas)
- b.绘制自己(onDraw)
- c.绘制children(dispatchDraw)
- d.绘制装饰(onDrawScrollBars)
- View绘制过程的传递是通过dispatchDraw来实现的,它会遍历所有的子元素的draw方法,如此draw事件就一层一层的传递下去了
- ps:view有一个特殊的方法setWillNotDraw,如果一个view不需要绘制内容,即不需要重写onDraw方法绘制,可以开启这个标记,系统会进行相应的优化。默认情况下,View没有开启这个标记,默认认为需要实现onDraw方法绘制,当我们继承ViewGroup实现自定义控件,并且明确知道不需要具备绘制功能时,可以开启这个标记,如果我们重写了onDraw,那么要显示的关闭这个标记
- 子view宽高可以超过父view?能
- 1.android:clipChildren = "false" 这个属性要设置在父 view 上。代表其中的子View 可以超出屏幕。
- 2.子view 要有具体的大小,一定要比父view 大 才能超出。比如 父view 高度 100px 子view 设置高度150px。子view 比父view大,这样超出的属性才有意义。(高度可以在代码中动态赋值,但不能用wrap_content / match_partent)。
- 3.对父布局还有要求,要求使用linearLayout(反正我用RelativeLayout 是不行)。你如果必须用其他布局可以在需要超出的view上面套一个linearLayout 外面再套其他的布局。
- 4.最外面的布局如果设置的padding 不能超出
- 自定义view需要注意的几点
- 1.让view支持wrap_content属性,在onMeasure方法中针对AT_MOST模式做专门处理,否则wrap_content会和match_parent效果一样(继承ViewGroup也同样要在onMeasure中做这个判断处理)
if(widthMeasureSpec == MeasureSpec.AT_MOST
&& heightMeasureSpec == MeasureSpec.AT_MOST)
{
setMeasuredDimension(200,200); // wrap_content情况下要设置一个默认值,200只是举个例子,最终的值需要计算得到刚好包裹内容的宽高值
}
else if(widthMeasureSpec == MeasureSpec.AT_MOST)
{
setMeasuredDimension(200,heightMeasureSpec );
}
else if(heightMeasureSpec == MeasureSpec.AT_MOST)
{
setMeasuredDimension(heightMeasureSpec ,200);
}
- 2.让view支持padding(onDraw的时候,宽高减去padding值,margin由父布局控制,不需要view考虑),自定义ViewGroup需要考虑自身的padding和子view的margin造成的影响
- 3.在view中尽量不要使用handler,使用view本身的post方法
- 4.在onDetachedFromWindow中及时停止线程或动画
- 5.view带有滑动嵌套情形时,处理好滑动冲突
- ACTION_DOWN没有拦截,ACTION_MOVE ACTION_UP还会拦截吗
面试常问基础
-
1、四大组件是什么
1)Activity:用户可操作的可视化界面,为用户提供一个完成操作指令的窗口。一个Activity通常是一个单独的屏幕,Activity通过Intent来进行通信。Android中会维持一个Activity Stack,当一个新Activity创建时,它就会放到栈顶,这个Activity就处于运行状态。
2)Service:服务,运行在手机后台,适合执行不需和用户交互且还需长期运行的任务。
3)ContentProvider:内容提供者,使一个应用程序的指定数据集提供给其他应用程序,其他应用可通过ContentResolver类从该内容提供者中获取或存入数据。它提供了一种跨进程数据共享的方式,当数据被修改后,ContentResolver接口的notifyChange函数通知那些注册监控特定URI的ContentObserver对象。如果ContentProvider和调用者在同一进程中,ContentProvider的方法(query/insert/update/delete等)和调用者在同一线程中;如果ContentProvider和调用者不在同一进程,ContentProvider方法会运行在它自身进程的一个Binder线程中。
4)Broadcast Receiver: 广播接收者,运用在应用程序间传输信息,可以使用广播接收器来让应用对一个外部事件做出响应。
-
2、四大组件的生命周期和简单用法
-
1)Activity:
onCreate()->onStart()->onResume()->onPause()->onStop()->onDestory()
onCreate():为Activity设置布局,此时界面还不可见;
onStart(): Activity可见但还不能与用户交互,不能获得焦点
onRestart(): 重新启动Activity时被回调
onResume(): Activity可见且可与用户进行交互
onPause(): 当前Activity暂停,不可与用户交互,但还可见。在新Activity启动前被系统调用保存现有的Activity中的持久数据、停止动画等。
onStop(): 当Activity被新的Activity覆盖不可见时被系统调用
onDestory(): 当Activity被系统销毁杀掉或是由于内存不足时调用
-
2)Service
- a) onBind方式绑定的:onCreate->onBind->onUnBind->onDestory(不管调用bindService几次,onCreate只会调用一次,onStart不会被调用,建立连接后,service会一直运行,直到调用unBindService或是之前调用的bindService的Context不存在了,系统会自动停止Service,对应的onDestory会被调用)
- b) startService启动的:onCreate->onStartCommand->onDestory(start多次,onCreate只会被调用一次,onStart会调用多次,该service会在后台运行,直至被调用stopService或是stopSelf)
- c) 又被启动又被绑定的服务,不管如何调用onCreate()只被调用一次,startService调用多少次,onStart就会被调用多少次,而unbindService不会停止服务,必须调用stopService或是stopSelf来停止服务。必须unbindService和stopService(stopSelf)同时都调用了才会停止服务。
-
3)BroadcastReceiver
- a) 动态注册:存活周期是在Context.registerReceiver和Context.unregisterReceiver之间,BroadcastReceiver每次收到广播都是使用注册传入的对象处理的。
- b) 静态注册:进程在的情况下,receiver会正常收到广播,调用onReceive方法;生命周期只存活在onReceive函数中,此方法结束,BroadcastReceiver就销毁了。onReceive()只有十几秒存活时间,在onReceive()内操作超过10S,就会报ANR。进程不存在的情况,广播相应的进程会被拉活,Application.onCreate会被调用,再调用onReceive。
4)ContentProvider:应该和应用的生命周期一样,它属于系统应用,应用启动时,它会跟着初始化,应用关闭或被杀,它会跟着结束。
-
-
3、Activity之间的通信方式
- 1)通过Intent方式传递参数跳转
- 2)通过广播方式
- 3)通过接口回调方式
- 4)借助类的静态变量或全局变量
- 5)借助SharedPreference或是外部存储,如数据库或本地文件
-
4、Activity各种情况下的生命周期
-
- 两个Activity(A->B)切换(B正常的Activity)的生命周期:
- onPause(A)->onCreate(B)->onStart(B)->onResume(B)->oStop(A)
- 这时如果按回退键回退到A
- onPause(B)->onRestart(A)->onStart(A)->onResume(A)->oStop(B)如果在切换到B后调用了A.finish(),则会走到onDestory(A),这时点回退键会退出应用
- 两个Activity(A->B)切换(B透明主题的Activity或是Dialog风格的Acivity)的生命周期:onPause(A)->onCreate(B)->onStart(B)->onResume(B)
这时如果回退到A onPause(B)->onResume(A)->oStop(B)->onDestory(B)
- 两个Activity(A->B)切换(B透明主题的Activity或是Dialog风格的Acivity)的生命周期:onPause(A)->onCreate(B)->onStart(B)->onResume(B)
- Activity(A)启动后点击Home键再回到应用的生命周期:onPause(A)->oStop(A)->onRestart(A)->onStart(A)->onResume(A)
-
-
5、横竖屏切换的时候,Activity 各种情况下的生命周期
- 1)切换横屏时:onSaveInstanceState->onPause->onStop->onDestory->onCreate->onStart->onRestoreInstanceState->onResume
- 切换竖屏时:会打印两次相同的log
- onSaveInstanceState->onPause->onStop->onDestory->onCreate->onStart->onRestoreInstanceState->onResume->onSaveInstanceState->onPause->onStop->onDestory->onCreate->onStart->onRestoreInstanceState->onResume
- 如果在AndroidMainfest.xml中修改该Activity的属性,添加android:configChanges="orientation"
横竖屏切换,打印的log一样,同1)
- 如果在AndroidMainfest.xml中修改该Activity的属性,添加android:configChanges="orientation"
- 如果AndroidMainfest.xml中该Activity中的android:configChanges="orientation|keyboardHidden",则只会打印onConfigurationChanged->
-
6、Activity与Fragment之间生命周期比较
- Fragment生命周期:
- onAttach->onCreate->onCreateView->onActivityCreated->onStart->onResume->onPause->onStop->onDestoryView->onDestory->onDetach
- 切换到该Fragment:onAttach->onCreate->onCreateView->onActivityCreated->onStart->onResume
- 按下Power键:onPause->onSaveInstanceState->onStop
- 点亮屏幕解锁:onStart->onRestoreInstanceState->onResume
- 切换到其他Fragment: onPause->onStop->onDestoryView
- 切回到该Fragment: onCreateView->onActivityCreated->onStart->onResume
- 退出应用:onPause->onStop->onDestoryView->onDestory->onDetach
-
7、Activity上有Dialog的时候按Home键时的生命周期
- AlertDialog并不会影响Activity的生命周期,按Home键后才会使Activity走onPause->onStop,AlertDialog只是一个组件,并不会使Activity进入后台。
-
8、两个Activity 之间跳转时必然会执行的是哪几个方法?
- 前一个Activity的onPause,后一个Activity的onResume
-
9、前台切换到后台,然后再回到前台,Activity生命周期回调方法。弹出Dialog,生命值周期回调方法。
- 1)前台切换到后台,会执行onPause->onStop,再回到前台,会执行onRestart->onStart->onResume
- 弹出Dialog,并不会影响Activity生命周期
-
10、Activity的四种启动模式对比
- 1)standard:标准启动模式(默认),每启动一次Activity,都会创建一个实例,即使从ActivityA startActivity ActivityA,也会再次创建A的实例放于栈顶,当回退时,回到上一个ActivityA的实例。
- singleTop:栈顶复用模式,每次启动Activity,如果待启动的Activity位于栈顶,则不会重新创建Activity的实例,即不会走onCreate->onStart,会直接进入Activity的onPause->onNewIntent->onResume方法
- singleInstance: 单一实例模式,整个手机操作系统里只有一个该Activity实例存在,没有其他Actvity,后续请求均不会创建新的Activity。若task中存在实例,执行实例的onNewIntent()。应用场景:闹钟、浏览器、电话
- singleTask:栈内复用,启动的Activity如果在指定的taskAffinity的task栈中存在相应的实例,则会把它上面的Activity都出栈,直到当前Activity实例位于栈顶,执行相应的onNewIntent()方法。如果指定的task不存在,创建指定的taskAffinity的task,taskAffinity的作用,进入指写taskAffinity的task,如果指定的task存在,将task移到前台,如果指定的task不存在,创建指定的taskAffinity的task. 应用场景:应用的主页面
-
11、Activity状态保存于恢复
- Activity被主动回收时,如按下Back键,系统不会保存它的状态,只有被动回收时,虽然这个Activity实例已被销毁,但系统在新建一个Activity实例时,会带上先前被回收Activity的信息。在当前Activity被销毁前调用onSaveInstanceState(onPause和onStop之间保存),重新创建Activity后会在onCreate后调用onRestoreInstanceState(onStart和onResume之间被调用),它们的参数Bundle用来数据保存和读取的。
- 保存View状态有两个前提:View的子类必须实现了onSaveInstanceState; 必须要设定Id,这个ID作为Bundle的Key
-
12、fragment各种情况下的生命周期
- 正常情况下的生命周期:onAttach->onCreate->onCreateView->onActivityCreated->onStart->onResume->onPause->onStop->onDestoryView->onDestory->onDetach
- Fragment在Activity中replace onPause(旧)->onAttach->onCreate->onCreateView->onActivityCreated->onStart->onResume->onStop(旧)->onDestoryView(旧)
- 如果添加到backStack中,调用remove()方法fragment的方法会走到onDestoryView,但不会执行onDetach(),即fragment本身的实例是存在的,成员变量也存在,但是view被销毁了。如果新替换的Fragment已在BackStack中,则不会执行onAttach->onCreate
-
13、Fragment状态保存onSaveInstanceState是哪个类的方法,在什么情况下使用?
- 在对应的FragmentActivity.onSaveInstanceState方法会调用FragmentController.saveAllState,其中会对mActive中各个Fragment的实例状态和View状态分别进行保存.当Activity在做状态保存和恢复的时候, 在它其中的fragment自然也需要做状态保存和恢复.
- 14、Fragment.startActivityForResult是和FragmentActivity的startActivityForResult?
- 如果希望在Fragment的onActivityResult接收数据,就要调用Fragment.startActivityForResult, 而不是Fragment.getActivity().startActivityForResult。Fragment.startActivityForResult->FragmentActivitymHost.HostCallbacks.onStartActivityFromFragment->FragmentActivity.startActivityFromFragment。如果request=-1则直接调用FragmentActivity.startActivityForResult,它会重新计算requestCode,使其大于0xfffff。
-
15、如何实现Fragment的滑动?
- ViewPager+FragmentPagerAdapter+List<Fragment>
-
16、fragment之间传递数据的方式?
- 1)在相应的fragment中编写方法,在需要回调的fragment里获取对应的Fragment实例,调用相应的方法;
- 2)采用接口回调的方式进行数据传递;
- a) 在Fragment1中创建一个接口及接口对应的set方法;
- b) 在Fragment1中调用接口的方法;
- c)在Fragment2中实现该接口;
- 3)利用第三方开源框架EventBus
-
17、service和activity怎么进行数据交互?
- 1)通过bindService启动服务,可以在ServiceConnection的onServiceConnected中获取到Service的实例,这样就可以调用service的方法,如果service想调用activity的方法,可以在service中定义接口类及相应的set方法,在activity中实现相应的接口,这样service就可以回调接口言法;
- 2)通过广播方式
-
18、说说ContentProvider、ContentResolver、ContentObserver 之间的关系
- ContentProvider实现各个应用程序间数据共享,用来提供内容给别的应用操作。如联系人应用中就使用了ContentProvider,可以在自己应用中读取和修改联系人信息,不过需要获取相应的权限。它也只是一个中间件,真正的数据源是文件或SQLite等。
- ContentResolver内容解析者,用于获取内容提供者提供的数据,通过ContentResolver.notifyChange(uri)发出消息
- ContentObserver内容监听者,可以监听数据的改变状态,观察特定Uri引起的数据库变化,继而做一些相应的处理,类似于数据库中的触发器,当ContentObserver所观察的Uri发生变化时,便会触发它。
-
19、请描述一下广播BroadcastReceiver的理解
- BroadcastReceiver是一种全局监听器,用来实现系统中不同组件之间的通信。有时候也会用来作为传输少量而且发送频率低的数据,但是如果数据的发送频率比较高或者数量比较大就不建议用广播接收者来接收了,因为这样的效率很不好,因为BroadcastReceiver接收数据的开销还是比较大的。
-
20、广播的分类
1)普通广播:完全异步的,可以在同一时刻(逻辑上)被所有接收者接收到,消息传递的效率比较高,并且无法中断广播的传播。
2)有序广播:发送有序广播后,广播接收者将按预先声明的优先级依次接收Broadcast。优先级高的优先接收到广播,而在其onReceiver()执行过程中,广播不会传播到下一个接收者,此时当前的广播接收者可以abortBroadcast()来终止广播继续向下传播,也可以将intent中的数据进行修改设置,然后将其传播到下一个广播接收者。 sendOrderedBroadcast(intent, null);//发送有序广播
3)粘性广播:sendStickyBroadcast()来发送该类型的广播信息,这种的广播的最大特点是,当粘性广播发送后,最后的一个粘性广播会滞留在操作系统中。如果在粘性广播发送后的一段时间里,如果有新的符合广播的动态注册的广播接收者注册,将会收到这个广播消息,虽然这个广播是在广播接收者注册之前发送的,另外一点,对于静态注册的广播接收者来说,这个等同于普通广播。
-
21、广播使用的方式和场景
- 1)App全局监听:在AndroidManifest中静态注册的广播接收器,一般我们在收到该消息后,需要做一些相应的动作,而这些动作与当前App的组件,比如Activity或者Service的是否运行无关,比如我们在集成第三方Push SDK时,一般都会添加一个静态注册的BroadcastReceiver来监听Push消息,当有Push消息过来时,会在后台做一些网络请求或者发送通知等等。
- 2)组件局部监听:这种主要是在Activity或者Service中使用registerReceiver()动态注册的广播接收器,因为当我们收到一些特定的消息,比如网络连接发生变化时,我们可能需要在当前Activity页面给用户一些UI上的提示,或者将Service中的网络请求任务暂停。所以这种动态注册的广播接收器适合特定组件的特定消息处理。
22、在manifest 和代码中如何注册和使用BroadcastReceiver?
1)mainfest中注册:静态注册的广播接收者就是一个常驻在系统中的全局监听器,也就是说如果你应用中配置了一个静态的BroadcastReceiver,而且你安装了应用而无论应用是否处于运行状态,广播接收者都是已经常驻在系统中了。
<receiver android:name=".MyBroadcastReceiver">
<intent-filter>
<action android:name="com.smilexie.test.intent.mybroadcastreceiver"/>
</intent-filter>
</receiver>
- 动态注册:动态注册的广播接收者只有执行了registerReceiver(receiver, filter)才会开始监听广播消息,并对广播消息作为相应的处理。
IntentFilter fiter = new IntentFilter("com.smilexie.test.intent.mybroadcastreceiver");
MyBroadcastReceiver receiver = new MyBroadcastReceiver();
registerReceiver(receiver, filter);
//撤销广播接受者的动态注册
unregisterReceiver(receiver);
-
23、本地广播和全局广播有什么差别?
- 1)LocalBroadcastReceiver仅在自己的应用内发送接收广播,也就是只有自己的应用能收到,数据更加安全。广播只在这个程序里,而且效率更高。只能动态注册,在发送和注册的时候采用LocalBroadcastManager的sendBroadcast方法和registerReceiver方法。
- 2)全局广播:发送的广播事件可被其他应用程序获取,也能响应其他应用程序发送的广播事件(可以通过 exported–是否监听其他应用程序发送的广播 在清单文件中控制) 全局广播既可以动态注册,也可以静态注册。
-
24、AlertDialog,popupWindow,Activity区别
- (1)Popupwindow在显示之前一定要设置宽高,Dialog无此限制。
- (2)Popupwindow默认不会响应物理键盘的back,除非显示设置了popup.setFocusable(true);而在点击back的时候,Dialog会消失。
- (3)Popupwindow不会给页面其他的部分添加蒙层,而Dialog会。
- (4)Popupwindow没有标题,Dialog默认有标题,可以通过dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);取消标题
- (5)二者显示的时候都要设置Gravity。如果不设置,Dialog默认是Gravity.CENTER。
- (6)二者都有默认的背景,都可以通过setBackgroundDrawable(new ColorDrawable(android.R.color.transparent));去掉。
- (7)Popupwindow弹出后,取得了用户操作的响应处理权限,使得其他UI控件不被触发。而AlertDialog弹出后,点击背景,AlertDialog会消失。
-
25、Application 和 Activity 的 Context 对象的区别
- 1)Application Context是伴随应用生命周期;不可以showDialog, startActivity, LayoutInflation。可以startService\BindService\sendBroadcast\registerBroadcast\load Resource values
- 2)Activity Context指生命周期只与当前Activity有关,而Activity Context这些操作都可以,即凡是跟UI相关的,都得用Activity做为Context来处理。
- 一个应用Context的数量=Activity数量+Service数量+1(Application数量)
26 Android属性动画特性
27 如何导入外部数据库?
28 LinearLayout、RelativeLayout、FrameLayout的特性及对比,并介绍使用场景。
29 谈谈对接口与回调的理解
30 回调的原理
31 写一个回调demo
32 介绍下SurfView
33 RecycleView的使用
34 序列化的作用,以及Android两种序列化的区别
-
35 差值器
- 实现Interpolator接口,设置值的变化趋势,SDK中包含了匀速插值器、加速插值器、减速插值器、先加速再减速、弹
--估值器 - 实现TypeEvaluatior接口
- 实现Interpolator接口,设置值的变化趋势,SDK中包含了匀速插值器、加速插值器、减速插值器、先加速再减速、弹
36 Android中数据存储方式
Android源码相关面试专题
- Android属性动画实现原理
- 工作原理:在一定时间间隔内,通过不断对值进行改变,并不断将该值赋给对象的属性,从而实现该对象在该属性上的动画效果。
- 1)ValueAnimator:通过不断控制值的变化(初始值->结束值),将值手动赋值给对象的属性,再不断调用View的invalidate()方法,去不断onDraw重绘view,达到动画的效果。
主要的三种方法:
a) ValueAnimator.ofInt(int values) // 估值器是整型估值器IntEaluator
b) ValueAnimator.ofFloat(float values) // 估值器是浮点型估值器FloatEaluator
c) ValueAnimator.ofObject(ObjectEvaluator, start, end) // 将初始值以对象的形式过渡到结束值,通过操作对象实现动画效果,需要实现Interpolator接口,自定义估值器
```
- 估值器TypeEvalutor,设置动画如何从初始值过渡到结束值的逻辑。
- 插值器(Interpolator)决定值的变化模式(匀速、加速等);
- 估值器(TypeEvalutor)决定值的具体变化数值。
// 自定义估值器,需要实现TypeEvaluator接口
public class ObjectEvaluator implements TypeEvaluator{
// 复写evaluate(),在evaluate()里写入对象动画过渡的逻辑
@Override
public Object evaluate(float fraction, Object startValue, Object endValue) {
// 参数说明
// fraction:表示动画完成度(根据它来计算当前动画的值)
// startValue、endValue:动画的初始值和结束值
... // 写入对象动画过渡的逻辑
return value;
// 返回对象动画过渡的逻辑计算后的值
}
```
-
- ObjectAnimator:直接对对象的属性值进行改变操作,从而实现动画效果
ObjectAnimator继承自ValueAnimator类,底层的动画实现机制还是基本值的改变。它是不断控制值的变化,再不断自动赋给对象的属性,从而实现动画效果。这里的自动赋值,是通过调用对象属性的set/get方法进行自动赋值,属性动画初始值如果有就直接取,没有则调用属性的get()方法获取,当值更新变化时,通过属性的set()方法进行赋值。每次赋值都是调用view的postInvalidate()/invalidate()方法不断刷新视图(实际调用了onDraw()方法进行了重绘视图)
- Object 需要操作的对象; propertyName 需要操作的对象的属性; values动画初始值&结束值,
- 如果是两个值,则从a->b值过渡,如果是三值,则从a->b->c
ObjectAnimator animator = ObjectAnimator.ofFloat(Object object, String propertyName, float ...values);
- ObjectAnimator:直接对对象的属性值进行改变操作,从而实现动画效果
如果采用ObjectAnimator类实现动画,操作的对象的属性必须有get()和set()方法。
其他用法:
AnimatorSet组合动画
AnimatorSet.play(Animator anim) // 播放当前动画 AnimatorSet.after(long delay) // 将现有动画延迟x毫秒后执行 AnimatorSet.with(Animator anim) // 将现有动画和传入的动画同时执行 AnimatorSet.after(Animator anim) // 将现有动画插入到传入的动画之后执行 AnimatorSet.before(Animator anim) // 将现有动画插入到传入的动画之前执行
- ViewPropertyAnimator直接对属性操作,View.animate()返回的是一个ViewPropertyAnimator对象,之后的调用方法都是基于该对象的操作,调用每个方法返回值都是它自身的实例
View.animate().alpha(0f).x(500).y(500).setDuration(500).setInterpolator()
- 3)设置动画监听
Animation.addListener(new AnimatorListener() {
@Override
public void onAnimationStart(Animation animation) {
//动画开始时执行
}
@Override
public void onAnimationRepeat(Animation animation) {
//动画重复时执行
}
@Override
public void onAnimationCancel()(Animation animation) {
//动画取消时执行
}
@Override
public void onAnimationEnd(Animation animation) {
//动画结束时执行
}
});
-
2、补间动画实现原理
- 主要有四种AlpahAnimation\ ScaleAnimation\ RotateAnimation\ TranslateAnimation四种,对透明度、缩放、旋转、位移四种动画。
- 在调用View.startAnimation时,先调用View.setAnimation(Animation)方法给自己设置一个Animation对象,再调用invalidate来重绘自己。
- 在View.draw(Canvas, ViewGroup, long)方法中进行了getAnimation(), 并调用了drawAnimation(ViewGroup, long, Animation, boolean)方法,此方法调用Animation.getTranformation()方法,再调用applyTranformation()方法,该方法中主要是对Transformation.getMatrix().setTranslate/setRotate/setAlpha/setScale来设置相应的值,这个方法系统会以60FPS的频率进行调用。具体是在调Animation.start()方法中会调用animationHandler.start()方法,从而调用了scheduleAnimation()方法,这里会调用mChoreographer.postCallback(Choregrapher.CALLBACK_ANIMATION, this, null)放入事件队列中,等待doFrame()来消耗事件。
当一个 ChildView 要重画时,它会调用其成员函数 invalidate() 函数将通知其 ParentView 这个 ChildView 要重画,这个过程一直向上遍历到 ViewRoot,当 ViewRoot 收到这个通知后就会调用ViewRoot 中的 draw 函数从而完成绘制。View::onDraw() 有一个画布参数 Canvas, 画布顾名思义就是画东西的地方,Android 会为每一个 View 设置好画布,View 就可以调用 Canvas 的方法,比如:drawText, drawBitmap, drawPath 等等去画内容。每一个 ChildView 的画布是由其 ParentView 设置的,ParentView 根据 ChildView 在其内部的布局来调整 Canvas,其中画布的属性之一就是定义和 ChildView 相关的坐标系,默认是横轴为 X 轴,从左至右,值逐渐增大,竖轴为 Y 轴,从上至下,值逐渐增大。
Android 补间动画就是通过 ParentView 来不断调整 ChildView 的画布坐标系来实现的,在ParentView的dispatchDraw方法会被调用。
dispatchDraw()
{
....
Animation a = ChildView.getAnimation()
Transformation tm = a.getTransformation();
Use tm to set ChildView's Canvas;
Invalidate();
....
}
- 这里有两个类:Animation 和 Transformation,这两个类是实现动画的主要的类
- Animation 中主要定义了动画的一些属性比如开始时间、持续时间、是否重复播放等,这个类主要有两个重要的函数:getTransformation 和 applyTransformation,在 getTransformation 中 Animation 会根据动画的属性来产生一系列的差值点,然后将这些差值点传给 applyTransformation,这个函数将根据这些点来生成不同的
- Transformation,Transformation 中包含一个矩阵和 alpha 值,矩阵是用来做平移、旋转和缩放动画的,而 alpha 值是用来做 alpha 动画的(简单理解的话,alpha 动画相当于不断变换透明度或颜色来实现动画),调用 dispatchDraw 时会调用 getTransformation 来得到当前的 Transformation。某一个 View 的动画的绘制并不是由他自己完成的而是由它的父 view 完成。
- 补间动画TranslateAnimation,View位置移动了,可是点击区域还在原来的位置,为什么?
- View在做动画是,根据动画时间的插值,计算出一个Matrix,不停的invalidate,在onDraw中的Canvas上使用这个计算出来的Matrix去draw view的内容。某个view的动画绘制并不是由它自己完成,而是由它的父view完成,使它的父view画布进行了移动,而点击时还是点击原来的画布。使得它看起来变化了。
-
3、Android各个版本API的区别
主要记住一些大版本变化:- android3.0 代号Honeycomb, 引入Fragments, ActionBar,属性动画,硬件加速
- android4.0 代号Ice Cream,API14:截图功能,人脸识别,虚拟按键,3D优化驱动
- android5.0 代号Lollipop API21:调整桌面图标及部件透明度等
- android6.0 代号M Marshmallow API23,软件权限管理,安卓支付,指纹支持,App关联,
- android7.0 代号N Preview API24,多窗口支持(不影响Activity生命周期),增加了JIT编译器,引入了新的应用签名方案APK Signature Scheme v2(缩短应用安装时间和更多未授权APK文件更改保护),严格了权限访问
- android8.0 代号O API26,取消静态广播注册,限制后台进程调用手机资源,桌面图标自适应
- android9.0 代号P API27,加强电池管理,系统界面添加了Home虚拟键,提供人工智能API,支持免打扰模式
-
4、Requestlayout,onlayout,onDraw,DrawChild区别与联系
- requestLayout()方法 :会导致调用measure()过程 和 layout()过程 。 说明:只是对View树重新布局layout过程包括measure()和layout()过程,如果view的l,t,r,b没有必变,那就不会触发onDraw;但是如果这次刷新是在动画里,mDirty非空,就会导致onDraw。
- onLayout()方法(如果该View是ViewGroup对象,需要实现该方法,对每个子视图进行布局)
- onDraw()方法绘制视图本身 (每个View都需要重载该方法,ViewGroup不需要实现该方法)
- drawChild()去重新回调每个子视图的draw()方法
-
5、invalidate和postInvalidate的区别及使用
View.invalidate(): 层层上传到父级,直到传递到ViewRootImpl后触发了scheduleTraversals(),然后整个View树开始重新按照View绘制流程进行重绘任务。- invalidate:在ui线程刷新view
- postInvalidate:在工作线程刷新view(底层还是handler)其实它的原理就是invalidate+handler
View.postInvalidate最终会调用ViewRootImpl.dispatchInvalidateDelayed()方法
public void dispatchInvalidateDelayed(View view, long delayMilliseconds) {
Message msg = mHandler.obtainMessage(MSG_INVALIDATE, view);
mHandler.sendMessageDelayed(msg, delayMilliseconds);
}
```
这里的mHandler是ViewRootHandler实例,在该Handler的handleMessage方法中调用了view.invalidate()方法。
case MSG_INVALIDATE:
((View) msg.obj).invalidate();
break;
-
6、Activity-Window-View三者的差别
- Activity:是安卓四大组件之一,负责界面展示、用户交互与业务逻辑处理;
- Window:就是负责界面展示以及交互的职能部门,就相当于Activity的下属,Activity的生命周期方法负责业务的处理;
- View:就是放在Window容器的元素,Window是View的载体,View是Window的具体展示。
三者的关系:Activity通过Window来实现视图元素的展示,window可以理解为一个容器,盛放着一个个的view,用来执行具体的展示工作。
7、谈谈对Volley的理解
-
8、如何优化自定义View
- 1)在要在onDraw或是onLayout()中去创建对象,因为onDraw()方法可能会被频繁调用,可以在view的构造函数中进行创建对象;
- 2)降低view的刷新频率,尽可能减少不必要的调用invalidate()方法。或是调用带四种参数不同类型的invalidate(),而不是调用无参的方法。无参变量需要刷新整个view,而带参数的方法只需刷新指定部分的view。在onDraw()方法中减少冗余代码。
- 3)使用硬件加速,GPU硬件加速可以带来性能增加。
- 4)状态保存与恢复,如果因内存不足,Activity置于后台被杀重启时,View应尽可能保存自己属性,可以重写onSaveInstanceState和onRestoreInstanceState方法,状态保存。
9、低版本SDK如何实现高版本api?
使用@TargetApi注解·
当代码中有比AndroidManifest中设置的android:minSdkVersion版本更高的方法,此时编译器会提示警告,解决方法是在方法上加上@SuppressLint("NewApi")或者@TargetApi()。但它们仅是屏蔽了android lint错误,在方法中还要判断版本做不同的操作。
@SuppressLint("NewApi")屏蔽一切新api中才能使用的方法报的android lint错误
@TargetApi() 只屏蔽某一新api中才能使用的方法报的android lint错误,如@TargetApi(11)如果在方法中用了只有API14才开始有的方法,还是会报错。-
10、描述一次网络请求的流程
- 1)域名解析
浏览器会先搜索自身DNS缓存且对应的IP地址没有过期;若未找到则搜索操作系统自身的DNS缓存;若还未找到则读本地的hotsts文件;还未找到会在TCP/IP设置的本地DNS服务器上找,如果要查询的域名在本地配置的区域资源中,则完成解析;否则根据本地DNS服务器会请求根DNS服务器;根DNS服务器是13台根DNS,会一级一级往下找。 - 2)TCP三次握手
客户端先发送SYN=1,ACK=0,序列号seq=x报文;(SYN在连接建立时用来同步序号,SYN=1,ACK=0代表这是一个连接请求报文,对方若同意建立连接,则应在响应报文中使SYN=1,ACK=1)
服务器返回SYN=1,ACK=1,seq=y, ack=x+1;
客户端再一次确认,但不用SYN了,回复服务端, ACK=1, seq=x+1, ack=y+1 - 3)建立TCP连接后发起HTTP请求
客户端按照指定的格式开始向服务端发送HTTP请求,HTTP请求格式由四部分组成,分别是请求行、请求头、空行、消息体,服务端接收到请求后,解析HTTP请求,处理完成逻辑,最后返回一个具有标准格式的HTTP响应给客户端。 - 4)服务器响应HTTP请求
服务器接收处理完请求后返回一个HTTP响应消息给客户端,HTTP响应信息格式包括:状态行、响应头、空行、消息体 - 5)浏览器解析HTML代码,请求HTML代码中的资源
浏览器拿到html文件后,就开始解析其中的html代码,遇到js/css/image等静态资源时,向服务器发起一个http请求,如果返回304状态码,浏览器会直接读取本地的缓存文件。否则开启线程向服务器请求下载。 - 6)浏览器对页面进行渲染并呈现给用户
- 7)TCP的四次挥手
当客户端没有东西要发送时就要释放连接(提出中断连接可以是Client也可以是Server),客户端会发送一个FIN=1的没有数据的报文,进入FIN_WAIT状态,服务端收到后会给客户端一个确认,此时客户端不能发送数据,但可接收信息。
- 1)域名解析
11、HttpUrlConnection 和 okhttp关系
两者都可以用来实现网络请求,android4.4之后的HttpUrlConnection的实现是基于okhttpBitmap对象的理解
looper架构
ActivityThread,AMS,WMS的工作原理
自定义View如何考虑机型适配
自定义View的事件
AstncTask+HttpClient 与 AsyncHttpClient有什么区别?
LaunchMode应用场景
AsyncTask 如何使用?
SpareArray原理
请介绍下ContentProvider 是如何实现数据共享的?
AndroidService与Activity之间通信的几种方式
-
IntentService原理及作用是什么?
- 原理:IntentService是继承Service的一个抽象类,它在onCreate()方法中创建了一个HandlerThread,并启动该线程。
- HandlerThread是带有自己消息队列和Looper的线程,根据HandlerThread的looper创建一个Handler,这样IntentService的ServiceHandler的handleMessage()方法就运行在子线程中。handleMessage中调用了onHandleIntent()方法,它是一个抽象方法,继承IntentService类需要实现该方法,把耗时操作放在onHandleIntent()方法中,等耗时操作运行完成后,会调用stopSelf()方法,服务会调用onDestory()方法消毁自己。如果onHandleIntent()中的耗时操作未运行完前就调用了stopSelf()方法,服务调用onDestory()方法,但耗时操作会继续运行,直至运行完毕。如果同时多次启动IntentService,任务会放在一个队列中,onCreate()和onDestory()方法都只会运行一次。
作用:用来处理后台耗时操作,如读取数据库或是本地文件等。
说说Activity、Intent、Service 是什么关系
ApplicationContext和ActivityContext的区别
SP是进程同步的吗?有什么方法做到同步?
谈谈多线程在Android中的使用
进程和 Application 的生命周期
封装View的时候怎么知道view的大小
RecycleView原理
AndroidManifest的作用与理解
Android面试帮助篇
-
1、要做一个尽可能流畅的ListView,你平时在工作中如何进行优化的?
- ①Item布局,层级越少越好,使用hierarchyview工具查看优化。
- ②复用convertView
- ③使用ViewHolder
- ④item中有图片时,异步加载
- ⑤快速滑动时,不加载图片
- ⑥item中有图片时,应对图片进行适当压缩
- ⑦实现数据的分页加载
-
2、对于Android 的安全问题,你知道多少
- ①错误导出组件
- ② 参数校验不严
- ③WebView引入各种安全问题,webview中的js注入
- ④不混淆、不防二次打包
- ⑤明文存储关键信息
- ⑦ 错误使用HTTPS
- ⑧山寨加密方法
- ⑨滥用权限、内存泄露、使用debug签名
-
3、如何缩减APK包大小?
-
代码
- 保持良好的编程习惯,不要重复或者不用的代码,谨慎添加libs,移除使用不到的libs。
- 使用proguard混淆代码,它会对不用的代码做优化,并且混淆后也能够减少安装包的大小。
- native code的部分,大多数情况下只需要支持armabi与x86的架构即可。如果非必须,可以考虑拿掉x86的部分。
-
资源
- 使用Lint工具查找没有使用到的资源。去除不使用的图片,String,XML等等。 assets目录下的资源请确保没有用不上的文件。
- 生成APK的时候,aapt工具本身会对png做优化,但是在此之前还可以使用其他工具如tinypng对图片进行进一步的压缩预处理。
- jpeg还是png,根据需要做选择,在某些时候jpeg可以减少图片的体积。 对于9.png的图片,可拉伸区域尽量切小,另外可以通过使用9.png拉伸达到大图效果的时候尽量不要使用整张大图。
-
策略
- 有选择性的提供hdpi,xhdpi,xxhdpi的图片资源。建议优先提供xhdpi的图片,对于mdpi,ldpi与xxxhdpi根据需要提供有差异的部分即可。
- 尽可能的重用已有的图片资源。例如对称的图片,只需要提供一张,另外一张图片可以通过代码旋转的方式实现。
- 能用代码绘制实现的功能,尽量不要使用大量的图片。例如减少使用多张图片组成animate-list的AnimationDrawable,这种方式提供了多张图片很占空间。
-
-
4、Android与服务器交互的方式中的对称加密和非对称加密是什么?
- 对称加密,就是加密和解密数据都是使用同一个key,这方面的算法有DES。
- 非对称加密,加密和解密是使用不同的key。发送数据之前要先和服务端约定生成公钥和私钥,使用公钥加密的数据可以用私钥解密,反之。这方面的算法有RSA。ssh 和 ssl都是典型的非对称加密。
-
5、设备横竖屏切换的时候,接下来会发生什么?
- 1、不设置Activity的android:configChanges时,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次
- 2、设置Activity的android:configChanges=”orientation”时,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次
- 3、设置Activity的android:configChanges=”orientation|keyboardHidden”时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法
-
6、Android启动Service的两种方式是什么? 它们的适用情况是什么?
- 如果后台服务开始后基本可以独立运行的话,可以用startService。音乐播放器就可以这样用。它们会一直运行直到你调用 stopSelf或者stopService。你可以通过发送Intent或者接收Intent来与正在运行的后台服务通信,但大部分时间,你只是启动服务并让它独立运行。如果你需要与后台服务通过一个持续的连接来比较频繁地通信,建议使用bind()。比如你需要定位服务不停地把更新后的地理位置传给UI。Binder比Intent开发起来复杂一些,但如果真的需要,你也只能使用它。
- startService:生命周期与调用者不同。启动后若调用者未调用stopService而直接退出,Service仍会运行
- bindService:生命周期与调用者绑定,调用者一旦退出,Service就会调用unBind->onDestroy
-
7、谈谈你对Android中Context的理解?
- Context:包含上下文信息(外部值) 的一个参数. Android 中的 Context 分三种,Application Context ,Activity Context ,Service Context.它描述的是一个应用程序环境的信息,通过它我们可以获取应用程序的资源和类,也包括一些应用级别操作,例如:启动一个Activity,发送广播,接受Intent信息等
-
8、Service的onCreate回调在UI线程中吗?
- Service生命周期的各个回调和其他的应用组件一样,是跑在主线程中,会影响到你的UI操作或者阻塞主线程中的其他事情
-
9、请介绍下AsyncTask的内部实现,适用的场景是?
- AsyncTask内部也是Handler机制来完成的,只不过Android提供了执行框架来提供线程池来执行相应地任务,因为线程池的大小问题,所以AsyncTask只应该用来执行耗时时间较短的任务,比如HTTP请求,大规模的下载和数据库的更改不适用于AsyncTask,因为会导致线程池堵塞,没有线程来执行其他的任务,导致的情形是会发生AsyncTask根本执行不了的问题。
-
10、谈谈你对binder机制的理解?
- binder是一种IPC机制,进程间通讯的一种工具.
- Java层可以利用aidl工具来实现相应的接口.
11、Android中进程间通信有哪些实现方式?
Intent,Binder(AIDL),Messenger,BroadcastReceiver-
12、介绍下实现一个自定义view的基本流程
- 1、自定义View的属性 编写attr.xml文件
- 2、在layout布局文件中引用,同时引用命名空间
- 3、在View的构造方法中获得我们自定义的属性 ,在自定义控件中进行读取(构造方法拿到attr.xml文件值)
- 4、重写onMesure
- 5、重写onDraw
-
13、Android中touch事件的传递机制是怎样的?
1、Touch事件传递的相关API有dispatchTouchEvent、onTouchEvent、onInterceptTouchEvent
2、Touch事件相关的类有View、ViewGroup、Activity
3、Touch事件会被封装成MotionEvent对象,该对象封装了手势按下、移动、松开等动作
4、Touch事件通常从Activity#dispatchTouchEvent发出,只要没有被消费,会一直往下传递,到最底层的View。
5、如果Touch事件传递到的每个View都不消费事件,那么Touch事件会反向向上传递,最终交由Activity#onTouchEvent处理.
6、onInterceptTouchEvent为ViewGroup特有,可以拦截事件.
7、Down事件到来时,如果一个View没有消费该事件,那么后续的MOVE/UP事件都不会再给它
-
14、Android多线程的实现方式有哪些?
- Thread & AsyncTask
- Thread 可以与Loop 和 Handler 共用建立消息处理队列
- AsyncTask 可以作为线程池并行处理多任务
-
15、Android开发中何时使用多进程?使用多进程的好处是什么?
- 要想知道如何使用多进程,先要知道Android里的多进程概念。一般情况下,一个应用程序就是一个进程,这个进程名称就是应用程序包名。我们知道进程是系统分配资源和调度的基本单位,所以每个进程都有自己独立的资源和内存空间,别的进程是不能任意访问其他进程的内存和资源的。
那如何让自己的应用拥有多个进程?
很简单,我们的四大组件在AndroidManifest文件中注册的时候,有个属性是android:process,- 1、这里可以指定组件的所处的进程。默认就是应用的主进程。指定为别的进程之后,系统在启动这个组件的时候,就先创建(如果还没创建的话)这个进程,然后再创建该组件。你可以重载Application类的onCreate方法,打印出它的进程名称,就可以清楚的看见了。再设置android:process属性时候,有个地方需要注意:如果是android:process=”:deamon”,以:开头的名字,则表示这是一个应用程序的私有进程,否则它是一个全局进程。私有进程的进程名称是会在冒号前自动加上包名,而全局进程则不会。一般我们都是有私有进程,很少使用全局进程。他们的具体区别不知道有没有谁能补充一下。
- 2、使用多进程显而易见的好处就是分担主进程的内存压力。我们的应用越做越大,内存越来越多,将一些独立的组件放到不同的进程,它就不占用主进程的内存空间了。当然还有其他好处,有心人会发现Android后台进程里有很多应用是多个进程的,因为它们要常驻后台,特别是即时通讯或者社交应用,不过现在多进程已经被用烂了。典型用法是在启动一个不可见的轻量级私有进程,在后台收发消息,或者做一些耗时的事情,或者开机启动这个进程,然后做监听等。还有就是防止主进程被杀守护进程,守护进程和主进程之间相互监视,有一方被杀就重新启动它。应该还有还有其他好处,这里就不多说了。
- 3、坏处的话,多占用了系统的空间,大家都这么用的话系统内存很容易占满而导致卡顿。消耗用户的电量。应用程序架构会变复杂,应为要处理多进程之间的通信。这里又是另外一个问题了。
- 要想知道如何使用多进程,先要知道Android里的多进程概念。一般情况下,一个应用程序就是一个进程,这个进程名称就是应用程序包名。我们知道进程是系统分配资源和调度的基本单位,所以每个进程都有自己独立的资源和内存空间,别的进程是不能任意访问其他进程的内存和资源的。
-
16、ANR是什么?怎样避免和解决ANR?
-
ANR:Application Not Responding,即应用无响应
ANR一般有三种类型:- 1:KeyDispatchTimeout(5 seconds) –主要类型
按键或触摸事件在特定时间内无响应 - 2:BroadcastTimeout(10 seconds)
BroadcastReceiver在特定时间内无法处理完成 - 3:ServiceTimeout(20 seconds) –小概率类型
Service在特定的时间内无法处理完成
- 1:KeyDispatchTimeout(5 seconds) –主要类型
-
超时的原因一般有两种:
- (1)当前的事件没有机会得到处理(UI线程正在处理前一个事件没有及时完成或者looper被某种原因阻塞住)
- (2)当前的事件正在处理,但没有及时完成
-
UI线程尽量只做跟UI相关的工作,耗时的工作(数据库操作,I/O,连接网络或者其他可能阻碍UI线程的操作)放入单独的线程处理,尽量用Handler来处理UI thread和thread之间的交互。
UI线程主要包括如下:- Activity:onCreate(), onResume(), onDestroy(), onKeyDown(), onClick()
- AsyncTask: onPreExecute(), onProgressUpdate(), onPostExecute(), onCancel()
- Mainthread handler: handleMessage(), post(runnable r)
-
-
17、Android下解决滑动冲突的常见思路是什么?
- 相关的滑动组件, 重写onInterceptTouchEvent,然后判断根据xy值,来决定是否要拦截当前操作
-
18、如何把一个应用设置为系统应用?
- 成为系统应用,首先要在 对应设备的 Android 源码 SDK 下编译,编译好之后:此 Android 设备是 Debug 版本,并且已经 root,直接将此 apk 用 adb 工具 push 到 system/app 或 system/priv-app 下即可。如果非 root 设备,需要编译后重新烧写设备镜像即可。
- 有些权限(如 WRITE_SECURE_SETTINGS ),是不开放给第三方应用的,只能在对应设备源码中编译然后作为系统 app 使用。
-
19、Android内存泄露研究
Android内存泄漏指的是进程中某些对象(垃圾对象)已经没有使用价值了,但是它们却可以直接或间接地引用到gc roots导致无法被GC回收。无用的对象占据着内存空间,使得实际可使用内存变小,形象地说法就是内存泄漏了。
-
场景
- 类的静态变量持有大数据对象
- 静态变量长期维持到大数据对象的引用,阻止垃圾回收。
- 非静态内部类的静态实例
- 非静态内部类会维持一个到外部类实例的引用,如果非静态内部类的实例是静态的,就会间接长期维持着外部类的引用,阻止被回收掉。
- 资源对象未关闭
资源性对象如Cursor、File、Socket,应该在使用后及时关闭。未在finally中关闭,会导致异常情况下资源对象未被释放的隐患。 - 注册对象未反注册
未反注册会导致观察者列表里维持着对象的引用,阻止垃圾回收。 - Handler临时性内存泄露
Handler通过发送Message与主线程交互,Message发出之后是存储在MessageQueue中的,有些Message也不是马上就被处理的。在Message中存在一个 target,是Handler的一个引用,如果Message在Queue中存在的时间越长,就会导致Handler无法被回收。如果Handler是非静态的,则会导致Activity或者Service不会被回收。 - 由于AsyncTask内部也是Handler机制,同样存在内存泄漏的风险。
此种内存泄露,一般是临时性的。
-
20、内存泄露检测有什么好方法?
- 检测:
- 1、DDMS Heap发现内存泄露
dataObject totalSize的大小,是否稳定在一个范围内,如果操作程序,不断增加,说明内存泄露 - 2、使用Heap Tool进行内存快照前后对比
BlankActivity手动触发GC进行前后对比,对象是否被及时回收
- 1、DDMS Heap发现内存泄露
- 定位:
- 1、MAT插件打开.hprof具体定位内存泄露:
查看histogram项,选中某一个对象,查看它的GC引用链,因为存在GC引用链的,说明无法回收 - 2、AndroidStudio的Allocation Tracker:
观测到期间的内存分配,哪些对象被创建,什么时候创建,从而准确定位
- 1、MAT插件打开.hprof具体定位内存泄露:
- 检测:
后台开发
设计模式
- 1.设计模式六大原则
- a.单一职责原则:就一个类来说,应该只有一个引起它变化的原因
一个类做一件事情,避免职责过多。比如这种情况是不太好的,在一个Activity中既有bean文件,又有http请求,还有adapter等等,这就导致我们需要修改任何一个东西的时候都会导致Activity的改变,这样一来就有多个引起它变化的原因,不符合单一职责原则 - b.开放封闭原则:类,模块,函数应该是可以扩展的,但是不可以修改
对于扩展是开放的,对于修改是封闭的。尽量做到面对需求的改变时,我们的代码能保持相对稳定,通过扩展的方式应对变化,而不是修改原有代码实现 - c.里氏替换原则:所有引用基类的地方,必须可以透明的时候其子类的对象
里氏替换原则是实现开放封闭原则的重要方式之一,我们知道,使用基类的地方都可以使用子类去实现,因为子类拥有基类的所有方法,所以在程序设计中尽量使用基类类型对对象进行定义,在运行时确定子类类型。 - d.依赖倒置原则:高层模块不应该依赖于底层模块,两者都应该依赖于抽象,抽象不应该依赖于细节,细节应该依赖于抽象
依赖倒置原则针对的是模块之间的依赖关系,高层模块指调用端,底层模块指具体的实现类,抽象指接口或抽象类,细节就是实现类。该原则的具体表现就是模块间的依赖通过抽象发生,直线类之间不发生直接依赖关系,依赖通过接口或抽象类产生,降低耦合,比如MVP模式下,View层和P层通过接口产生依赖关系 - e.迪米特原则(最少知识原则):一个软件实体应该尽可能少的与其他实体发生相互作用
- 迪米特原则要求我们在设计系统时,尽量减少对象之间的交互
- f.接口隔离原则:一个类对另一个类的依赖应该建立在最小的接口上
接口隔离原则的关键是接口以及这个接口要小,如何小呢,也就是我们要为专门的类创建专门的接口,这个接口只对它有效,不要试图让一个接口包罗万象,要建立最小的依赖关系
- a.单一职责原则:就一个类来说,应该只有一个引起它变化的原因
- 2.设计模式的分类
设计模式分为三类- 创建型设计模式
与对象创建有关包括单例模式,工厂方法模式,抽象工厂模式,建造者模式,原型模式 - 结构型设计模式
结构性设计模式是从程序的结构上解决模块之间的耦合问题,包括适配器模式,代理模式,装饰模式,外观模式,桥接模式,组合模式和享元模式 - 行为型设计模式
主要处理类或对象如何交互及如何分配职责,包括策略模式,模板方法模式,观察者模式,迭代器模式,责任链模式,命令模式,备忘录模式,状态模式,访问者模式,中介模式,解析器模式
- 创建型设计模式