学习福利
【Android 详细知识点思维脑图(技能树)】
其实Android开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。
虽然 Android 没有前几年火热了,已经过去了会四大组件就能找到高薪职位的时代了。这只能说明 Android 中级以下的岗位饱和了,现在高级工程师还是比较缺少的,很多高级职位给的薪资真的特别高(钱多也不一定能找到合适的),所以努力让自己成为高级工程师才是最重要的。
这里附上上述的面试题相关的几十套字节跳动,京东,小米,腾讯、头条、阿里、美团等公司19年的面试题。把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。
由于篇幅有限,这里以图片的形式给大家展示一小部分。
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
问题描述:在启动频繁的Activity中,为了避免重复创建相同的数据资源,会在Activity内部创建一个非静态内部类的单例,每次启动Activity时都会使用该单例的数据。若非静态内部类所创建的实例的生命周期等于应用的生命周期,会因非静态内部类持有外部类的引用,而导致外部类无法释放,最终造成内存泄露。
public class MainActivity extends AppCompatActivity {
private static TestResource mResource = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(mManager == null){
mManager = new TestResource();
}
//…
}
private class TestResource {
//…
}
}
以上代码就在Activity内部创建了一个非静态内部类的单例,每次启动Activity时都会使用该单例的数据,这样虽然避免了资源的重复创建,却会造成内存泄漏。非静态内部类会持有外部类的引用,而又使用非静态内部类创建静态实例,该实例和应用的生命周期一样长,这就导致该静态实例一直会持有Activity的引用,导致Activity的内存资源不能回收。
解决办法:
1.将非静态内部类改为静态内部类(静态内部类默认不持有外部类的引用)
2.该内部类抽取出来封装成一个单例。若需使用Context,建议使用 Application 的 Context
2、Handler造成的内存泄漏
问题描述:Handler的使用造成的内存泄漏问题应该最为常见,在处理网络任务或者封装一些请求回调等api都应该会借助Handler来处理,对于Handler的使用代码编写一不规范即有可能造成内存泄漏,如下示例:
public class MainActivity extends AppCompatActivity {
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
//…doSomething
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
loadData();
}
private void loadData(){
//…request http
Message message = Message.obtain();
mHandler.sendMessage(message);
}
}
这种创建Handler的方式会造成内存泄漏,由于mHandler是Handler的非静态匿名内部类的实例,所以它持有外部类Activity的引用,消息队列MessageQueue在一个Looper线程中不断轮询处理消息,那么当这个Activity退出时,消息队列中还有未处理的消息Message或者正在处理消息,而消息队列中的Message持有mHandler实例的引用,mHandler又持有Activity的引用,所以导致该Activity的内存资源无法及时回收,引发内存泄漏。
解决办法:
1.创建一个静态Handler内部类,然后对Handler持有的对象使用弱引用,这样在回收时也可以回收Handler持有的对象,这样虽然避免了Activity泄漏。
2.Looper线程的消息队列中还是可能会有待处理的消息,所以在Activity的Destroy时或者Stop时应该移除消息队列中的消息。
public class MainActivity extends AppCompatActivity {
private MyHandler mHandler;
private TextView mTextView
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = (TextView)findViewById(R.id.textview);
mHandler = new MyHandler(this);
loadData();
}
private void loadData() {
//…request http
Message message = Message.obtain();
mHandler.sendMessage(message);
}
private static class MyHandler extends Handler {
private WeakReference reference;
public MyHandler(Context context) {
reference = new WeakReference<>(context);
}
@Override
public void handleMessage(Message msg) {
MainActivity activity = (MainActivity) reference.get();
if(activity != null){
activity.mTextView.setText(“请求成功”);
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
mHandler.removeCallbacksAndMessages(null);
}
}
3、多线程造成的内存泄漏(AsyncTask、实现Runnable接口、继承Thread类)
问题描述:工作线程Thread类属于非静态内部类/匿名内部类,运行时默认持有外部类的引用。当工作线程运行时,若外部类MainActivity需销毁,由于此时工作线程类实例持有外部类的引用,将使得外部类无法被垃圾回收器(GC)回收,从而造成内存泄露。
对于线程造成的内存泄漏,也是平时比较常见的,如下这两个示例:
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void… params) {
SystemClock.sleep(10000);
return null;
}
}.execute();
new Thread(new Runnable() {
@Override
public void run() {
SystemClock.sleep(10000);
}
}).start();
解决办法:
-
静态内部类–静态内部类不持有外部类的引用,从而使得工作线程实例不会持有外部类引用。
-
当外部类结束生命周期时,强制结束线程–使得工作线程实例的生命周期与外部类的生命周期同步。使用静态内部类 & 强制结束线程 的方式,如下:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(new MyRunnable()).start();
}
static class MyRunnable implements Runnable{
@Override
public void run() {
SystemClock.sleep(10000);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
Thread.stop();// 外部类Activity生命周期结束时,强制结束线程
}
}
(三)资源未关闭造成的内存泄漏
问题描述:对于资源的使用(如 广播BraodcastReceiver、文件流File、数据库游标Cursor、图片资源Bitmap等),若在Activity销毁时无及时关闭或者注销这些资源,则这些资源将不会被回收,从而造成内存泄漏。
解决办法:
在Activity销毁时及时关闭或者注销资源
//广播BraodcastReceiver:注销注册
unregisterReceiver()
//文件流File:关闭流
InputStream/OutputStream.close()
//数据库游标cursor:使用后关闭游标
cursor.close()
//图片资源Bitmap:当它不再被使用时,应调用recycle()回收此对象的像素所占用的内存;最后再赋为null
Bitmap.recycle()
文末
面试:如果不准备充分的面试,完全是浪费时间,更是对自己的不负责!
不管怎么样,不论是什么样的大小面试,要想不被面试官虐的不要不要的,只有刷爆面试题题做好全面的准备,当然除了这个还需要在平时把自己的基础打扎实,这样不论面试官怎么样一个知识点里往死里凿,你也能应付如流啊
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
文末
面试:如果不准备充分的面试,完全是浪费时间,更是对自己的不负责!
不管怎么样,不论是什么样的大小面试,要想不被面试官虐的不要不要的,只有刷爆面试题题做好全面的准备,当然除了这个还需要在平时把自己的基础打扎实,这样不论面试官怎么样一个知识点里往死里凿,你也能应付如流啊
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!