面试题总结并附上答案1

面试题总结并附上答案1

Posted by pxf on March 16, 2018

面试题总结并附上答案1

  • 基础知识 – 四大组件(生命周期,使用场景,如何启动)
  • java基础 – 数据结构,线程,mvc框架
  • 通信 – 网络连接(HttpClient,HttpUrlConnetion),Socket
  • 数据持久化 – SQLite,SharedPreferences,ContentProvider
  • 性能优化 – 布局优化,内存优化,电量优化
  • 安全 – 数据加密,代码混淆,WebView/Js调用,https
  • UI– 动画
  • 其他 – JNI,AIDL,Handler,Intent等
  • 开源框架 – Volley,Gilde,RxJava等(简历上写你会的,用过的)
  • 拓展 – Android6.0/7.0/8.0特性,kotlin语言,I/O大会

Android中的设计模式

Android中的设计模式

1.单例 全局性 减少对象创建,降低系统内存的消耗,减少gc次数和卡顿时间 初始化资源的工具类 2.创建者模式 创建对象的参数很多且不定,参数个数和类型不确定, AlertDialog glide okhttp 3.适配器模式 baseAdapter 4.装饰模式 context 5.外观模式 contextImple 6.策略模式 属性动画 插值器 7.组合模式 view和viewGroup 8.模板方法 Activity fragment 9.观察者模式 RxJava 10.责任链模式 try catch,事件分发机制

Android各组件和机制

Activity

Activity

1.四大组件中Activity是用的最多也是最基础的,Activity提供窗口来与用户进行交互

2.Android默认会为每一个App应用提供一个task的栈老存放和管理该App所有的Activity task默认的name是该App的包名 也可以在mainfest清单文件中设置Activity的taskAffinity属性来自定义task,但是不建议这样使用,如果别App也设置一样的task,它就可能启动你的Activity,带来各种安全隐患

3.Activity的生命周期 onCreate -> onStart -> onResume -> onPause -> onStop -> onDestroy

正常情况下生命周期的调用 (1)A页面打开B页面或者切回到桌面 A Activity:onPause -》 onStop,如果B Activity采用了透明主题 则不会调用 onStop

(2)先调用A的onPause再调用B的onResume方法

(3)再回到A页面时,onRestart -》 onStart -》onResume

(4)按返回键时,onPause -》 onStop -》onDestroy

异常情况下在生命周期的调用

(1)资源相关的系统配置发生改变时,Activity被杀死 例如Activity竖屏,突然旋转屏幕,Activity默认会销毁并重新创建

onSaveInstanceState -》 onDestroy 重新创建 onCreate 。。。-》 onDestroy

(2)系统资源不足导致优先级低的Activity被回收

onSaveInstanceState -》 onDestroy 重新创建 onCreate 。。。-》 onDestroy

4.Activity信息保存和恢复

系统会调用onSaveInstanceState来保存当前Activity的状态,这个方法调用的时机是在onStop之前,他和onPause没有既定的时序关系,他即可能在onPause之前调用,也有可能在之后调用,需要强调的是,这个方法只出现在Activity被异常终止的情况下,正常情况下是不会走这个方法的,当我们onSaveInstanceState保存到Bundler对象作为参数传递给onRestoreInstanceState和onCreate方法,因此我们可以通过onRestoreInstanceState和onCreate方法来判断Activity是否被重建。如果被重建了,我们就取出之前的数据恢复,从时序上来说,onRestoreInstanceState的调用时机应该在onStart之后

5.启动模式

(1)定义启动模式的方法

manifest中声明

<activity ...... android:launchMode=”standard” > ....... </activity>

使用Intent标志

Intent i = new Intent(this,NewActivity.class); i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(i);

(2)模式类型

standard 默认模式 singleTop 当目标Activity位于task栈的栈顶时,直接复用,不会创建新的Activity,否则泽创建新的Activity并添加到task栈中 singleTask 如果目标Activity位于Task栈顶中,则复用,如果没有位于栈顶就将目标Activity之前的Activity移除栈,然后调用,否则就创建新的Activity并添加到栈中 singleInstance 如果目标Activity不存在,创建一个新的task栈添加目标Activity并调用,如果Activity已存在就将Activity所在的task移到前台并调用

Service

Service 应用程序组件 能够在后台执行耗时较长的操作,并且不提供界面

Service分类

按运行分类
  • 前台Service

startForeground 开启前台服务

stopForeground 移除前台服务

  • 后台Service
按使用分类
  • 本地服务

运用于应用程序内部实现一些耗时操作,并不占用Activity的线程,而是新开一个线程在后台执行

Context.startService 启动

Context.stopService 结束

在内部可以调用Service.stopSelf() 或 Service.stopSelfResult()来自己停止。

  • 远程服务

用于应用程序之间,可被其他应用程序复用 例如天气预报,其他应用程序不需要在写这样的服务,调用即可

通过暴露接口供其他应用程序调用

Contex.bindService 连接

Context.unbinService 断开连接

Service生命周期

  • onCreate -》 onStartCommand -》 onDestroy

  • onCreate -》 onBind =》onUnBind -》onDestroy

onStartCommand()方法必须返回一个整数。

  • START_NOT_STICKY 杀死了服务,则不会重建服务

  • START_STICKY 杀死了服务,则将重建服务并调用onStartCommand(),但不会再次送入上一个intent 这适用于媒体播放器(或类似服务),它们不执行命令,但需要一直运行并随时待命

  • START_REDELIVER_INTENT 杀死了服务,则将重建服务并用上一个已送过的intent调用onStartCommand() 适用于那些需要立即恢复工作的活跃服务,比如下载文件

start服务

bind服务

在mainfest中声明服务

<manifest ... >
	... 
	<application ... >
	 <service android:name=".ExampleService" /> 
	... 
	</application> 
</manifest>

ContentProvider

ContentProvider(内容提供者)是Android的四大组件之一,管理android以结构化方式存放的数据,以相对安全的方式封装数据(表)并且提供简易的处理机制和统一的访问接口供其他程序调用。 

  Android的数据存储方式总共有五种,分别是:Shared Preferences、网络存储、文件存储、外储存储、SQLite。但一般这些存储都只是在单独的一个应用程序之中达到一个数据的共享,有时候我们需要操作其他应用程序的一些数据,就会用到ContentProvider。而且Android为常见的一些数据提供了默认的ContentProvider(包括音频、视频、图片和通讯录等)。

  但注意ContentProvider它也只是一个中间人,真正操作的数据源可能是数据库,也可以是文件、xml或网络等其他存储方式。

BroadcastReceiver

广播是一种广泛运用的在应用程序之间传输信息的机制,主要用来监听系统或者应用发出的广播信息,然后根据广播信息作为相应的逻辑处理,也可以用来传输少量、频率低的数据。

  在实现开机启动服务和网络状态改变、电量变化、短信和来电时通过接收系统的广播让应用程序作出相应的处理。

  BroadcastReceiver 自身并不实现图形用户界面,但是当它收到某个通知后, BroadcastReceiver 可以通过启动 Service 、启动 Activity 或是 NotificationMananger 提醒用户。

静态注册

在AndroidManifest.xml的application里面定义receiver并设置要接收的action。

< receiver android:name = ".MyBroadcastReceiver" > 
 < intent-filter android:priority = "777" > <action android:name = "android.provider.Telephony.SMS_RECEIVED" /> </ intent-filter >  </ receiver > 

动态注册 在Activity中声明BroadcastReceiver的扩展对象,在onResume中注册,onPause中卸载.

静态注册和动态注册的区别

  1、静态注册的广播接收者一经安装就常驻在系统之中,不需要重新启动唤醒接收者;   动态注册的广播接收者随着应用的生命周期,由registerReceiver开始监听,由unregisterReceiver撤销监听,如果应用退出后,没有撤销已经注册的接收者应用应用将会报错。

  2、当广播接收者通过intent启动一个activity或者service时,如果intent中无法匹配到相应的组件。    动态注册的广播接收者将会导致应用报错   而静态注册的广播接收者将不会有任何报错,因为自从应用安装完成后,广播接收者跟应用已经脱离了关系。

总结

  • 静态广播接收的处理器是由PackageManagerService负责,当手机启动或者新安装了应用的时候,PackageManagerService会扫描手机中所有已安装的APP应用,将AndroidManifest.xml中有关注册广播的信息解析出来,存储至一个全局静态变量当中。

  • 动态广播接收的处理器是由ActivityManagerService负责,当APP的服务或者进程起来之后,执行了注册广播接收的代码逻辑,即进行加载,最后会存储在一个另外的全局静态变量中。需要注意的是:

   1、 这个并非是一成不变的,当程序被杀死之后,已注册的动态广播接收器也会被移出全局变量,直到下次程序启动,再进行动态广播的注册,当然这里面的顺序也已经变更了一次。

   2、这里也并没完整的进行广播的排序,只记录的注册的先后顺序,并未有结合优先级的处理。

  • 广播发出的时候,广播接收者接收的顺序如下:

  1.当广播为普通广播时,有如下的接收顺序:

  无视优先级   动态优先于静态   同优先级的动态广播接收器,先注册的大于后注册的   同优先级的静态广播接收器,先扫描的大于后扫描的 

  2.如果广播为有序广播,那么会将动态广播处理器和静态广播处理器合并在一起处理广播的消息,最终确定广播接收的顺序: 

  优先级高的先接收    同优先级的动静态广播接收器,动态优先于静态   同优先级的动态广播接收器,先注册的大于后注册的   同优先级的静态广播接收器,先扫描的大于后扫描的

广播怎么不跨进程

无论是使用扩展变量作用域、基于接口的回调还是Handler-post/Handler-Message等方式,都可以直接处理此类问题,若适用广播机制,显然有些“杀鸡牛刀”的感觉,会显太“重”;

模块化和组件化开发

模块化和组件化开发

模块化

按照功能划分成相互独立的模块,使其只包含与其相关的功能。登录,搜索,交易,社区等

组件化

基于可重用的目的,将一个系统按照拆分关注点的形式,拆分成一个的组件,减少解耦

模块化vs组件化

模块化和组件化本质思想是一样的,都是”大化小”,两者的目的都是为了重用和解耦,只是叫法不一样.如果非要说区别,那么可以认为模块化粒度更小,更侧重于重用,而组件化粒度稍大于模块,更侧重于业务解耦。

优点

  • 1.模块间解耦,复用,避免重复造轮子,节省开发维护成本 对业务进行模块化拆分后,为了使各业务模块间解耦,因此各个都是独立的模块,它们之间是没有依赖关系。每个模块负责的功能不同,业务逻辑不同,模块间业务解耦。模块功能比较单一,可在多个项目中使用

  • 2.可单独编译某个模块,降低项目复杂性,提升开发效率 每个模块实际上也是一个完整的项目,可以进行单独编译,调试

  • 3.可以多团队并行开发,测试 多个团队公用同一个组件,在一定层度上确保了技术方案的统一性 每个团队负责不同的模块,提升开发,测试效率

Android开发模式(MVC,MVVM,MVP)

Android App的设计架构:MVC,MVP,MVVM与架构经验谈

MVC

View xml Controller Activity Model 建立的数据机构和相关的类

1.View传达指令到Controller 2.Controller完成业务逻辑后,要求Model改变状态 3.Model将新的数据发送给View,更新视图,用户得到反馈

MVC可分为2种

1.通过View接受指令 2.通过Controller接受指令

缺点

在Android开发中,Activity并不是一个标准的MVC模式中的Controller,它的首要职责是加载应用的布局和初始化用户界面 ,并接受并处理来自用户的操作请求,进而作出响应。随着界面及其逻辑的复杂度不断提升, Activity类的职责不断增加,以致变得庞大臃肿。

MVP

在App开发过程中,经常出现的问题就是某部分的代码过 ,虽然做模块划分和接隔离,但也很难完全避免。从实践中看到,这多的出现在UI部分,也就是Activity 。想象下, 个2000+ 以上基本 带注释的Activity,我的第 反应就是想吐。Activity内容过多的原因其实很好解释,因为Activity本身需要担负 与用户之间的操作交互,界面的展示, 是单纯的Controller或View。 且现在部分的Activity还对整个App 起到类似iOS中的【ViewController】的作 ,这又带入的逻辑代码,造成Activity的臃肿。为 解决这个问题,让我们引MVP框架。

将Controller改成了Presenter,同时改变了通信方向 1.Presenter和View是可以双向通信的 2.Presenter和Model也是可以双向通信的 3.View和Model之间不能通信,都是通过Presenter传递的 4.View和薄,没有任何业务逻辑,而Presenter很厚,所有的业务逻辑都在这处理

MVVM

将Presenter改成了Viewmodel,通信方式基本和MVP差不多,最大的区别是MVVM有双向绑定,View的变动反映在ViewModel上,ViewModel的修改也会更新View

事件分发机制

事件分发机制

onTouchEvent

Handler机制

Handler机制

  • 1.在创建Activity之前,当系统启动的时候,先加载ActivityThread这个类,在这个类中的main函数,调用了Looper.prepareMainLooper()方法进行初始化Looper对象;

  • 2.然后创建了主线程的handler对象(Tips:加载ActivityThread的时候,其内部的Handler对象[静态的]还未创建);

  • 3.随后才创建了ActivityThread对象;

  • 4.最后调用了Looper.loop();方法,不断的进行轮询消息队列的消息。

  • 也就是说,在ActivityThread和Activity创建之前(同样也是Handler创建之前,当然handler由于这两者初始化),就已经开启了Looper的loop()方法,不断的进行轮询消息。

  • 需要注意的是,这个轮询的方法是阻塞式的,没有消息就一直等待(实际是等着MessageQueue的next()方法返回消息)。

  • 在应用一执行的时候,就已经开启了Looper,并初始化了Handler对象。此时,系统的某些组件或者其他的一些活动等发送了系统级别的消息,这个时候主线程中的Looper就可以进行轮询消息,并调用msg.target.dispatchMessage(msg)(msg.target即为handler)进行分发消息,并通过handler的handleMessage方法进行处理;所以会优于我们自己创建的handler中的消息而处理系统消息。

下面,我们按照启动顺序来进行源码分析:

从Handler+Message+Looper源码带你分析Android系统的消息处理机制

让主线程给子线程发送消息

1、首先我们在Activity中的onCreate方法中开启一个子线程,然后在子线程中进行Looper和Handler的创建等

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        new Thread(new Runnable() {
            @Override
            public void run() {
                /**
                 *  1、创建了Looper对象,然后Looper对象中创建了MessageQueue
                 *  2、并将当前的Looper对象跟当前的线程(子线程)绑定ThreadLocal
                 */
                Looper.prepare();

                /**
                 * 1、创建Handler对象,然后从当前线程中获取Looper对象,然后获取到MessageQueue对象
                 */
                subHandler = new Handler(){
                    @Override
                    public void handleMessage(Message msg) {
                        super.handleMessage(msg);
                        Toast.makeText(MainActivity.this,msg.obj.toString(),Toast.LENGTH_LONG).show();
                    }
                };

                /**
                 * 1、从当前线程中找到之前创建的Looper对象,然后找到MessageQueue
                 * 2、开启死循环,遍历消息池中的消息
                 * 3、当获取到msg的时候,调用这个msg的handler的dispatchMsg方法,让msg执行起来
                 */
                Looper.loop();
                Log.e("zxj","loop()方法执行完了");
            }
        }).start();
    }

2、然后在点击事件中发送消息

public void onSendClick(View v){
    //从消息池中获取一个旧的msg,如果没有,重新创建消息
    Message message = subHandler.obtainMessage(1, "我是主线程发送来是消息");
    message.sendToTarget();
}

3、问题

以上的写法还是存在一些问题的,其实你在运行起来后,点击按钮发送消息后,当你退出当前Activity后,发现上面定义的log日志,没有执行,这就说明,那个子线程一直未执行完,为啥呢?其实就是因为loop()这个方法,因为这个方法就一个死循环的,所以这个方法一直在循环中,从而导致子线程一直没有执行完,最终会导致内存泄露

4、解决方法

在子线程中拿到创建的Looper对象(Looper.myLooper()),然后在onDestroy中中止loop方法

@Override
protected void onDestroy() {
    super.onDestroy();
    if(myLooper != null)
        myLooper.quit();
}

5、最终代码

public class MainActivity extends AppCompatActivity {
    private Handler subHandler;//是在子线程中创建的Handler对象
    private Looper myLooper;//子线程中的Looper对象

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        new Thread(new Runnable() {
            @Override
            public void run() {
                /**
                 *  1、创建了Looper对象,然后Looper对象中创建了MessageQueue
                 *  2、并将当前的Looper对象跟当前的线程(子线程)绑定ThreadLocal
                 */
                Looper.prepare();

                /**
                 * 1、创建Handler对象,然后从当前线程中获取Looper对象,然后获取到MessageQueue对象
                 */
                subHandler = new Handler(){
                    @Override
                    public void handleMessage(Message msg) {
                        super.handleMessage(msg);
                        Toast.makeText(MainActivity.this,msg.obj.toString(),Toast.LENGTH_LONG).show();
                    }
                };

                //获取当前线程中的Looper对象
                myLooper = Looper.myLooper();

                /**
                 * 1、从当前线程中找到之前创建的Looper对象,然后找到MessageQueue
                 * 2、开启死循环,遍历消息池中的消息
                 * 3、当获取到msg的时候,调用这个msg的handler的dispatchMsg方法,让msg执行起来
                 */
                Looper.loop();
                Log.e("zxj","loop()方法执行完了");
            }
        }).start();
    }

    public void onSendClick(View v){
        //从消息池中获取一个旧的msg,如果没有,重新创建消息
        Message message = subHandler.obtainMessage(1, "我是主线程发送来是消息");
        message.sendToTarget();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if(myLooper != null)
            myLooper.quit();
    }
}

机型适配

机型适配

  • 魅族3和魅族4

场景:魅族手机ListView的Item中的EditText无法编辑,点击EditText弹出软键盘后,软键盘会立即自动隐藏 机型:[魅族3,魅族4] 解决方案: 方法一:将ListView换成RecyclerView

方法二:https://github.com/Aspsine/EditTextInListView

  • HTC M8

场景:HTC M8 从一个Activity 使用QQSDK 登陆, 登陆成功后, 返回Activity结果Activity 被销毁了 机型:HTC M8 等某些带有 虚拟 Menu 键盘的手机 解决方案:后来调查发现是这个Activity是全屏,屏蔽了Menu键盘的黑条. 但是跳转到QQ却把那个Menu的黑条显示了出来, 这导致发生了 screenSize 的变化 从而导致我的Activity销毁了. 知道了这个原因, 在manifest中的 configChanges 添加screenSize 解决了这个问题.

  • 所有手机

PopupWindow中嵌套EditText,会出现EditText长按无法触发“粘贴”选项,可以改成Dialog嵌套EditText,包括DialogFragment。

  • 所有手机

场景:输入法中的emoji适配,Android4.1之前的系统不支持emoji显示

解决方案:所以对于Android4.1之前的系统,我采用了bitmap来显示emoji。

  • OPPO 手机

场景:OPPO 手机启动 Service 报 SecurityException 解决方案:try catch 该异常

性能优化

性能优化

内存泄漏

内存泄漏

Binder机制

Binder机制

混合开发

混合开发

HTTPS和HTTP的区别

什么是 HTTPS?

HTTPS (基于安全套接字层的超文本传输协议 或者是 HTTP over SSL) 是一个 Netscape 开发的 Web 协议。

你也可以说:HTTPS = HTTP + SSL

HTTPS 在 HTTP 应用层的基础上使用安全套接字层作为子层。

为什么需要 HTTPS ?

超文本传输协议 (HTTP) 是一个用来通过互联网传输和接收信息的协议。HTTP 使用请求/响应的过程,因此信息可在服务器间快速、轻松而且精确的进行传输。当你访问 Web 页面的时候你就是在使用 HTTP 协议,但 HTTP 是不安全的,可以轻松对窃听你跟 Web 服务器之间的数据传输。在很多情况下,客户和服务器之间传输的是敏感歇息,需要防止未经授权的访问。为了满足这个要求,网景公司(Netscape)推出了HTTPS,也就是基于安全套接字层的 HTTP 协议。

HTTP 和 HTTPS 的相同点

大多数情况下,HTTP 和 HTTPS 是相同的,因为都是采用同一个基础的协议,作为 HTTP 或 HTTPS 客户端——浏览器,设立一个连接到 Web 服务器指定的端口。当服务器接收到请求,它会返回一个状态码以及消息,这个回应可能是请求信息、或者指示某个错误发送的错误信息。系统使用统一资源定位器 URI 模式,因此资源可以被唯一指定。而 HTTPS 和 HTTP 唯一不同的只是一个协议头(https)的说明,其他都是一样的。

HTTP 和 HTTPS 的不同之处

  1. HTTP 的 URL 以 http:// 开头,而 HTTPS 的 URL 以 https:// 开头
  2. HTTP 是不安全的,而 HTTPS 是安全的
  3. HTTP 标准端口是 80 ,而 HTTPS 的标准端口是 443
  4. 在 OSI 网络模型中,HTTP 工作于应用层,而 HTTPS 工作在传输层
  5. HTTP 无需加密,而 HTTPS 对传输的数据进行加密
  6. HTTP 无需证书,而 HTTPS 需要认证证书

HTTPS 如何工作?

使用 HTTPS 连接时,服务器要求有公钥和签名的证书。

当使用 https 连接,服务器响应初始连接,并提供它所支持的加密方法。作为回应,客户端选择一个连接方法,并且客户端和服务器端交换证书验证彼此身份。完成之后,在确保使用相同密钥的情况下传输加密信息,然后关闭连接。为了提供 https 连接支持,服务器必须有一个公钥证书,该证书包含经过证书机构认证的密钥信息,大部分证书都是通过第三方机构授权的,以保证证书是安全的。

换句话说,HTTPS 跟 HTTP 一样,只不过增加了 SSL

HTTP 包含如下动作:

  1. 浏览器打开一个 TCP 连接
  2. 浏览器发送 HTTP 请求到服务器端
  3. 服务器发送 HTTP 回应信息到浏览器
  4. TCP 连接关闭

SSL 包含如下动作:

  1. 验证服务器端
  2. 允许客户端和服务器端选择加密算法和密码,确保双方都支持
  3. 验证客户端(可选)
  4. 使用公钥加密技术来生成共享加密数据
  5. 创建一个加密的 SSL 连接
  6. 基于该 SSL 连接传递 HTTP 请求

什么时候该使用 HTTPS?

银行网站、支付网关、购物网站、登录页、电子邮件以及一些企业部门的网站应该使用 HTTPS,例如:

  • PayPal: https://www.paypal.com
  • Google AdSense: https://www.google.com/adsense/

如果某个网站要求你填写信用卡信息,首先你要检查该网页是否使用 https 加密连接,如果没有,那么请不要输入任何敏感信息如信用卡号。

如何有效的减少重复的代码

一、使用include标签引用重复布局

二、使用style定义样式

三、使用ViewStub减少整体布局的重复

四、多使用应用资源

五、代码的抽象与继承

Android进程保活

黑色保活

利用不同App进程相互广播来相互唤醒

场景1:开机,网络切换、拍照、拍视频时候,利用系统产生的广播唤醒app

场景2:接入第三方SDK也会唤醒相应的app进程,如微信sdk会唤醒微信,支付宝sdk会唤醒支付宝。由此发散开去,就会直接触发了下面的 场景3

场景3:假如你手机里装了支付宝、淘宝、天猫、UC等阿里系的app,那么你打开任意一个阿里系的app后,有可能就顺便把其他阿里系的app给唤醒了。(只是拿阿里打个比方,其实BAT系都差不多)

针对场景1,估计Google已经开始意识到这些问题,所以在最新的Android N取消了 ACTION_NEW_PICTURE(拍照),ACTION_NEW_VIDEO(拍视频),CONNECTIVITY_ACTION(网络切换)等三种广播,无疑给了很多app沉重的打击。

而开机广播的话,记得有一些定制ROM的厂商早已经将其去掉。

针对场景2场景3,因为调用SDK唤醒app进程属于正常行为,此处不讨论。但是在借助LBE分析app之间的唤醒路径的时候,发现了两个问题:

  1. 很多推送SDK也存在唤醒app的功能
  2. app之间的唤醒路径真是多,且错综复杂

白色保活

调用系统api启动一个前台的Service进程,这样会在系统的通知栏生成一个Notification,用来让用户知道有这样一个app在运行着,哪怕当前的app退到了后台。QQ音乐就是这样。

灰色保活

灰色保活,这种保活手段是应用范围最广泛。它是利用系统的漏洞来启动一个前台的Service进程,与普通的启动方式区别在于,它不会在系统通知栏处出现一个Notification,看起来就如同运行着一个后台Service进程一样。这样做带来的好处就是,用户无法察觉到你运行着一个前台进程(因为看不到Notification),但你的进程优先级又是高于普通后台进程的。

那么如何利用系统的漏洞呢,大致的实现思路和代码如下:

  • 思路一:API < 18,启动前台Service时直接传入new Notification();
  • 思路二:API >= 18,同时启动两个id相同的前台Service,然后再将后启动的Service做stop处理;

public class GrayService extends Service {

    private final static int GRAY_SERVICE_ID = 1001;

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (Build.VERSION.SDK_INT < 18) {
            startForeground(GRAY_SERVICE_ID, new Notification());//API < 18 ,此方法能有效隐藏Notification上的图标
        } else {
            Intent innerIntent = new Intent(this, GrayInnerService.class);
            startService(innerIntent);
            startForeground(GRAY_SERVICE_ID, new Notification());
        }

        return super.onStartCommand(intent, flags, startId);
    }

    ...
    ...

    /**
     * 给 API >= 18 的平台上用的灰色保活手段
     */
    public static class GrayInnerService extends Service {

        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            startForeground(GRAY_SERVICE_ID, new Notification());
            stopForeground(true);
            stopSelf();
            return super.onStartCommand(intent, flags, startId);
        }

    }
}

Android 进程保活招式大全

Dalvik虚拟机与java虚拟机的区别

  • 1.java虚拟机运行的是Java字节码,Dalvik虚拟机运行的是Dalvik字节码;传统的Java程序经过编译,生成Java字节码保存在class文件中,java虚拟机通过解码class文件中的内容来运行程序。而Dalvik虚拟机运行的是Dalvik字节码,所有的Dalvik字节码由Java字节码转换而来,并被打包到一个DEX(Dalvik Executable)可执行文件中Dalvik虚拟机通过解释Dex文件来执行这些字节码。

  • 2.Dalvik可执行文件体积更小。SDK中有一个叫dx的工具负责将java字节码转换为Dalvik字节码。

  • 3.java虚拟机与Dalvik虚拟机架构不同。java虚拟机基于栈架构。程序在运行时虚拟机需要频繁的从栈上读取或写入数据。这过程需要更多的指令分派与内存访问次数,会耗费不少CPU时间,对于像手机设备资源有限的设备来说,这是相当大的一笔开销。Dalvik虚拟机基于寄存器架构,数据的访问通过寄存器间直接传递,这样的访问方式比基于栈方式快的多.

HttpClient与HttpUrlConnection的区别

Volley框架的源码,在Android 2.3及以上版本,使用的是HttpURLConnection,而在Android 2.2及以下版本,使用的是HttpClient。

哪一种才是最好的?

在Android 2.2版本之前,HttpClient拥有较少的bug,因此使用它是最好的选择。

而在Android 2.3版本及以后,HttpURLConnection则是最佳的选择。它的API简单,体积较小,因而非常适用于Android项目。压缩和缓存机制可以有效地减少网络访问的流量,在提升速度和省电方面也起到了较大的作用。对于新的应用程序应该更加偏向于使用HttpURLConnection.

Android访问网络,使用HttpURLConnection还是HttpClient?

三级缓存(各大图片框架都可以扯到这上面来)

(1)内存缓存,(2)本地缓存,(3)网络

RecyclerView

有了ListView、GridView为什么还需要RecyclerView这样的控件呢?整体上看RecyclerView架构,提供了一种插拔式的体验,高度的解耦,异常的灵活,通过设置它提供的不同LayoutManager,ItemDecoration , ItemAnimator实现令人瞠目的效果。

  • 你想要控制其显示的方式,请通过布局管理器LayoutManager
  • 你想要控制Item间的间隔(可绘制),请通过ItemDecoration
  • 你想要控制Item增删的动画,请通过ItemAnimator
  • 你想要控制点击、长按事件,请自己写(擦,这点尼玛。)

多个进程同时调用一个ContentProvider的query获取数据,ContentPrvoider是如何反应的呢?

一个content provider可以接受来自另外一个进程的数据请求。尽管ContentResolver与ContentProvider类隐藏了实现细节,但是ContentProvider所提供的query(),insert(),delete(),update()都是在ContentProvider进程的线程池中被调用执行的,而不是进程的主线程中。这个线程池是有Binder创建和维护的,其实使用的就是每个应用进程中的Binder线程池。

Android设计ContentProvider的目的是什么?

    1. 隐藏数据的实现方式,对外提供统一的数据访问接口;
    1. 更好的数据访问权限管理。ContentProvider可以对开发的数据进行权限设置,不同的URI可以对应不同的权限,只有符合权限要求的组件才能访问到ContentProvider的具体操作。
    1. ContentProvider封装了跨进程共享的逻辑,我们只需要Uri即可访问数据。由系统来管理ContentProvider的创建、生命周期及访问的线程分配,简化我们在应用间共享数据(进程间通信)的方式。我们只管通过ContentResolver访问ContentProvider所提示的数据接口,而不需要担心它所在进程是启动还是未启动

运行在主线程的ContentProvider为什么不会影响主线程的UI操作?

    1. ContentProvider的onCreate()是运行在UI线程的,而query(),insert(),delete(),update()是运行在线程池中的工作线程的,所以调用这向个方法并不会阻塞ContentProvider所在进程的主线程,但可能会阻塞调用者所在的进程的UI线程!
    1. 所以,调用ContentProvider的操作仍然要放在子线程中去做。虽然直接的CRUD的操作是在工作线程的,但系统会让你的调用线程等待这个异步的操作完成,你才可以继续线程之前的工作。

synchronized函数和synchronized代码块的区别

    1. 首先synchronized函数和synchronized代码快的作用范围有区别,synchronized函数一般锁定的是当前类对象,synchronized代码块锁定作用域可以选择是本对象,也可以是字符串等等.
    1. 当前类对象锁没有释放的时候,本类的所有synchronized(this)同步代码块都阻塞。如果有并发请求synchronized函数,同一时间只能有一个请求执行 .
    1. 但是当前类对象锁没有释放的时候,其他请求可以访问本类中不带synchronized(this)的代码块,也可以访问非同一把锁的代码块例如synchronized(Str)等.
    1. 由于作用范围有区别,一般作用范围越小执行效率越高,平时开发中一般选择作用范围较小的synchronized.

Android中Service是否是区别于Activity的进程还是在你应用中的进程?

类别 区别 优点 缺点 应用  
本地服务 该服务依附在主进程上 服务依附在主进程上而不是独立的进程,这样在一定程度上节约了资源,另外Local服务因为是在同一进程因此不需要IPC,也不需要AIDL。相应bindService会方便很多。 主进程被Kill后,服务便会终止。 如:音乐播放器播放等不需要常驻的服务。 如:音乐播放器播放等不需要常驻的服务。
远程服务 该服务是独立的进程 服务为独立的进程,对应进程名格式为所在包名加上你指定的android:process字符串。由于是独立的进程,因此在Activity所在进程被Kill的时候,该服务依然在运行,不受其他进程影响,有利于为多个进程提供服务具有较高的灵活性。 该服务是独立的进程,会占用一定资源,并且使用AIDL进行IPC稍微麻烦一点。 一些提供系统服务的Service,这种Service是常驻的。  

Android的AMS和WMS理解

Android逆向

Android中如果函数内的方法需要被更改,你是如何做的呢?

直接进行使用汇编在此处进行使用跳转语句修改后,再跳回来。好好运行的程序怎么可能让你直接用汇编操作,有一个框架已经实现了。 Android中的so注入(inject)和挂钩(hook) - For both x86 and arm 看雪论坛上 inline hook的实现原理 看雪牛人放出的:libinject

可以给我说一下 大端和小端(Big endian and Little endian)嘛?

大端和小端(Big endian and Little endian)

Android JNI开发

如何捕获在线上服务中SO出现的异常,你是怎么获取奔溃的堆栈信息呢。

这个问题我真的没想过,都是用Application 实现`Thread.UncaughtExceptionHandler`接口做未捕获异常上报。这下在JNI层的so出现异常该怎么做呢。 
[JNI异常处理](http://blog.csdn.net/xyang81/article/details/45770551)

**题外话:**

1.  [JNI/NDK的开发指南的专栏](http://blog.csdn.net/column/details/blogjnindk.html)
2.  [NDK-Stack定位crash](http://blog.csdn.net/a568478312/article/details/78182422) ###   ENV是几级指针 
这里要区分 C还是C++了,C是二级指针,而C++中是一级指针

adb你会使用吗?说说你经常用的命令?

[ADB的相关命令](https://developer.android.com/studio/command-line/adb.html?hl=zh-cn) ###   你会linux嘛?罗列进程是那个指令?

ps命令查找与进程相关的PID号
使用kill命令结束进程:kill xxx 
常用:kill -9 324
Linux下还提供了一个killall命令,可以直接使用进程的名字而	     不是进程标识号 
例如:# killall -9 NAME ###  Android中如果你声明了一个Activity的class类,但没有在清单文件中声明他,是否可以将其吊起? 如果不能 有什么方法可以吊起嘛? [Android免清单注册吊起Activity](http://blog.csdn.net/qfanmingyiq/article/details/78216446)

IOS逆向

ASO体系并不是这么简单,你确定你开发的是有效的?

我只能说,在我开发出来时候,做了(改机,清keychain,AppStore排名优化,自动启动AppStore,账号登录登出,适配10系统,评论功能,模拟触碰,动态IP池,自动下载和成功后的取消下载)。我只能说我当时做出来是有效的,Apple的策略改变没有,在公司倒闭后 我就没再更深的了解了。这个东西并不是那么容易玩的转的。

你除了对arm汇编 了解完 你懂x86汇编嘛?(哎 我一个玩arm平台的 突然问我x86我还是依然懵逼)

[X86汇编指令](http://www.cnblogs.com/YukiJohnson/archive/2012/10/27/2741836.html)

final, finally, finalize的区别

  • final 用于声明属性,方法和类, 分别表示属性不可变, 方法不可覆盖, 类不可继承.

  • finally 是异常处理语句结构的一部分,表示总是执行.

  • finalize 是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,可以覆盖此方法提供垃圾收集时的其他资源回收,例如关闭文件等. JVM不保证此方法总被调用.

Android 多线程编程的总结

RecyclerView 和 ListView

布局效果对比

ListView只有一种滑动布局 RecyclerView通过布局管理器能展示多种布局

API 使用对比

ListView 的基础使用大家再熟悉不过,其使用的关键点主要如下:

  • 继承重写 BaseAdapter 类
  • 自定义 ViewHolder 和 convertView 一起完成复用优化工作

由于 ListView 已经老生常谈,所以此处就不去写示例代码了。 RecyclerView 基础使用关键点同样有两点:

  • 继承重写 RecyclerView.Adapter 和 RecyclerView.ViewHolder
  • 设置布局管理器,控制布局效果

空数据处理

  • ListView 提供了 setEmptyView 这个 API 来让我们处理 Adapter 中数据为空的情况,只需轻轻一 set 就能搞定一切。代码设置和效果如下

mListView = (ListView) findViewById(R.id.listview);
        mListView.setEmptyView(findViewById(R.id.empty_layout));//设置内容为空时显示的视图

  • RecyclerView没有相应的API,需要自己动手去完成

HeaderView 和 FooterView

  • 在 ListView 的设计中,存在着 HeaderView 和 FooterView 两种类型的视图,并且系统也提供了相应的 API 来让我们设置

  • RecyclerView没有相应的API,需要自己动手去完成

局部刷新

  • ListView 并没有提供局部刷新刷新某个 Item 的 API 给我们,同样自己自足,套路大致如下方的 updateItemView:

public class AuthorListAdapter extends BaseAdapter {

    ...

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ...
        return convertView;
    }

    /**
     * 更新Item视图,减少不必要的重绘
     *
     * @param listView
     * @param position
     */
    public void updateItemView(ListView listView, int position) {
        //换算成 Item View 在 ViewGroup 中的 index
        int index = position - listView.getFirstVisiblePosition();
        if (index >= 0 && index < listView.getChildCount()) {
            //更新数据
            AuthorInfo authorInfo = mAuthorInfoList.get(position);
            authorInfo.setNickName("Google Android");
            authorInfo.setMotto("My name is Android .");
            authorInfo.setPortrait(R.mipmap.ic_launcher);
            //更新单个Item
            View itemView = listView.getChildAt(index);
            getView(position, itemView, listView);
        }
    }

}

即可实现刷新单个 Item 的效果

RecyclerView.Adapter 则我们提供了 notifyItemChanged 用于更新单个 Item View 的刷新,我们可以省去自己写局部更新的工作。

动画效果

  • 作为 ListView 自身并没有为我们提供封装好的 API 来实现动画效果切换。所以,如果要给 ListView 的 Item 加动画,我们只能自己通过属性动画来操作 Item 的视图。 Github 也有很多封装得好好的开源库给我们用,如:ListViewAnimations

  • RecyclerView 则为我们提供了很多基本的动画 API ,如下方的增删移改

简单的调用即可实现相应的效果,用起来方便很多,视觉交互上也会更好些

监听 Item 的事件

  • ListView 为我们准备了几个专门用于监听 Item 的回调接口,如单击、长按、选中某个 Item 等

  • RecyclerView ,它并没有像 ListView 提供太多关于 Item 的某种事件监听,唯一的就是 addOnItemTouchListener

嵌套滚动机制

RecyclerView支持嵌套滑动 ListView不支持

RecyclerView与ListView的滑动对比

  • RecyclerView的滑动

RecyclerView的滑动过程可以分为2个阶段:手指在屏幕上移动,使RecyclerView滑动的过程,可以称为scroll;手指离开屏幕,RecyclerView继续滑动一段距离的过程,可以称为fling。

当RecyclerView接收到ACTION_MOVE事件后,会先计算出手指移动距离(dy),并与滑动阀值(mTouchSlop)比较,当大于此阀值时将滑动状态设置为SCROLL_STATE_DRAGGING,而后调用scrollByInternal()方法,使RecyclerView滑动,这样RecyclerView的滑动的第一阶段scroll就完成了;当接收到ACTION_UP事件时,会根据之前的滑动距离与时间计算出一个初速度yvel,这步计算是由VelocityTracker实现的,然后再以此初速度,调用方法fling(),完成RecyclerView滑动的第二阶段fling。

  • ListView的滑动

Android中设计模式的6大原则

6大原则

WebView的基本使用以及Android和js的交互

Retrofit+okhttp网络框架介绍

Okhttp3结合Retrofit2 实现缓存

RecyclerView 添加HeaderView和FooterView

SmartHeaderFooterRecyclerview

非常方便的实现Recyclerview添加HeaderView和FooterView,

  • 不需要修改Target Adapter
  • 不会破坏Target adapter中的 position (PS: 不需要 +1 -1)
  • 支持动态添加移除
  • 支持 LinearLayoutManager & GridLayoutManager 两种布局管理器
RecyclerView.Adapter targetAdapter = new RecyclerView.Adapter() { ... };
      GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 3);
      SmartRecyclerAdapter smartRecyclerAdapter = new SmartRecyclerAdapter(targetAdapter, gridLayoutManager);
      smartRecyclerAdapter.setFooterView(footerView);
      smartRecyclerAdapter.setHeaderView(headerView);
      recyclerView.setAdapter(smartRecyclerAdapter);

retrofit 上传文件

/**
 * 上传信息 */
@Multipart @POST("files/upload/")
Call<GMResponse<JSONObject>> uploadFile(
@Part("type") String type,
@Part() MultipartBody.Part file);

upload_img

uoload_img2

刷新

PullToRefresh

社区卡片化

  • 1.new CardViewAdapter(getActivity(), beans)
  • 2.beans extends CardBean
  • 3.CardBean getCardType(卡片类型),getUniqueId(去重id)
  • 4..registerCard(Constants.CardViewType.TYPE_DIARY, new DiaryCardProvider(mCurrentTab.tab_name))

  • 5.public abstract class CardViewProvider<T, V extends GMRecyclerAdapter.GMRecyclerViewHolder>

rcv_cardview_provider

  • 6.CardViewAdapter

rcv_card_view_adpater

直播弹幕

  • 1.获取弹幕,保存到list集合中
  • 2.根据同步弹幕间隔时间和弹幕延迟加载时间算出每次同步弹幕条数
    // 向上取整
    double count = 1000 / GET_DANMU_INTERVAL * Double.valueOf(danMuBean.delay); syncDanMuSize = (int) Math.ceil(((double) mDanmuMessages.size()) / count);
    
  • 3.handler循环发送延迟消息,将积压队列中的消息同步到UI队列里
    mDanmakuHandler.sendMessageDelayed(mDanmakuHandler.obtainMessage(MSG_WHAT_SYNC), GET_DANMU_INTERVAL);
    ...
    

danmu_handler

if (addMessage(messages)) {
    mDanmuMessages.removeAll(messages); }

danmu_add_msgs

danmu_add_msg

直播点赞动画

handler循环发送延迟消息,随机添加一个ImageView,给其添加一个Bezier曲线的动画。 //因为不停的add 导致子view数量只增不减,所以在view动画结束后remove掉 removeView((target));

直播伸缩动画

scale_anima