2021年3月21日 星期日

Android 面試

面试复习:


來源:
https://blog.csdn.net/qq_30379689/article/details/73698192#activity%E9%9D%A2%E8%AF%95%E9%A2%98

https://www.jianshu.com/p/6bace0b3761a

https://hit-alibaba.github.io/interview/basic/algo/Greedy.html

https://www.jianshu.com/p/a0db5e588506

需修:
7-4,12-3,

目錄:
1. Activity
2.Service
3.Broadcast Receiver
4.Content Provider
5.Fragment

6.Binder
7.Handler
8. AsyncTask
9. HandlerThread
10. 视图工作机制
11.事件分发机制
12. ListView
13.Android项目构建面试题
14.ANR面试题
15. OOM (out of memory)
16. kotlin
17. 注解


-------------------------------------

1.Activity


1-1Activity主要的四种状态:


 Running(运行):在屏幕前台(位于当前任务堆栈的顶部)
* onCreate() ->onStart() -> onResume()。 

 Paused(暂停):失去焦点但仍然对用户可见(覆盖Activity可能是透明或未完全遮挡)
*运行状态 到 暂停状态:onResume() -> onPuased()。
*暂停状态 到 运行状态:  onPuased()->onResume()。

 Stopped(停止):完全被另一个Activity覆盖
*暂停状态 到 停止状态 触发的函数及顺序为:onPuased() -> onStop()。 
*停止状态 恢复至 运行状态 所触发的函数及顺序为:onStop() -> onRestart() -> onStart() -> onResume()。 

   Destroyed(销毁):退出,完全销毁.
*停止状态到死亡状态分为两种情况:(1)由用户操作导致,则执行:onStop() -> onDestroy()。(2)由系统自动强制执行,则该Activity被强行结束。 


1-2 Activity生命周期
  • onCreate()
  • onRestart()(stop恢復時)
  • onStart()
  • onResume()
  • onPause()
  • onStop()
  • onDestroy()
onCreate()只有在该Activity是第一次被创建时才会被调用.

onStart()是当Activity正在变为可见状态时才会被调用。一般地在此期间可以注册一个广播等等。

onPuased()是当一个Activity 失去系统焦点 后将会被调用.

onStop()是当一个Activity变为不可见时将会被调用

onDestroy()是当一个Activity正在被系统finished期间被调用的。


1-3.Activity切换横屏时生命周期

  • onSaveInstanceState() (沒?)
  • onPause()
  • onStop()
  • onDestroy()
  • onCreate()
  • onStart()
  • onRestoreInstanceState()
  • onResume()
AndroidManifest.xml
把该Activity添加
android:configChanges="orientation",

1-4. 进程的优先级
沒用過,
oom_adj是linux内核分配给每个系统进程的一个值.

1-5. Activity启动模式 (Intent)
  • standard
  • singletop
  • singletask
  • singleinstance
<activity
    android:name=".SingleTopActivity"
    android:label="@string/singletop"
    android:launchMode="singleTop" >
</activity>

1-6. scheme跳转协议
沒用過

1-7.Context、Activity、Application之间有什么区别


  • ContextImpl:實作 Context
  • ContextWrapper:包裝、封裝,且初始化過程會創建ContextImpl實作Context
    • ContextThemeWrapper:Activity 需要主題
    • Service, Application:不需要主題,因為不需要介面,完全在背景處理



6OKePTl
  • 看的見的:Dialog,Activity等,就必須要用Activity的Context
  • 看不見的:調用getString或getResource方法獲取res文件夾下的資源時,所有的Context對像都可以使用

https://spicyboyd.blogspot.com/2018/04/appcontext.html

取Context 方法

*View.getContext()
=>返回當前View對象的Context對象

*this 或 Activity.this
=>返回當前的Activity

*Activity.getApplicationContext()
=>app 的context
*ContextWrapper.getBaseContext()
=>獲取一個ContextWrapper進行裝飾之前的Context

總結:
0_1330607569Vj4c
虽然Activity和Application都是Context的子类,但是他们维护的生命周期不一样.
前者维护一个Acitivity的生命周期,所以其对应的Context也只能访问该activity内的各种资源。
后者则是维护一个Application的生命周期(整個app).

1-8.Activity启动过程
*startActivity(intent);

*startActivityForResult()。由于startActivityForResult()是有返回结果的.
setResult(RESULT_OK, gobackIntent).// class B

startActivityForResult(intent, EDIT);//class A
//class A
protected void onActivityResult(int requestCode, int resultCode, Intent data){.... }

-------------以下沒用過-----
*scheduleLaunchActivity

*handleLaunchActivity


Else:
四大元件:
Activity
Broadcast Receiver
Service
Content Provider


2.Service

Service是四大组件之一,可以在 后台执行 长时间 运行操作 而 没有用户界面 的应用组件.

1-2、Service和Thread的区别
Service 运行在 独立进程 的主线程中,不可以执行耗时操作。
Thread是 程序执行的最小单元,分配CPU的基本单位,开启 子线程执行 耗时操作.

1-3.Service启动方式
  • startService (以下生命週期)
  • onCreate()
  • onStartCommand()
  • onDestroy()

  • bindService(以下生命週期)
  • onCreate()
  • onBind()
  • onUnbind()
  • onDestroy()
兩種啟動Service方式的生命週期


1-4. example

後台播放音樂、定時檢查更新資料. or 接收boardcastReceiver
推波?

AndroidManifest.xml

< service android:name = "com.example.serviceTest.MyService">
</ service >



3. Broadcast Receiver
Broadcast是四大组件之一,广泛运用在 应用程序之间 传输信息的机制,通过发送Intent来传送我们的数据.

3-2.Broadcast Receiver的使用场景
  • 同一App具有多个进程的不同组件之间的消息通信
  • 不同App之间的组件之间的消息通信
3-3Broadcast Receiver的种类
  • 普通广播
  • 有序广播
  • 本地广播
  • Sticky广播
3-4.Broadcast Receiver的实现
  • 静态注册:注册后一直运行,尽管Activity、进程、App被杀死还是可以接收到广播
  • 动态注册:跟随Activity的生命周期


example:

BroadcastReceiver mWifiScanReceiver = new BroadcastReceiver()
{
       @Override       
      public void onReceive(Context c, Intent intent) {
           if (intent.getAction().equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
               List<ScanResult> mScanResults = mWifi.getScanResults();         
            String ConnectingWifi = mWifi.getConnectionInfo().getSSID();
               for(int i = 0; i<mScanResults.size(); i++)
               {
                   if(!("\""+mScanResults.get(i).SSID+"\"").equals(ConnectingWifi))
                      WifiList.add(mScanResults.get(i).SSID);               }           
               spinnerArrayAdapter.notifyDataSetChanged();           }
       }
   };




4.Content Provider

內容提供者,通過Binder向其他元件以及其他應用提供資料.

AndroidManifest.xml

<provider
 android:name=".MyContentProvider"
 android:authorities="com.xxxx.xxx.MyContentProvider" //唯一標識,通過它外部應用訪問MyContentProvider
 android:exported="true" // 屬性用於指示該服務是否能夠被其他應用程式元件呼叫或跟它互動。如果設定為true,則能夠被呼叫或互動,否則不能
 android:multiprocess="true" //為true時:多例項 false:單例項
 /> 
*contentProvider 實作


public class MyContentProvider extents ContentProvider{
 /*
 * 建立時被呼叫
 */
 @Override
 public boolean onCreate(){
  return false;
 }
 /*
 * 根據Uri查詢符合selection條件的全部記錄,projection是一個列名列表,選擇出指定的資料列,有點sql的select的意思 
 */
 @Override
 public Cursor query(Uri uri,String[] project,String selection,String[] selectionArgs,String sortOrder){
  //一般使用SQLiteDatabase進行資料的增刪查改
  return null;
 }
 /*
 * 返回Uri查詢出的selection條件的資料的MIME型別 
 */
 @Override
 public String getType(Uri uri){
  //當對應多條記錄時 以vnd.android.cursor.dir/開頭
  //當對應單條記錄時 以vnd.android.cursor.item/開頭
  return null;
 }
 /*
 * 根據Uri插入對應的value的資料
 */
 @Override
 public Uri insert(Uri uri,ContentValues values){
  return null;
 }
 /*
 * 根據Uri刪除selection匹配的記錄
 */
 @Override
 public int delete(Uri uri,String selection,String[] selectionArgs){
  return 0;
 }
 /*
 * 根據Uri修改selection匹配的記錄
 */
 @Override
 public int update(Uri uri,ContentValues values,String selection,String[] selectionArgs){
  return 0;
 }
}

contentProvider的作用是用來暴露資料,而ContentResolver就是用來操作資料.


//將內容字串解析成Uri物件
Uri uri = Uri.parse("content://com.xxx.xxx.myContentProvider/hello");
Cursor cursor = getContentResolver().query(uri,project,selection,selectionArgs,sortOrder);


https://www.itread01.com/content/1544722235.html
****code中沒用

5.Fragment

定义可知:
Fragment比Activity更节省内存,且有自己的生命周期,而且必须依附于Activity.


5-1、Fragment为什么被称为第五大组件
Fragment比Activity更节省内存,其切换模式也更加舒适.


5-2、Activity创建Fragment的方式






6.Binder
6-1.Linux内核的基本知识

  *进程隔离/虚拟地址空间:进程间是不可以共享数据的,相当于被隔离,每个进程被分配到不同的虚拟地址中.
*系统调用:Linux内核对应用有访问权限,用户只能在应用层通过系统调用,调用内核的某些程序
*binder驱动:它负责各个用户的进程,通过binder通信内核来进行交互的模块


6-2.为什么使用Binder
  • 性能上,相比传统的Socket更加高效
  • 安全性高,支持协议双方互相校验

my code not use. 底層?

---------------以下為詳細----
http://gityuan.com/2016/09/04/binder-start-service/
Android内核是基于Linux系统, 而Linux现存多种进程间IPC方式: 管道, 消息队列, 共享内存, 套接字.
从我个人的理解角度, 曾尝试着在知乎回答同样一个问题 为什么Android要采用Binder作为IPC机制?. 

類似TCP 五層架構,这样分层设计的思想, 让每一个子问题都设计成一个独立的协议,

Binder架构也是采用分层架构设计, 每一层都有其不同的功能:




  • Java应用层: 对于上层应用通过调用 AMP.startService,  完全可以不用关心底层,经过层层调用,最终必然会调用到AMS.startService.
  • Java IPC层: Binder通信是采用C/S架构, Android系统的 基础架构 便已设计好Binder在Java framework层的Binder客户类BinderProxy和服务类Binder;
  • Native IPC层: 对于Native层,如果需要直接使用Binder(比如media相关), 则可以直接使用BpBinder和BBinder(当然这里还有JavaBBinder)即可, 对于上一层Java IPC的通信也是基于这个层面.
  • Kernel物理层: 这里是Binder Driver, 前面3层都跑在用户空间,对于用户空间的内存资源是不共享的,每个Android的进程只能运行在自己进程所拥有的虚拟地址空间, 而内核空间却是可共享的. 真正通信的核心环节还是在Binder Driver.
Binder牵涉颇为广泛, 几乎是整个Android架构的顶梁柱, 許多文章没有将Binder IPC(进程间通信)的过程彻底说透.

以startService流程分析为例子 来说说Binder所其作用. 首先在 发起方进程调用AMP.startService,经过binder驱动,最终调用系统进程AMS.startService,如下图:

AMP和AMN都是实现了IActivityManager接口,AMS继承于AMN. 其中AMP作为Binder的客户端,运行在各个app所在进程, AMN(或AMS)运行在系统进程system_server.



7.Handler

7-1、Handler是什么
Handler通过发送 和 处理Message 和Runnable对象 来 关联相对应 线程的MessageQueue

7-2、Handler使用方法
  • post(runnable)
  • sendMessage(message)
7-3、Handler工作原理
7-4、Handler引起的内存泄漏
  • 原因:非静态内部类 持有 外部类 的匿名引用,导致Activity无法释放
  • 解决:
    • Handler内部持有外部Activity的弱引用
    • Handler改为静态内部类
    • Handler.removeCallback()

private final Handler mLeakyHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
      // ...
    }
  };

Handler class 具有隱式引用.
該引用將一直持續到消息被處理為止,從而防止了 活動上下文(context) 被垃圾回收並洩漏了所有應用程序資源。

解決:
要解決此問題,請Handler在新文件中將其 子類化,或改用靜態內部類


  private static class MyHandler extends Handler {
    private final WeakReference<SampleActivity> mActivity;

    public MyHandler(SampleActivity activity) {
      mActivity = new WeakReference<SampleActivity>(activity);
    }

    @Override
    public void handleMessage(Message msg) {
      SampleActivity activity = mActivity.get();
      if (activity != null) {
        // ...
      }
    }
  }


8. AsyncTask


private class GetImage extends AsyncTask<String , Integer , Bitmap>{

        @Override
        protected void onPreExecute() {
            //執行前 設定可以在這邊設定
            super.onPreExecute();
        }

        @Override
        protected Bitmap doInBackground(String... params) {
            //執行中 在背景做事情
            return null;
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            //執行中 可以在這邊告知使用者進度
            super.onProgressUpdate(values);
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            //執行後 完成背景任務
            super.onPostExecute(bitmap);
        }
    }

9. HandlerThread

2、HanlderThread的特点、

*HandlerThread本质上是一个线程,继承自Thread
*HandlerThread有自己的Looper对象,可以进行Looper循环,可以创建Handler
*HandlerThread可以在Handler的handlerMessage中执行异步方法
*HandlerThread优点是异步不会堵塞,减少对性能的消耗
*HandlerThread缺点是不能同时继续进行多任务处理,需要等待进行处理,处理效率较低
*HandlerThread与线程池不同,HandlerThread是一个串行队列,背后只有一个线程.

10. 视图工作机制


Android进阶——Android视图工作机制之measure、layout、draw


10-1.Android视图工作机制按顺序分为以下三步:

  1. measure:确定View的宽高
  2. layout:确定View的位置
  3. draw:绘制出View的形状



相关概念:
  • View(照片框):自定义View
  • measure(尺子):测量View大小
  • MeasureSpec(尺子刻度):测量View大小的测量单位
  • layout(照片框的位置):View的具体位置
  • draw(笔):绘制View
例如: 画图步骤:
  1. 首先画一个100 x 100的照片框,需要尺子测量出宽高的长度(measure过程)
  2. 然后确定照片框在屏幕中的位置(layout过程)
  3. 最后借助尺子用手画出我们的照片框(draw过程)
10-2. MeasureSpec

自定义View第一步是 测量, 测量 需要 测量规格(或测量标准)才能知道View的宽高,所以在 测量之前需要认识MeasureSpec类. MeasureSpec的表示形式是32位的int值
  • 高2位(前面2位):表示测量模式,即SpecMode
  • 低30位(后面30位):表示在测量模式下的测量规格大小,即SpecSize
一、结论:子View的MeasureSpec 由 "父容器的MeasureSpec" 和 "自身的LayoutParams"   来共同决定的
  1. 首先要知道LayoutParams有三种情况:MATCH_PARENT、WARP_CONTENT、100dp(精确大小).
  2. 只要子View的MeasureSpec被确定,那么就可以在measure过程中,测量出子View的宽高
二、通过  例子来解释 结论

其實決定權在 子view 的layoutParams


三.measure过程

一、ViewGroup的measure过程
ViewGroup源码中, 提供了一个 measureChildren 的方法 来遍 历调用子 View的measure方法.
从源码中分析,getChildMeasureSpec()逻辑其实就是总结的内容.

//如果父View为具体的大小,比如100dp
case MeasureSpec.EXACTLY: 
//如果子View的宽高有具体值,比如50dp,就直接用50dp
    if (childDimension >= 0) { 
        resultSize = childDimension; //50dp
        resultMode = MeasureSpec.EXACTLY; //精确模式

//如果子View的宽高是MATCH_PARENT,就用子View真实可以容纳最大的大小
    } else if (childDimension == LayoutParams.MATCH_PARENT) { 
        resultSize = size; //子View真实可以容纳最大的大小
        resultMode = MeasureSpec.EXACTLY; //精确模式

//如果子View的宽高是WRAP_CONTENT,就用子View真实可以容纳最大的大小,但是不能超过父View的大小
    } else if (childDimension == LayoutParams.WRAP_CONTENT) { 
        resultSize = size;  //子View真实可以容纳最大的大小
        resultMode = MeasureSpec.AT_MOST; //最大值模式:不能超过父View的大小
    }
    break;


10-3. Android视图工作机制之layout过程

1. setFrame()
onLayout()方法其实就是一个空方法, 自定义View时重写onLayout()方法,其实就是让我们重新设置View的位置. setFrame()去挖掘它是如何判断View已经发生位置改变的.

前视图的大小或者位置与上次相比发生了变化, 执行两次invalidate().


2. invalidate()

方法在 UI 線程中調用,重繪當前 UI。

其他:

postInvalidate() 方法在非 UI 線程中調用,通知 UI 線程重繪。


10-4. Android视图工作机制之draw过程

1.绘制当前视图的背景:drawBackground(canvas)
2.保存当前画布的堆栈状态,并且在在当前画布上创建额外的图层,以便接下来可以用来绘制当前视图在滑动时的边框渐变效果
3.绘制当前视图的内容:if (!dirtyOpaque) onDraw(canvas)
4.绘制当前视图的子视图的内容:dispatchDraw(canvas)
5.绘制当前视图在滑动时的边框渐变效果
6.绘制当前视图的滚动条:onDrawForeground(canvas)



10-5.Android视图工作机制中的重绘

一. invalidate()和requestLayout(),常用于View重绘和更新,其主要区别如下
  • invalidate方法只会执行onDraw方法
  • requestLayout方法只会执行onMeasure方法和onLayout方法,并不会执行onDraw方法。
*View更新时, View的显示内容发生改变 且  新显示内容不影响View的大小、位置,
只需调用invalidate方法;
*View宽高、位置发生改变 且显示 内容不变,只需调用requestLayout方法;
*两者均发生改变,则需调用两者,按照View的绘制流程,推荐先调用requestLayout方法再调用invalidate方法.

二、invalidate()和postInvalidate()
  • invalidate方法用于UI线程中重新绘制视图
  • postInvalidate方法用于非UI线程中重新绘制视图,省去使用handler


11.事件分发机制


Android事件分发机制之dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent


12. ListView

ListView是能将一个 数据集合以动态滚动 的方式展示到用户界面上的View.

12-3. ListView的优化 ??

  • 重用convertView
  • 使用ViewHolder
  • 图片三级缓存
  • 监听滑动事件
  • 少用透明View
  • 开启硬件加速




13.Android项目构建面试题


这里写图片描述

*proguard是什么?
ProGuard工具是用于压缩、优化和混淆我们的代码,其主作用是移除或混淆代码中无用类、字段、方法和属性. 我們程式中沒使用(有lib使用).

*proguard技术功能
  • 压缩
  • 优化
  • 混淆
  • 预检测



14.ANR

UI thread & worker thread.
通常如果再UI thread 上做過多計算容易發生 , (官網是5s; 有時不到or 更久)

解決方法:
AsyncTask
Thread
Handler




15. OOM (out of memory)

通常為bitmap 沒 recycle 造成( 新版幾乎有自己回收機制,不怎發生)