PS:以记笔记的方式,汇总一些android基础知识。
1、android 四种启动模式
1、standard 标准模式
2、singleTop 栈顶复用模式,activity 不在栈顶时跟standard一样,复用时调用onNewIntent()方法
3、singleInstance 栈内复用模式,复用时清除栈内该Activity上层的所有Activity(clear top),调用onNewIntent()方法
4、singleTask 独自放在一个任务栈里面,相当于自己就是一个应用,有自己的上下文activity
2、activity或fragment被系统销毁时数据缓存
activity 缓存: onSaveInstanceState() 和 onRestoreInstanceState()
fragment缓存: onSaveInstanceState() 和 onViewStateRestored()
3、为什么在Service中创建子线程而不是再Activity中?
因为Activity很难对Thread进行控制,当Activity销毁后就没有任何办法获取之前创建的子线程的实例了。然而Service就不一样,任何Activity都可以与之关联,又能获取原有Service穿件的Bindle实例。从而更好的控制后台任务。
4、Service两种启动方法及区别
startService : activity启动service之后,service并不和activity产生关联
bindService : service和activity的context关联并启动,生命周期与context一致。
5、广播(BroadCast Receiver)的两种注册状态(静态注册和动态注册)
静态注册:在Manifest里面注册,应用退出后依然在工作
动态注册:代码中注册,应用退出后无法工作
6、如何保持Android进程不被杀死
1、1px Activity进程保活,应用退出后台时启动1像素的Activity,占用内存小,降低被回收的概率
2、提升Service 进程优先级
3、onDestory的时候重启service
7、android动画
1、tween 补间动画:通过制定View的初末状态和变化时间、方式,对View的内容完成一系列的变化方式来实现。Alpha , Scale , Translate , Rotate 。
2、frame 帧动画:AnimationDrawable 控制animation-list xml布局
3、PropertyAnimation 属性动画
4、SVG矢量动画
8、自定义View 过程:onMeasure() onLayout() onDraw()
1、自定义属性的声明和获取:
a、分析需要的自定义属性
b、在res/values/attr.xml定义声明
c、在layout文件中进行使用
d、在View的构造方法中进行获取
2、测量onMeasure
3、布局onLayout(ViewGroup)
4、绘制onDraw
5、onTouchEvent
6、onInterceptTouchEvent(ViewGroup) :事件分发
7、状态的恢复和保存
9、android事件分发机制
dispatchTouchEvent()
onFilterTouchEventForSecurity() ( onInterceptTouchEvent())
onTouch()
onTouchEvent()
onClick()
View中的dispatchTouchEvent()方法源码如下:
public boolean dispatchTouchEvent(MotionEvent event) {
// If the event should be handled by accessibility focus first
......
boolean result = false;
......
if (onFilterTouchEventForSecurity(event)) {
if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
result = true;
}
//noinspection SimplifiableIfStatement
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
if (!result && onTouchEvent(event)) {
result = true;
}
}
......
return result;
}
可以看到onFilterTouchEventSecurity() 为开关,并且当onTouch()方法返回True时,之后不会调用onTouchEvent()方法,并且进一步查看TextView的onTouchEvent()源码可以看到里面调用了onClick()方法。
如下log可以看到调用的相关方法,推测出事件的传递机制:
09-04 14:45:11.519 2998-2998/com.sleep.sunshine I/TestTouchEventLayout: dispatchTouchEvent: 1
09-04 14:45:11.519 2998-2998/com.sleep.sunshine I/TestTouchEventLayout: onFilterTouchEventForSecurity:
09-04 14:45:11.519 2998-2998/com.sleep.sunshine I/TestTouchEventButton: dispatchTouchEvent: 1
09-04 14:45:11.519 2998-2998/com.sleep.sunshine I/TestTouchEventButton: onFilterTouchEventForSecurity: 1
09-04 14:45:11.519 2998-2998/com.sleep.sunshine I/TestTouchEventActivity: onTouch: 1
09-04 14:45:11.519 2998-2998/com.sleep.sunshine I/TestTouchEventButton: onTouchEvent: 1
09-04 14:45:11.521 2998-2998/com.sleep.sunshine I/TestTouchEventActivity: onClick:
10、Handler消息机制
Handler
Looper :轮询器,每个线程只能有一个Looper
MessageQueue:消息队列,储存消息,先进先出
Message
11、子线程中创建handler
1、方法1:利用子线程的Looper
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
handler.sendEmptyMessage(1);
Looper.loop();
}
}).start();
2、方法2:获取主线程的looper,或者说是UI线程的looper
new Thread(new Runnable() {
public void run() {
Handler handler = new Handler(Looper.getMainLooper()){ // 区别在这!!!!
@Override
public void handleMessage(Message msg) {
Toast.makeText(getApplicationContext(), "handler msg", Toast.LENGTH_LONG).show();
}
};
handler.sendEmptyMessage(1);
};
}).start();
12、内存泄漏
原因:生命周期长的对象持有生命周期短的对象的引用导致该对象不能及时回收,导致内存泄漏。
容易导致内存泄露的地方:
1、单例对象初始化时持有外部对象的引用(其他类或者context)
2、非静态内部类默认持有外部类的引用,这时候创建内部类的静态实例会造成内存泄露
3、匿名内部类和异步线程:匿名内部类会持有外部类的引用
4、handler造成内存泄漏:延时消息可能会导致handler与activity生命周期不一致。(用弱引用)
5、cursor 、bitmap 、broadcast 、soeket 、stream 等及时关闭
handler弱引用示例:
private static class MyHandler extends Handler {
private final WeakReference mActivity;
public MyHandler(SampleActivity activity) {
mActivity = new WeakReference(activity);
}
@Override
public void handleMessage(Message msg) {
SampleActivity activity = mActivity.get();
if (activity != null) {
// ...
}
}
}
private final MyHandler mHandler = new MyHandler(this);
/**
* Instances of anonymous classes do not hold an implicit
* reference to their outer class when they are "static".
*/
private static final Runnable sRunnable = new Runnable() {
@Override
public void run() { /* ... */ }
};
13、Android 性能优化
1、合理管理内存:
a、节制使用service
b、界面不可见时释放内存,bitmap不要加载不需要的分辨率等
c、少用依赖注入框架
2、分析内存使用情况,解决内存泄露等问题:LeakCanary等工具
3、高性能编码,如:
a、字符串拼接时用StringBuffer或者StringBuilder ,少用String
b、多用系统API
c、ArrayList 遍历传统方式最快,数组等用增强for循环最快等
d、避免在内部调用Getters/Setters方法
4、布局优化、避免复杂布局或者嵌套布局
14、取消Android TextView 内边距’
android:includeFontPadding=“false"
15、Android弹出软键盘后点击键盘外面关闭
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
View v = getCurrentFocus();
if (isShouldHideInput(v, ev)) {
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) {
imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
}
}
return super.dispatchTouchEvent(ev);
}
// 必不可少,否则所有的组件都不会有TouchEvent了
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
public boolean isShouldHideInput(View v, MotionEvent event) {
if (v != null && (v instanceof EditText)) {
int[] leftTop = {0, 0};
//获取输入框当前的location位置
v.getLocationInWindow(leftTop);
int left = leftTop[0];
int top = leftTop[1];
int bottom = top + v.getHeight();
int right = left + v.getWidth();
if (event.getX() > left && event.getX() < right
&& event.getY() > top && event.getY() < bottom) {
// 点击的是输入框区域,保留点击EditText的事件
return false;
} else {
//使EditText触发一次失去焦点事件
v.setFocusable(false);
// v.setFocusable(true); //这里不需要是因为下面一句代码会同时实现这个功能
v.setFocusableInTouchMode(true);
return true;
}
}
return false;
}
16、gson 解析List
Gson gson = new Gson();
Type founderListType = new TypeToken>(){}.getType();
List founderList = gson.fromJson(founderJson, founderListType);
17、RecyclerView加载更多
/**
* 提前多少个item触发加载更多
*/
private int visibleThreshold = 2;
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
int itemCount = layoutManager.getItemCount();
int lastPosition = layoutManager.findLastVisibleItemPosition();
//如果当前不是正在加载更多,并且到了该加载更多的位置,加载更多。
if (!isLoading && (lastPosition >= (itemCount - visibleThreshold))) {
loadMore();
}
}
});
18、Android5.0水波纹效果ripple实现
// 波纹有边界
android:background="?android:attr/selectableItemBackground"
// 波纹超出边界
android:background="?android:attr/selectableItemBackgroundBorderless"
19、混淆代码后崩溃日志中不显示行号的问题
原因是在混淆代码时默认会去掉class文件中的调试信息(源码的行号、源文件信息等),需要在混淆配置文件中申明保持这些信息:
-renamesourcefileattribute SourceFile
-keepattributes SourceFile,LineNumberTable