前言:大家好,最近不管是看书也好,看博客也好,看到许多自己曾经看过但是可能时间久了有点记得不太清的内容,于是乎就想以后要是这种情况,现在何不随手记录下来呢,方便自己以后阅读回顾,同时也可以给大家提供帮助,所以在很长的一段时间内,我都会简短但是清楚的说明一个知识点,考虑到如果每个知识点都写的非常详细的话,篇幅会很长,并且查找起来也非常费力,所以如果有关的重点,我会单独的抽取出来详细讲解,本篇博客只为重点,谢谢大家!
注:本篇博客中可能会有部分内容源自于外界博文内容,我只是链接了去处,作为一个总结和分享用途,不作为商业用途,如果有涉及版权问题,请联系我删除,谢谢!
知识点1.
从activityA打开跳转到activityB,生命周期先执行的是activityA的onPause()方法,然后才走的activityB的onCreate()、onStart()、onResume()方法,最后才走的activityA的onStop()、onDestroy()方法;
知识点2.
如果为一个activity设置了launchMode为SingleTask模式的话,重复启动它并不会每次执行onCreate()方法(第一次除外),但是会每次执行onPause()、onNewIntent()、onResume()方法;
知识点3
线程和进程的区别是什么?
线程:CPU调度的最小单元,是一种有限的系统资源;
进程:一般指一个执行单元,在PC和移动设备上指一个程序或一个应用;
进程和线程是包含与被包含的关系,一个进程中可以有一个线程(主线程)也可以有多个线程(多线程);
知识点4
在Android中开启多进程只有一种方法,那就是给四大组件(Activity、Service、Receiver、ContentProvider)在AndroidManifest.xml中指定android:process属性;
知识点5
使用多进程可能会造成以下问题:
1.静态成员和单例模式完全失效;
2.线程同步完全失效;
3.SharedPreferences可靠性下降;
4.Application会多次创建;
知识点6
Serializable和Parcelable的区别:
Serializable:是java中的序列化接口,使用简单但是开销大;
Parcelable:是Android中的序列化方式,虽然使用起来麻烦一点,但是效率很高;
在使用内存的时候,Parcelable比Serializable性能高,所以推荐使用Parcelable。
Serializable在序列化的时候会产生大量的临时变量,从而引起频繁的GC。
Parcelable不能使用在要将数据存储在磁盘上的情况,因为Parcelable不能很好的保证数据的持续性在外界有变化的情况下。尽管Serializable效率低点,但此时还是建议使用Serializable 。
知识点7
RelativeLayout和LinearLayout绘制问题:
RelativeLayout绘制时间比LinearLayout绘制时间稍长,因为RelativeLayout会进行两次的绘制工作,一次横向,一次纵向,而LinearLayout在没有设置weight属性的情况下只进行一次绘制,只有当设置了weight属性的情况下才会进行两次绘制,所以在绘制时间上,LinearLayout稍快。
知识点8
static只能用来修饰成员变量,不能修饰局部变量,编译时会报错,例如下列代码会报错:
public class Student{
public void study(){
static int num = 10;
}
}
知识点9
java中四种访问控制级别:
1.private(类访问级别):如果类的成员被private访问控制符修饰,则这个成员只能被该类的其它成员访问,其他类无法直接访问。类的良好封装性就是通过private关键字来实现的。
2.default(包访问级别):如果一个类或者类的成员不使用任何访问控制修饰符,则称它为默认访问控制级别,这个类或者类的成员只能被本包中的其他类访问。
3.protected(子类访问级别):如果一个类的成员被protected访问控制符修饰,那么这个成员既能被同一包下的其他类访问,也能被不同包下该类的子类访问。
4.public(公共访问级别):这是一个最宽松的访问控制级别,如果一个类或者类的成员被public访问控制符修饰,那么这个类或者类的成员能被所有的类访问,不管访问类与被访问类是否在同一个包中。
访问范围 | private | default | protected | public |
---|---|---|---|---|
同一类中 | ☑️ | ☑️ | ☑️ | ☑️ |
同一包中 | ☑️ | ☑️ | ☑️ | |
子类中 | ☑️ | ☑️ | ||
全局范围 | ☑️ |
知识点10
Collection有两个重要的子接口,分别是List和Set,其中List的特点是元素有序,元素可重复。Set的特点是元素无序且不可重复。List接口的主要实现类有ArrayList和LinkedList,Set接口的主要实现类有HashSet和TreeSet。
知识点11
由于ArrayList集合的底层是使用一个数组来保存元素,在增加或删除指定位置的元素时,会导致创建新的数组,效率比较低,因此不适合做大量的增删操作。但是这种数组的结构允许程序通过索引的方式来访问元素,因此使用ArrayList集合查找元素和便捷。
知识点12
LinkedList集合内部维护了一个双向循环列表,链表中的每一个元素都使用引用的方式来记住它的前一个元素和后一个元素,从而可以将所有的元素彼此链接起来。当插入一个新元素时,只需要修改元素之间的这种引用关系即可,删除一个节点也是如此。正因为这样的存储结构,所以LinkedList集合对于元素的增删操作具有很高的效率。
知识点13
常见的内存泄漏 :
1.查询数据库没有关闭Cursor。
2.使用BaseAdapter作为适配器时没有复用convertView。
3.bitmap没有回收。
4.注册对象后没有反注册,比如Broadcast Receiver等。
5.handler问题,如果handler是非静态的,会导致Activity或者Service不被回收,所以应当注册为静态内部类,同时在onDestroy时停止线程:mThread.getLooper().quit()。
6.Activity被静态引用,特别是缓存bitmap时,解决方法可以考虑使用Application的context代替Activity的context。
7.View在callback中被引用,可能回调还没有结束,但是view处于引用状态,无法回收。
8.WebView的泄露问题:在魅族上面发现webView打开再关闭就会内存泄露。目前使用的解决方法是在webview外面嵌套一层layout作为container.在Activity的onDestroy中调用container.removeAllViews()方法。
9.Dialog导致Window泄露,如果需要在dialog依附的Activity销毁前没有调用dialog.dismiss()会导致Activity泄露 。
10.如果还有其他的,欢迎补充!
知识点14
安卓xml绘制虚线:
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="line">
<!-- 显示一条虚线,破折线的宽度为dashWith,破折线之间的空隙的宽度为dashGap,当dashGap=0dp时,为实线 -->
<stroke android:width="1dp" android:color="#D5D5D5"
android:dashWidth="2dp" android:dashGap="3dp" />
<!-- 虚线的高度 -->
<size android:height="2dp" />
</shape>
知识点15
ExpandableListView 的子列表不能点击(禁用)要把 Adapter 的 isChildSelectable 方法返回 true。
知识点16
什么叫垃圾回收机制?
垃圾回收是一种动态存储管理技术,它自动地释放不再被程序引用的对象,按照特定的垃圾收集算法来实现资源自动回收的功能。当一个对象不再被引用的时候,内存回收它占领的空间,以便空间被后来的新对象使用,以免造成内存泄露。
知识点17
java的垃圾回收有什么特点?
JAVA语言不允许程序员直接控制内存空间的使用。内存空间的分配和回收都是由JRE负责在后台自动进行的,尤其是无用内存空间的回收操作(garbagecollection,也称垃圾回收),只能由运行环境提供的一个超级线程进行监测和控制。
知识点18
垃圾回收器什么时候会运行?
一般是在CPU空闲或空间不足时自动进行垃圾回收,而程序员无法精确控制垃圾回收的时机和顺序等。
知识点19
垃圾回收器是怎样工作的?
垃圾回收器如发现一个对象不能被任何活线程访问时,他将认为该对象符合删除条件,就将其加入回收队列,但不是立即销毁对象,何时销毁并释放内存是无法预知的。垃圾回收不能强制执行,然而Java提供了一些方法(如:System.gc()方法),允许你请求JVM执行垃圾回收,而不是要求,虚拟机会尽其所能满足请求,但是不能保证JVM从内存中删除所有不用的对象。
知识点20
如何显式的使对象符合垃圾回收条件?
1.空引用:当对象没有对他可到达引用时,他就符合垃圾回收的条件。也就是说如果没有对他的引用,删除对象的引用就可以达到目的,因此我们可以把引用变量设置为null,来符合垃圾回收的条件。
StringBuffer sb = new StringBuffer("hello");
System.out.println(sb);
sb=null;
2.重新为引用变量赋值:可以通过设置引用变量引用另一个对象来解除该引用变量与一个对象间的引用关系。
StringBuffer sb1 = new StringBuffer("hello");
StringBuffer sb2 = new StringBuffer("goodbye");
System.out.println(sb1);
sb1=sb2;//此时"hello"符合回收条件
3.方法内创建的对象:所创建的局部变量仅在该方法的作用期间内存在。一旦该方法返回,在这个方法内创建的对象就符合垃圾收集条件。有一种明显的例外情况,就是方法的返回对象。
public static void main(String[] args) {
Date d = getDate();
System.out.println("d = " + d);
}
private static Date getDate() {
Date d2 = new Date();
StringBuffer now = new StringBuffer(d2.toString());
System.out.println(now);
return d2;
}
4.隔离引用:这种情况中,被回收的对象仍具有引用,这种情况称作隔离岛。若存在这两个实例,他们互相引用,并且这两个对象的所有其他引用都删除,其他任何线程无法访问这两个对象中的任意一个。也可以符合垃圾回收条件。
public class Island {
Island i;
public static void main(String[] args) {
Island i2 = new Island();
Island i3 = new Island();
Island i4 = new Island();
i2.i=i3;
i3.i=i4;
i4.i=i2;
i2=null;
i3=null;
i4=null;
}
}
知识点21
垃圾收集前进行清理------finalize()方法 java提供了一种机制,使你能够在对象刚要被垃圾回收之前运行一些代码。这段代码位于名为finalize()的方法内,所有类从Object类继承这个方法。由于不能保证垃圾回收器会删除某个对象。因此放在finalize()中的代码无法保证运行。因此建议不要重写finalize()。
知识点22
定义字符串应该尽量使用 String str=“hello”; 的形式 ,避免使用String str = new String(“hello”); 的形式。因为要使用内容相同的字符串,不必每次都new一个String。
后者每次都会调用构造器,生成新对象,性能低下且内存开销大,并且没有意义,因为String对象不可改变,所以对于内容相同的字符串,只要一个String对象来表示就可以了。也就说,多次调用上面的构造器创建多个对象,他们的String类型属性s都指向同一个对象。
知识点23
尽量避免在类的构造函数里创建、初始化大量的对象 ,防止在调用其自身类的构造器时造成不必要的内存资源浪费,尤其是大对象,JVM会突然需要大量内存,这时必然会触发GC优化系统内存环境;显示的声明数组空间,而且申请数量还极大。
知识点24
碰到一个需求,EditText默认的hint文字大小为12sp,当有文字填写时,文字大小变为18sp,在xml文件中无法实现,只能通过下列java代码实现:
etPhone = (EditText) findViewById(R.id.msg_phone);
SpannableString ss = new SpannableString("请填写已经注册的手机号码");//定义hint的值
AbsoluteSizeSpan ass = new AbsoluteSizeSpan(12, true);//设置字体大小 true表示单位是sp
ss.setSpan(ass, 0, ss.length(),Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
etPhone.setHint(new SpannedString(ss));
知识点25
关于Toast的优化:
如果每次都使用Toast.makeText(context,content,Toast.LENGTH_SHORT).show();来给出吐司提示的话,当你在短时间内不断点击,停止点击后会发现吐司会停留很长的一段时间,这是因为每次点击都是重新创建了一个Toast,所以这种做法会给用户造成较差的用户体验,那么正确的做法应该如下:
public class ToastUtils {
private static Toast toast;
public static void toast(Context cxt, String str) {
if (toast == null) {
toast = Toast.makeText(cxt, str, Toast.LENGTH_SHORT);
} else {
toast.setText(str);
}
toast.show();
}
}
知识点26
OkHttp和Volley的有什么区别?
- OkHttp
1.占用储存空间
使用OkHttp需要 okio.jar (80k), okhttp.jar(330k)这2个jar包,总大小差不多400k,加上自己的封装,差不多得410k。
2.功能介绍
Square 公司开源的 OkHttp 是一个专注于连接效率的 HTTP 客户端。OkHttp 提供了对 HTTP/2 和 SPDY 的支持,并提供了连接池,GZIP 压缩和 HTTP 响应缓存功能。
3.优点
支持http请求,https请求。
支持文件下载。
使用的是HttpURLConnection,不要担心android版本的变换。(至少目前是都支持的)。
支持get,post请求。
基于Http的文件上传。
加载图片。
4.缺点
比如callback回来是在线程里面, 不能刷新UI,需要我们手动处理。封装比较麻烦。
- Volley
1.占用储存空间
使用Volley 需要Volley.jar(120k),加上自己的封装最多140k。
2.功能介绍
Volley是Goole在2013年Google I/O大会上推出了一个新的网络通信框架,它是开源的。Volley 的特点:特别适合数据量小,通信频繁的网络操作。
3.优点
非常适合进行数据量不大,但通信频繁的网络操作。
内部分装了异步线程。
支持get,post网络请求。
图片下载。
可直接在主线程调用服务端并处理返回结果。
可以取消请求,容易扩展,面向接口编程。
4.缺点
对大文件下载 Volley的表现非常糟糕。
只支持http请求。
在BasicNetwork中判断了statusCode(statusCode < 200 || statusCode >
299),如果符合条件直接图片加载,性能一般。
使用的是httpclient,HttpURLConnection。不过在android
6.0不支持httpclient了,如果想支持得添加org.apache.http.legacy.jar。
- 总结
在我们平时的项目使用volley就可以了,相对okhttp,volley非常稳定。Okhttp一般混合来用,能够胜任相对复杂的需求。如今,在AndroidStudio中,网络请求还是推荐使用Retrofit2+okhttp。
知识点27
Toast可以通过以下方法改变显示的位置:
Toast toast = Toast.makeText(MainActivity.this,"Bottom Right",Toast.LENGTH_SHORT);
toast.setGravity(Gravity.BOTTOM | Gravity.RIGHT,0,0);
toast.show();
知识点28
在Android中提供了三种解析XML的方式:SAX(Simple API XML),DOM(Document Objrect Model),以及Android推荐的Pull解析方式。
SAX: 是事件驱动型XML解析的一个标准接口,简单地说就是对文档进行顺序扫描,当扫描到文档(document)开始与结束、元素(element)开始与结束、文档(document)结束等地方时通知事件处理函数,由事件处理函数做相应动作,然后继续同样的扫描,直至文档结束。
DOM:即对象文档模型,它是将整个XML文档载入内存(所以效率较低,不推荐使用),使用DOM API遍历XML树、检索所需的数据,每一个节点当做一个对象。
Pull:运行方式与 SAX 解析器相似。它提供了类似的事件,SAX解析器的工作方式是自动将事件推入事件处理器进行处理,因此你不能控制事件的处理主动结束;而Pull解析器的工作方式为允许你的应用程序代码主动从解析器中获取事件,正因为是主动获取事件,因此可以在满足了需要的条件后不再获取事件,结束解析。pull是一个while循环,随时可以跳出,而sax是只要解析了,就必须解析完成。
知识点29
try catch finally,try里有return,finally还执行么?
Condition 1: 如果try中没有异常且try中有return (执行顺序)
try ---- finally — return
Condition 2: 如果try中有异常并且try中有return
try----catch—finally— return
总之 finally 永远执行!
Condition 3: try中有异常,try-catch-finally里都没有return ,finally 之后有个return
try----catch—finally
try中有异常以后,根据java的异常机制先执行catch后执行finally,此时错误异常已经抛出,程序因异常而终止,所以你的return是不会执行的
Condition 4: 当 try和finally中都有return时,finally中的return会覆盖掉其它位置的return(多个return会报unreachable code,编译不会通过)。
Condition 5: 当finally中不存在return,而catch中存在return,但finally中要修改catch中return 的变量值时
int ret = 0;
try{
throw new Exception();
}
catch(Exception e)
{
ret = 1; return ret;
}
finally{
ret = 2;
}
最后返回值是1,因为return的值在执行finally之前已经确定下来了
知识点30
不同版本的系统如何统一弹出Material Design风格?
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Title")
.setMessage("Dialog content.")
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog,
int which) {
}
})
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog,
int which) {
}
})
.show();
这段代码就可以弹出一个非常精美的Dialog了,如下图所示:
现在这个Dialog是Material Design风格的,因为我是在6.0系统上运行的,因此会自动赋予这样的风格。但是如果在老版本系统上运行,比如说2.3系统,会是什么样的效果呢?运行一下就知道了,如下图所示:
人的审美总是在进步的,我们有没有什么办法在老版本的系统中也使用Material Design风格的Dialog呢?当然有,Google已经充分考虑到了这一点,在appcompat-v7库中也提供了一个AlertDialog类,完整路径是:
android.support.v7.app.AlertDialog
我们使用这个包中的AlertDialog,就能让对话框在所有的系统版本中都保持一致的风格了。现在在2.3系统中重新运行一下,效果如下所示:
可以看到,现在的效果就比较不错了,这也算是一个小技巧吧。
Dialog的作用是给用户一个提示信息,并让用户根据提示做出判断。而Dialog的特征就是,它会阻止你原本正在进行的操作,必须停止下来对Dialog进行处理。但是,大多数的人可能并不喜欢这样被打断,也许用户正在处理一项重要的操作,突然弹出一个Dialog遮挡住了他原本的操作,这个时候用户会变得很恼火。
因此,使用Dialog的时候还是谨慎一点比较好,尽量不要给用户带来糟糕的体验感。
知识点31
如果说Dialog和Toast是两个极端的话,那么Snackbar就是处于中间的位置了。Snackbar和Toast比较相似,但是用途更加广泛,并且它是可以和用户进行交互的。Snackbar使用一个动画效果从屏幕的底部弹出来,过一段时间后也会自动消失。
在使用Snackbar之前,首先需要在app/build.gradle中添加相应的依赖:
dependencies {
compile 'com.android.support:design:23.4.0'
}
然后就可以使用Snackbar了,它的用法和Toast是比较相似的:
Snackbar.make(view, "data deleted",Snackbar.LENGTH_LONG)
.setAction("Undo", new View.OnClickListener(){
@Override
public void onClick(View v) {
}
})
.show();
这里调用Snackbar的make()方法来创建一个Snackbar对象,make()方法的第一个参数需要传入一个view,只要是当前界面布局的任意一个view都可以,Snackbar会使用这个view来自动查找最外层的布局,用于展示Snackbar。第二个参数就是Snackbar中显示的内容,第三个参数是Snackbar显示的时长。这些和Toast都是类似的。
接着这里又调用了一个setAction()方法来设置一个动作,从而让Snackbar不仅仅是一个提示,而是可以和用户进行交互的。最后调用show()方法让Snackbar显示出来。
现在重新运行一下程序,效果如下图所示:
可以看到,Snackbar的效果有点类似于Toast,不过它是从屏幕底部弹出来的。另外Snackbar上面可以加入和用户交互的按钮,比如删除数据的时候给用户一个Undo的选项,从这些小的细节方面都可以提升很多的用户体验。
知识点32
Dialog、Toast和Snackbar的使用总结:
现在你有三种方式可以给用户提示信息,Dialog、Toast和Snackbar,下面我们对这三种方式的使用时机做个总结吧。
-
Dialog:当提示信息是至关重要的,并且必须要由用户做出决定才能继续的时候,使用Dialog。
-
Toast:当提示信息只是告知用户某个事情发生了,用户不需要对这个事情做出响应的时候,使用Toast。
-
Snackbar:以上两者之外的任何其他场景,Snackbar可能会是你最好的选择。
知识点33
单例模式的最佳实现:
public class SingleTest {
private SingleTest() {
}
private static volatile SingleTest instance;
public static SingleTest getInstance() {
if (instance == null) {
synchronized (SingleTest.class) {
if (instance == null) {
instance = new SingleTest();
}
}
}
return instance;
}
}
知识点34
在一个子线程中使用Handler的方式应该是这样的:
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
}
};
Looper.loop();
}
}
- 为什么我们平常在MainActivity中使用Handler时并没有调用Looper.prepare()也没有报错呢?
这是因为UI线程是主线程,系统会自动调用Looper.prepareMainLooper()方法创建主线程的Looper和消息队列MessageQueue
知识点35
使用Handler的错误姿势及其潜在风险
1.handler.post(Runnable runnable)、view.post(Runnable runnable)、Activity.runOnUiThread(Runnable runnable)的runnable均会在主线程中执行,所以切勿在其run()方法中执行耗时的操作。
知识点36
Fragment生命周期场景演示:
1.切换到该Fragment
11-29 14:26:35.095: D/AppListFragment(7649): onAttach
11-29 14:26:35.095: D/AppListFragment(7649): onCreate
11-29 14:26:35.095: D/AppListFragment(7649): onCreateView
11-29 14:26:35.100: D/AppListFragment(7649): onActivityCreated
11-29 14:26:35.120: D/AppListFragment(7649): onStart
11-29 14:26:35.120: D/AppListFragment(7649): onResume
2.屏幕灭掉:
11-29 14:27:35.185: D/AppListFragment(7649): onPause
11-29 14:27:35.205: D/AppListFragment(7649): onSaveInstanceState
11-29 14:27:35.205: D/AppListFragment(7649): onStop
3.屏幕解锁
11-29 14:33:13.240: D/AppListFragment(7649): onStart
11-29 14:33:13.275: D/AppListFragment(7649): onResume
4.切换到其他Fragment:
11-29 14:33:33.655: D/AppListFragment(7649): onPause
11-29 14:33:33.655: D/AppListFragment(7649): onStop
11-29 14:33:33.660: D/AppListFragment(7649): onDestroyView
5.切换回本身的Fragment:
11-29 14:33:55.820: D/AppListFragment(7649): onCreateView
11-29 14:33:55.825: D/AppListFragment(7649): onActivityCreated
11-29 14:33:55.825: D/AppListFragment(7649): onStart
11-29 14:33:55.825: D/AppListFragment(7649): onResume
6.回到桌面
11-29 14:34:26.590: D/AppListFragment(7649): onPause
11-29 14:34:26.880: D/AppListFragment(7649): onSaveInstanceState
11-29 14:34:26.880: D/AppListFragment(7649): onStop
7.回到应用
11-29 14:36:51.940: D/AppListFragment(7649): onStart
11-29 14:36:51.940: D/AppListFragment(7649): onResume
8.退出应用
11-29 14:37:03.020: D/AppListFragment(7649): onPause
11-29 14:37:03.155: D/AppListFragment(7649): onStop
11-29 14:37:03.155: D/AppListFragment(7649): onDestroyView
11-29 14:37:03.165: D/AppListFragment(7649): onDestroy
11-29 14:37:03.165: D/AppListFragment(7649): onDetach
知识点37
高度注意Map类集合k/v存储null值的情况,如下表格:
知识点38
ListView卡顿的原因与性能优化:
1.重用converView: 通过复用converview来减少不必要的view的创建,另外Infalte操作会把xml文件实例化成相应的View实例,属于IO操作,是耗时操作。
2.减少findViewById()操作: 将xml文件中的元素封装成viewholder静态类,通过converview的setTag和getTag方法将view与相应的holder对象绑定在一起,避免不必要的findviewbyid操作。
3.避免在 getView 方法中做耗时的操作: 例如加载本地 Image 需要载入内存以及解析 Bitmap ,都是比较耗时的操作,如果用户快速滑动listview,会因为getview逻辑过于复杂耗时而造成滑动卡顿现象。用户滑动时候不要加载图片,待滑动完成再加载,可以使用这个第三方库glide
4.Item的布局层次结构尽量简单,避免布局太深或者不必要的重绘。
5.尽量能保证 Adapter 的 hasStableIds() 返回 true 这样在 notifyDataSetChanged() 的时候,如果item内容并没有变化,ListView 将不会重新绘制这个 View,达到优化的目的。
6.在一些场景中,ScollView内会包含多个ListView,可以把listview的高度写死固定下来。 由于ScollView在快速滑动过程中需要大量计算每一个listview的高度,阻塞了UI线程导致卡顿现象出现,如果我们每一个item的高度都是均匀的,可以通过计算把listview的高度确定下来,避免卡顿现象出现。
7.使用 RecycleView 代替listview: 每个item内容的变动,listview都需要去调用notifyDataSetChanged来更新全部的item,太浪费性能了。RecycleView可以实现当个item的局部刷新,并且引入了增加和删除的动态效果,在性能上和定制上都有很大的改善。
8.ListView 中元素避免半透明: 半透明绘制需要大量乘法计算,在滑动时不停重绘会造成大量的计算,在比较差的机子上会比较卡。 在设计上能不半透明就不不半透明。实在要弄就把在滑动的时候把半透明设置成不透明,滑动完再重新设置成半透明。
9.尽量开启硬件加速: 硬件加速提升巨大,避免使用一些不支持的函数导致含泪关闭某个地方的硬件加速。当然这一条不只是对 ListView。
知识点39
谈谈你对android系统(体系)架构的理解?
Linux操作系统为核心,从下往上,依赖关系。
1.应用程序层:包括系统应用以及第三方应用。
2.应用程序框架:提供应用开发所必须的一些API框架,是软件复用的重要手段
3.库:android运行时(核心包(相当于JDK提供的包),虚拟机(优化过的JVM));C/C++的一些库
4.Linux核心:提供了电源管理、进程调度、内存管理、网络协议栈、驱动模型等核心系统服务
知识点40
Activity的生命周期
当打开新的Activity,采用透明主题的时候,当前Activity不会回调onStop
知识点41
Service生命周期及注意事项
Service默认是运行在main线程的,因此Service中如果需要执行耗时操作(大文件的操作,数据库的拷贝,网络请求,文件下载等)的话应该在子线程中完成。
知识点42
为什么在子线程中创建Handler会抛异常?
Handler的工作是依赖于Looper的,而Looper(与消息队列)又是属于某一个线程(ThreadLocal是线程内部的数据存储类,通过它可以在指定线程中存储数据,其他线程则无法获取到),其他线程不能访问。因此Handler就是间接跟线程是绑定在一起了。因此要使用Handler必须要保证Handler所创建的线程中有Looper对象并且启动循环。因为子线程中默认是没有Looper的,所以会报错。
正确的使用方法是:
handler = null;
new Thread(new Runnable() {
private Looper mLooper;
@Override
public void run() {
//必须调用Looper的prepare方法为当前线程创建一个Looper对象,然后启动循环
//prepare方法中实质是给ThreadLocal对象创建了一个Looper对象
//如果当前线程已经创建过Looper对象了,那么会报错
Looper.prepare();
handler = new Handler();
//获取Looper对象
mLooper = Looper.myLooper();
//启动消息循环
Looper.loop();
//在适当的时候退出Looper的消息循环,防止内存泄漏
mLooper.quit();
}
}).start();
主线程中默认是创建了Looper并且启动了消息的循环的,因此不会报错:
应用程序的入口是ActivityThread的main方法,在这个方法里面会创建Looper,并且执行Looper的loop方法来启动消息的循环,使得应用程序一直运行。
子线程中可以通过Handler发送消息给主线程吗?
可以。有时候出于业务需要,主线程可以向子线程发送消息。子线程的Handler必须按照上述方法创建,并且关联Looper。
知识点43
View的绘制-onDraw
View绘制,需要掌握Android中View的坐标体系:
View的坐标体系是以左上角为坐标原点,向右为X轴正方向,向下为Y轴正方向。
View绘制,主要是通过Android的2D绘图机制来完成,时机是onDraw方法中,其中包括画布Canvas,画笔Paint。下面给出示例代码。相关API不是介绍的重点,重点是Canvas的save和restore方法,通过save以后可以对画布进行一些放大缩小旋转倾斜等操作,这两个方法一般配套使用,其中save的调用次数可以多于restore。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Bitmap bitmap = ImageUtils.drawable2Bitmap(mDrawable);
canvas.drawBitmap(bitmap, getLeft(), getTop(), mPaint);
canvas.save();
//注意,这里的旋转是指画布的旋转
canvas.rotate(90);
mPaint.setColor(Color.parseColor("#FF4081"));
mPaint.setTextSize(30);
canvas.drawText("测试", 100, -100, mPaint);
canvas.restore();
}
知识点44
布局优化
1.使用include标签,通过layout属性复用相同的布局。
<include
android:id="@+id/v_test"
layout="@layout/include_view" />
2.使用merge标签,去除同类的视图
3.使用ViewStub来进行布局的延迟加载一些不是马上就用到的布局。例如列表页中,列表在没有拿到数据之前不加载,这样做可以使UI变得流畅。
<ViewStub
android:id="@+id/v_stub"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout="@layout/view_stub" />
//需要手动调用inflate方法,布局才会显示出来。
stub.inflate();
//其中setVisibility在底层也是会调用inflate方法
//stub.setVisibility(View.VISIBLE);
//之后,如果要使用ViewStub标签里面的View,只需要按照平常来即可。
TextView tv_1 = (TextView) findViewById(R.id.tv_1);
4.尽量多使用RelativeLayout,因为这样可以大大减少视图的层级。
知识点45
APP设计以及代码编写阶段都应该考虑内存优化:
1.珍惜Service,尽量使得Service在使用的时候才处于运行状态。尽量使用IntentService。
IntentService在内部其实是通过线程以及Handler实现的,当有新的Intent到来的时候,会创建线程并且处理这个Intent,处理完毕以后就自动销毁自身。因此使用IntentService能够节省系统资源。
2.内存紧张的时候释放资源(例如UI隐藏的时候释放资源等)。复写Activity的回调方法。
@Override
public void onLowMemory() {
super.onLowMemory();
}
@Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
switch (level) {
case TRIM_MEMORY_COMPLETE:
//...
break;
case 其他:
}
}
3.通过Manifest中对Application配置更大的内存,但是一般不推荐
android:largeHeap=“true”
4.避免Bitmap的浪费,应该尽量去适配屏幕设备。尽量使用成熟的图片加载框架,Picasso,Fresco,Glide等。
5.使用优化的容器,SparseArray等
6.其他建议:尽量少用枚举变量,尽量少用抽象,尽量少增加类,避免使用依赖注入框架,谨慎使用library,使用代码混淆,时当场合考虑使用多进程等。
7.避免内存泄漏(本来应该被回收的对象没有被回收)。一旦APP的内存短时间内快速增长或者GC非常频繁的时候,就应该考虑是否是内存泄漏导致的。
分析方法
-
使用Android Studio提供的Android Monitors中Memory工具查看内存的使用以及没使用的情况。
-
使用DDMS提供的Heap工具查看内存使用情况,也可以手动触发GC。
-
使用性能分析的依赖库,例如Square的LeakCanary,这个库会在内存泄漏的前后通过Notification通知你。
知识点46
性能优化
1.防止过度绘制,通过打开手机的“显示过度绘制区域”即可查看过度绘制的情况。
2.最小化渲染时间,使用视图树查看节点,对节点进行性能分析。
3.通过TraceView进行数据的采集以及分析。在有大概定位的时候,使用Android官方提供的Debug类进行采集。最后通过DDMS即可打开这个.trace文件,分析函数的调用情况(包括在指定情况下执行时间,调用次数)
//开启数据采集
Debug.startMethodTracing("test.trace");
//关闭
Debug.stopMethodTracing();
知识点47
避免OOM的一些常见方法:
1.App资源中尽量少用大图。使用Bitmap的时候要注意等比例缩小图片,并且注意Bitmap的回收。
BitmapFactory.Options options = new BitmapFactory.Option();
options.inSampleSize = 2;
//Options 只保存图片尺寸大小,不保存图片到内存
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inSampleSize = 2;
Bitmap bmp = null;
bmp = BitmapFactory.decodeResource(getResources(),
mImageIds[position],opts);
//回收
bmp.recycle();
2.结合组件的生命周期,释放资源
3.IO流,数据库查询的游标等应该在使用完之后及时关闭。
4.ListView中应该使用ViewHolder模式缓存ConverView
5.页面切换的时候尽量去传递(复用)一些对象
知识点48
ANR:
不同的组件发生ANR 的时间不一样,主线程(Activity、Service)是5 秒,BroadCastReceiver 是10 秒。
ANR一般有三种类型:
1.KeyDispatchTimeout(5 seconds)
主要类型按键或触摸事件在特定时间内无响应
2.BroadcastTimeout(10 seconds)
BroadcastReceiver在特定时间内无法处理完成
3.ServiceTimeout(20 seconds)
小概率类型Service在特定的时间内无法处理完成
解决方案:
-
UI线程只进行UI相关的操作。所有耗时操作,比如访问网络,Socket 通信,查询大量SQL 语句,复杂逻辑计算等都放在子线程中去,然后通过handler.sendMessage、runonUITread、AsyncTask 等方式更新UI。
-
无论如何都要确保用户界面操作的流畅度。如果耗时操作需要让用户等待,那么可以在界面上显示进度条。
-
BroadCastReceiver要进行复杂操作的的时候,可以在onReceive()方法中启动一个Service来处理。
知识点49
个人觉得写的很容易理解的一篇关于HashMap源码的理解文章
知识点50
方阵斜对角打印
public class DaYinXieDuiJiao {
public static void main(String[] args) {
/**
* 1. 问题描述:
输入一个矩阵,从右上角开始按照斜对角线打印矩阵的值,如矩阵为:
1, 2, 3, 4
5, 6, 7, 8
9, 10, 11, 12
13,14, 15, 16
输出:
4, 3, 8, 2, 7, 12, 1, 6, 11, 16, 5, 10, 15, 9, 14, 13
*/
/**
* 2. 思路:
思路:
将整个输出以最长的斜对角线分为两部分:右上部分和左下部分。
右上部分:对角线的起点在第一行,列数递减,对角线上相邻元素之间横坐标和纵坐标均相差1;
左下部分:对角线的起点在第一列上,行数递减,对角线上相邻元素之间横坐标和纵坐标均相差1;
复杂度:O(n^2)
*/
int[][] a = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}};
print(a, 4);
}
private static void print(int[][] a, int n) {
int row;
int col;
//输出右上角代码,包括对角线上的元素
for (int i = n - 1; i >= 0; i--) {//每次都是从第0行开始,所以需要row = 0,然后row col 同时自增
row = 0;
col = i;
while (row >= 0 && row < n && col >= 0 && col < n) {
System.out.print(a[row][col] + " ");
row++;
col++;
}
}
//for输出左下角代码,对角线上的元素已经打印完了,所以在这里从1开始
for (int i = 1; i <= n - 1; i++) {//每次都是从第0列开始,所以需要col = 0,然后row col 同时自增
row = i;
col = 0;
while (row >= 0 && row < n && col >= 0 && col < n) {
System.out.print(a[row][col]+" ");
row++;
col++;
}
}
}
}
知识点51
getFragmentManager 、getSupportFragmentManager 与getChildFragmentManager三者之间的区别?
首先getFragmentManager 、getSupportFragmentManager这个两个FragmentManager用的最多,Fragment是安卓3.0以后引入的API,FragmentManager是管理Fragment的片段管理器,
简单点说如果你的项目是运行在安卓系统3.0以后的版本(高版本)就用getFragmentManager来得到FragmentManager,那么问题来了,如果你要运行在安卓系统3.0之前的版本(低版本)能运行吗?答案当然是不行滴!!!
会报错,如下图所示!
那么我就想运行在低版本的手机上该怎么办呢?安卓为我们提供了一个向下兼容的包—-android.support.v4。这时我们获取片段管理器就需要用getSupportFragmentManager这个方法了,然后修改你自己写的继承Fragment的类 修改导包 android.app.Fragment—>android.support.v4.app.Fragment
然后在你切换Fragment的Activity改为继承FragmentActivity ,最后运行项目即可。
了解完上面两个FragmentManager之后我们再来看看getChildFragmentManager, 当Fragment嵌套Fragment时(也就是说你的Fragment里面还有子Fragment),里面需要用getChildFragmentManager来获得FragmentManager。
知识点52
知识点53
知识点54
知识点55
这是一份很详细的 Retrofit 2.0 使用教程(含实例讲解)
知识点56
彻底搞懂startActivityForResult在FragmentActivity和Fragment中的异同
知识点57
知识点58
知识点59
分享一篇非常好的Tinker热更新的文章
知识点60
Android:JNI 与 NDK到底是什么?(含实例教学)
知识点61
不用构造函数可以创建对象吗?
Java创建对象的几种方式(重要):
(1) 用new语句创建对象,这是最常见的创建对象的方法。
(2) 运用反射手段,调用java.lang.Class或者java.lang.reflect.Constructor类的newInstance()实例方法。
(3) 调用对象的clone()方法。
(4) 运用反序列化手段,调用java.io.ObjectInputStream对象的 readObject()方法。
(1)和(2)都会明确的显式的调用构造函数 ;(3)是在内存上对已有对象的影印,所以不会调用构造函数 ;(4)是从文件中还原类的对象,也不会调用构造函数。
知识点62
ArrayList list = new ArrayList(20);中的list扩充几次?
这里有点迷惑人,大家都知道默认ArrayList的长度是10个,所以如果你要往list里添加20个元素肯定要扩充一次(扩充为原来的1.5倍),但是这里显示指明了需要多少空间,所以就一次性为你分配这么多空间,也就是不需要扩充了。
知识点63
下面哪些是对称加密算法?
常用的对称加密算法有:DES、3DES、RC2、RC4、AES
常用的非对称加密算法有:RSA、DSA、ECC
使用单向散列函数的加密算法:MD5、SHA
知识点64
知识点65
Android中Activity四种启动模式和taskAffinity属性详解
知识点66
存在继承的情况下,代码执行顺序: 父类(静态变量、静态初始化块)-> 子类(静态变量、静态初始化块) -> 父类(变量、初始化块)-> 父类(构造器)-> 子类(变量、初始化块)-> 子类(构造器)
知识点67
String, StringBuilder, StringBuffer, CharSequence的区别?
CharSequence:表示一个字符序列,是一个接口。String, StringBuilder, StringBuffer都实现了它。
String:final修饰的不可变类型,所以线程安全。
StringBuilder:可变对象,未同步,不是线程安全的,不支持高并发情况,适用于单线程情况。效率很高,但不是线程安全的。
StringBuffer:可变对象,使用synchronized同步,线程安全,支持高并发情况,适用于多线程情况。效率低于StringBuilder。
string使用+相加的过程,JVM会先创建一个StringBuilder对象,通过StringBuilder.append()方法将str3与str4的值拼接,然后通过StringBuilder.toString()返回一个String对象赋值
当通过语句str.intern()调用intern()方法后,JVM 就会在当前类的常量池中查找是否存在与str等值的String,若存在则直接返回常量池中相应String的引用;若不存在,则会在常量池中创建一个等值的String,然后返回这个String在常量池中的引用。因此,只要是等值的String对象,使用intern()方法返回的都是常量池中同一个String引用,所以,这些等值的String对象通过intern()后使用==是可以匹配的。
知识点68
生产者消费者队列实现:
public class MyBlockingQueue<T> {
private int size;
private final LinkedList<T> mList = new LinkedList<>();
private final Object lock = new Object();
public MyBlockingQueue(int size) {
this.size = size;
}
public void put(T task) throws InterruptedException {
synchronized (lock) {
while (mList.size() >= size) {
lock.wait();
}
mList.addLast(task);
lock.notifyAll();
}
}
public T take() throws InterruptedException {
synchronized (lock) {
while (mList.size() <= 0) {
lock.wait();
}
T task = mList.getFirst();
lock.notifyAll();
return task;
}
}
}
知识点69
知识点70
知识点71
知识点72
知识点73
用ScrollView嵌套RecyclerView,RecyclerView会不显示,如果你遇到了这个问题,请往下看。
解决办法:首先,将嵌套在外层的ScrollView的宽、高均设置为match_parent,然后,在ScrollView的布局中加入一句话:
android:fillViewport=“true”
(ScrollView如果宽高为wrap_content,或者ScrollView中没有android:fillViewport=“true”,RecyclerView会不显示)。
知识点74
知识点75
Java的四种引用,强弱软虚,用到的场景
-
强引用:
如果一个对象具有强引用,它就不会被垃圾回收器回收。即使当前内存空间不足,JVM也不会回收它,而是抛出 OutOfMemoryError 错误,使程序异常终止。如果想中断强引用和某个对象之间的关联,可以显式地将引用赋值为null,这样一来的话,JVM在合适的时间就会回收该对象
-
软引用:
在使用软引用时,如果内存的空间足够,软引用就能继续被使用,而不会被垃圾回收器回收,只有在内存不足时,软引用才会被垃圾回收器回收。
-
弱引用:
具有弱引用的对象拥有的生命周期更短暂。因为当 JVM 进行垃圾回收,一旦发现弱引用对象,无论当前内存空间是否充足,都会将弱引用回收。不过由于垃圾回收器是一个优先级较低的线程,所以并不一定能迅速发现弱引用对象
-
虚引用:
顾名思义,就是形同虚设,如果一个对象仅持有虚引用,那么它相当于没有引用,在任何时候都可能被垃圾回收器回收。
-
使用场景:
利用软引用和弱引用解决OOM问题:用一个HashMap来保存图片的路径和相应图片对象关联的软引用之间的映射关系,在内存不足时,JVM会自动回收这些缓存图片对象所占用的空间,从而有效地避免了OOM的问题
通过软可及对象重获方法实现Java对象的高速缓存:比如我们创建了一Employee的类,如果每次需要查询一个雇员的信息。哪怕是几秒中之前刚刚查询过的,都要重新构建一个实例,这是需要消耗很多时间的。我们可以通过软引用和 HashMap 的结合,先是保存引用方面:以软引用的方式对一个Employee对象的实例进行引用并保存该引用到HashMap 上,key 为此雇员的 id,value为这个对象的软引用,另一方面是取出引用,缓存中是否有该Employee实例的软引用,如果有,从软引用中取得。如果没有软引用,或者从软引用中得到的实例是null,重新构建一个实例,并保存对这个新建实例的软引用
知识点76
Android ScrollView监听滑动到顶部和底部的两种方式(你可能不知道的细节)
知识点77
Fragment嵌套Fragment使用存在的一些BUG以及解决方法
知识点78
Android_性能优化之ViewPager加载成百上千高清大图oom解决方案
知识点79
知识点80
题目:给最外层的rootview,把这个根视图下的全部button背景设置成红色,手写代码,不许用递归
知识点81
知识点82
知识点83
知识点84
知识点85
知识点86
知识点87
知识点88
知识点89
知识点90
知识点91
知识点92
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虚拟机基于寄存器架构,数据的访问通过寄存器间直接传递,这样的访问方式比基于栈方式快的多.
知识点93
关于android中的内存和性能优化,推荐郭霖的系列博文
知识点94
int和Integer的区别?
知识点95
java中equals,hashcode和==的区别
知识点96
Android子线程真的不能更新UI么
为什么不能在子线程中更新UI
知识点97
简单来说,onReceivedError只有在遇到不可用的(unrecoverable)错误时,才会被调用)。
比如,当WebView加载链接www.baidu.com时,"不可用"的情况有可以包括有:
没有网络连接
连接超时
找不到页面www.baidus.com
而下面的情况则不会被报告:
网页内引用其他资源加载错误,比如图片、css不可用
js执行错误
持续更新中,欢迎关注……
如果你觉得我的文章对你有帮助,并且希望帮助更多人,欢迎分享并关注我的微信公众号“Android开发的奥秘”,或扫描识别下方的二维码,我会不定期的分享给大家更多有用的资讯,谢谢!