2018.03.12、Android知识点-Android篇

395 阅读31分钟

1、Activity相关:

1、Activity的生命周期

image


2、Activity的启动模式以及使用场景

启动模式

  1. standard:默认的启动模式,每次创建都会产生新的实例,谁启动了该模式的Activity,该Activity就属于启动它的Activity的任务栈中
  2. singleTop:栈顶复用模式,如果新的activity已经位于栈顶,那么这个Activity不会被重写创建,同时它的onNewIntent(Intent intent)方法会被调用,通过此方法的参数我们可以去除当前请求的信息,该 Activity的实例不在该栈或者不在栈顶 时,其行为同standard启动模式
  3. singleTask:栈内复用模式,如果栈中存在这个Activity的实例就会复用这个Activity,不管它是否位于栈顶,复用时,会将它上面的Activity全部出栈,并且会回调该实例的onNewIntent方法。
  4. singleInstance:全局唯一模式,具备singleTask模式的所有特性外,与它的区别就是,这种模式下的Activity会单独占用一个Task栈,具有全局唯一性,即整个系统中就这么一个实例,由于栈内复用的特性,后续的请求均不会创建新的Activity实例,除非这个特殊的任务栈被销毁了,当复用该实例 会回调onNewIntent方法

3、Activity的启动过程(不要回答生命周期)

Activity 启动流程图

Activity的启动过程 必须掌握的Activity启动过程

4、在Activity中如何保存/恢复状态?

分别调用onSaveInstanceState(Bundle outState)和 
onRestoreInstanceState(Bundle outState)  2个方法保存和恢复状态。

5、在Activity中如何保存/恢复状态?

  • 不设置Activity的android:configChanges时,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次;
  • 设置Activity的android:configChanges="orientation"时,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次;
  • 设置Activity的android:configChanges="orientation|keyboardHidden"时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法;

6、Activity上有Dialog的时候按Home键时的生命周期?

按Home键,再开启activity.

7、Application 和 Activity 的 Context 对象的区别

  • 1、生命周期不同,Application的context随应用存在而存在,activity的context随该activity的存在而存在。
  • 2、适用范围不同,Application的context 不能用在 show dialog()、start activity()、Layout inflation().ativity 可以使用。

2、Service相关

1. Service的startService(Intent)启动方式

  • 使用这种start方式启动的Service的生命周期如下: onCreate()--->onStartCommand()(onStart()方法已过时) ---> onDestory()
  • 如果服务已经开启,不会重复的执行onCreate(), 而是会调用onStart()和onStartCommand()
  • 服务停止的时候调用 onDestory()。服务只会被停止一次。
  • 特点:一旦服务开启跟调用者(开启者)就没有任何关系了。 开启者退出了,开启者挂了,服务还在后台长期的运行。 开启者不能调用服务里面的方法。

2. 采用bind的方式开启服务

  • 使用这种start方式启动的Service的生命周期如下:onCreate() --->onBind()--->onunbind()--->onDestory()
  • bind的方式开启服务,绑定服务,调用者挂了,服务也会跟着挂掉。 绑定者可以调用服务里面的方法

3.service和activity怎么进行数据交互?

一种是使用broadcast,另一种是使用bindService。

  • 1、通过广播:在service中发送广播,在activity中创建一个BroadcastReceive()接收;
  • 2、bindService:activity通过Intent传值给service,并创建ServiceConnection()对象,获得BindService.MyBind。

4.怎么保证service不被杀死?

  • 1.onStartCommand方法,返回START_STICKY,当service因内存不足被kill,当内存又有的时候,service又被重新创建。
  • 2.提升service优先级,在AndroidManifest.xml文件中对于intent-filter可以通过android:priority = "1000"这个属性设置最高优先级,1000是最高值,如果数字越小则优先级越低
  • 3、提升service进程优先级
  • 4、service +broadcast 方式,就是当service走ondestory的时候,发送一个自定义的广播,当收到广播的时候,重新启动service;
  • 5、两个守护进程

3、Broadcast相关

1、Broadcast注册方式与区别

  1. Manifest.xml中静态注册:
//new出上边定义好的BroadcastReceiver
MyBroadCastReceiver yBroadCastReceiver = new MyBroadCastReceiver();

//实例化过滤器并设置要过滤的广播  
IntentFilter intentFilter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED");

//注册广播   
myContext.registerReceiver(smsBroadCastReceiver,intentFilter, 
             "android.permission.RECEIVE_SMS", null);

  1. 代码中动态注册:

直接在Manifest.xml文件的节点中配置广播接收者

<receiver android:name=".MyBroadCastReceiver">  
            <!-- android:priority属性是设置此接收者的优先级(从-1000到1000) -->
            <intent-filter android:priority="20">
            <actionandroid:name="android.provider.Telephony.SMS_RECEIVED"/>  
            </intent-filter>  
</receiver>


2、两种注册广播的不同

  • 第一种不是常驻型广播,也就是说广播跟随程序的生命周期。
  • 第二种是常驻型,也就是说当应用程序关闭后,如果有信息广播来,程序也会被系统调用自动运行。

3、发送广播的两种方式

  • 无序广播:所有的接收者都会接收事件,不可以被拦截,不可以被修改。
  • 有序广播:按照优先级,一级一级的向下传递,接收者可以修改广播数据,也可以终止广播事件。

Android 两种注册、发送广播的区别


补1、ContentProvider相关:

ContentProvider的基本使用方法和作用。ContentValue的使用方法,他和HashMap的区别是什么?

ContentProvider 是Android系统中提供的专门用户不同应用间进行数据共享的组件,提供了一套标准的接口用来获取以及操作数据,准许开发者把自己的应用数据根据需求开放给其他应用进行增删改查,而无须担心直接开放数据库权限而带来的安全问题。

ContentValue: 存储数据封装的HashMap,提供 put、get等方法

1、说说ContentProvider、ContentResolver、ContentObserver 之间的关系?

  • 1.ContentProvider 内容提供者,用于对外提供数据
  • 2.ContentResolver 内容解析者,用于获取内容提供者提供的数据
  • 3.ContentObserver 内容监听器,可以监听数据的改变状态
  • 4.ContentResolver.notifyChange(uri)发出消息
  • 5.ContentResolver.registerContentObserver()监听消息。

2、ContentProvider与数据库(SQL)的区别?

  • 1、ContentProvider 屏蔽了数据存储细节,可以在不同应用间进行数据操作。
  • 2、SQL也有增删改查的方法,只能对本应用的数据进行操作。

Android ContentProvider基本用法

4、网络相关

1、HttpClient与HttpUrlConnection的区别 2

  1. 功能上:
  • Http Client:适用于web浏览器,拥有大量灵活的API,提供了很多工具,封装了http的请求头,参数,内容体,响应,还有一些高级功能,代理、COOKIE、鉴权、压缩、连接池的处理正因此,在不破坏兼容性的前提下,其庞大的API也使人难以改进。
  • HttpURLConnection: 对于大部分功能都进行了包装,Http Client的高级功能代码会较复杂,

2.性能上:

  • HttpURLConnection直接支持GZIP压缩,默认添加"Accept-Encoding: gzip"头字段到请求中,并处理相应的回应,
  • 而Http Client虽然支持,但需要自己写代码处理。

3.选用:

  • Volley里用的哪种请求方式(2.3前HttpClient,2.3后HttpUrlConnection)

2、HTTPS和HTTP的区别 2

1、什么是 HTTPS

HTTPS(全称:Hyper Text Transfer Protocol over Secure Socket Layer),是以安全为目标的HTTP通道,简单讲是HTTP的安全版。

即HTTP下加入SSL (Secure Socket Layer)层,HTTPS的安全基础是SSL,因此加密的详细内容就需要SSL。


2、HTTPS 和 HTTP 的区别
  • https 用的 443 端口, http 用的 80 端口
  • https协议需要到ca申请证书,一般免费证书很少,需要交费。
  • http是超文本传输协议,信息是明文传输,https 则是具有安全性的ssl加密传输协议。
  • http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
  • http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。

Android使用OkHttp请求自签名的https网站 Android Https相关完全解析 当OkHttp遇到Https HTTPS和HTTP的区别


5、View相关

1、view的Touch事件传递机制

1、和touch相关的三个方法
  1. public boolean dispatchTouchEvent(MotionEvent ev); //用来分派event
  2. public boolean onInterceptTouchEvent(MotionEvent ev); //用来拦截event
  3. public boolean onTouchEvent(MotionEvent ev); //用来处理event
方 法 解析
dispatchTouchEvent() 用来分派事件。其中调用了onInterceptTouchEvent()和onTouchEvent(),一般不重写该方法,返回true则表示该事件被消费
onInterceptTouchEvent() 用来拦截事件。ViewGroup类中的源码实现就是{return false;}表示不拦截该事件,事件将向下传递(传递给其子View);若手动重写该方法,使其返回true则表示拦截,事件将终止向下传递,事件由当前ViewGroup类来处理,就是调用该类的onTouchEvent()方法
onTouchEvent() 用来处理事件。返回true则表示该View能处理该事件,事件将终止向上传递(传递给其父View);返回false表示不能处理,则把事件传递给其父View的onTouchEvent()方法来处

Android中的dispatchTouchEvent()、onInterceptTouchEvent()和onTouchEvent()


6、动画相关

1、Android中有几种动画?

Android3.0之前有2种,3.0后有3种。
  • FrameAnimation(逐帧动画):将多张图片组合起来进行播放,类似于早期电影的工作原理,很多App的loading是采用这种方式。
  • TweenAnimation(补间动画):是对某个View进行一系列的动画的操作,包括淡入淡出(AlphaAnimation),缩放(ScaleAnimation),平移(TranslateAnimation),旋转(RotateAnimation)四种模式。
  • PropertyAnimation(属性动画):属性动画不再仅仅是一种视觉效果了,而是一种不断地对值进行操作的机制,并将值赋到指定对象的指定属性上,可以是任意对象的任意属性。

注1:AnimationSet 继承自Animation,是上面四种的组合容器管理类,没有自己特有的属性,他的属性继承自Animation,所以特别注意,当我们对set标签使用Animation的属性时会对该标签下的所有子控件都产生影响。

注2:补间动画执行之后并未改变View的真实布局属性值。切记这一点,譬如我们在Activity中有一个Button在屏幕上方,我们设置了平移动画移动到屏幕下方然后保持动画最后执行状态呆在屏幕下方,这时如果点击屏幕下方动画执行之后的Button是没有任何反应的,而点击原来屏幕上方没有Button的地方却响应的是点击Button的事件。


2、属性动画

  • ObjectAnimator:继承自ValueAnimator,允许你指定要进行动画的对象以及该对象的一个属性。
  • 大多数的情况使用ObjectAnimator就足够了,因为它使得目标对象动画值的处理过程变得足够简单,不用像ValueAnimator那样自己写动画更新的逻辑
  • ObjectAnimator的动画原理是不停的调用setXXX方法更新属性值,所有使用ObjectAnimator更新属性时的前提是Object必须声明有getXXX和setXXX方法
ObjectAnimator mObjectAnimator= ObjectAnimator.ofInt(view, "customerDefineAnyThingName", 0,  1).setDuration(2000);
mObjectAnimator.addUpdateListener(new AnimatorUpdateListener()
        {
            @Override
            public void onAnimationUpdate(ValueAnimator animation)
            {
                //int value = animation.getAnimatedValue();  可以获取当前属性值
                //view.postInvalidate();  可以主动刷新
                //view.setXXX(value);
                //view.setXXX(value);
                //......可以批量修改属性
            }
        });
ObjectAnimator.ofFloat(view, "rotationY", 0.0f, 360.0f).setDuration(1000).start();
  • PropertyValuesHolder:多属性动画同时工作管理类。有时候我们需要同时修改多个属性,那就可以用到此类
PropertyValuesHolder a1 = PropertyValuesHolder.ofFloat("alpha", 0f, 1f);  
PropertyValuesHolder a2 = PropertyValuesHolder.ofFloat("translationY", 0, viewWidth);  
......
ObjectAnimator.ofPropertyValuesHolder(view, a1, a2, ......).setDuration(1000).start();
  • ValueAnimator:属性动画中的时间驱动,管理着动画时间的开始、结束属性值,相应时间属性值计算方法等。包含所有计算动画值的核心函数以及每一个动画时间节点上的信息、一个动画是否重复、是否监听更新事件等,并且还可以设置自定义的计算类型。
ValueAnimator animator = ValueAnimator.ofFloat(0, mContentHeight);  //定义动画
animator.setTarget(view);   //设置作用目标
animator.setDuration(5000).start();
animator.addUpdateListener(new AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation){
        float value = (float) animation.getAnimatedValue();
        view.setXXX(value);  //必须通过这里设置属性值才有效
        view.mXXX = value;  //不需要setXXX属性方法
    }
});

特别注意:ValueAnimator只是动画计算管理驱动,设置了作用目标,但没有设置属性,需要通过updateListener里设置属性才会生效。

  • AnimationSet:动画集合,提供把多个动画组合成一个组合的机制,并可设置动画的时序关系,如同时播放、顺序播放或延迟播放。具体使用方法比较简单,如下:

Android应用开发之所有动画使用详解

7、Android中跨进程通讯

android系统中应用程序之间不能共享内存。 因此,在不同应用程序之间交互数据(跨进程通讯)就稍微麻烦一些

  • 1、访问其他应用程序的Activity
如调用系统通话应用
Intent callIntent=new Intent(Intent.ACTION_CALL,Uri.parse("tel:12345678");
startActivity(callIntent);
  • 2、Content Provider

Content Provider提供了一种在多个应用程序之间数据共享的方式(跨进程共享数据)。应用程序可以利用Content Provider完成下面的工作

  1. 查询数据
  2. 修改数据
  3. 添加数据
  4. 删除数据

虽然Content Provider也可以在同一个应用程序中被访问,但这么做并没有什么意义。Content Provider存在的目的向其他应用程序共享数据和允许其他应用程序对数据进行增、删、改操作。 Android系统本身提供了很多Content Provider,例如,音频、视频、联系人信息等等。我们可以通过这些Content Provider获得相关信息的列表。这些列表数据将以Cursor对象返回。因此,从Content Provider返回的数据是二维表的形式。

如访问系统相册
  • 3、广播(Broadcast)
广播是一种被动跨进程通讯的方式。当某个程序向系统发送广播时,其他的应用程序只能被动地接收广播数据。这就象电台进行广播一样,听众只能被动地收听,而不能主动与电台进行沟通。例如获取手机电量信息
  • 4、AIDL服务

2、AIDL理解

1、定义:Android系统中的进程之间不能共享内存,因此,需要提供一些机制在不同进程之间进行数据通信。 为了使其他的应用程序也可以访问本应用程序提供的服务,Android系统采用了远程过程调用(Remote Procedure Call,RPC)方式来实现。与很多其他的基于RPC的解决方案一样,Android使用一种接口定义语言(Interface Definition Language,IDL)来公开服务的接口。我们知道4个Android应用程序组件中的3个(Activity、BroadcastReceiver和ContentProvider)都可以进行跨进程访问,另外一个Android应用程序组件Service同样可以。因此,可以将这种可以跨进程访问的服务称为AIDL(Android Interface Definition Language)服务。


3、Binder理解

1、什么是binder:

Binder是Android实现 跨进程通讯(IPC)的方式,是一种虚拟的物理设备驱动,实现了IBindler 接口。

2、知识概念
  1. 一个进程空间分为 用户空间 & 内核空间(Kernel),即把进程内 用户 & 内核 隔离开来

区别:

  1. 进程间,用户空间的数据不可共享,所以用户空间 = 不可共享空间
  2. 进程间,内核空间的数据可共享,所以内核空间 = 可共享空间
  1. 进程隔离:保证 安全性 & 独立性,一个进程 不能直接操作或者访问另一个进程,即Android的进程是相互独立、隔离的
  2. 跨进程通信( IPC ):隔离后,由于某些需求,进程间 需要合作 / 交互
  3. 跨进程间通信的原理
  • 先通过 进程间 的内核空间进行 数据交互
  • 再通过 进程内 的用户空间 & 内核空间进行 数据交互,从而实现 进程间的用户空间 的数据交互
    image
  • 而Binder,就是充当 连接 两个进程(内核空间)的通道。
3、Binder 跨进程通信机制 模型
  1. 模型原理:

image

4、优点

对比 Linux (Android基于Linux)上的其他进程通信方式(管道、消息队列、共享内存、 信号量、Socket),Binder 机制的优点有:

image

Binder学习指南

图文详解 Android Binder跨进程通信的原理


8、Handler相关

1、handler 消息传递分析

作用:Handle 进行消息传递

  1. Handle发送的msg通过enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)添加进(MessageQueue)消息队列中.

Android 消息传递机制分析


2、Handle、Message、MessageQueue、Looper之间的关系

  • Handle:主要发送 Message
  • Message:消息
  • MessageQueue:消息队列.保存由Looper发送的消息列表。
  • Looper:用于为线程运行消息循环的类。Thread默认情况下没有与之关联,通过prepare()循环运行在线程中,通过loop(for(;;))来处理消息。

9、热修复相关

1、热修复包含:

  1. 资源替换
  2. 类替换(四大组件、类)
  3. SO补丁

2、资源替换方法:

思路:Andorid APP默认的类加载是PathClassLoader,这个只能加载自己APK的dex文件,所以我们需要使用DexClassLoader。我们用DexClassLoader加载外部的APK之后,通过反射获取对应的资源。


3、类替换(四大组件、类):

  1. 通过PathClassLoader 来加载我们自身App的dex
  2. 通过DexClassLoader来加载我们的补丁dex文件,这里面就是没有bug的dex
  3. 反射两个classLoader的<DexPathList pathList;>
  4. 接着我们来继续反射两个classloader中的pathList(注意:是两个!一个是我们自己应用的,另一个是我们补丁的,PathClassLoader和DexClassLoader都继承BaseDexClassLoader),DexPathList里面的<Element[] dexElements;>,没错还是拿到这个数组的值
  5. 合并两个反射到的Element 数组!这里是重中之重.我们需要把我们的补丁dex放在数组的最前面!
  6. 将合并的新的数组,通过Field重新设置到我们自身App的DexPathList->dexElements.没错!就是合并之后覆盖有bug那个loader的Element 数组!!
  7. 通过Android build-tools 中的dx命令打包一个没有bug的dex

Android 热修复(全网最简单的热修复讲解)


4、SO补丁:


10、图片加载缓存相关

1、设计一套图片异步加载缓存方案

缓存层分为三层:内存层,磁盘层,网络层

  • 内存层:内存缓存相对于磁盘缓存而言,速度要来的快很多,但缺点容量较小且会被系统回收,这里的实现我用到了LruCache。
  • 磁盘层:相比内存缓存而言速度要来得慢很多,但容量很大,这里的实现我用到了DiskLruCache类。
  • 网络层:网络访问实现我用到了开源框架Volley

11、内存泄露及管理

1、内存泄漏:

  • 内存泄露:程序在向系统申请分配内存空间后(new),在使用完毕后未释放。结果导致一直占据该内存单元,我们和程序都无法再使用该内存单元,直到程序结束,这是内存泄露。
  • 内存溢出:程序向系统申请的内存空间超出了系统能给的。比如内存只能分配一个int类型,我却要塞给他一个long类型,系统就出现oom。又比如一车最多能坐5个人,你却非要塞下10个,车就挤爆了。
  • 大量的内存泄露会导致内存溢出(oom)

2、内存:

  • 栈(stack):是简单的数据结构,但在计算机中使用广泛。栈最显著的特征是:LIFO(Last In, First Out, 后进先出),栈中只存放基本类型和对象的引用(不是对象)。
  • 堆(heap):堆内存用于存放由new创建的对象和数组。在堆中分配的内存,由java虚拟机自动垃圾回收器来管理。JVM只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身。
  • 方法区(method):又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的class和static变量。

3、内存优化

  1. 单例导致内存泄露
public class AppSettings {

    private static AppSettings sInstance;
    private Context mContext;

    private AppSettings(Context context) {
        this.mContext = context;
        
        //this.mContext = context.getApplicationContext();
    }

    public static AppSettings getInstance(Context context) {
        if (sInstance == null) {
            sInstance = new AppSettings(context);
        }
        return sInstance;
    }
}

2、静态变量导致内存泄露

public class MainActivity extends AppCompatActivity {

    private static Info sInfo;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if (sInfo != null) {
            sInfo = new Info(this);
        }
    }
}

class Info {
    public Info(Activity activity) {
    }
}

Info作为Activity的静态成员,并且持有Activity的引用,但是sInfo作为静态变量,生命周期肯定比Activity长。所以当Activity退出后,sInfo仍然引用了Activity,Activity不能被回收,这就导致了内存泄露。

3、非静态内部类导致内存泄露

非静态内部类(包括匿名内部类)默认就会持有外部类的引用,当非静态内部类对象的生命周期比外部类对象的生命周期长时,就会导致内存泄露。

public class MainActivity extends AppCompatActivity {
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        start();
    }

    private void start() {
        Message msg = Message.obtain();
        msg.what = 1;
        mHandler.sendMessage(msg);
    }

    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == 1) {
                // 做相应逻辑
            }
        }
    };
}

熟悉Handler消息机制的都知道,mHandler会作为成员变量保存在发送的消息msg中,即msg持有mHandler的引用,而mHandler是Activity的非静态内部类实例,即mHandler持有Activity的引用,那么我们就可以理解为msg间接持有Activity的引用。msg被发送后先放到消息队列MessageQueue中,然后等待Looper的轮询处理(MessageQueue和Looper都是与线程相关联的,MessageQueue是Looper引用的成员变量,而Looper是保存在ThreadLocal中的)。那么当Activity退出后,msg可能仍然存在于消息对列MessageQueue中未处理或者正在处理,那么这样就会导致Activity无法被回收,以致发生Activity的内存泄露。

通常在Android开发中如果要使用内部类,但又要规避内存泄露,一般都会采用静态内部类+弱引用的方式。

public class MainActivity extends AppCompatActivity {

    private Handler mHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mHandler = new MyHandler(this);
        start();
    }

    private void start() {
        Message msg = Message.obtain();
        msg.what = 1;
        mHandler.sendMessage(msg);
    }

    private static class MyHandler extends Handler {

        private WeakReference<MainActivity> activityWeakReference;

        public MyHandler(MainActivity activity) {
            activityWeakReference = new WeakReference<>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            MainActivity activity = activityWeakReference.get();
            if (activity != null) {
                if (msg.what == 1) {
                    // 做相应逻辑
                }
            }
        }
    }
}

@Override
protected void onDestroy() {
    super.onDestroy();
    mHandler.removeCallbacksAndMessages(null);
}

非静态内部类造成内存泄露还有一种情况就是使用Thread或者AsyncTask

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        new Thread(new Runnable() {
            @Override
            public void run() {
                // 模拟相应耗时逻辑
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}


这种方式新建的子线程Thread和AsyncTask都是匿名内部类对象,默认就隐式的持有外部Activity的引用,导致Activity内存泄露。

4、未取消注册或回调导致内存泄露

  • 在Activity中注册广播,如果在Activity销毁后不取消注册

5、Timer和TimerTask导致内存泄露

  • Timer和TimerTask在Android中通常会被用来做一些计时或循环任务

6、集合中的对象未清理造成内存泄露

如果一个对象放入到ArrayList、HashMap等集合中,这个集合就会持有该对象的引用。当我们不再需要这个对象时,也并没有将它从集合中移除,这样只要集合还在使用(而此对象已经无用了),这个对象就造成了内存泄露。并且如果集合被静态引用的话,集合里面那些没有用的对象更会造成内存泄露了。所以在使用集合时要及时将不用的对象从集合remove,或者clear集合,以避免内存泄漏。

7、资源未关闭或释放导致内存泄露

在使用IO、File流或者Sqlite、Cursor等资源时要及时关闭。这些资源在进行读写操作时通常都使用了缓冲,如果及时不关闭,这些缓冲对象就会一直被占用而得不到释放,以致发生内存泄露。因此我们在不需要使用它们的时候就及时关闭,以便缓冲能及时得到释放,从而避免内存泄露。

8、属性动画造成内存泄露

动画同样是一个耗时任务,比如在Activity中启动了属性动画(ObjectAnimator),但是在销毁的时候,没有调用cancle方法,虽然我们看不到动画了,但是这个动画依然会不断地播放下去,动画引用所在的控件,所在的控件引用Activity,这就造成Activity无法正常释放。因此同样要在Activity销毁的时候cancel掉属性动画,避免发生内存泄漏。

@Override
protected void onDestroy() {
    super.onDestroy();
    mAnimator.cancel();
}

9、WebView造成内存泄露

关于WebView的内存泄露,因为WebView在加载网页后会长期占用内存而不能被释放,因此我们在Activity销毁后要调用它的destory()方法来销毁它以释放内存。

@Override
protected void onDestroy() {
    super.onDestroy();
    // 先从父控件中移除WebView
    mWebViewContainer.removeView(mWebView);
    mWebView.stopLoading();
    mWebView.getSettings().setJavaScriptEnabled(false);
    mWebView.clearHistory();
    mWebView.removeAllViews();
    mWebView.destroy();
}

10、总结

1).资源对象没关闭造成的内存泄漏 2).构造Adapter时,没有使用缓存的convertView 3).Bitmap对象不在使用时调用recycle()释放内存 4).试着使用关于application的context来替代和activity相关的context 5).注册没取消造成的内存泄漏 6).集合中对象没清理造成的内存泄漏

查找内存泄漏可以使用Android Stdio 自带的Android Profiler工具,也可以使用Square产品的LeadCanary.

12、android 屏幕适配

  • 在 XML 布局文件中指定尺寸时使用 wrap_content、match_parent 或 dp 单位 。
  • 不要在应用代码中使用硬编码的像素值
  • 不要使用 AbsoluteLayout(已弃用)
  • 为不同屏幕密度提供替代位图可绘制对象

image

支持多种屏幕

面向多种屏幕的设计

Android 屏幕适配:最全面的解决方案

13、HybridJAVA 与JS交互

  • Android 调 JS : 1、java
webView.loadUrl("javascript:javacalljs()");

  2、Html

image

**注意:**考虑有返回值情况;Android在4.4之前并没有提供直接调用js函数并获取值的方法,所以在此之前,常用的思路是 java调用js方法,js方法执行完毕,再次调用java代码将值返回。

// 4.4之后 java代码时用evaluateJavascript方法调用
private void testEvaluateJavascript(WebView webView) {
  webView.evaluateJavascript("getGreetings()", new ValueCallback<String>() {

  @Override
  public void onReceiveValue(String value) {
      Log.i(LOGTAG, "onReceiveValue value=" + value);
  }});
}
  • JS 调 Android : 1、java
webView.addJavascriptInterface(MainActivity.this,"android");

  2、html

<body>
HTML 内容显示 <br/>
<h1><div id="content">内容显示</div></h1><br/>
<input type="button"  value="点击调用java代码" onclick="window.android.startFunction()" /><br/>
<input type="button"  value="点击调用java代码并传递参数" onclick="window.android.startFunction('http://blog.csdn.net/Leejizhou')"  />
</body>

Android中Java和JavaScript交互

14、单例模式(手写)

//懒汉式
public class Singleton {
    private static Singleton singleton;

    private Singleton() {

    }

    public static synchronized Singleton getSingleton() {
        if (singleton == null) {
            singleton = new Singleton();
        }

        return singleton;
    }
}

//饿汉式
public class Singleton {
    private static final Singleton singleton = new Singleton();

    private Singleton () {

    }

    public static Singleton getSingleton() {
        return singleton;
    }
}
//double-lock
public class Singleton {
    private volatile static Singleton singleton;

    private Singleton() {

    }

    public static Singleton getSingleton() {
        if (singleton == null) {
            synchronized(Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }

        return singleton;
    }
}

14、ANR相关

1、什么是ANR

在Android上,如果你的应用程序有一段时间响应不够灵敏,系统会向用户显示一个对话框,这个对话框称作应用程序无响应(ANR:Application Not Responding)对话框。


2、ANR产生的原因

ANR产生的根本原因是APP阻塞了UI线程


3、ANR产生的原因

1:UI线程尽量只做跟UI相关的工作,但一些复杂的UI操作,还是需要一些技巧来处理,不如你让一个Button去setText一个10M的文本,UI肯定崩掉了,不过对于此类问题,分段加载貌似是最好的方法了。 2:让耗时的工作(比如数据库操作,I/O,连接网络或者别的有可能阻碍UI线程的操作)把它放入单独的线程处理。 3:尽量用Handler来处理UIthread和别的thread之间的交互。


ANR完全解析

15、SharedPreference相关

  • SharedPreference三种获得方法和区别:
1、 Activity类中的getPreferences(int mode)文件自动命名为当前活动的类名。
2、 Context类中getSharedPreferences("fileName", int mode) 此方法可以指定文件名,mode同上。
3、PreferenceManager类中的getDefaultSharedPreferences(Context context)它只接收一个context参数。
文件用当前应用程序的包名和PreferenceActivity一起来命名。属于整个应用程序

SharedPreference获得方法对比

  • commit和apply的区别:
    1. apply没有返回值而commit返回boolean表明修改是否提交成功。
    2. apply是将修改数据原子提交到内存, 而后异步真正提交到硬件磁盘, 而commit是同步的提交到硬件磁盘,因此,在多个并发的提交commit的时候,他们会等待正在处理的commit保存到磁盘后在操作,从而降低了效率。而apply只是原子的提交到内容,后面有调用apply的函数的将会直接覆盖前面的内存数据,这样从一定程度上提高了很多效率。
    3. apply方法不会提示任何失败的提示。 由于在一个进程中,sharedPreference是单实例,一般不会出现并发冲突,如果对提交的结果不关心的话,建议使用apply,当然需要确保提交成功且有后续操作的话,还是需要用commit的。

16、View,SurfaceView,GLSurfaceView的关系和区别:

  • View:显示视图,内置画布,提供图形绘制函数、触屏事件、按键事件函数等;必须在UI主线程内更新画面,速度较慢。
  • SurfaceView:基于view视图进行拓展的视图类,更适合2D游戏的开发;是view的子类,类似使用双缓机制,在新的线程中更新画面所以刷新界面速度比view快。
  • GLSurfaceView:基于SurfaceView视图再次进行拓展的视图类,专用于3D游戏开发的视图;是SurfaceView的子类,openGL专用。

17、其他

1、Android 中序列化有哪些方式?区别?

  • Serializable(Java自带): Serializable是序列化的意思,表示将一个对象转换成可存储或可传输的状态。序列化后的对象可以在网络上进行传输,也可以存储到本地。
  • Parcelable(android 专用): 不过不同于将对象进行序列化,Parcelable方式的实现原理是将一个完整的对象进行分解, 而分解后的每一部分都是Intent所支持的数据类型
  • 区别:
    • Parcelable比Serializable性能高,所以应用内传递数据推荐使用Parcelable
    • Serializable代码量少,写起来方便,缺点是使用了反射,序列化的过程较慢。这种机制会在序列化的时候创建许多的临时对象,容易触发垃圾回收。

2、glide 源码

3、Android中进程的级别,以及各自的区别。

  • 1、前台进程

用户当前正在做的事情需要这个进程。如果满足下面的条件之一,一个进程就被认为是前台进程: 1).这个进程拥有一个正在与用户交互的Activity(这个Activity的onResume()方法被调用)。 2).这个进程拥有一个绑定到正在与用户交互的activity上的Service。 3).这个进程拥有一个前台运行的Service(service调用了方法startForeground()). 4).这个进程拥有一个正在执行其任何一个生命周期回调方法(onCreate(),onStart(),或onDestroy())的Service。 5).这个进程拥有正在执行其onReceive()方法的BroadcastReceiver。 通常,在任何时间点,只有很少的前台进程存在。它们只有在达到无法调合的矛盾时才会被杀--如内存太小而不能继续运行时。通常,到了这时,设备就达到了一个内存分页调度状态,所以需要杀一些前台进程来保证用户界面的反应.

  • 2、可见进程

一个进程不拥有运行于前台的组件,但是依然能影响用户所见。满足下列条件时,进程即为可见: 这个进程拥有一个不在前台但仍可见的Activity(它的onPause()方法被调用)。当一个前台activity启动一个对话框时,就出了这种情况。

  • 3、服务进程

一个可见进程被认为是极其重要的。并且,除非只有杀掉它才可以保证所有前台进程的运行,否则是不能动它的。 这个进程拥有一个绑定到可见activity的Service。 一个进程不在上述两种之内,但它运行着一个被startService()所启动的service。 尽管一个服务进程不直接影响用户所见,但是它们通常做一些用户关心的事情(比如播放音乐或下载数据),所以系统不到前台进程和可见进程活不下去时不会杀它。

  • 4、后台进程

一个进程拥有一个当前不可见的activity(activity的onStop()方法被调用)。

  • 5、空进程

一个进程不拥有任何active组件。

4、线程池的相关知识。

Android中的线程池都是之间或间接通过配置ThreadPoolExecutor来实现不同特性的线程池.Android中最常见的四类具有不同特性的线程池分别为FixThreadPool、CachedThreadPool、SingleThreadPool、ScheduleThreadExecutor.

  • **FixThreadPool: **只有核心线程,并且数量固定的,也不会被回收,所有线程都活动时,因为队列没有限制大小,新任务会等待执行. 优点:更快的响应外界请求.

  • **SingleThreadPool:**只有一个核心线程,确保所有的任务都在同一线程中按顺序完成.因此不需要处理线程同步的问题.

  • **CachedThreadPool:**只有非核心线程,最大线程数非常大,所有线程都活动时,会为新任务创建新线程,否则会利用空闲线程(60s空闲时间,过了就会被回收,所以线程池中有0个线程的可能)处理任务. 优点:任何任务都会被立即执行(任务队列SynchronousQueue相当于一个空集合);比较适合执行大量的耗时较少的任务.

  • **ScheduleThreadExecutor:**核心线程数固定,非核心线程(闲着没活干会被立即回收)数没有限制. 优点:执行定时任务以及有固定周期的重复任务

Android开发之线程池使用总结

5、怎样计算一张图片的大小,加载bitmap过程(怎样保证不产生内存溢出),二级缓存,LRUCache算法。

1、计算一张图片的大小

图片占用内存的计算公式:图片高度 * 图片宽度 * 一个像素占用的内存大小.所以,计算图片占用内存大小的时候,要考虑图片所在的目录跟设备密度,这两个因素其实影响的是图片的高宽,android会对图片进行拉升跟压缩。

2、加载bitmap过程(怎样保证不产生内存溢出)

由于Android对图片使用内存有限制,若是加载几兆的大图片便内存溢出。
Bitmap会将图片的所有像素(即长x宽)加载到内存中,如果图片分辨率过大,会直接导致内存OOM,只有在BitmapFactory加载图片时使用BitmapFactory.Options对相关参数进行配置来减少加载的像素。

3、BitmapFactory.Options相关参数详解

(1).Options.inPreferredConfig值来降低内存消耗。
  比如:默认值ARGB_8888改为RGB_565,节约一半内存。
(2).设置Options.inSampleSize 缩放比例,对大图片进行压缩 。
(3).设置Options.inPurgeable和inInputShareable:让系统能及时回 收内存。
  A:inPurgeable:设置为True时,表示系统内存不足时可以被回 收,设置为False时,表示不能被回收。
  B:inInputShareable:设置是否深拷贝,与inPurgeable结合使用,inPurgeable为false时,该参数无意义。

(4).使用decodeStream代替其他方法。
decodeResource,setImageResource,setImageBitmap等方法


**LRUCache算法:**内部存在一个LinkedHashMap和maxSize,把最近使用的对象用强引用存储在 LinkedHashMap中,给出来put和get方法,每次put图片时计算缓存中所有图片总大小,跟maxSize进行比较,大于maxSize,就将最久添加的图片移除;反之小于maxSize就添加进来。(400行代码不到...)

6、layout、Merge、ViewStub的作用。

  • merge: 它可以删减多余的层级,优化UI。例如你的主布局文件是垂直布局,引入了一个垂直布局的include,这是如果include布局使用的LinearLayout就没意义了,使用的话反而减慢你的UI表现。这时可以使用标签优化。

  • ViewStub: 标签最大的优点是当你需要时才会加载,使用他并不会影响UI初始化时的性能。各种不常用的布局想进度条、显示错误消息等可以使用标签,以减少内存使用量,加快渲染速度。是一个不可见的,大小为0的View。