win10开机checkingmedia解决,win10开机黑屏checkingmedia

以下主要针对往期收录的面试题进行一个分类归纳整理,方便大家统一回顾和参考。本篇是第三集~

强调一下:【因篇幅问题:文中只放部分内容,全部文档需要的可找作者获取。】

第一篇面试题在这: Android中高级进阶开发面试题冲刺合集(一)

第二篇面试题在这: Android中高级进阶开发面试题冲刺合集(二)

Android 方面

Android 考察点比较纷杂,以下针对之前收录的面试题做一个大概的划分:

Android 四大组件相关

1.Activity 与 Fragment 之间常见的几种通信方式?

参考答案:

1、对于Activity和Fragment之间的相互调用 (1)Activity调用Fragment 直接调用就好,Activity一般持有Fragment实例,或者通过Fragment id 或者tag获取到Fragment实例 (2)Fragment调用Activity 通过activity设置监听器到Fragment进行回调,或者是直接在fragment直接getActivity获取到activity实例 2、Activity如果更好的传递参数给Fragment 如果直接通过普通方法的调用传递参数的话,那么在fragment回收后恢复不能恢复这些数据。google给我们提供了一个方法 setArguments(bundle) 可以通过这个方法传递参数给fragment,然后在fragment中用getArguments获取到。能保证在fragment销毁重建后还能获取到数据

2.谈谈 Android 中几种 LaunchMode 的特点和应用场景?

参考答案:

LaunchMode 有四种,分别为 Standard,SingleTop,SingleTask 和 SingleInstance,每种模式的实现原理一楼都做了较详细说明,下面说一下具体使用场景:

Standard: Standard 模式是系统默认的启动模式,一般我们 app 中大部分页面都是由该模式的页面构成的,比较常见的场景是:社交应用中,点击查看用户A信息->查看用户A粉丝->在粉丝中挑选查看用户B信息->查看用户A粉丝… 这种情况下一般我们需要保留用户操作 Activity 栈的页面所有执行顺序。SingleTop: SingleTop 模式一般常见于社交应用中的通知栏行为功能,例如:App 用户收到几条好友请求的推送消息,需要用户点击推送通知进入到请求者个人信息页,将信息页设置为 SingleTop 模式就可以增强复用性。SingleTask: SingleTask 模式一般用作应用的首页,例如浏览器主页,用户可能从多个应用启动浏览器,但主界面仅仅启动一次,其余情况都会走onNewIntent,并且会清空主界面上面的其他页面。SingleInstance: SingleInstance 模式常应用于独立栈操作的应用,如闹钟的提醒页面,当你在A应用中看视频时,闹钟响了,你点击闹钟提醒通知后进入提醒详情页面,然后点击返回就再次回到A的视频页面,这样就不会过多干扰到用户先前的操作了。

3.BroadcastReceiver 与 LocalBroadcastReceiver 有什么区别?

参考答案:

BroadcastReceiver 是跨应用广播,利用Binder机制实现,支持动态和静态两种方式注册方式。LocalBroadcastReceiver 是应用内广播,利用Handler实现,利用了IntentFilter的match功能,提供消息的发布与接收功能,实现应用内通信,效率和安全性比较高,仅支持动态注册。

4.对于 Context,你了解多少?

参考答案:

在 Android 平台上 , Context 是一个基本的概念,它在逻辑上表示一个运行期的“上下文”Context 体现到代码上来说,是个抽象类,其主要表达的行为有:使用系统提供的服务、访问资源 、信息 存储相关以及AMS的交互在 Android 平台上,Activity、Service 和 Application 在本质上都是个 ContextAndroid 中上下文访问应用资源或系统服务的动作都被统一封装进 ContextImpl 类中.Activity、 Service、Application 内部都含有自己的 ContextImpl,每当自己需要访问应用资源或系统服务时,就是 把请求委托给内部的 ContextImplContextWrapper 为 Context 的包装类, 它在做和上下文相关的动作时,基本上都是委托给 ContextImpl 去做ContextImpl 为一个上下文的核心部件,其负责和Android平台进行通信. 就以启动 activity 动作来说,最后 会走到 ContextImpl 的 startActivity(),而这个函数内部大体上是进一步调用 mMainThread.getInstrumentation().execStartActivity(),从而将语义发送给Android系统Context数量 = Activity数量 + Service数量 + 1 上面的1表示 Application 数量,一个应用程序里面可以有多个 Application,可是在配置文件 AndroidManifest.xml中只能注册一个, 只有注册的这个 Application 才是真正的 Application

5.IntentFilter 是什么?有哪些使用场景?匹配机制是怎样的?

参考答案:

1.IntentFilter是意图过滤器,常用于Intent的隐式调用匹配。 2.IntentFilter有3种匹配规则,分别是action、categroy、data。

action的匹配原则: IntentFilter可以有多个action,Intent最多能有1个。 1.如果IntentFilter中不存在action,那么所有的intent都无法通过。 2.如果IntentFilter存在action。 a.如果intent不存在,那么可以通过。 b.如果intent存在,那么intent中的action必须是IntentFilter中的其中一个,对比区分大小写。

category的匹配原则: IntentFilter可以有多个category,Intent也可以有多个。 1.如果IntentFilter不存在category,那么所有的intent都无法通过,因为隐式调用的时候,系统默认给Intent附加了“android.intent.category.DEFAULT”。 2.如果IntentFilter存在category a.如果intent不存在,可以通过。 b.如果intent存在,那么intent中的所有category都包含在IntentFilter中,才可以通过。

data的匹配原则: IntentFilter可以有多个data,Intent最多能有1个。 IntentFilter和Intent完全匹配才能通过,也适用于通配符。

匹配规则: Intent需要匹配多组intent-fliter中的任意一组,每一组包含action、data、category,即Intent必须同时满足这三者的过滤规则。 在同一个应用中,尽量使用显示意图,因为显示意图比隐式意图的效率高。

6.谈一谈 startService 和 bindService 方法的区别,生命周期以及使用场景?

参考答案:

1、生命周期上的区别

执行startService时,Service会经历onCreate->onStartCommand。当执行stopService时,直接调用onDestroy方法。调用者如果没有stopService,Service会一直在后台运行,下次调用者再起来仍然可以stopService。执行bindService时,Service会经历onCreate->onBind。这个时候调用者和Service绑定在一起。调用者调用unbindService方法或者调用者Context不存在了(如Activity被finish了),Service就会调用onUnbind->onDestroy。这里所谓的绑定在一起就是说两者共存亡了。多次调用startService,该Service只能被创建一次,即该Service的onCreate方法只会被调用一次。但是每次调用startService,onStartCommand方法都会被调用。Service的onStart方法在API 5时被废弃,替代它的是onStartCommand方法。第一次执行bindService时,onCreate和onBind方法会被调用,但是多次执行bindService时,onCreate和onBind方法并不会被多次调用,即并不会多次创建服务和绑定服务。

2、调用者如何获取绑定后的Service的方法

onBind回调方法将返回给客户端一个IBinder接口实例,IBinder允许客户端回调服务的方法,比如得到Service运行的状态或其他操作。我们需要IBinder对象返回具体的Service对象才能操作,所以说具体的Service对象必须首先实现Binder对象。

3、既使用startService又使用bindService的情况

如果一个Service又被启动又被绑定,则该Service会一直在后台运行。首先不管如何调用,onCreate始终只会调用一次。对应startService调用多少次,Service的onStart方法便会调用多少次。Service的终止,需要unbindService和stopService同时调用才行。不管startService与bindService的调用顺序,如果先调用unbindService,此时服务不会自动终止,再调用stopService之后,服务才会终止;如果先调用stopService,此时服务也不会终止,而再调用unbindService或者之前调用bindService的Context不存在了(如Activity被finish的时候)之后,服务才会自动停止。那么,什么情况下既使用startService,又使用bindService呢?如果你只是想要启动一个后台服务长期进行某项任务,那么使用startService便可以了。如果你还想要与正在运行的Service取得联系,那么有两种方法:一种是使用broadcast,另一种是使用bindService。前者的缺点是如果交流较为频繁,容易造成性能上的问题,而后者则没有这些问题。因此,这种情况就需要startService和bindService一起使用了。另外,如果你的服务只是公开一个远程接口,供连接上的客户端(Android的Service是C/S架构)远程调用执行方法,这个时候你可以不让服务一开始就运行,而只是bindService,这样在第一次bindService的时候才会创建服务的实例运行它,这会节约很多系统资源,特别是如果你的服务是远程服务,那么效果会越明显(当然在Servcie创建的是偶会花去一定时间,这点需要注意)。

4、本地服务与远程服务

本地服务依附在主进程上,在一定程度上节约了资源。本地服务因为是在同一进程,因此不需要IPC,也不需要AIDL。相应bindService会方便很多。缺点是主进程被kill后,服务变会终止。远程服务是独立的进程,对应进程名格式为所在包名加上你指定的android:process字符串。由于是独立的进程,因此在Activity所在进程被kill的是偶,该服务依然在运行。缺点是该服务是独立的进程,会占用一定资源,并且使用AIDL进行IPC稍微麻烦一点。对于startService来说,不管是本地服务还是远程服务,我们需要做的工作都一样简单。

7.Service 如何进行保活?

参考答案:

1:跟各大系统厂商建立合作关系,把App加入系统内存清理的白名单

2:白色保活

用startForeground()启动前台服务,这是官方提供的后台保活方式,不足的就是通知栏会常驻一条通知,像360的状态栏。

3:灰色保活

开启前台Service,开启另一个Service将通知栏移除,其oom_adj值还是没变的,这样用户就察觉不到app在后台保活。 用广播唤醒自启,像开机广播、网络切换广播等,但在国产Rom中几乎都被堵上了。

多个app关联唤醒,就像BAT的全家桶,打开一个App的时候会启动、唤醒其他App,包括一些第三方推送也是,对于大多数单独app,比较难以实现。

4:黑色保活

1 像素activity保活方案,监听息屏事件,在息屏时启动个一像素的activity,提升自身优先级; Service中循环播放一段无声音频,伪装音乐app,播放音乐中的app优先级还是蛮高的,也能很大程度保活效果较好,但耗电量高,谨慎使用; 双进程守护,这在国产rom中几乎没用,因为划掉app会把所有相关进程都杀死。 3、实现过程:

1)、用startForeground()启动前台服务

前台Service,使用startForeground这个Service尽量要轻,不要占用过多的系统资源,否则系统在资源紧张时,照样会将其杀死。

DaemonService.java

可以参考下面的 Android实现进程保活方案解析

8.简单介绍下 ContentProvider 是如何实现数据共享的?

参考答案:

当一个应用程序要把自己的数据暴露给其他程序时,可以通过ContentProvider来实现。 其他应用可以通过ContenrResolver来操作ContentProvider暴露的数据。

如果应用程序A通过ContentProvider暴露自己的数据操作接口,那么不管A 是否启动,其他程序都可以通过该接口来操作A的内部数据,常有增、删、查、改。

ContentProvider是以Uri的形式对外提供数据,ContenrResolver是根据Uri来访问数据。

步骤

定义自己的ContentProvider类,该类需要继承Android系统提供的ContentProvider基类。

在Manifest.xml 文件中注册ContentProvider,(四大组件的使用都需要在Manifest文件中注册) 注册时需要绑定一个URL。

例如: android:authorities="com.myit.providers.MyProvider" 说明:authorities就相当于为该ContentProvider指定URL。 注册后,其他应用程序就可以通过该Uri来访问MyProvider所暴露的数据了。 其他程序使用ContentResolver来操作。

调用Activity的ContentResolver获取ContentResolver对象 调用ContentResolver的insert(),delete(),update(),query()进行增删改查。

一般来说,ContentProvider是单例模式,也就是说,当多个应用程序通过ContentResolver来操作ContentProvider提供的数据时,ContentResolver调用的数据操作将会委托给同一个ContentResolver。

9.说下切换横竖屏时 Activity 的生命周期变化?

参考答案:

竖屏: 启动:onCreat->onStart->onResume. 切换横屏时: onPause-> onSaveInstanceState ->onStop->onDestory

onCreat->onStart->onSaveInstanceState->onResume.

但是,我们在如果配置这个属性:android:configChanges="orientation|keyboardHidden|screenSize" 就不会在调用Activity的生命周期,只会调用onConfigurationChanged方法

10.Activity 中 onNewIntent 方法的调用时机和使用场景?

参考答案:

Activity 的 onNewIntent方法的调用可总结如下:

在该Activity的实例已经存在于Task和Back stack中(或者通俗的说可以通过按返回键返回到该Activity )时,当使用intent来再次启动该Activity的时候,如果此次启动不创建该Activity的新实例,则系统会调用原有实例的onNewIntent()方法来处理此intent.

且在下面情况下系统不会创建该Activity的新实例:

1,如果该Activity在Manifest中的android:launchMode定义为singleTask或者singleInstance.

2,如果该Activity在Manifest中的android:launchMode定义为singleTop且该实例位于Back stack的栈顶.

3,如果该Activity在Manifest中的android:launchMode=“singleInstance”,或者intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)标志.

4,如果上述intent中包含 Intent.FLAG_ACTIVITY_CLEAR_TOP 标志和且包含 Intent.FLAG_ACTIVITY_SINGLE_TOP 标志.

5,如果上述intent中包含 Intent.FLAG_ACTIVITY_SINGLE_TOP 标志且该实例位于Back stack的栈顶.

上述情况满足其一,则系统将不会创建该Activity的新实例.

根据现有实例所处的状态不同onNewIntent()方法的调用时机也不同,总的说如果系统调用onNewIntent()方法则系统会在onResume()方法执行之前调用它.这也是官方API为什么只说"you can count on onResume() being called after this method",而不具体说明调用时机的原因.

11.Intent 传输数据的大小有限制吗?如何解决?

参考答案:

先说结论:

有大小限制

再说原因:

Intent 是消息传递对象,用于各组件间通信。各组件以及个程序间通信都用到了进程间通信。因此 Intent 的数据传递是基于 Binder 的,Intent 中的数据会存储在 Bundle 中,然后 IPC 过程中会将各个数据以 Parcel 的形式存储在 Binder 的事物缓冲区(Binder transaction buffer)进程传递,而 Binder 的事物缓冲区有个固定的大小,大小在 1M 附近。

因为这 1M 大小是当前进程共享的,Intent 中也会带有其他相关的必要信息,所以实际使用中比这个数字要小很多。

解决方式:

降低传递数据的大小,或考虑其他方式,见2;IPC: 将大数据缓存到文件,或者存入数据库,或者图片使用 id 等;使用 Socket;非 IPC:可以考虑共享内存,EventBus 等

12.说说 ContentProvider、ContentResolver、ContentObserver 之间的关系?

参考答案:

ContentProvider * 内容提供者, 用于对外提供数据,比如联系人应用中就是用了ContentProvider, * 一个应用可以实现ContentProvider来提供给别的应用操作,通过ContentResolver来操作别的应用数据

ContentResolver * 内容解析者, 用于获取内容提供者提供的数据 * ContentResolver.notifyChange(uri)发出消息

ContentObserver * 内容监听者,可以监听数据的改变状态 * 观察(捕捉)特定的Uri引起的数据库的变化 * ContentResolver.registerContentObserver()监听消息

概括: 使用ContentResolver来获取ContentProvider提供的数据, 同时注册ContentObserver监听数据的变化

13.说说 Activity 加载的流程?

参考答案:

App 启动流程(基于Android8.0):

点击桌面 App 图标,Launcher 进程采用 Binder IPC(具体为ActivityManager.getService 获取 AMS 实例) 向 system_server 的 AMS 发起 startActivity 请求system_server 进程收到请求后,向 Zygote 进程发送创建进程的请求;Zygote 进程 fork 出新的子进程,即 App 进程App 进程创建即初始化 ActivityThread,然后通过 Binder IPC 向 system_server 进程的 AMS 发起 attachApplication 请求system_server 进程的 AMS 在收到 attachApplication 请求后,做一系列操作后,通知 ApplicationThread bindApplication,然后发送 H.BIND_APPLICATION 消息主线程收到 H.BIND_APPLICATION 消息,调用 handleBindApplication 处理后做一系列的初始化操作,初始化 Application 等system_server 进程的 AMS 在 bindApplication 后,会调用 ActivityStackSupervisor.attachApplicationLocked,之后经过一系列操作,在 realStartActivityLocked 方法通过 Binder IPC 向 App 进程发送 scheduleLaunchActivity 请求;App进程的 binder 线程(ApplicationThread)在收到请求后,通过 handler 向主线程发送 LAUNCH_ACTIVITY 消息;主线程收到 message 后经过 handleLaunchActivity,performLaunchActivity 方法,然后通过反射机制创建目标 Activity;通过 Activity attach 方法创建 window 并且和 Activity 关联,然后设置 WindowManager 用来管理 window,然后通知 Activity 已创建,即调用 onCreate然后调用 handleResumeActivity,Activity 可见

补充:

ActivityManagerService 是一个注册到 SystemServer 进程并实现了 IActivityManager 的 Binder,可以通过 ActivityManager 的 getService 方法获取 AMS 的DL对象,进而调用 AMS 方法ApplicationThread 是 ActivityThread 的内部类,是一个实现了 IApplicationThread 的 Binder。AMS通过 Binder IPC 经 ApplicationThread 对应用进行控制普通的 Activity 启动和本流程差不多,至少不需要再创建 App 进程了Activity A 启动 Activity B,A 先 pause 然后 B 才能 resume,因此在 onPause 中不能做耗时操作,不然会影响下一个 Activity 的启动Android 异步任务和消息机制

1.HandlerThread 的使用场景和实现原理?

参考答案:

HandlerThread 是 Android 封装的一个线程类,将 Thread 跟 Handler 封装。使用步骤如下:

创建 HandlerThread 实例对象 HandlerThread mHandlerThread = new HandlerThread("mHandlerThread");启动线程 mHandlerThread .start();创建Handler对象,重写handleMessage方法 Handler mHandler= new Handler( mHandlerThread.getLooper() ) { @Override public boolean handleMessage(Message msg) { //消息处理 return true; } });使用工作线程Handler向工作线程的消息队列发送消息: Message message = Message.obtain(); message.what = “2” message.obj = "骚风" mHandler.sendMessage(message);结束线程,即停止线程的消息循环 mHandlerThread.quit();

2.IntentService 的应用场景和内部实现原理?

参考答案:

IntentService 是 Service 的子类,默认为我们开启了一个工作线程,使用这个工作线程逐一处理所有启动请求,在任务执行完毕后会自动停止服务,使用简单,只要实现一个方法 onHandleIntent,该方法会接收每个启动请求的 Intent,能够执行后台工作和耗时操作。

可以启动 IntentService 多次,而每一个耗时操作会以队列的方式在 IntentService 的 onHandlerIntent 回调方法中执行,并且,每一次只会执行一个工作线程,执行完第一个再执行第二个。并且等待所有消息都执行完后才终止服务。

IntentService 适用于 APP 在不影响当前用户的操作的前提下,在后台默默的做一些操作。

IntentService源码:

通过 HandlerThread 单独开启一个名为 IntentService 的线程创建一个名叫 ServiceHandler 的内部 Handler把内部Handler与HandlerThread所对应的子线程进行绑定通过 onStartCommand() 传递给服务 intent,依次插入到工作队列中,并逐个发送给 onHandleIntent()通过 onHandleIntent() 来依次处理所有 Intent 请求对象所对应的任务

使用示例:

public class MyIntentService extends IntentService { public static final String TAG ="MyIntentService"; public MyIntentService() { super("MyIntentService"); } @Override protected void onHandleIntent(@Nullable Intent intent) { boolean isMainThread = Thread.currentThread() == Looper.getMainLooper().getThread(); Log.i(TAG,"is main thread:"+isMainThread); // 这里会打印false,说明不是主线程 // 模拟耗时操作 download(); } /** * 模拟执行下载 */ private void download(){ try { Thread.sleep(5000); Log.i(TAG,"下载完成…"); }catch (Exception e){ e.printStackTrace(); } }}

3.AsyncTask 的优点和缺点?内部实现原理是怎样的?

参考答案:

优点:使用方便 缺点:默认使用串行任务执行效率低,不能充分利用多线程加快执行速度;如果使用并行任务执行,在任务特别多的时候会阻塞UI线程获得CPU时间片,后续做线程收敛需要自定义AsynTask,将其设置为全局统一的线程池,改动量比较大

AsyncTask的实现原理: 1.AsyncTask是一个抽象类,主要由Handler+2个线程池构成,SERIAL_EXECUTOR是任务队列线程池,用于调度任务,按顺序排列执行,THREAD_POOL_EXECUTOR是执行线程池,真正执行具体的线程任务。Handler用于工作线程和主线程的异步通信。

2.AsyncTask<Params,Progress,Result>,其中Params是doInBackground()方法的参数类型,Result是doInBackground()方法的返回值类型,Progress是onProgressUpdate()方法的参数类型。

3.当执行execute()方法的时候,其实就是调用SERIAL_EXECUTOR的execute()方法,就是把任务添加到队列的尾部,然后从头开始取出队列中的任务,调用THREAD_POOL_EXECUTOR的execute()方法依次执行,当队列中没有任务时就停止。

4.AsyncTask只能执行一次execute(params)方法,否则会报错。但是SERIAL_EXECUTOR和THREAD_POOL_EXECUTOR线程池都是静态的,所以可以形成队列。

Q:AsyncTask只能执行一次execute()方法,那么为什么用线程池队列管理 ? 因为SERIAL_EXECUTOR和THREAD_POOL_EXECUTOR线程池都是静态的,所有的AsyncTask实例都共享这2个线程池,因此形成了队列。

Q:AsyncTask的onPreExecute()、doInBackground()、onPostExecute()方法的调用流程?

AsyncTask在创建对象的时候,会在构造函数中创建mWorker(workerRunnable)和mFuture(FutureTask)对象。 mWorker实现了Callable接口的call()方法,在call()方法中,调用了doInBackground()方法,并在最后调用了postResult()方法,也就是通过Handler发送消息给主线程。

在主线程中调用AsyncTask的finish()方法,决定是调用onCancelled()还是onPostExecute(). mFuture实现了Runnable和Future接口,在创建对象时,初始化成员变量mWorker,在run()方法中,调用mWorker的call()方法。

当asyncTask执行execute()方法的时候,会先调用onPreExecute()方法,然后调用SERIAL_EXECUTOR的execute(mFuture),把任务加入到队列的尾部等待执行。执行的时候调用THREAD_POOL_EXECUTOR的execute(mFuture).

4.谈谈你对 Activity.runOnUiThread 的理解?

参考答案:

一般是用来将一个runnable绑定到主线程,在runOnUiThread源码里面会判断当前runnable是否是主线程,如果是直接run,如果不是,通过一个默认的空构造函数handler将runnable post 到looper里面,创建构造函数handler,会默认绑定一个主线程的looper对象

5.Android 的子线程能否做到更新 UI?

参考答案:

子线程是不能直接更新UI的

注意这句话,是不能直接更新,不是不能更新(极端情况下可更新)

绘制过程要保持同步(否则页面不流畅),而我们的主线程负责绘制ui,极端情况就是,在Activity的onResume(含)之前的生命周期中子线程都可以进行更新ui,也就是 onCreate,onStart和onResume,此时主线程的绘制还没开始。

6.谈谈 Android 中消息机制和原理?

参考答案:

首先在主线程创建一个 Handler 对象 ,并重写 handleMessage() 方法。然后当在子线程中需要进行更新UI的操作,我们就创建一个 Message 对象,并通过 Handler 发送这条消息出去。

之后这条消息被加入到 MessageQueue 队列中等待被处理,通过 Looper 对象会一直尝试从 Message Queue 中取出待处理的消息,最后分发回 Handler 的 handleMessage() 方法中。

7.为什么在子线程中创建 Handler 会抛异常?

参考答案:

子线程创建Handler会抛出异常的原因是因为:

在looper里面ThreadLocal sThreadLocal = new ThreadLocal() ThreadLocal 就是为了保存的Looper只能在指定线程中获取Looper ,因为子线程创建new Handler()并没有指定Looper ,所以它就去获取ActivityThread的main方法中创建的looper 。

而此时的这个looper 是受线程保护的,所以子线程是无法获取的,因此抛出异常所以在子线程中没有looper,如果需要在子线程中开启handle要手动创建looper。

8.试从源码角度分析 Handler 的 post 和 sendMessage 方法的区别和应用场景?

参考答案:

post是将一个Runnbale封装成Message, 并赋值给callback参数,从这个过程之后就和sendMessge没有任何区别,会接着执行sendMessageDelayed->sendMessageAtTime,然后进入消息队列等待执行,到达Message执行时间时调用Handler的dispatchMessage方法。

其中有逻辑判断: 如果Message的callback不为空,就会执行callback的run方法,如果Message的callback为null,就会判断Handler的callback是否为空,不为空的话会执行Handler的callback的handleMessage方法,如果Handler的callback为空,则会执行Handler的handleMessage方法。 所以:

post是属于sendMessage的一种赋值callback的特例post和sendMessage本质上没有区别,两种都会涉及到内存泄露的问题post方式配合lambda表达式写法更精简

话外:

现在都是使用rxjava或者其他构建好的线程切换逻辑,以前有一段时间我是手写handle的主线程和子线程切换,如果遇到这种经常需要切换线程的逻辑时,我觉得可能sendMessage方式更合适一些,举个栗子: post方式: Thread1.post(() -> (Thread2.post(() -> Thread1.post(…….);,这种写法嵌套好像有点儿多,但是线程切换清晰一点儿 sendMessage写法你懂得我就不写了,只要构建好两个Handler, 代码更简洁一点儿 这两种哪个比较好还是看个人习惯吧。涉及到内存泄露问题时没法直接使用lambda表达式,如果有多个不同的Message需要处理的话我觉得多数场景下sendMessage更好一点儿,毕竟写一个弱引用就行了Android UI 绘制相关

此类主要涵盖 Android 的 View 绘制过程、常见 UI 组件、自定义 View、动画等。

1.Android 补间动画和属性动画的区别?

参考答案:

特性

补间动画

属性动画

view 动画

支持

支持

非view动画

不支持

支持

可扩展性和灵活性

view属性是否变化

无变化

发生变化

复杂动画能力

局限

良好

场景应用范围

一般

满足大部分应用场景

2.Window 和 DecorView 是什么?DecorView 又是如何和 Window 建立联系的?

参考答案:

Window 是 WindowManager 最顶层的视图,它负责背景(窗口背景)、Title之类的标准的UI元素,Window 是一个抽象类,整个Android系统中, PhoneWindow是 Window 的唯一实现类。至于 DecorView,它是一个顶级 View,内部会包含一个竖直方向的LinearLayout,这个 LinearLayout 有上下两部分,分为 titlebar 和 contentParent 两个子元素,contentParent 的 id 是 content,而我们自定义的 Activity 的布局就是 contentParent 里面的一个子元素。View 层的所有事件都要先经过 DecorView 后才传递给我们的 View。 DecorView 是 Window 的一个变量,即 DecorView 作为一切视图的根布局,被 Window 所持有,我们自定义的 View 会被添加到 DecorView ,而DecorView 又会被添加到 Window 中加载和渲染显示。此处放一张它们的简单内部层次结构图:

image.png

3.简述一下 Android 中 UI 的刷新机制?

参考答案:

界面刷新的本质流程

通过ViewRootImpl的scheduleTraversals()进行界面的三大流程。调用到scheduleTraversals()时不会立即执行,而是将该操作保存到待执行队列中。并给底层的刷新信号注册监听。当VSYNC信号到来时,会从待执行队列中取出对应的scheduleTraversals()操作,并将其加入到主线程的消息队列中。主线程从消息队列中取出并执行三大流程: onMeasure()-onLayout()-onDraw()

同步屏障的作用

同步屏障用于阻塞住所有的同步消息(底层VSYNC的回调onVsync方法提交的消息是异步消息)用于保证界面刷新功能的performTraversals()的优先执行。

同步屏障的原理?

主线程的Looper会一直循环调用MessageQueue的next方法并且取出队列头部的Message执行,遇到同步屏障(一种特殊消息)后会去寻找异步消息执行。如果没有找到异步消息就会一直阻塞下去,除非将同步屏障取出,否则永远不会执行同步消息。界面刷新操作是异步消息,具有最高优先级我们发送的消息是同步消息,再多耗时操作也不会影响UI的刷新操作

4.你认为 LinearLayout、FrameLayout 和 RelativeLayout 哪个效率高, 为什么?

参考答案:

对于比较三者的效率那肯定是要在相同布局条件下比较绘制的流畅度及绘制过程,在这里流畅度不好表达,并且受其他外部因素干扰比较多,比如CPU、GPU等等,我说下在绘制过程中的比较:

1、Fragment是从上到下的一个堆叠的方式布局的,那当然是绘制速度最快,只需要将本身绘制出来即可,但是由于它的绘制方式导致在复杂场景中直接是不能使用的,所以工作效率来说Fragment仅使用于单一场景

2、LinearLayout 在两个方向上绘制的布局,在工作中使用页比较多,绘制的时候只需要按照指定的方向绘制,绘制效率比Fragment要慢,但使用场景比较多

3、RelativeLayout 它的没个子控件都是需要相对的其他控件来计算,按照View树的绘制流程、在不同的分支上要进行计算相对应的位置,绘制效率最低,但是一般工作中的布局使用较多,所以说这三者之间效率分开来讲个有优势、不足,那一起来讲也是有优势、不足,所以不能绝对的区分三者的效率。

5.说一下 Android 中的事件分发机制?

参考答案:

1.触发过程:Activity->Window->DocerView->ViewGroup->View,View不触发再返回由父级处理依次向上推。 2.在每个阶段都要经过三个方法 dispatchTouchEvent(分发)、onInterceptTouchEvent(拦截)、onTouch(处理)

大体流程: Activity中走了Window 的 dispatch,Window 的 dispatch 方法直接走了 DocerView 的 dispatch 方法,DocerView 又直接分发给了 ViewGroup,ViewGroup 中走的是 onInterce 判断是否拦截,拦截的话会走 onTouch 来处理,不拦截则继续下发给 View。到 View 这里已经是最底层了,View 若继续不处理,那就调用上层的 onTouch 处理,上层不处理继续往上推。

6.有针对 RecyclerView 做过哪些优化?

参考答案:

RecyclerView作为android的重要View,有很大的灵活性,可以替代ListView GridView ScrollView,所以需要深入了解一下Rv的性能以及如何去处理优化,实现更加流畅体验,这点是毋庸置疑的,所谓的RV优化其实也是对适配器以及刷新数据的,还有资源复用的优化,下面是本人对RV的一点点优化处理:

1 onBindViewHolder 这个方法含义应该都知道是绑定数据,并且是在UI线程,所以要尽量在这个方法中少做一些业务处理 2 数据优化 采用android Support 包下的DIffUtil集合工具类结合RV分页加载会更加友好,节省性能 3item优化 减少item的View的层级,(pps:当然推荐把一个item自定义成一个View,如果有能力的话),如果item的高度固定的话可以设置setHasFixedSize(true),避免requestLayout浪费资源 4 使用RecycledViewPool RecycledViewPool是对item进行缓存的,item相同的不同RV可以才使用这种方式进行性能提升 5 Prefetch预取 这是在RV25.1.0及以上添加的新功能 6 资源回收 通过重写RecyclerView.onViewRecycled(holder)来合理的回收资源。

7.谈谈你是如何优化 ListView 的?

参考答案:

下面是优化建议: 怎样最大化的优化ListView的性能?

1.在adapter中的getView方法中尽量少使用逻辑2.尽最大可能避免GC3.滑动的时候不载入图片4.将ListView的scrollingCache和animateCache设置为false5.item的布局层级越少越好6.使用ViewHolder

1.在adapter中的getView方法中尽量少使用逻辑

不要在你的getView()中写过多的逻辑代码,我们能够将这些代码放在别的地方。比如:

优化前的getView():

@Overridepublic View getView(int position, View convertView, ViewGroup paramViewGroup) { Object current_event = mObjects.get(position); ViewHolder holder = null; if (convertView == null) { holder = new ViewHolder(); convertView = inflater.inflate(R.layout.row_event, null); holder.ThreeDimension = (ImageView) convertView.findViewById(R.id.ThreeDim); holder.EventPoster = (ImageView) convertView.findViewById(R.id.EventPoster); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } //在这里进行逻辑推断。这是有问题的 if (doesSomeComplexChecking()) { holder.ThreeDimention.setVisibility(View.VISIBLE); } else { holder.ThreeDimention.setVisibility(View.GONE); } // 这是设置image的參数,每次getView方法运行时都会运行这段代码。这显然是有问题的 RelativeLayout.LayoutParams imageParams = new RelativeLayout.LayoutParams(measuredwidth, rowHeight); holder.EventPoster.setLayoutParams(imageParams); return convertView;}

优化后的getView():

@Overridepublic View getView(int position, View convertView, ViewGroup paramViewGroup) { Object object = mObjects.get(position); ViewHolder holder = null; if (convertView == null) { holder = new ViewHolder(); convertView = inflater.inflate(R.layout.row_event, null); holder.ThreeDimension = (ImageView) convertView.findViewById(R.id.ThreeDim); holder.EventPoster = (ImageView) convertView.findViewById(R.id.EventPoster); //设置參数提到这里,仅仅有第一次的时候会运行,之后会复用 RelativeLayout.LayoutParams imageParams = new RelativeLayout.LayoutParams(measuredwidth, rowHeight); holder.EventPoster.setLayoutParams(imageParams); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } // 我们直接通过对象的getter方法取代刚才那些逻辑推断。那些逻辑推断放到别的地方去运行了 holder.ThreeDimension.setVisibility(object.getVisibility()); return convertView;}

2.GC 垃圾回收器

当你创建了大量的对象的时候。GC就会频繁的运行。所以在getView()方法中不要创建非常多的对象。最好的优化是,不要在ViewHolder以外创建不论什么对象。假设你的你的log里面发现“GC has freed some memory”频繁出现的话。那你的程序肯定有问题了。

你能够检查一下: a) item布局的层级是否太深 b) getView()方法中是否有大量对象存在 c) ListView的布局属性

3.载入图片

假设你的ListView中须要显示从网络上下载的图片的话。我们不要在ListView滑动的时候载入图片,那样会使ListView变得卡顿,所以我们须要再监听器里面监听ListView的状态。假设滑动的时候,停止载入图片,假设没有滑动,则開始载入图片

listView.setOnScrollListener(new OnScrollListener() { @Override public void onScrollStateChanged(AbsListView listView, int scrollState) { //停止载入图片 if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_FLING) { imageLoader.stopProcessingQueue(); } else { //開始载入图片 imageLoader.startProcessingQueue(); } } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { // TODO Auto-generated method stub } });

4.将ListView的scrollingCache和animateCache设置为false

scrollingCache: scrollingCache本质上是drawing cache,你能够让一个View将他自己的drawing保存在cache中(保存为一个bitmap),这样下次再显示View的时候就不用重画了,而是从cache中取出。默认情况下drawing cahce是禁用的。由于它太耗内存了,可是它确实比重画来的更加平滑。

而在ListView中,scrollingCache是默认开启的,我们能够手动将它关闭。

animateCache: ListView默认开启了animateCache,这会消耗大量的内存,因此会频繁调用GC,我们能够手动将它关闭掉

优化前的ListView

<ListView android:id="@android:id/list" android:layout_width="match_parent" android:layout_height="wrap_content" android:cacheColorHint="#00000000" android:divider="@color/list_background_color" android:dividerHeight="0dp" android:listSelector="#00000000" android:smoothScrollbar="true" android:visibility="gone" />

优化后的ListView

<ListView android:id="@android:id/list" android:layout_width="match_parent" android:layout_height="wrap_content" android:divider="@color/list_background_color" android:dividerHeight="0dp" android:listSelector="#00000000" android:scrollingCache="false" android:animationCache="false" android:smoothScrollbar="true" android:visibility="gone" />

5.降低item的布局的深度

我们应该尽量降低item布局深度,由于当滑动ListView的时候,这回直接导致測量与绘制,因此会浪费大量的时间。所以我们应该将一些不必要的布局嵌套关系去掉。降低item布局深度

6.使用ViewHolder

这个大家应该非常熟悉了,可是不要小看这个ViewHolder,它能够大大提高我们ListView的性能

再次建议使用RecyclerView

8.谈一谈自定义 RecyclerView.LayoutManager 的流程?

参考答案:

1.确定Itemview的LayoutParams generateDefaultLayoutParams

2.确定所有itemview在recyclerview的位置,并且回收和复用itemview onLayoutChildren

3.添加滑动 canScrollVertically

重点其实是 回收和复用itemview,你需要判断

本文地址:https://www.cknow.cn/archives/1474

以上内容源自互联网,由百科助手整理汇总,其目的在于收集传播生活技巧,行业技能,本网站不对其真实性、可靠性承担任何法律责任。特此声明!

如发现本站文章存在版权问题,烦请提供版权疑问、侵权链接、联系方式等信息发邮件至candieraddenipc92@gmail.com,我们将及时沟通与处理。