Android内存泄漏

参考博客1
参考博客2

内存泄漏本质

程序在申请内存后,当该内存不需再使用;但却无法被释放&归还给程序的现象

  • 忘记释放分配的内存
  • 应用不需要某对象时,该对象仍然保持强引用状态

进程优先级

  1. 前台进程(与用户正在交互的进程)
  2. 可见进程
  3. 服务进程
  4. 后台进程
  5. 空进程


当进程空间紧张时,会按进程优先级低到高的顺序自动回收进程

最容易引发内存泄漏

  • Activity
  • Service
  • Application
  • BroadcastReceiver,ContentProvider虽然不在Context继承树,但其内部会持有Context

常见的内存泄漏原因&解决方法

(1)集合类

List objectList = new ArrayList();

for ( int i = 0 ; i < 10 ; i++){

Object o = new Object();

objectList.add(0);

o = null;

}

虽释放了集合元素引用的本身:o = null;

但集合List仍然引用该对象,故垃圾回收器GC,依然不可回收该对象


解决方案:
objectList.clear(); objectList = null;

(2)static关键字修饰的成员变量
使用Application的Context代替Context,使用弱引用
(3)非静态内部类/匿名类
解决方法:
1⃣️将非静态内部类设置为静态内部类
2⃣️将内部类抽取出来封装成一个单例
3⃣️尽量避免非静态内部类所创建的实例 = 静态
(4) 资源对象使用后未关闭


原理:很可能Activity作为Context传递给某些类,Activity生命周期结束后,某些类仍然存活并保持着该Activity的引用,Activity是重量级对象,却保持引用无法被回收

避免内存泄漏的一些方法

  1. 尽量避免使用static变量
  2. 如果逻辑上允许,则使用弱引用(使用WeakReference类实现,可能会有空指针异常)
  3. 继续使用static变量,记得在Activity被销毁的时候,释放static变量引用
  4. 非静态内部类、匿名内部类持有外部类的引用(原理:编译的时候,编译器会自动为内部类构造方法中加上外部类的引用)
  5. Handler内存泄漏问题(使用弱引用)

垃圾回收算法

  1. 标记-清除 算法
  2. 复制 算法
  3. 标记-整理 算法
  4. 分代收集 算法

LiveData

参考博客:https://blog.csdn.net/chuyouyinghe/article/details/125554330

数据丢失举例:https://blog.csdn.net/cpcpcp123/article/details/121960528

setValue

只能在主线程中执行,否则会抛错

1
2
3
4
5
6
7
@MainThread
protected void setValue(T value) {
assertMainThread("setValue");
mVersion++;
mData = value;
dispatchingValue(null);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void dispatchingValue(@Nullable ObserverWrapper initiator) {
if (mDispatchingValue) {
mDispatchInvalidated = true;
return;
}
mDispatchingValue = true;
do {
mDispatchInvalidated = false;
if (initiator != null) {
considerNotify(initiator);
initiator = null;
} else {
for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
considerNotify(iterator.next().getValue());
if (mDispatchInvalidated) {
break;
}
}
}
} while (mDispatchInvalidated);
mDispatchingValue = false;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private void considerNotify(ObserverWrapper observer) {
if (!observer.mActive) {
return;
}
// Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.
//
// we still first check observer.active to keep it as the entrance for events. So even if
// the observer moved to an active state, if we've not received that event, we better not
// notify for a more predictable notification order.
if (!observer.shouldBeActive()) {
observer.activeStateChanged(false);
return;
}
if (observer.mLastVersion >= mVersion) {
return;
}
observer.mLastVersion = mVersion;
observer.mObserver.onChanged((T) mData);
}
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
private abstract class ObserverWrapper {
final Observer<? super T> mObserver;
boolean mActive;
int mLastVersion = START_VERSION;

ObserverWrapper(Observer<? super T> observer) {
mObserver = observer;
}

abstract boolean shouldBeActive();

boolean isAttachedTo(LifecycleOwner owner) {
return false;
}

void detachObserver() {
}

void activeStateChanged(boolean newActive) {
if (newActive == mActive) {
return;
}
// immediately set active state, so we'd never dispatch anything to inactive
// owner
mActive = newActive;
changeActiveCounter(mActive ? 1 : -1);
if (mActive) {
dispatchingValue(this);
}
}
}
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
class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
@NonNull
final LifecycleOwner mOwner;

LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
super(observer);
mOwner = owner;
}

@Override
boolean shouldBeActive() {
return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
}

@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();
if (currentState == DESTROYED) {
removeObserver(mObserver);
return;
}
Lifecycle.State prevState = null;
while (prevState != currentState) {
prevState = currentState;
activeStateChanged(shouldBeActive());
currentState = mOwner.getLifecycle().getCurrentState();
}
}

@Override
boolean isAttachedTo(LifecycleOwner owner) {
return mOwner == owner;
}

@Override
void detachObserver() {
mOwner.getLifecycle().removeObserver(this);
}
}

postValue

1
2
3
4
5
6
7
8
9
10
11
protected void postValue(T value) {
boolean postTask;
synchronized (mDataLock) {
postTask = mPendingData == NOT_SET;
mPendingData = value;
}
if (!postTask) {
return;
}
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
1
2
3
4
5
6
7
8
9
10
11
12
private final Runnable mPostValueRunnable = new Runnable() {
@SuppressWarnings("unchecked")
@Override
public void run() {
Object newValue;
synchronized (mDataLock) {
newValue = mPendingData;
mPendingData = NOT_SET;
}
setValue((T) newValue);
}
};
1
2
3
4
5
6
7
8
9
10
11
12
@Override
public void postToMainThread(Runnable runnable) {
if (mMainHandler == null) {
synchronized (mLock) {
if (mMainHandler == null) {
mMainHandler = createAsync(Looper.getMainLooper());
}
}
}
//noinspection ConstantConditions
mMainHandler.post(runnable);
}

团队成员激励

本文来自面试题:如何激励一个团队的成员

美国著名的心理学家亚伯拉罕·马斯洛强调人有两类需求,一类是生物属性带给我们的低级需求,比如延续生命;一类是进化后逐渐显现的高级需求,比如释放潜能。马斯洛提出了著名的“五层次需求理论”:

  • 第一,生理需求(首先要活着)
  • 第二,安全需求(活着不够,不能整天担惊受怕)
  • 第三,社会需求(安全后还需要融入集体,寻求一种归属感)
  • 第四,尊重需求(不仅需要被团队接受,还需要被尊重)
  • 第五,自我实现需求(让自己的潜能完全释放)

如果我们把这些需求来划分团队成员,再按其所处的不同层次施与不同的激励方式肯定会比我之前单一的奖励方式要好。这也是我们做为技术出身的人走到管理时经常会搞不清楚方向的地方,即我们其实并没有真正理解团队及团队中各个人的需求,而是只按自己看得到的需求进行处理。

按层次用不同的方式激励组员

按马斯洛的说法,其实我们的激励政策要分级别制定。对于处于第一阶段的员工,他需要的是“生理需求”,首先要解决生存问题(如租房、吃饭),方式很简单给钱就行。和处于这个阶段的员工谈成长、梦想是不现实的。第二阶段安全需求,对于那些工资不按时发放,没有业界相应的福利和保险,员工不知道第二天公司还能不能再运行下去的,去和员工谈激励谈公司的愿景、文化或者价值观什么,完全是侮辱别人的智商。

好在程员序的收入及行业发展在国内来说,其本上还是可以满足这两个层次的需求,如果达不到的话,其时也没有必要再和员工讲其他的激励措施了,因为他们会直接用脚投票(换一家能满足的公司)。所以,我们可以改进的地方在第三到第五层次。对于第三层次,社会需求,其实一个公司或者一个团队都是一个“社会”,组织一些Team Building之类的活动,增加成员之家的交流是很效的手段。

第四层决的尊重需求需要管理层和团队一起努力,“尊重”往往从上到下会有更明显的效果(比如老板给员工一个肯定)。而处理第五层次的人,已经不需要你去激励了,他们会自我激励!

之前和朋友聊管理团队时,有位朋友提过这样的场景:他们组里90后的程序员家庭条件都比较好,如家里都给买好房了,生活上的压力并没有80后那么大,虽然是刚毕业没几年,但他们把编程开发仅仅当做一份工作。只要能达到一般的要求就不再努力了,也不愿意在业务和技术上投入更多的精力,有时候你都为他们的职业发展担忧。对于这类型的成员来说,去激发他的社会需求和尊重需求是最有效的手段。

ViewModel

ViewModel在Activity重建后为啥不会被销毁

ViewModel存在于HolderFragment中,而HolderFragment因为设置了setRetainInstance(true),利用此特性可以在Activity重建时存活下来

  • ViewModel以键值对的形式存在Activity或Fragment的HolderFragment的ViewModelStore的HashMap中
  • 一个Activity或者Fragment可以有很多个ViewModel
  • 一个Activity或者Fragment只会有一个HolderFragment
  • Activity或者Fragment的HolderFragment会保存在全局单例的HolderFragmentManager的HashMap中,在Activity或者Fragment销毁的时候会移除HashMap中对应的value
  • 因为ViewModel是以Activity或者Fragment为存在基础,所以ViewModel可以在当前Activity和Fragment中实现数据共享,前提是传入相同的key值

Android Studio常用Git操作

1.回滚已经commit的代码

对于commit(不是push)的代码如何回滚,这块对于刚转到git的开发者还是比较陌生的。今天我来说下这块的内容:

按照惯例,先上图,下图是还没有commit任何代码的push界面,这个时候是没有commit任何内容的:

img

然后我修改了string.xml的内容,并且执行了commit操作:

img

接下来就要说如何进行revert commit了,通过选中工程,点击右键,在弹出的列表中选择Git–>Repository–>Reset HEAD…,有些人可能会说我右键弹出来的列表中没有发现有Git的选项,这个可以配置的,请戳这:

在这里插入图片描述

这边对上图进行分析:

Git Root:选择你要revert的目录

Current Branch:你当前在哪个branch

Reset Type:

Soft:选择这个模式意思是仅仅撤销commit而已,不影响你本地的任何文件,也不影响(index)缓存区的任何文 件。

Hard:不仅撤销commit的内容,还将本地的文件指向你commit前的版本,同时index也会指向commit前的版本。

Mixed:这个模式从我个人角度来看其实还是有点模糊的。从我的角度理解就是只是回滚index,其余的都不变。

但是这边跟大家说下,如果你把HEAD后面加个“1”(HEAD1),这里的数字代表的是次数,比如你commit了三次, 你写1,就是回滚最后一次提交的,如果是2,就是后两次提交的都回滚。这时候你会发现它的功能和soft 这个模式一样了。

我把官方的说法贴上来吧:Resets the index but not the working tree (i.e., the changed files are preserved but not marked for commit) and reports what has not been updated. This is the default action.

Validate:这个选项是看当前会影响到的文件有哪些,具体影响是什么。

我这边执行下soft模式的,执行完成后,看下图,刚commit的记录已经没了:

img

当然你想用命令行也是一样的,

1
git reset [--soft | --mixed [-N] | --hard] HEAD~X   X:代表次数

3.git reset soft,hard,mixed之区别深解

​ 其实就是–soft 、–mixed以及–hard是三个恢复等级。使用–soft就仅仅将头指针恢复,已经add的缓存以及工作空间的所有东西都不变。如果使用–mixed,就将头恢复掉,已经add的缓存也会丢失掉,工作空间的代码什么的是不变的。如果使用–hard,那么一切就全都恢复了,头变,aad的缓存消失,代码什么的也恢复到以前状态

4.cherry pick使用(合并某次提交)

  1. 需求:一般项目有分支 master,假如还有分支 branch1 和 branch2。如果我们的成员 A 在分支 branch1 上面修复了 bug2020 并提交,我们想在自己管理的分支 branch2 也同步这个修改提交的代码。

    git cherry-pick在Android Stduio中怎么使用?

    git cherry-pick在Android Stduio中怎么使用?

    git cherry-pick在Android Stduio中怎么使用?

  2. 2

    首先我们需要在 Android Studio 中先把分支切换到我们需要进行修改的分支 branch2 上面。(在需要修改的分支进行代码提交操作)

    git cherry-pick在Android Stduio中怎么使用?

    git cherry-pick在Android Stduio中怎么使用?

  3. 3

    然后在 Android Studio 的下面找到 Version Control 内置版本控制工具,切换到分支 branch1,并且找到想要同步的提交。在 Version Control 中找到我们要同步的提交,选中后鼠标右键点击 Cherry-Pick。

    git cherry-pick在Android Stduio中怎么使用?

    git cherry-pick在Android Stduio中怎么使用?

  4. 4

    然后就会出现下面这样的图片,接着我们就发现我们需要的代码已经修改到我们的 branch2 上面了,接着我们点击提交就可以了。这样我们就顺利的完成了把 branch1 上修改同步到 branch2 的操作。

    git cherry-pick在Android Stduio中怎么使用?

  5. 5

    当然有时候可能没有那么顺利,我们中间有可能遇到一些冲突,这个时候需要我们手动处理了冲突之后,才能够继续下一步。

    git cherry-pick在Android Stduio中怎么使用?

    git cherry-pick在Android Stduio中怎么使用?

5.git常用命令速查表

640?wx_fmt=png

杂项

代理的分类

(1)透明代理:远程服务器可以知道你使用了代理,并且透明也会将本机真实的IP发送至远程服务器,因为无法达到隐藏身份的目的

(2)匿名代理:远程服务器可以知道你使用了代理,但不知道你的真实IP

(3)高匿代理:隐藏了真实的IP,同时访问对象也不知道你使用了代理,因此隐蔽度最高

代理网站

西祠代理/块代理/89代理/豌豆代理/66代理/蘑菇代理/熊猫代理/飞蚁代理/站大爷(http://ip.zdaye.com/dayProxy/ip/312659.html)

测试本机IP

http://httpbin.org/get

http://www.ip138.com/

爬虫平台

https://www.spiderflow.org/

https://github.com/ssssssss-team/spider-flow

创意内容平台

小点

网易云音乐iframe分享方法