当前位置: 首页 > news >正文

Android 面试2018最新最全

为啥分享的文章不会同步更新??? 观看效果不好,请查看原文链接 blog.csdn.net/liu3364575/…

相信大家都有面试的经历,相对比面试官的问的一些问题其实都是基础的知识,但就是一些基础的知识我们也不是很完美的回答出来,我们也知道现在的开发人员很多,一家公司一个岗位就会有很多的开发者投递,在那么多开发者中你如何让面试官很深的认识你,给面试官一个很深的印象,能让他在技术水平差不多的情况的下第一个想起的是你。 从这篇文章对整个面试中所问到的问题进行梳理,查缺补漏。

JAVA String,StringBuffer,StringBuilder区别? 一般回答是这样的: String 字符串常量 StringBuffer 字符串变量(线程安全) StringBuilder 字符串变量(非线程安全) 面试的时候千万不要这么说,这样说会让面试官你还只停留在会用的层面,不知道原理,你应该这么说:

String,StringBuffer,StringBuilder最终底层存储与操作的都是char数组,但是String里面的char数组是final的,而StringBuffer,StringBuilder不是,也就是说,String是不可变的,想要新的字符串只能重新生成String;而StringBuffer和StringBuilder只需要修改底层的char数组就行,相对来说,开销要小很多,String的大多数方法都是重新new一个新String对象返回,频繁重新生成容易生成很多垃圾。

为什么StringBuffer 是线程安全的? StringBuffer是线程安全的,StringBuilder是线程不安全的,因为StringBuffer的方法是加了synchronized锁起来了的,而StringBuilder没有

增删比较多时用StringBuffer或StringBuilder(注意单线程与多线程)。

HashMap的实现机制? HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体。HashMap底层就是一个数组结构,数组中的每一项又是一个链表。当新建一个HashMap的时候,就会初始化一个数组。

/**

  • The table, resized as necessary. Length MUST Always be a power of two. */ transient Entry[] table;

static class Entry<K,V> implements Map.Entry<K,V> { final K key; V value; Entry<K,V> next; final int hash; …… } 1 2 3 4 5 6 7 8 9 10 11 12 可以看出,Entry就是数组中的元素,每个 Map.Entry 其实就是一个key-value对,它持有一个指向下一个元素的引用,这就构成了链表。

HashMap如果Hash冲突了怎么解决? 可以看下这篇文章 https://blog.csdn.net/qq_27093465/article/details/52269862

如何线程安全的使用HashMap? 无非就三种实现方式: 1)Hashtable 2)ConcurrentHashMap 3)Synchronized Map

//Hashtable Map<String, String> hashtable = new Hashtable<>();

//synchronizedMap Map<String, String> synchronizedHashMap = Collections.synchronizedMap(new HashMap<String, String>());

//ConcurrentHashMap Map<String, String> concurrentHashMap = new ConcurrentHashMap<>(); 1 2 3 4 5 6 7 8 HashTable源码中是使用synchronized来保证线程安全的

public synchronized V get(Object key) { // 省略实现 } public synchronized V put(K key, V value) { // 省略实现 } 1 2 3 4 5 6 所以当一个线程访问HashTable的同步方法时,其他线程如果也要访问同步方法,会被阻塞住。举个例子,当一个线程使用put方法时,另一个线程不但不可以使用put方法,连get方法都不可以,好霸道啊!!!so~~,效率很低,现在基本不会选择它了。

ConcurrentHashMap(以下简称CHM)是JUC包中的一个类,Spring的源码中有很多使用CHM的地方。CHM的一些重要特性和什么情况下应该使用CHM。需要注意的是,上面博客是基于Java 7的,和8有区别,在8中CHM摒弃了Segment(锁段)的概念,而是启用了一种全新的方式实现,利用CAS算法。

// synchronizedMap方法 public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) { return new SynchronizedMap<>(m); } // SynchronizedMap类 private static class SynchronizedMap<K,V> implements Map<K,V>, Serializable { private static final long serialVersionUID = 1978198479659022715L;

   private final Map<K,V> m;     // Backing Map
   final Object      mutex;        // Object on which to synchronize

   SynchronizedMap(Map<K,V> m) {
       this.m = Objects.requireNonNull(m);
       mutex = this;
   }

   SynchronizedMap(Map<K,V> m, Object mutex) {
       this.m = m;
       this.mutex = mutex;
   }

   public int size() {
       synchronized (mutex) {return m.size();}
   }
   public boolean isEmpty() {
       synchronized (mutex) {return m.isEmpty();}
   }
   public boolean containsKey(Object key) {
       synchronized (mutex) {return m.containsKey(key);}
   }
   public boolean containsValue(Object value) {
       synchronized (mutex) {return m.containsValue(value);}
   }
   public V get(Object key) {
       synchronized (mutex) {return m.get(key);}
   }

   public V put(K key, V value) {
       synchronized (mutex) {return m.put(key, value);}
   }
   public V remove(Object key) {
       synchronized (mutex) {return m.remove(key);}
   }
   // 省略其他方法
复制代码

} 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 synchronizedMap从源码中可以看出调用synchronizedMap()方法后会返回一个SynchronizedMap类的对象,而在SynchronizedMap类中使用了synchronized同步关键字来保证对Map的操作是线程安全的。

HasMap为什么会线程不安全? 个人觉得HashMap在并发时可能出现的问题主要是两方面, 首先如果多个线程同时使用put方法添加元素,而且假设正好存在两个put的key发生了碰撞(hash值一样),那么根据HashMap的实现,这两个key会添加到数组的同一个位置,这样最终就会发生其中一个线程的put的数据被覆盖。

第二就是如果多个线程同时检测到元素个数超过数组大小*loadFactor,这样就会发生多个线程同时对Node数组进行扩容,都在重新计算元素位置以及复制数据,但是最终只有一个线程扩容后的数组会赋给table,也就是说其他线程的都会丢失,并且各自线程put的数据也丢失。

HashMap出现死循环的原因? 因为多线程会导致HashMap的Entry节点形成环链,这样当遍历集合时Entry的next节点用于不为空,从而形成死循环

Activity: 当面试官问你什么Activity,你是不是会觉得一阵懵* ?

在日常应用中,Android是用户交互的接口,他提供了一个界面,让用户进行点击,各种滑动操作等。

Activity的四种状态: running / paused / stopped / killed running:点击屏幕,屏幕做出响应,处于activity栈顶的状态 paused:失去焦点不能进行操作,或者是一个透明的在栈顶的Activity,注意并不是被销毁了啊,它的成员信息和变量都还在,当然还有另外一种状态,当内存紧张的时候会北回收掉 stopped:当一个Activity被另外一个Activity完全覆盖的时候,被覆盖的那个处于stopped状态,不可见,成员信息和变量都还在,如果内存紧张也是会被回收的 killed:已经被系统回收

生命周期: 启动:onCreate >> onStart >> onResume 点击HOME键回到主界面:onPause >> onStop 再次回到远Activity:onResrart>>onStart >> onResume 退出当前Activity:onPause >> onStop >> onDestroy

进程优先级: 前台 / 可见 /服务 /后台 / 空 前台:正在进行交互的Activity或者与前台activity绑定的services 可见:一个activity可见但并处于前台,不能点击 服务:在后台开启的服务 后台:当一个Activity被点击home键,退居后台,没有=被回收 空 :不属于前面四种进程的任意一种,处于缓存的目的而保留

启动模式: standard / singletop / singletask / singleinstance

如何配置Activity的启动模式? 直接在AndroidManifest.xml配置的android:launchMode属性为以上四种之一即可

standrd:标准模式,每次启动都会重新创建一个activity实例加入到任务栈中,不会考虑是不是有此实例存在,不会复用,消耗内存资源

singletop :栈顶复用模式,只检测任务栈栈顶,只有在栈顶的Activity不会被创建,就算是在第二位也是会被创建

singletask:栈内复用模式,也是一个单例模式,检测整个任务栈,如果有并把在之上的实例全部移除掉,回掉onNewIntent方法

singleinstance:一个activity如果在整个系统中只存在一个实例,而且这个activity独享整个任务栈

应用场景: singleTop:适合接收通知启动的内容显示页面。例如,某个新闻客户端的新闻内容页面,如果收到10个新闻推送,每次都打开一个新闻内容页面是很烦人,另外,singleTop启动模式适合于一些不常用的Activity页面,比如“找回密码”、“设置界面”等。

singleTask:最典型的样例就是应用中展示的主页(Home页),假设用户在主页跳转到其他页面,运行多次操作后想返回到主页,假设不使用SingleTask模式,在点击返回的过程中会多次看到主页,这明显就是设计不合理了。

singleInstance:比如说,使用微信调起自己的客户端某个页面,不做任何处理的情况下,按下回退或者当前Activity.finish(),页面不会停留在自己的客户端而是返回到微信的客户端页面。但是如果这个页面的启动模式设置为singleTask,当按下返回键或者Activity。finish(),页面都会停留在自己的客户端(因为自己的Application回退栈不为空),这明显不符合逻辑的。产品的要求是,回退必须回到微信客户端,而且要保证不杀死自己的Application.因此,显然其他的其他的启动模式都不具备这个功能。

使用方法: 方法一:

android:launchMode="standard|singleInstance|single Task|singleTop 1 使用android:launchMode=”standard|singleInstance|single Task|singleTop”来控制Acivity任务栈。

方法二:

Intent Flags: Intent intent=new Intent(); intent.setClass(MainActivity.this, MainActivity2.class); intent.addFlags(Intent. FLAG_ACTIVITY_CLEAR_TOP); startActivity(intent); 1 2 3 4 5 Flags有很多,比如: Intent.FLAG_ACTIVITY_NEW_TASK 相当于singleTask Intent. FLAG_ACTIVITY_CLEAR_TOP 相当于singleTop

scheme(sigeimo)跳转协议: android中的scheme是一种页内跳转协议,是一种非常好的实现机制,通过定义自己的scheme协议,可以非常方便的在app内部跳转到各个界面;通过scheme协议,服务器可以定制化告诉app跳转到哪个页面,可以通过通知栏消息栏定制化跳转页面,也可以通过H5页面中定义连接跳转指定的activity页面等

应用场景: 1 服务端下发一个url路径,客户端根据下方的路径跳转到指定界面 2 从H5跳转到App内 3 App根据url挑战到另外的一个App

两个 Activity 之间跳转时必然会执行的是哪几个方法? 一般情况下比如说有两个 activity,分别叫 A,B,当在 A 里面激活 B 组件的时候, A 会调用 onPause()方法,然后 B 调用 onCreate() ,onStart(), onResume()。 这个时候 B 覆盖了窗体, A 会调用 onStop()方法. 如果 B 是个透明的,或者 是对话框的样式, 就不会调用 A 的 onStop()方法。

横竖屏切换时 Activity 的生命周期? 此时的生命周期跟清单文件里的配置有关系。 1.不设置 Activity 的 android:configChanges 时,切屏会重新调用各个生命周期默认首先销毁当前 activity,然后重新加载。 2.设置 Activity android:configChanges=”orientation|keyboardHidden|screenSize”时,切 屏不会重新调用各个生命周期,只会执行 onConfigurationChanged 方法。 通常在游戏开发, 屏幕的朝向都是写死的。

如何将一个 Activity 设置成窗口的样式? 只需要给我们的 Activity 配置如下属性即可。 android:theme=”@android:style/Theme.Dialog”

Android 中的 Context, Activity,Appliction 有什么区别? 相同:Activity 和 Application 都是 Context 的子类。 Context 从字面上理解就是上下文的意思,在实际应用中它也确实是起到了管理 上下文环境中各个参数和变量的总用,方便我们可以简单的访问到各种资源。

不同:维护的生命周期不同。Context 维护的是当前的 Activity 的生命周期, Application 维护的是整个项目的生命周期。使用 context 的时候,小心内存泄露,防止内存泄露,注意一下几个方面: 1)不要让生命周期长的对象引用 activity context,即保证引用 activity 的对 象要与 activity 本身生命周期是一样的。 2 )对于生命周期长的对象,可以使用 application,context。 3 )避免非静态的内部类,尽量使用静态类,避免生命周期问题,注意内部类 对外部对象引用导致的生命周期变化。

如何获取当前屏幕Activity的对象? 使用ActivityLifecycleCallbacks 应用场景:可以利用ActivityLifecycleCallbacks 做一些数据埋点,统计之类的应用,对其统一做处理。这样对减少Activity的代码入侵。尽量简化和模块化的注入生命周期方法。

ActivityLifecycleCallbacks 是什么? 见名知意,Activity生命周期回调,Application通过此接口提供了一套回调方法,用于让开发者对Activity的生命周期事件进行集中处理。 但是这个要求API 14+ (Android 4.0+)以上使用,幸好我们这个最低支持,满足需求。

ActivityLifecycleCallbacks 怎么使用? 重写Application的onCreate()方法,或在Application的无参构造方法内,调用Application.registerActivityLifecycleCallbacks()方法,并实现ActivityLifecycleCallbacks接口

知道onNewIntent吗? 如果IntentActivity处于任务栈的顶端,也就是说之前打开过的Activity,现在处于onPause、onStop 状态的话,其他应用再发送Intent的话,执行顺序为: onNewIntent,onRestart,onStart,onResume。

除了用Intent 去启动一个Activity,还有其他方法吗? 使用adb shell am 命令

1)ams启动一个activity adb shell am start com.example.fuchenxuan/.MainActivity 2)am发送一个广播,使用action adb shell am broadcast -a magcomm.action.TOUCH_LETTER

Android Service与Activity之间通信的几种方式? 通过Binder对象 1)当Activity通过调用bindService(Intent service, ServiceConnection conn,int flags),得到一个Service的一个对象,通过这个对象我们可以直接访问Service中的方法。 2)通过Broadcast Receiver(广播)的形式 3)EventBus 是一个Android开源事件总线框架 后面我们会有专门的讲解。

TaskAffinity 是什么? 标识Activity任务栈名称的属性:TaskAffinity,默认为应用包名

如果新Activity是透明主题时,旧Activity会不会走onStop? 不会!

android完全退出应用程序的三种方式?

第一种方法:首先获取当前进程的id,然后杀死该进程。 建议使用这种方式

android.os.Process.killProcess(android.os.Process.myPid()) 1 第二种方法:终止当前正在运行的Java虚拟机,导致程序终止

System.exit(0); 1 2 第三种方法:强制关闭与该包有关联的一切执行

ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
manager.restartPackage(getPackageName()); 1 2 使用这种方式关闭应用程序需要加上权限

1 介绍下Android应用程序启动过程 1)Launcher通过Binder进程间通信机制通知ActivityManagerService,它要启动一个Activity;

2)ActivityManagerService通过Binder进程间通信机制通知Launcher进入Paused状态;

3)Launcher通过Binder进程间通信机制通知ActivityManagerService,它已经准备就绪进入Paused状态,于是ActivityManagerService就创建一个新的进程,用来启动一个ActivityThread实例,即将要启动的Activity就是在这个ActivityThread实例中运行;

4)ActivityThread通过Binder进程间通信机制将一个ApplicationThread类型的Binder对象传递给ActivityManagerService,以便以后ActivityManagerService能够通过这个Binder对象和它进行通信;

5)ActivityManagerService通过Binder进程间通信机制通知ActivityThread,现在一切准备就绪,它可以真正执行Activity的启动操作了。 如果想深入研究的可以查看罗升阳大神的博客https://blog.csdn.net/luoshengyang/article/details/6689748

Fragment: 三个小问题看你能完美解答几个 1 Fragment为什么被称为第五大组件? 2 Fragment的生命周期 ? 3 Fragment之间的通信 ?

1 Fragment为什么被称为第五大组件? 首先Fragment的使用频率并不低于其他四大组件,他有自己的生命周期,同时可以动态灵活的加载到Activity中,所以说Fragment可以被称为第五大组件

加载到Activity的两种方式: 1)添加Fragment到Activity的布局文件当中,也叫静态加载,name属性哦 2)动态的在activity中添加fragment ,也叫动态加载 步骤: FragmentManage用来管理所有要启动的fragment,并用FragmentTransaction添加和替换相对应的fragment,并用容器资源来作为标志位来设置fragment所要显示到activity当中的位置,最后提交commit方法 (1)创建待添加的碎片实例 (2)获取FragmentManager,在活动中可以直接通过调用 getSupportFragmentManager()方法得到。 (3)开启一个事务,通过调用beginTransaction()方法开启。 (4)向容器内添加或替换碎片,一般使用repalce()方法实现,需要传入容器的id和待添加的碎片实例。 (5)提交事务,调用commit()方法来完成。

FragmentPageAdapter与FragmentStatePageAdapter的区别? 1 FragmentStatePageAdapter适合界面多,FragmentPageAdapter适合界面少 2 FragmentStatePageAdapter比FragmentPageAdapter更节省内存 源码分析: FragmentPageAdapter适用于页面较少的情况下,因为只对ui分离并没有回收内存, 因为源码里面destoryItem是detach方法()只是对fragment和activity的ui脱离开来,并不回收内存 FragmentStatePageAdapter用于界面较多的情况下,界面多也就意味着更加的消耗内存,FragmentStatePageAdapter在每次切换fragment的时候,他是回收内存的,因为源码里面destoryItem的remove方法真正的释放的内存

Fragement的生命周期: onAttach >> onCreate >> onCreateView >> onActivityCreated >>onStart >> onResume >> onPause >> onStop >> onDestoryView >> onDestory >>onDetach 很显然你要是像背书似的将上面的生命周期说一遍,你认为你跟其他的竞争者有什么优势?

你应该将Activity与Fragment的生命周期结合起来说: 首先,调用Activity的onCreate方法 当Fragment创建的时候会执行onAttach,是在Activity与fragment关联之后调用的 在之后执行onCreate方法,也就是在fragment创建的时候调用,注意,此时的activity还没有创建完毕 在调用fragment的onCreateView方法,系统首次绘制用户界面的时候调用 调用fragment的onActivityCreated方法,也就是activity被渲染绘制成功后调用, 现在是要调用activity的onStart的方法,当activity可见之后调用, fragment的onStart方法,表示fragment也可见了 接着调用activity的onResume的方法,表示当前activity可以与用户交互了,当activity可见之后调用 fragment的onResume方法,表示当前fragment也可以与用户进行交互操作了,以上步骤完成了从创建到交互的所有步骤

在之后,Fragment的onPause Activity的onPause Fragment的onStop Activity的onStop 现在来到了Frament的onDestoryView方法,表示当前fragment不在被使用,界面销毁,紧接着来到onDestory方法,表示fragment已经销毁了,最后调用onDetach来解除与activity的联系 最后调用Activity的onDestory方法

Fragment通信: 1)在Fragment中调用Activity的方法 geyActivity 2)在Activity中调用Fragment的方法 接口回调(在Fragment创建接口,在Activity实现) 3)在Fragment调用Fragment的方法 getActivity.findFragmentById 或者广播

Fragment的replace、add、remove?

add:将一个fragment实例添加到Activity的最上层,一般在使用add的时候会配合 hide,show一起使用,为了避免Fragment的重复创建节省资源。 remove:将fragment从fragment队列中删除 replace:替换fragment的实例,replace是FragmentManager的方法

commitAllowingStateLoss与commit的区别? Activity被系统回收(界面已经不存在了),为了能在下次打开的时候恢复原来的样子,系统为我们保存界面的所有状态,这个时候再去修改界面理论上肯定是不被允许的,为了避免这种异常可以使用:

transaction.commitAllowingStateLoss(); 1 来提交添加Fragment到Activity的事务,与commit()不同的是使用这种方法允许丢失界面的状态和信息。

ViewPager与Fragment结合使用时的懒加载问题? 所谓的 “懒加载” 就是数据只有在Fragment对于用户可见的时才进行加载,我们需要判定Fragment在什么时候是处于可见的状态。一般我们通常是通过Fragment中的生命周期方法onResume来判断Fragment是否可见,但是由于ViewPager预加载的特性,Fragment即便不可见也会执行onResume方法,可以通过setUserVisibleHint()方法来进行判断:

什么时候被调用?

当fragment被创建的时,setUserVisibleHint(boolean isVisibleToUser)方法会被调用,且传入参数值为false。

当fragment可见时,setUserVisibleHint(boolean isVisibleToUser)方法会被调用,且传入参数值为true。

当fragment由 可见 -> 不可见 时,setUserVisibleHint(boolean isVisibleToUser)方法会被调用,且传入参数值为false。 1 2 3 4 5 所以我们只需要当setUserVisibleHint(boolean isVisibleToUser)方法中的 isVisibleToUser 参数的值为true的时候我们才开始进行数据的加载就可以了。

但是有一点需要需要注意的是 setUserVisibleHint(boolean isVisibleToUser)方法在Fragment的生命周期方法onCreate 之前调用的,也就是说他并不在Fragment的生命周期中。既然是在 onCreate 方法之前被调用,这样就存在许多不确定因素,如果Fragmnet的View还没有完成初始化之前,就在setUserVisibleHint()方法中进行UI的操作,这样显然会导致空指针的出现。因此我们需要对Fragment创建的View进行缓存,确保缓存的View不为空的情况下我们才可以在setUserVisibleHint方法中进行UI操作。

Service Service是什么? 四大组件之一,可以在后台处理一些耗时的逻辑,也可以执行某些长时间运行的任务,而且看不到界面,包括在程序退出的时候依然能在继续运行

Service与Broadcastrecevier有一个共同点,都是运行在主线程当中,都不能进行长耗时操作

Service与Thread的区别? Thread程序最小的执行单元,Thread可以进行异步操作,相对独立;而Service是Android的一种机制,如果是本地的Service,依赖与它所在的主线程之上,相比Thread没有那么独立

为什么要用Service而不是Thread呢? Thread的运行是独立于Activity的,也就是当一个Activity被finish之后,如果没有主动停止Thread或者Thread中的run没有执行完毕时那么这个线程会一直执行下去。因此这里会出现一个问题:当 Activity 被 finish 之后,你不再持有该 Thread 的引用。另一方面,你没有办法在不同的 Activity 中对同一 Thread 进行控制。

Service 是否在 main thread 中执行, service 里面是否 能执行耗时的操作? 默认情况,如果没有显示的指 servic 所运行的进程, Service 和 activity 是运 行在当前 app 所在进程的 main thread(UI 主线程)里面。 service 里面不能执行耗时的操作(网络请求,拷贝数据库,大文件 ) 特殊情况 ,可以在清单文件配置 service 执行所在的进程 ,让 service 在另 外的进程中执行

1 2 3 Service 里面可以弹吐司么? 可以的。弹吐司有个条件就是得有一个 Context 上下文,而 Service 本身就是 Context 的子类,因此在 Service 里面弹吐司是完全可以的。比如我们在 Service 中完成下载任务后可以弹一个吐司通知用户

Service 的 onStartCommand 方法有几种返回值?各代表什么意思?

有四种返回值,不同值代表的意思如下: START_STICKY:如果 service 进程被 kill 掉,保留 service 的状态为开始状态,但不保留递送的 intent 对象。随 后系统会尝试重新创建 service,由于服务状态为开始状态,所以创建服务后一定会调用 onStartCommand(Intent,int,int)方法。如果在此期间没有任何启动命令被传递到 service,那么参数 Intent 将为 null。

START_NOT_STICKY:“非粘性的”。使用这个返回值时,如果在执行完 onStartCommand 后,服务被异常 kill 掉,系统不会自动重启该服务。

START_REDELIVER_INTENT:重传 Intent。使用这个返回值时,如果在执行完 onStartCommand 后,服务被异 常 kill 掉,系统会自动重启该服务,并将 Intent 的值传入。

START_STICKY_COMPATIBILITY:START_STICKY 的兼容版本,但不保证服务被 kill 后一定能重启。

Service 的 onRebind(Intent)方法在什么情况下会执行? 如果在 onUnbind()方法返回 true 的情况下会执行,否则不执行。

Activity 调用 Service 中的方法都有哪些方式? Binder: 通过 Binder 接口的形式实现,当 Activity 绑定 Service 成功的时候 Activity 会在 ServiceConnection 的类 的 onServiceConnected()回调方法中获取到 Service 的 onBind()方法 return 过来的 Binder 的子类,然后通过对象调用方法。 Aidl: aidl 比较适合当客户端和服务端不在同一个应用下的场景。

Messenger: 它引用了一个Handler对象,以便others能够向它发送消息(使用mMessenger.send(Message msg)方法)。该类允许跨进程间基于Message的通信(即两个进程间可以通过Message进行通信),在服务端使用Handler创建一个Messenger,客户端持有这个Messenger就可以与服务端通信了。一个Messeger不能同时双向发送,两个就就能双向发送了

如何提高service的优先级? 可以用 setForeground(true) 来设置 Service 的优先级。

service 如何定时执行? 使用AlarmManager,根据AlarmManager的工作原理,alarmmanager会定时的发出一条广播,然后在自己的项目里面注册这个广播,重写onReceive方法,在这个方法里面启动一个service,然后在service里面进行网络的访问操作,当获取到新消息的时候进行推送,同时再设置一个alarmmanager进行下一次的轮询,当本次轮询结束的时候可以stopself结束改service。这样即使这一次的轮询失败了,也不会影响到下一次的轮询。这样就能保证推送任务不会中断

在 service 的生命周期方法 onstartConmand()可不可以执行网络操作?如何在 service 中执行网络操作? 可以直接在 Service 中执行网络操作,在 onStartCommand()方法中可以执行网络操作

Service 和 Activity 在同一个线程吗? 对于同一 app 来说默认情况下是在同一个线程中的,main Thread (UI Thread)。

什么是 IntentService?有何优点? 会创建独立的工作线程来处理所有的 Intent 请求; 会创建独立的工作线程来处理 onHandleIntent()方法实现的代码,无需 处理多线程问题; 所有请求处理完成后,IntentService 会自动停止,无需调用 stopSelf()方法 停止 Service; 为 Service 的 onBind()提供默认实现,返回 null; 为 Service 的 onStartCommand 提供默认实现,将请求 Intent 添加到队列 中;

Activity 怎么和 Service 绑定,怎么在 Activity 中启动自 己对应的 Service? Activity 通过 bindService(Intent service, ServiceConnection conn, int flags)跟 Service 进行绑定,当绑定成功的时候 Service 会将代理对象通过回调 的形式传给 conn,这样我们就拿到了 Service 提供的服务代理对象。 在 Activity 中可以通过 startService 和 bindService 方法启动 Service。一 般情况下如果想获取 Service 的服务对象那么肯定需要通过 bindService()方 法,比如音乐播放器,第三方支付等。如果仅仅只是为了开启一个后台任务那么 可以使用 startService()方法。

IntentService 适用场景 IntentService 内置的是 HandlerThread 作为异步线程,每一个交给 IntentService 的任务都将以队列的方式逐个被执行到,一旦队列中有某个任务执行时间过长,那么就会导致后续的任务都会被延迟处理 正在运行的 IntentService 的程序相比起纯粹的后台程序更不容易被系统杀死,该程序的优先级是介于前台程序与纯后台程序之间的

Broadcast Receiver 广播的定义: 在Android中,Broadcast是一种广泛运用在程序之间的传输信息的机制,Android中我们要发送的广播内容中是一个Intent,这个Intent可以携带我们要传输的数据

广播的使用场景: 1)在同一个App具有多个进程的不同组件之间的消息传递 2)不同app之间的消息通信

广播的种类: 1)普通广播 2)系统广播 (sendOrderedBroadcast) 3 ) 本地广播(只在app内部传播)

不同注册方式广播接收器回调onReceive(context, intent)中context类型不一致? manifest静态注册的ContextReceiver,回调onReceive(context, intent)中的context是ReceiverRestrictedContext;

代码动态注册的ContextReceiver,回调onReceive(context, intent)中的context是Activity Context;

内部实现机制? 1)自定义广播接受者Broadcast Receiver,并复写onRecvice方法 2)通过Binder机制像AMS进行注册 3)广播发送者通过Binder机制像AMS发送广播 4)AMS查找符合相应条件(IntentFilter/Permission等)的Broadcast Receiver,将广播发送到Broadcast Receiver相应的消息队列循环当中去(一般是Activity中) 5)消息循环拿到此广播,回调Broadcast Receiver中的onReceive方法、

如何让自己的广播只让指定的 app 接收? 通过自定义广播权限来保护自己发出的广播。 在清单文件里receiver必须有这个权限才能收到广播。 首先,需要定义权限: 然后,声明权限: 这时接收者就能收到发送的广播。

广播的优先级对无序广播生效吗? 生效的**

动态注册的广播优先级谁高? 谁先注册谁优先级高。

如何判断当前 BroadcastReceiver 接收到的是有序广播还是无序广播? 在 BroadcastReceiver 类中 onReceive()方法中,可以调用 boolean b = isOrderedBroadcast();判断接收到的广播是否为有序广播。

粘性广播有什么作用?怎么使用?

粘性广播主要为了解决,在发送完广播之后,动态注册的接收者,也能够收到广播。举个例子首先发送一广播,我的接收者是通过程序中的某个按钮动态注册的。如果不是粘性广播,我注册完接收者肯定无法收到广播了。这是通过发送粘性广播就能够在我动态注册接收者后也能收到广播。

网络 HttpConnection 与HttpURLConnection的区别? 在Android 2.2版本之前,HttpClient拥有较少的bug,因此使用它是最好的选择 2.3之后使用HttpURLConnection,它的API简单,体积较小,压缩和缓存机制可以有效地减少网络访问的流量,在提升速度和省电方面也起到了较大的作用,利于维护与优化

TCP与UDP的区别? 1)基于连接与无连接 2)对系统资源的要求(TCP较多,UDP较少) 3)UDP程序结构较简单一些 4)流模式与数据包模式 5)TCP保证数据的顺序性以及正确性,UDP不能保证,可能存在丢包

Socket: soket是套接字,我们可以先在服务端初始化ServerSocket,然后对指定的端口进行绑定与监听,通过调用accept方法与getInputstream方法进行等待客户端的连接与数据的接收。现在客户端进行创建socket对象传入ip和端口号,通过getOutputStream进行数据的输入,并且制定结束字符,否则服务端会一直处于阻塞状态。

socket.close() 与socket.shutdownOutput()的区别? 1)在客户端或者服务端通过socket.shutdownOutput()都是单向关闭的,即关闭客户端的输出流并不会关闭服务端的输出流,所以是一种单方向的关闭流; 2)通过socket.shutdownOutput()关闭输出流,但socket仍然是连接状态,连接并未关闭 3)如果直接关闭输入或者输出流,即:in.close()或者out.close(),会直接关闭socket

socket长连接? 在不关闭流的情况下,开启循环线程进行定时发送与服务端约定的心跳包数据

Handler 什么是Handler? handler通过发送和处理Message和Runnable对象来关联相对应线程的MessageQueue 作用: 1)可以让对应的Message和Runnable在未来的某个时间点进行相应处理 2)让自己想要处理的耗时操作放在子线程,让更新ui的操作放在主线程

Handler发送消息的几种方式? 1)post(Runnable)延迟消息处理 2)sendMessage(message)

PS:post(Runnable)的Runnable中可以更新UI吗? 可以的 ,因为经常会post一个Runnable,处理的代码直接写在Runnable的run方法中,其实就是将这个Runnable发送到Handler所在线程(一般是主线程)的消息队列中。sendMessage方法主线程处理方法一般则是写在handleMessage中

Handler处理消息有哪几种方式?

直接看dispatchMessage()源码

//1. post()方法的最终处理方法 private static void handleCallback(Message message) { message.callback.run(); } //2. sendMessage()方法的最终处理方法 public void handleMessage(Message msg) { } 1 2 3 4 5 6 7 8 Handler、Message、Looper、MessageQueue

Message(消息) 定义:Handler接收和处理的消息对象(Bean对象) 作用:通信时相关信息的存放和传递

ThreadLocal 定义:ThreadLocal是线程内部的存储类,通过它可以实现在每个线程中存储自己的私有数据。即数据存储以后,只能在指定的线程中获取这个存储的对象,而其它线程则不能获取到当前线程存储的这个对象。 作用:负责存储和获取本线程的Looper

MessageQueue(消息队列) 定义:采用单链表的数据结构来存储消息列表 作用:用来存放通过Handler发过来的Message,按照先进先出执行

Handler(处理者) 定义:Message的主要处理者 作用:负责发送Message到消息队列&处理Looper分派过来的Message

Looper(循环器) 定义:扮演Message Queue和Handler之间桥梁的角色 作用: 消息循环:循环取出Message Queue的Message 消息派发:将取出的Message交付给相应的Handler

首先知道定义人,然后结合关系说清楚一些在加上HandlerThread基本上Handler这块是没什么大问题了

Message、Handler、MessageQueue、Looper的之间的关系? 首先,是这个MessagQueue,MessageQueue是一个消息队列,它可以存储Handler发送过来的消息,其内部提供了进队和出队的方法来管理这个消息队列,其出队和进队的原理是采用单链表的数据结构进行插入和删除的,即enqueueMessage()方法和next()方法。这里提到的Message,其实就是一个Bean对象,里面的属性用来记录Message的各种信息。 然后,是这个Looper,Looper是一个循环器,它可以循环的取出MessageQueue中的Message,其内部提供了Looper的初始化和循环出去Message的方法,即prepare()方法和loop()方法。在prepare()方法中,Looper会关联一个MessageQueue,而且将Looper存进一个ThreadLocal中,在loop()方法中,通过ThreadLocal取出Looper,使用MessageQueue的next()方法取出Message后,判断Message是否为空,如果是则Looper阻塞,如果不是,则通过dispatchMessage()方法分发该Message到Handler中,而Handler执行handlerMessage()方法,由于handlerMessage()方法是个空方法,这也是为什么需要在Handler中重写handlerMessage()方法的原因。这里要注意的是Looper只能在一个线程中只能存在一个。这里提到的ThreadLocal,其实就是一个对象,用来在不同线程中存放对应线程的Looper。 最后,是这个Handler,Handler是Looper和MessageQueue的桥梁,Handler内部提供了发送Message的一系列方法,最终会通过MessageQueue的enqueueMessage()方法将Message存进MessageQueue中。我们平时可以直接在主线程中使用Handler,那是因为在应用程序启动时,在入口的main方法中已经默认为我们创建好了Looper。

Handler引起的内存泄漏以及解决办法?

HandlerThread作用 当系统有多个耗时任务需要执行时,每个任务都会开启一个新线程去执行耗时任务,这样会导致系统多次创建和销毁线程,从而影响性能。为了解决这一问题,Google提供了HandlerThread,HandlerThread是在线程中创建一个Looper循环器,让Looper轮询消息队列,当有耗时任务进入队列时,则不需要开启新线程,在原有的线程中执行耗时任务即可,否则线程阻塞。

HandlerThread是什么?有哪些特点

1)HandlerThread本质上是一个线程类,它继承了Thread 2)HandlerThread有自己内部Looper对象,可以进行Looper循环 3)通过获取HandlerThread的looper对象传递给Handler对象,可以在handlerMessage方法中执行异步任务 4)优点是不会阻塞,减少对性能的消耗,缺点是不能同时进行多任务的处理,需要等待进行处理 5)与线程池注重并发不同,HandlerThread是一个串行队列,HandlerThread背后只有一个线程

子线程为什么不能开启handler? handler在调用sendMessage或者post(Runnable)的时候都需要一个MessageQueue 消息队列来保存发送的消息,而默认子线程中是没有开启Looper轮循器的,而消息队列又是由Looper来进行管理的,所以是没有办法开启的, 如果子线程想要开启,需要初始化looper,并且调用loop.loopers开启一个循环

如果简历中写了AsyncTask,就看一下,没写的直接跳过上Rxjava

AsyncTask是什么 AsyncTask是一种轻量级的异步任务类,它可以在线程池中执行后台任务,然后把执行的进度和最终结果传递给主线程并主线程中更新UI,通过AsyncTask可以更加方便执行后台任务以及在主线程中访问UI,但是AsyncTask并不适合进行特别耗时的后台任务,对于特别耗时的任务来说,建议使用线程池。

AsyncTask使用方法 三个参数 Params:表示后台任务执行时的参数类型,该参数会传给AysncTask的doInBackground()方法 Progress:表示后台任务的执行进度的参数类型,该参数会作为onProgressUpdate()方法的参数 Result:表示后台任务的返回结果的参数类型,该参数会作为onPostExecute()方法的参数 五个方法 onPreExecute():异步任务开启之前回调,在主线程中执行 doInBackground():执行异步任务,在线程池中执行 onProgressUpdate():当doInBackground中调用publishProgress时回调,在主线程中执行 onPostExecute():在异步任务执行之后回调,在主线程中执行 onCancelled():在异步任务被取消时回调

AsyncTask引起的内存泄漏 原因:非静态内部类持有外部类的匿名引用,导致Activity无法释放 解决: AsyncTask内部持有外部Activity的弱引用 AsyncTask改为静态内部类 Activity的onDestory()中调用AsyncTask.cancel()

结果丢失 屏幕旋转或Activity在后台被系统杀掉等情况会导致Activity的重新创建,之前运行的AsyncTask会持有一个之前Activity的引用,这个引用已经无效,这时调用onPostExecute()再去更新界面将不再生效。

AsyncTask并行or串行 AsyncTask在Android 2.3之前默认采用并行执行任务,AsyncTask在Android 2.3之后默认采用串行执行任务 如果需要在Android 2.3之后采用并行执行任务,可以调用AsyncTask的executeOnExecutor();

AsyncTask内部的线程池 private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR; sDefaultExecutor是SerialExecutor的一个实例,而且它是个静态变量。也就是说,一个进程里面所有AsyncTask对象都共享同一个SerialExecutor对象。

ListView+RecyclerView 既然RecyclerView在很多方面能取代ListView,Google为什么没把ListView划上一条过时的横线? ListView采用的是RecyclerBin的回收机制在一些轻量级的List显示时效率更高

ListView怎么和ScrollView兼容? 方法一:重写ListView, 覆盖onMeasure()方法 方法二:动态设置listview的高度,不需要重写ListView 方法三:在xml文件中,直接将Listview的高度写死

listview怎么优化? 1)、convertView复用,对convetView进行判空,当convertView不为空时重复使用,为空则初始化,从而减少了很多不必要的View的创建 2)定义一个ViewHolder,封装Listview Item条目中所有的组件,将convetView的tag设置为ViewHolder,不为空时通过ViewHolder的属性获取对应组件即可 3)、当ListView加载数据量较大时可以采用分页加载和图片异步加载

上拉加载和下拉刷新怎么实现? 实现OnScrollListener 接口重写onScrollStateChanged 和onScroll方法, 使用onscroll方法实现”滑动“后处理检查是否还有新的记录,如果有,调用 addFooterView,添加记录到adapter, adapter调notifyDataSetChanged 更新数据;如果没有记录了,把自定义的mFooterView去掉。使用onScrollStateChanged可以检测是否滚到最后一行且停止滚动然后执行加载

listview失去焦点怎么处理? 在listview子布局里面写,可以解决焦点失去的问题 android:descendantFocusability=”blocksDescendants”

ListView图片异步加载实现思路? 1.先从内存缓存中获取图片显示(内存缓冲) 2.获取不到的话从SD卡里获取(SD卡缓冲,,从SD卡获取图片是放在子线程里执行的,否则快速滑屏的话会不够流畅) 3.都获取不到的话从网络下载图片并保存到SD卡同时加入内存并显示(视情况看是否要显示)

你知道Listview里有Button就点不动了你知道吗? 原因是button强制获取了item的焦点,只要设置button的focusable为false即可。

listview分页加载的步骤? 通常实现分页加载有两种方式,一种是在ListView底部设置一个按钮,用户点击即加载。另一种是当用户滑动到底部时自动加载。 当用户滑动到底部时自动加载实现思路: 实现OnScrollListener 接口重写onScrollStateChanged 和onScroll方法,使用onscroll方法实现”滑动“后处理检查是否还有新的记录,如果有,添加记录到adapter, adapter调用 notifyDataSetChanged 更新数据;如果没有记录了,则不再加载数据。使用onScrollStateChanged可以检测是否滚到最后一行且停止滚动然后执行加载.

ViewHolder内部类非得要声明成static的呢? 因为非静态成员类的实例会包含一个额外的指向外围对象的引用,保存这份引用要消耗时间和空间,并且导致外围类实例符合垃圾回收时仍然被保留。如果没有外围实例的情况下,也需要分配实例,就不能使用非静态成员类,因为非静态成员类的实例必须要有一个外围实例。

ScrollView嵌套ListView和GridView冲突的方法 重写ListView的onMeasure方法,来自定义高度:

@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST); super.onMeasure(widthMeasureSpec, expandSpec); } 1 2 3 4 5 BaseAdapter四个关键方法的含义? 1) getCount() 返回数据源中数据的个数,如果该方法的返回值为0,那么适配器就不用生成布局对象了,提高了程序性能 2)getItem(int position) 根据位置返回数据项 3)getItemId(int position) 返回数据项的位置 4)getView(int position, View convertView, ViewGroup parent)

RecyclerView滑动删除原理实现? 两种方法: 一种就是通过重写RecyclerView的onTouchEvent()方法来检测手势的变化实现的,大致的流程如下: 1、根据手指触摸的坐标点找到对应Item的ViewHolder,进而得到相应的Item布局View。 2、手指继续移动,在条件满足的情况下,通过scrollBy()使Item布局View内容跟随手指一起移动,当然要注意边界检测。 3、手指抬起时,根据Item布局View内容移动的距离以及手指的滑动速度,判断是否显示删除按钮,进而通过startScroll()使Item布局View自动滑动到目标位置。 4、点击删除按钮则删除对应Item,点击其它区域则隐藏删除按钮。

另外一种: 实现原理 主要是借助 ItemTouchHelper.Callback 类来实现,我们要关注的方法为

  • getMovementFlags( )
  • onMove()
  • onSwiped()
  • onSelectedChanged()
  • clearView()
  • isLongPressDragEnabled()

首先自定义一个MyCallback类继承 ItemTouchHelper.Callback ,定义两个int变量dragFlags 与 swipeFlags并实现下方法。这个方法主要是为了获取我们当前的事件是拖动还是滑动

其他: Android View刷新机制? 在Android的布局体系中,父View负责刷新、布局显示子View;而当子View需要刷新时,则是通知父View来完成

RelativeLayout和LinearLayout性能比较? 1.RelativeLayout会让子View调用2次onMeasure,LinearLayout 在有weight时,也会调用子View2次onMeasure 2.RelativeLayout的子View如果高度和RelativeLayout不同,则会引发效率问题,当子View很复杂时,这个问题会更加严重。如果可以,尽量使用padding代替margin。 3.在不影响层级深度的情况下,使用LinearLayout和FrameLayout而不是RelativeLayout。

View和ViewGroup什么区别? Android的UI界面都是由View和ViewGroup及其派生类组合而成的。其中,View是所有UI组件的基类,而ViewGroup是容纳这些组件的容器,其本身也是从View派生出来的

自定义View优化策略 为了加速你的view,对于频繁调用的方法,需要尽量减少不必要的代码。先从onDraw开始,需要特别注意不应该在这里做内存分配的事情,因为它会导致GC,从而导致卡顿。在初始化或者动画间隙期间做分配内存的动作。不要在动画正在执行的时候做内存分配的事情。 你还需要尽可能的减少onDraw被调用的次数,大多数时候导致onDraw都是因为调用了invalidate().因此请尽量减少调用invaildate()的次数。如果可能的话,尽量调用含有4个参数的invalidate()方法而不是没有参数的invalidate()。没有参数的invalidate会强制重绘整个view。 另外一个非常耗时的操作是请求layout。任何时候执行requestLayout(),会使得Android UI系统去遍历整个View的层级来计算出每一个view的大小。如果找到有冲突的值,它会需要重新计算好几次。另外需要尽量保持View的层级是扁平化的,这样对提高效率很有帮助。 如果你有一个复杂的UI,你应该考虑写一个自定义的ViewGroup来执行他的layout操作。与内置的view不同,自定义的view可以使得程序仅仅测量这一部分,这避免了遍历整个view的层级结构来计算大小。这个PieChart 例子展示了如何继承ViewGroup作为自定义view的一部分。PieChart 有子views,但是它从来不测量它们。而是根据他自身的layout法则,直接设置它们的大小

音视频 SurfaceView是什么 ? 它继承自类View,因此它本质上是一个View。但与普通View不同的是,它有自己的Surface。有自己的Surface,在WMS中有对应的WindowState,在SurfaceFlinger中有Layer。我们知道,一般的Activity包含的多个View会组成View hierachy的树形结构,只有最顶层的DecorView,也就是根结点视图,才是对WMS可见的。这个DecorView在WMS中有一个对应的WindowState。相应地,在SF中对应的Layer。而SurfaceView自带一个Surface,这个Surface在WMS中有自己对应的WindowState,在SF中也会有自己的Layer。虽然在App端它仍在View hierachy中,但在Server端(WMS和SF)中,它与宿主窗口是分离的。这样的好处是对这个Surface的渲染可以放到单独线程去做,渲染时可以有自己的GL context。这对于一些游戏、视频等性能相关的应用非常有益,因为它不会影响主线程对事件的响应。但它也有缺点,因为这个Surface不在View hierachy中,它的显示也不受View的属性控制,所以不能进行平移,缩放等变换,也不能放在其它ViewGroup中,一些View中的特性也无法使用。

SurfaceView优点及缺点? 优点:可以在一个独立的线程中进行绘制,不会影响主线程。使用双缓冲机制,播放视频时画面更流畅

缺点:Surface不在View hierachy中,它的显示也不受View的属性控制,所以不能进行平移,缩放等变换,也不能放在其它ViewGroup中。SurfaceView 不能嵌套使用

SurfaceView中双缓冲? 双缓冲:在运用时可以理解为:SurfaceView在更新视图时用到了两张Canvas,一张frontCanvas和一张backCanvas,每次实际显示的是frontCanvas,backCanvas存储的是上一次更改前的视图,当使用lockCanvas()获取画布时,得到的实际上是backCanvas而不是正在显示的frontCanvas,之后你在获取到的backCanvas上绘制新视图,再unlockCanvasAndPost(canvas)此视图,那么上传的这张canvas将替换原来的frontCanvas作为新的frontCanvas,原来的frontCanvas将切换到后台作为backCanvas。例如,如果你已经先后两次绘制了视图A和B,那么你再调用lockCanvas()获取视图,获得的将是A而不是正在显示的B,之后你讲重绘的C视图上传,那么C将取代B作为新的frontCanvas显示在SurfaceView上,原来的B则转换为backCanvas。

TextureView是什么? 在4.0(API level 14)中引入,与SurfaceView一样继承View, 它可以将内容流直接投影到View中,它可以将内容流直接投影到View中,可以用于实现Live preview等功能。和SurfaceView不同,它不会在WMS中单独创建窗口,而是作为View hierachy中的一个普通View,因此可以和其它普通View一样进行移动,旋转,缩放,动画等变化。值得注意的是TextureView必须在硬件加速的窗口中。它显示的内容流数据可以来自App进程或是远端进程。从类图中可以看到,TextureView继承自View,它与其它的View一样在View hierachy中管理与绘制。TextureView重载了draw()方法,其中主要SurfaceTexture中收到的图像数据作为纹理更新到对应的HardwareLayer中。SurfaceTexture.OnFrameAvailableListener用于通知TextureView内容流有新图像到来。SurfaceTextureListener接口用于让TextureView的使用者知道SurfaceTexture已准备好,这样就可以把SurfaceTexture交给相应的内容源。Surface为BufferQueue的Producer接口实现类,使生产者可以通过它的软件或硬件渲染接口为SurfaceTexture内部的BufferQueue提供graphic buffer。

TextureView优点及缺点?

优点:支持移动、旋转、缩放等动画,支持截图

缺点:必须在硬件加速的窗口中使用,占用内存比SurfaceView高,在5.0以前在主线程渲染,5.0以后有单独的渲染线程。

谁的性能更优? 这里写图片描述

播放器应该选择谁?

从性能和安全性角度出发,使用播放器优先选SurfaceView。 1、在android 7.0上系统surfaceview的性能比TextureView更有优势,支持对象的内容位置和包含的应用内容同步更新,平移、缩放不会产生黑边。 在7.0以下系统如果使用场景有动画效果,可以选择性使用TextureView

2、由于失效(invalidation)和缓冲的特性,TextureView增加了额外1~3帧的延迟显示画面更新

3、TextureView总是使用GL合成,而SurfaceView可以使用硬件overlay后端,可以占用更少的内存带宽,消耗更少的能量

4、TextureView的内部缓冲队列导致比SurfaceView使用更多的内存

5、SurfaceView: 内部自己持有surface,surface 创建、销毁、大小改变时系统来处理的,通过surfaceHolder 的callback回调通知。当画布创建好时,可以将surface绑定到MediaPlayer中。SurfaceView如果为用户可见的时候,创建SurfaceView的SurfaceHolder用于显示视频流解析的帧图片,如果发现SurfaceView变为用户不可见的时候,则立即销毁SurfaceView的SurfaceHolder,以达到节约系统资源的目的

视频编码标准两大系统是什么?

视频编码标准有两大系统:MPEG和ITU-T,如下 视频编码标准 MPEG标准由MPEG制定 MPEG-1 | MPEG-2 | (MPEG-3) | MPEG-4 | MPEG-7 | MPEG-21 ITU-T标准由VCEG制定 H.261 | (H.262) | H.263 | H.263v2 | H.264

什么是音视频编码格式?什么是音视频封装格式?

常见的AVI、RMVB、MKV、ASF、WMV、MP4、3GP、FLV等文件其实只能算是一种封装标准。

一个完整的视频文件是由音频和视频2部分组成的。H264、Xvid等就是视频编码格式,MP3、AAC等就是音频编码格式。

例如:将一个Xvid视频编码文件和一个MP3视频编码文件按AVI封装标准封装以后,就得到一个AVI后缀的视频文件,这个就是我们常见的AVI视频文件了。

由于很多种视频编码文件、音频编码文件都符合AVI封装要求,则意味着即使是AVI后缀,也可能里面的具体编码格式不同。因此出现在一些设备上,同是AVI后缀文件,一些能正常播放,还有一些就无法播放。

同样的情况也存在于其他容器格式。即使RMVB、WMV等也不例外,事实上,很多封装容器对音频编码和视频编码的组合方式放的很开,如AVI还可以使用H.264+AAC组合,可以在具体使用中自己体会。尤其是MKV封装容器,基本无论什么样的组合都可以!但一般MKV用的最多的就是H.264+AAC组合,此组合文件体积最小,清晰度最高。因此网上很多MKV视频都是高清晰度的。

因此,视频转换需要设置的本质就是:A设置需要的视频编码、B设置需要的音频编码、C选择需要的容器封装。一个完整的视频转换设置都至少包括了上面3个步骤。

平时说的软解和硬解,具体是什么?

硬解就是硬件解码,指利用GPU来部分代替CPU进行解码,软解就是软件解码,指利用软件让CPU来进行解码。两者的具体区别如下所示:

硬解码:是将原来全部交由CPU来处理的视频数据的一部分交由GPU来做,而GPU的并行运算能力要远远高于CPU,这样可以大大的降低对CPU的负载,CPU的占用率较低了之后就可以同时运行一些其他的程序了,当然,对于较好的处理器来说,比如i5 2320,或者AMD 任何一款四核心处理器来说,硬解和软件的区别只是个人偏好问题了吧。  

软解码:即通过软件让CPU来对视频进行解码处理;而硬解码:指不借助于CPU,而通过专用的子卡设备来独立完成视频解码任务。曾经的VCD/DVD解压卡、视频压缩卡等都隶属于硬解码这个范畴。而现如今,要完成高清解码已经不再需要额外的子卡,因为硬解码的模块已经被整合到显卡GPU的内部,所以目前的主流显卡(集显)都能够支持硬解码技术。

何为直播?何为点播?

直播:是一个三方交互(主播、服务器、观众),这个交互式实时的!尽管会根据选择的协议不同而有一些延迟,但我们仍认为它直播是实时的!—>主播在本地发送音视频给服务器(推流),观众从服务器实时解码(拉流)收看收听主播发送给服务器的音视频(直播内容)。直播是不能快进的 点播:首先一定要明确的一点,点播不存在推流这一过程,你本身你的流已经早就推给服务器了,或者这么说也不对,应该是你的音视频早就上传到了服务器,观众只需要在线收看即可,由于你的音视频上传到了服务器,观众则可以通过快进,快退,调整进度条等方式进行收看!

简述推流、拉流的工作流程? 推流:在直播中,一方向服务器发送请求,向服务器推送自己正在实时直播的数据,而这些内容在推送到服务器的这一过程中是以 “流” 的形式传递的,这就是“推流”,把音视频数据以流的方式推送(或上传)到服务器的过程就是“推流”!推流方的音视频往往会很大,在推流的过程中首先按照 aac音频-编码 和 h264视频-编码的标准把推过来的音视频压缩 ,然后合并成 MP4或者 FLV格式,然后根据直播的封装协议,最后传给服务器完成推流过程。

拉流:与推流正好相反,拉流是用户从服务器获取推流方给服务器的音视频的过程,这就是“拉流”!拉流首先aac音频-解码 和 h.264视频-解码的内部把推过来的音视频解压缩,然后合成 MP4或者 FLV 格式,再解封装,最后到我们的客户端与观众进行交互。

常见的直播协议有哪些?之间有什么区别?

常见的直播协议有三种 RTMP、HLS、FLV…

1)RTMP:real time messaging protocol~实时传输协议,RTMP协议比较全能,既可以用来推送又可以用来直播,其核心理念是将大块的视频帧和音频帧“剁碎”,然后以小数据包的形式在互联网上进行传输,而且支持加密,因此隐私性相对比较理想,但拆包组包的过程比较复杂,所以在海量并发时也容易出现一些不可预期的稳定性问题。

2)FLV:FLV协议由Adobe公司主推,格式极其简单,只是在大块的视频帧和音视频头部加入一些标记头信息,由于这种极致的简洁,在延迟表现和大规模并发方面都很成熟。唯一的不足就是在手机浏览器上的支持非常有限,但是用作手机端APP直播协议却异常合适。

3)HLS:苹果原生:HTTP Live Streaming,遵循的是 HTTP 超文本传输协议,端口号8080,将视频分成5-10秒的视频小分片,然后用m3u8索引表进行管理,由于客户端下载到的视频都是5-10秒的完整数据,故视频的流畅性很好,但也同样引入了很大的延迟(HLS的一般延迟在10-30s左右)。

点播中常见的数据传输协议主要有哪些?

常见的点播协议:HLS,HTTP

何为Nginx?有什么特点? Nginx 是一个遵循 HTTP 协议的服务器!内存占用少,并发能力强! 还有等等的优点,可自行google

何为homebrew?你用它安装过什么?常用命令有哪些? homebrew是一个 Mac系统下所独有的套件管理器,我要做直播,需要 rtmp 和 nginx ,单独安装很复杂,只要在终端里输入简单的安装相应的套件命令即可完成安装,复杂的过程都靠 homebrew 规避掉了! 我用它安装过很多东西,比如nginx 搭建流媒体服务器等。 常用命令:brew install 、brew uninstall、brew search、brew list、brew update、brew help 等~

FFmpeg是什么?

FFmpeg是一套用来记录和转换数字音视频,并能将其转化为流的开源计算机程序。拉流和推流离不开 FFmpeg 的帮助!

RTMP、HLS协议各自的默认端口号是?

RTMP端口号:1935 HLS端口号 :8080

m3u8构成是?直播中m3u8、ts如何实时更新?

是一个索引地址/播放列表,通过FFmpeg将本地的xxx.mp4进行切片处理,生成m3u8播放列表(索引文件)和N多个 .ts文件,并将其(m3u8、N个ts)放置在本地搭建好的webServer服务器的指定目录下,我就可以得到一个可以实时播放的网址,我们把这个m3u8地址复制到 VLC 上就可以实时观看! 在 HLS 流下,本地视频被分割成一个一个的小切片,一般10秒一个,这些个小切片被 m3u8管理,并且随着终端的FFmpeg 向本地拉流的命令而实时更新,影片进度随着拉流的进度而更新,播放过的片段不在本地保存,自动删除,直到该文件播放完毕或停止,ts 切片会相应的被删除,流停止,影片不会立即停止,影片播放会滞后于拉流一段时间

PS: FFmpeg推流至Nginx:可以推两种流:RTMP流,推流至rtmplive;HLS流,推流至hls;其中,HLS流表现较明显,在nginx的临时目录下,直观的可看到m3u8索引文件和N多个.ts文件。m3u8列表会实时更新,且会动态更改当前播放索引切片(.ts)。这种实时更新的机制,不会使得.ts文件长时间存在于Nginx服务器上,且当推流结束之后,该目录下的内容会被全部清除,这样无形中减缓了nginx服务器的压力。另外,也阐释了HLS这种流媒体播放相较RTMP延时较高的原因。

说说你平时在播放过程中做的优化工作 预加载,弱网优化,播放出错重试机制,运营商劫持引起的起播慢,mediaserver的cpu占有率很高,引起播放卡顿。起播时,只保留播放进程,kill 其他进程 。

未完待续

转载于:https://juejin.im/post/5afd512f6fb9a07aaf356e6a

相关文章:

  • js中添加node.js语法支持
  • 理解CAS算法在JAVA中的作用
  • 单体架构,SOA架构,微服务架构,分布式架构,集群架构
  • SqlServer查询出数据库中所有的表及其字段属性
  • 排除网络故障课后习题参考答案
  • Java并发(零)教程目录
  • 大数据盛宴!帆软成功举办东北及华南地区大数据巡展
  • 微软宣布公开预览其内容分发网络
  • 03、漫游用户
  • 57. Netty源代码分析-服务器端启动ServerBootstrap初始化
  • 玩转云企业网(上):为什么需要云企业网
  • Google Photos上线一年 照片存储达13.7PB
  • gojs 流程图框架-基础绘图(一)
  • 第三代DRDS分布式SQL引擎全新发布
  • 六十四、Linux监控平台介绍、zabbix监控介绍、安装zabbix、忘记Admin密码如何做
  • 08.Android之View事件问题
  • canvas 绘制双线技巧
  • express + mock 让前后台并行开发
  • IE报vuex requires a Promise polyfill in this browser问题解决
  • Java Agent 学习笔记
  • Java 内存分配及垃圾回收机制初探
  • Java深入 - 深入理解Java集合
  • JS数组方法汇总
  • Laravel核心解读--Facades
  • LeetCode29.两数相除 JavaScript
  • Netty 框架总结「ChannelHandler 及 EventLoop」
  • sublime配置文件
  • vue的全局变量和全局拦截请求器
  • 编写符合Python风格的对象
  • 番外篇1:在Windows环境下安装JDK
  • 分类模型——Logistics Regression
  • 区块链技术特点之去中心化特性
  • 如何打造100亿SDK累计覆盖量的大数据系统
  • 收藏好这篇,别再只说“数据劫持”了
  • 原生 js 实现移动端 Touch 滑动反弹
  • ​LeetCode解法汇总2696. 删除子串后的字符串最小长度
  • ​ssh-keyscan命令--Linux命令应用大词典729个命令解读
  • ​VRRP 虚拟路由冗余协议(华为)
  • ​第20课 在Android Native开发中加入新的C++类
  • #Lua:Lua调用C++生成的DLL库
  • $ git push -u origin master 推送到远程库出错
  • (6)【Python/机器学习/深度学习】Machine-Learning模型与算法应用—使用Adaboost建模及工作环境下的数据分析整理
  • (react踩过的坑)Antd Select(设置了labelInValue)在FormItem中initialValue的问题
  • (附源码)springboot炼糖厂地磅全自动控制系统 毕业设计 341357
  • (附源码)ssm教师工作量核算统计系统 毕业设计 162307
  • (免费领源码)Python#MySQL图书馆管理系统071718-计算机毕业设计项目选题推荐
  • (学习日记)2024.03.12:UCOSIII第十四节:时基列表
  • (转贴)用VML开发工作流设计器 UCML.NET工作流管理系统
  • *Algs4-1.5.25随机网格的倍率测试-(未读懂题)
  • .NET 8.0 中有哪些新的变化?
  • .net 打包工具_pyinstaller打包的exe太大?你需要站在巨人的肩膀上-VC++才是王道
  • .Net 中的反射(动态创建类型实例) - Part.4(转自http://www.tracefact.net/CLR-and-Framework/Reflection-Part4.aspx)...
  • .netcore 如何获取系统中所有session_如何把百度推广中获取的线索(基木鱼,电话,百度商桥等)同步到企业微信或者企业CRM等企业营销系统中...
  • .net程序集学习心得
  • .Net中的集合