1、Activity生命周期、fragment生命周期。
详细:http://blog.csdn.net/javazejian/article/details/51932554
http://blog.csdn.net/lmj623565791/article/details/37970961/
http://blog.csdn.net/asdf717/article/details/51383750
Fragment 生命周期
对比图
2、Activity异常生命周期。
详细:http://blog.csdn.net/javazejian/article/details/51932562
主要分以下两种情况:
1、相关的系统配置发生改变导致Activity被杀死并重新创建(一般指横竖屏切换)
我们正常启动Activity时,onCreate,onStart,onResume方法都会依次被回调,而如果我们此时把竖屏的Activity人为的调整为横屏,我们可以发现onPause,onSaveInstanceState,onStop,onDestroy,onCreate,onStart,onRestoreInstanceState,onResume依次被调用,单从调用的方法我们就可以知道,Activity先被销毁后再重新创建,其异常生命周期如下:现在异常生命周期的流程我们大概也就都明白,但是onSaveInstanceState和onRestoreInstanceState方法是干什么用的呢?实际上这两个方法是系统自动调用的,当系统配置发生变化后,Activity会被销毁,也就是onPause,onStop,onDestroy会被依次调用,同时因为Activity是在异常情况下销毁的,Android系统会自动调用onSaveInstanceState方法来保存当前Activity的状态信息,因此我们可以在onSaveInstanceState方法中存储一些数据以便Activity重建之后可以恢复这些数据,当然这个方法的调用时机必须在onStop方法之前,也就是Activity停止之前。至跟onPause方法的调用时机可以随意。而通过前面的Log信息我们也可以知道当Activity被重新创建之后,系统还会去调用onRestoreInstanceState方法,并把Activity销毁时通过onSaveInstanceState方法保存的Bundle对象作为参数同时传递给onRestoreInstanceState和onCreate方法,因此我们可以通过onRestoreInstanceState和onCreate方法来判断Activity是否被重新创建,倘若被重建了,我们就可以对之前的数据进行恢复。从Log信息,我们可以看出onRestoreInstanceState方法的调用时机是在onStart之后的。这里有点需要特别注意,onSaveInstanceState和onRestoreInstanceState只有在Activity异常终止时才会被调用的,正常情况是不会调用这两个方法的。
到这里大家可能还有一个疑问,onRestoreInstanceState和onCreate方法都可以进行数据恢复,那到底用哪个啊?其实两者都可以,两者的区别在于,onRestoreInstanceState方法一旦被系统回调,其参数Bundle一定不为空,无需额外的判断,而onCreate的Bundle却不一定有值,因为如果Activity是正常启动的话,Bundle参数是不会有值的,因此我们需要额外的判断条件,当然虽说两者都可以数据恢复,但更倾向于onRestoreInstanceState方法。
最后还有点我们要知道的是,在onSaveInstanceState方法和onRestoreInstanceState方法中,android系统会自动帮我们恢复一定的数据,如当前Activity的视图结构,文本框的数据,ListView的滚动位置等,这些View相关的状态系统都会帮我们恢复,这是因为每个View也有onSaveInstanceState方法和onRestoreInstanceState方法
2、内存不足导致低优先级的Activity被杀死
接下来我们来聊聊内存不足导致低优先级的Activity被杀死,然后重建,其实也不用聊了,其数据的存储过程和恢复过程跟上面的情况基本没差。我们还是继续聊聊Activity被杀死的情况吧,当系统内存不足的时候,系统就会按照一定的优先级去杀死目标Acitivity的进程来回收内存,并且此时Activity的onSaveInstanceState方法会被调用来存储数据,并在后续Activity恢复时调用onRestoreInstanceState方法来恢复数据,所以为了Activity所在进程尽量不被杀死,我们应该尽量让其保持高的优先级。
3、Activity的几种LaunchMode及使用场景。
详细:http://blog.csdn.net/javazejian/article/details/52071885
standard 模式
这是默认模式,每次激活Activity时都会创建Activity实例,并放入任务栈中。使用场景:大多数Activity。
singleTop 模式
如果在任务的栈顶正好存在该Activity的实例,就重用该实例( 会调用实例的 onNewIntent() ),否则就会创建新的实例并放入栈顶,即使栈中已经存在该Activity的实例,只要不在栈顶,都会创建新的实例。使用场景如新闻类或者阅读类App的内容页面。
singleTask 模式
如果在栈中已经有该Activity的实例,就重用该实例(会调用实例的 onNewIntent() )。重用时,会让该实例回到栈顶,因此在它上面的实例将会被移出栈。如果栈中不存在该实例,将会创建新的实例放入栈中。使用场景如浏览器的主界面。不管从多少个应用启动浏览器,只会启动主界面一次,其余情况都会走onNewIntent,并且会清空主界面上面的其他页面。
singleInstance 模式
在一个新栈中创建该Activity的实例,并让多个应用共享该栈中的该Activity实例。一旦该模式的Activity实例已经存在于某个栈中,任何应用再激活该Activity时都会重用该栈中的实例( 会调用实例的 onNewIntent() )。其效果相当于多个应用共享一个应用,不管谁激活该 Activity 都会进入同一个应用中。使用场景如闹铃提醒,将闹铃提醒与闹铃设置分离。singleInstance不要用于中间页面,如果用于中间页面,跳转会有问题,比如:A -> B (singleInstance) -> C,完全退出后,在此启动,首先打开的是B。
4、如何理解Activity,View,Window三者之间的关系?
详细:http://blog.csdn.net/huachao1001/article/details/51866287
这个问题真的很不好回答。所以这里先来个算是比较恰当的比喻来形容下它们的关系吧。Activity像一个工匠(控制单元),Window像窗户(承载模型),View像窗花(显示视图)LayoutInflater像剪刀,Xml配置像窗花图纸。
1:Activity构造的时候会初始化一个Window,准确的说是PhoneWindow。
2:这个PhoneWindow有一个“ViewRoot”,这个“ViewRoot”是一个View或者说ViewGroup,是最初始的根视图。
3:“ViewRoot”通过addView方法来一个个的添加View。比如TextView,Button等
4:这些View的事件监听,是由WindowManagerService来接受消息,并且回调Activity函数。比如onClickListener,onKeyDown等。
5、View的绘制流程
详细:http://blog.csdn.net/yanbober/article/details/46128379/
http://blog.csdn.net/jdsjlzx/article/details/52224219
http://www.cnblogs.com/jycboy/p/6219915.html
http://blog.csdn.net/xyz_lmn/article/details/20385049
measure过程
layout过程
draw过程
6、Touch事件的传递(分发)机制
详细:http://blog.csdn.net/carson_ho/article/details/54136311
http://blog.csdn.net/morgan_xww/article/details/9372285
http://www.open-open.com/lib/view/open1422428386548.html
http://blog.csdn.net/jdsjlzx/article/details/52221308
http://blog.csdn.net/lmj623565791/article/details/39102591
http://www.jianshu.com/p/7daf0feb6c2d
publicbooleandispatchTouchEvent(MotionEventev); //用来分派event
publicbooleanonInterceptTouchEvent(MotionEventev);//用来拦截event
publicbooleanonTouchEvent(MotionEventev);//用来处理event
其中Activity和View控件(TextView)拥有分派和处理事件方法,View容器(LinearLayout)具有分派,拦截,处理事件方法。这里也有个比喻:领导都会把任务向下分派,一旦下面的人把事情做不好,就不会再把后续的任务交给下面的人来做了,只能自己亲自做,如果自己也做不了,就只能告诉上级不能完成任务,上级又会重复他的过程。另外,领导都有权利拦截任务,对下级隐瞒该任务,而直接自己去做,如果做不成,也只能向上级报告不能完成任务。
7、Android中的几种动画
详细:http://blog.csdn.net/yanbober/article/details/46481171
1、Tween Animation(补间动画),也叫 View Animation(视图动画)
AlphaAnimation<alpha>
放置在res/anim/目录下渐变透明度动画效果
RotateAnimation<rotate>
放置在res/anim/目录下画面转移旋转动画效果
ScaleAnimation<scale>
放置在res/anim/目录下渐变尺寸伸缩动画效果
TranslateAnimation<translate>
放置在res/anim/目录下画面转换位置移动动画效果
AnimationSet<set>
放置在res/anim/目录下一个持有其它动画元素alpha、scale、translate、rotate或者其它set元素的容器
AnimationSet继承自Animation,是上面四种的组合容器管理类,没有自己特有的属性,他的属性继承自Animation,所以特别注意,当我们对set标签使用Animation的属性时会对该标签下的所有子控件都产生影响。
特别特别注意:补间动画执行之后并未改变View的真实布局属性值。切记这一点,譬如我们在Activity中有一个Button在屏幕上方,我们设置了平移动画移动到屏幕下方然后保持动画最后执行状态呆在屏幕下方,这时如果点击屏幕下方动画执行之后的Button是没有任何反应的,而点击原来屏幕上方没有Button的地方却响应的是点击Button的事件。
View动画只能够为View添加动画,如果想为非View对象添加动画须自己实现;且View动画支持的种类很少;尤其是他改变的是View的绘制效果,View的属性没有改变,其位置与大小都不变; View动画代码量少,使用简单方便。
2、Frame Animation(帧动画),也称Drawable Animation(Drawable 动画)
它允许你实现像播放幻灯片一样的效果(若干图片循环播放),这种动画的实质其实是Drawable,所以这种动画的XML定义方式文件一般放在res/drawable/目录下。
3、Property Animation(属性动画)
Android 3.0以后引入了属性动画,属性动画可以轻而易举的实现许多View动画做不到的事,上面也看见了,View动画无非也就做那几种事情,别的也搞不定,而属性动画就可以的,譬如3D旋转一张图片。其实说白了,你记住一点就行,属性动画实现原理就是修改控件的属性值实现的动画。
弥补了View动画的缺陷,你可以为一个对象的任意属性添加动画,对象自己的属性会被真的改变;当对象的属性变化的时候,属性动画会自动刷新屏幕;属性动画改变的是对象的真实属性,而且属性动画不止用于View,还可以用于任何对象。
8、Android中跨进程通讯的4种方式
详细:http://blog.csdn.net/happyq/article/details/46682285
http://blog.csdn.net/zhuangyalei/article/details/50515039
http://www.jianshu.com/p/61f681145cd8
1:访问其他应用程序的Activity
2:Content Provider
3:广播(Broadcast)
4:AIDL服务
4种方式正好对应于android系统中4种应用程序组件:Activity、Content Provider、Broadcast和Service。其中Activity可以跨进程调用其他应用程序的Activity;Content Provider可以跨进程访问其他应用程序中的数据(以Cursor对象形式返回),当然,也可以对其他应用程序的数据进行增、删、改操 作;Broadcast可以向android系统中所有应用程序发送广播,而需要跨进程通讯的应用程序可以监听这些广播;Service和Content Provider类似,也可以访问其他应用程序中的数据,但不同的是,Content Provider返回的是Cursor对象,而Service返回的是Java对象,这种可以跨进程通讯的服务叫AIDL服务。
其中Activity可以跨进程调用其他应用程序的Activity;ContentProvider可以访问其他应用程序返回的Cursor对象;Broadcast采用的是被动接收的方法,也就是说,客户端只能接收广播数据,而不能向发送广播的程序发送信息。AIDL Service可以将程序中的某个接口公开,这样在其他的应用程序中就可以象访问本地对象一样访问AIDL服务对象了。这4种跨进程通讯的方式可以应用在 不同的场合,例如,在需要显示可视化的界面时可以用Activity,需要返回记录集时可以用ContentProvider。至于在应用程序中具体要用 到哪一种或几种方式进行跨进程通讯,读者可以根据实际情况进行选择。
9、Android异步消息处理机制(并发编程)(Looper , Handler , Message关系)
详细:http://www.jianshu.com/p/9fe944ee02f7
http://blog.csdn.net/lfdfhl/article/details/53332936
http://blog.csdn.net/jdsjlzx/article/details/51407911
http://blog.csdn.net/lmj623565791/article/details/38377229/
http://blog.csdn.net/yanbober/article/details/45936145
http://blog.csdn.net/guolin_blog/article/details/11711405
一个Looper对应着一个消息队列以及当前线程。
当收到消息Message后系统会将其存入消息队列中等候处理。至于Looper,它在Android的消息机制中担负着消息轮询的职责,它会不间断地查看MessageQueue中是否有新的未处理的消息;若有则立刻处理,若无则进入阻塞。
- 一个线程对应一个Looper
- 一个Looper对应一个消息队列
- 一个线程对应一个消息队列
- 线程,Looper,消息队列三者一一对应
所以,在一个子线程中使用Handler的方式应该是这样的:
为什么我们平常在MainActivity中使用Handler时并没有调用Looper.prepare()也没有报错呢?这是因为UI线程是主线程,系统会自动调用Looper.prepareMainLooper()方法创建主线程的Looper和消息队列MessageQueue
Android异步消息机制中主要涉及到:Thread、Handler、MessageQueue、Looper,在整个机制中它们扮演着不同的角色也承担着各自的不同责任。
-
Thread
负责业务逻辑的实施。
线程中的操作是由各自的业务逻辑所决定的,视具体情况进行。 -
Handler
负责发送消息和处理消息。
通常的做法是在主线程中建立Handler并利用它在子线程中向主线程发送消息,在主线程接收到消息后会对其进行处理 -
MessageQueue
负责保存消息。
Handler发出的消息均会被保存到消息队列MessageQueue中,系统会根据Message距离触发时间的长短决定该消息在队列中位置。在队列中的消息会依次出队得到相应的处理。 -
Looper
负责轮询消息队列。
Looper使用其loop()方法一直轮询消息队列,并在消息出队时将其派发至对应的Handler.
为了更好地理解这几者的相互关系及其作用,请参见如下示图
优化该问题的方式有多种,在此展示其中一种比较好的实现方式:
- 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
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 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
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
看到这段代码,我们发现了一个陌生的东西WeakReference。什么是WeakReference呢?它有什么特点呢?
从JDK1.2开始,Java把对象的引用分为四种级别,这四种级别由高到低依次为:强引用、软引用、弱引用和虚引用。
-
强引用
我们一般使用的就是强引用,垃圾回收器一般都不会对其进行回收操作。当内存空间不足时Java虚拟机宁愿抛出OutOfMemoryError错误使程序异常终止,也不会回收具有强引用的对象 -
软引用
如果一个对象具有软引用(SoftReference),在内存空间足够的时候GC不会回收它,如果内存空间不足了GC就会回收这些对象的内存空间。 -
弱引用
如果一个对象具有弱引用(WeakReference),那么当GC线程扫描的过程中一旦发现某个对象只具有弱引用而不存在强引用时不管当前内存空间足够与否GC都会回收它的内存。由于垃圾回收器是一个优先级较低的线程,所以不一定会很快发现那些只具有弱引用的对象。为了防止内存溢出,在处理一些占用内存大而且生命周期较长的对象时候,可以尽量使用软引用和弱引用. -
虚引用
虚引用(PhantomReference)与其他三种引用都不同,它并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。所以,虚引用主要用来跟踪对象被垃圾回收器回收的活动,在一般的开发中并不会使用它
嗯哼,在了解这四种引用之后我们继续分析刚才的代码。在该示例中做了如下主要操作:
-
将Handler和Runnable定义为Activity的静态内部类
这两者定义为静态内部类后它们就不再持有外部类(Activity)的引用,具体代码请参见示例中BetterHandler和BetterRunnable的实现
-
在BetterHandler内使用弱引用WeakReference持有Activity
在完成这两步操作之后,我们再来分析刚才的场景:进入该Activity后在20秒以内的任意时间旋转屏幕导致Activity重新绘制。此时,消息持有Handler的引用,但Handler对象不再持有Activity的强引用,所以系统可以回收该Activity从而避免了内存泄露的发生。对于这样的做法,可能有的人觉得不是特别好理解,那我再换一种直白的通俗的描述:如果直接将Activity传入BetterHandler中并且不对其使用WeakReference那么它依然是一个强引用,这和之前未优化的代码相比是没有任何差别的。假若把Activity传进BetterHandler之后并用WeakReference“包裹”了它,使之不再是一个强引用而变成了一个弱引用。当Activity发生重绘时,GC发现对于这个Activity没有强引用而只存在一个弱引用,那么系统就将其回收。
-
在handleMessage( )对Activity进行非空判断
因为Activity可能已经被GC回收,所以在处理消息时要判断Activity是否为null,即if(activityWeakReference.get() != null)从而避免异常的发生。
Handler和Looper,MessageQueue之间是什么关系?
Looper和MessageQueue是线程中的概念,但是线程默认是没有Looper和MessageQueue的,我们需要手动去设置他们,当一个线程有了Looper和MessageQueue后,就可以关联一个Handler,我们再通过这个Handler,就可以从别的线程中发送消息给这个线程来执行。
我们给一个线程配置了Looper和MessageQueue后,当有消息通过Handler发送到本线程后,就会加入到MessageQueue中,然后Looper会不断的循环从MessageQueue中取出消息,然后放到Handler的handleMessage()中去执行。
如何给一个线程配置一个Looper和MessageQueue:给一个线程配置一个Looper和MessageQueue很简单,只需要调用Looper.prepare()和Looper.loop()就可以了。
10、Android热修复的原理
详细:http://blog.csdn.net/caihongdao123/article/details/52051799
http://blog.csdn.net/qq_31530015/article/details/51785228?locationNum=11
http://mp.weixin.qq.com/s?__biz=MzA3Mjk1MjA4Nw==&mid=400452659&idx=1&sn=841b49b875ec3b307f261ed52a7d9c4e&scene=23&srcid=1119JWRt0adNwGxTHiyok460#rd
当一个App发布之后,突然发现了一个严重bug需要进行紧急修复,这时候公司各方就会忙得焦头烂额:重新打包App、测试、向各个应用市场和渠道换包、提示用户升级、用户下载、覆盖安装。有时候仅仅是为了修改了一行代码,也要付出巨大的成本进行换包和重新发布。
这时候就提出一个问题:有没有办法以补丁的方式动态修复紧急Bug,不再需要重新发布App,不再需要用户重新下载,覆盖安装?
阿里巴巴热修复框架:
AndFix
AndFix提炼精华,轻便、简洁(详见源码)的完成了热修复方案。完美的支持了Android 2.3到6.0系统,以及x86框架,机型覆盖率广。 根据修复后的apk、有bug的apk两者之间的差别生成一个.apatch格式的补丁文件(比较的包不能加固,加固后会修复失败),再通过请求接口下载这个补丁文件,再解析补丁文件,识别需要修复的含有注解的方法,最后进行修复。(解析apatch->解析dex->加载类->识别含有MethodReplace注解的方法->根据原方法签名已经新方法smali描述进行hook并替换。)
10、Android内存泄露及管理
详细:https://juejin.im/entry/56d64b9e816dfa005943a55c
为什么内存会泄露?
堆内存中的长生命周期的对象持有短生命周期对象的强/软引用,尽管短生命周期对象已经不再需要,但是因为长生命周期对象持有它的引用而导致不能被回收,这就是Java中内存泄露的根本原因。
Android中常见的内存泄漏汇总
1、单例造成的内存泄漏
由于单例的静态特性使得单例的生命周期和应用的生命周期一样长,这就说明了如果一个对象已经不需要使用了,而单例对象还持有该对象的引用,那么这个对象将不能被正常回收,这就导致了内存泄漏。2、非静态内部类创建静态实例造成的内存泄漏
因为非静态内部类默认会持有外部类的引用,而又使用了该非静态内部类创建了一个静态的实例,该实例的生命周期和应用的一样长,这就导致了该静态实例一直会持有该Activity的引用,导致Activity的内存资源不能正常回收。正确的做法为:
将该内部类设为静态内部类。
3、Handler造成的内存泄漏
由于mHandler是Handler的非静态匿名内部类的实例,所以它持有外部类Activity的引用,我们知道消息队列是在一个Looper线程中不断轮询处理消息,那么当这个Activity退出时消息队列中还有未处理的消息或者正在处理消息,而消息队列中的Message持有mHandler实例的引用,mHandler又持有Activity的引用,所以导致该Activity的内存资源无法及时回收,引发内存泄漏
创建一个静态Handler内部类,然后对Handler持有的对象使用弱引用,这样在回收时也可以回收Handler持有的对象,这样虽然避免了Activity泄漏,不过Looper线程的消息队列中还是可能会有待处理的消息,所以我们在Activity的Destroy时或者Stop时应该移除消息队列中的消息,更准确的做法如下:
public class MainActivity extends AppCompatActivity {
private MyHandler mHandler = new MyHandler(this);
private TextView mTextView ;
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 onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = (TextView)findViewById(R.id.textview);
loadData();
}
private void loadData() {
//...request
Message message = Message.obtain();
mHandler.sendMessage(message);
}
@Override
protected void onDestroy() {
super.onDestroy();
mHandler.removeCallbacksAndMessages(null);
}
}
4、线程造成的内存泄漏
对于线程造成的内存泄漏,也是平时比较常见的,异步任务和Runnable都是一个匿名内部类,因此它们对当前Activity都有一个隐式引用。如果Activity在销毁之前,任务还未完成,
那么将导致Activity的内存资源无法回收,造成内存泄漏。正确的做法还是使用静态内部类的方式,如下:
static class MyAsyncTask extends AsyncTask {
private WeakReference weakReference;
public MyAsyncTask(Context context) {
weakReference = new WeakReference<>(context);
}
@Override
protected Void doInBackground(Void... params) {
SystemClock.sleep(10000);
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
MainActivity activity = (MainActivity) weakReference.get();
if (activity != null) {
//...
}
}
}
static class MyRunnable implements Runnable{
@Override
public void run() {
SystemClock.sleep(10000);
}
}
//——————
new Thread(new MyRunnable()).start();
new MyAsyncTask(this).execute();
5、
资源未关闭造成的内存泄漏
对于使用了BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap等资源的使用,应该在Activity销毁时及时关闭或者注销,否则这些资源将不会被回收,造成内存泄漏。
11、Activity 与 Fragment 通信
详细:https://juejin.im/entry/56a87b2b2e958a0051906227
1、
接口方案
我想这种方案是大家最易想到,使用最多的一种方案吧,具体上代码:
//MainActivity实现MainFragment开放的接口
public class MainActivity extends FragmentActivity implements FragmentListener{
@override
public void toH5Page(){ }
...其他处理代码省略
}
public class MainFragment extends Fragment{
public FragmentListener mListener;
//MainFragment开放的接口
public static interface FragmentListener{
//跳到h5页面
void toH5Page();
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
//对传递进来的Activity进行接口转换
if(activity instance FragmentListener){
mListener = ((FragmentListener)activity);
}
}
...其他处理代码省略
}
这种方案应该是既能达到复用,又能达到很好的可维护性,并且性能也是杠杠的。但是唯一的一个遗憾是假如项目很大了,Activity与Fragment的数量也会增加,这时候为每对Activity与Fragment交互定义交互接口就是一个很头疼的问题(包括为接口的命名,新定义的接口相应的Activity还得实现,相应的Fragment还得进行强制转换)。
2、使用handler
3、使用广播
4、使用EventBus
12、android系统的一些bug
1、fragment的那些坑
(1)在support 23.2.0以下的支持库中,对于在嵌套子Fragment的startActivityForResult ()
,会发现无论如何都不能在onActivityResult()
中接收到返回值,只有最顶层的父Fragment才能接收到,这是一个support v4库的一个BUG,不过在前两天发布的support 23.2.0库中,已经修复了该问题,嵌套的子Fragment也能正常接收到返回数据了!
(2)getActivity()空指针
大多数情况下的原因:你在调用了getActivity()时,当前的Fragment已经onDetach()
了宿主Activity。
比如:你在pop了Fragment之后,该Fragment的异步任务仍然在执行,并且在执行完成后调用了getActivity()方法,这样就会空指针。
解决办法:
在Fragment基类里设置一个Activity mActivity的全局变量,在onAttach(Activity activity)
里赋值,使用mActivity代替getActivity()
,保证Fragment即使在onDetach
后,仍持有Activity的引用(有引起内存泄露的风险,但是异步任务没停止的情况下,本身就可能已内存泄漏,相比Crash,这种做法“安全”些),即:
protected Activity mActivity;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
this.mActivity = activity;
}
/**
* 如果你用了support 23的库,上面的方法会提示过时,有强迫症的小伙伴,可以用下面的方法代替
*/
@Override
public void onAttach(Context context) {
super.onAttach(context);
this.mActivity = (Activity)context;
}
(3)
Fragment重叠异常
add()
了几个Fragment,使用show()、hide()
方法控制,比如微信、QQ的底部tab等情景,如果你什么都不做的话,在“内存重启”后回到前台,app的这几个Fragment界面会重叠。 标准写法如下:通过getFragments()
可以获取到当前FragmentManager管理的栈内所有Fragment。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity);
TargetFragment targetFragment;
HideFragment hideFragment;
if (savedInstanceState != null) { // “内存重启”时调用
List<Fragment> fragmentList = getSupportFragmentManager().getFragments();
for (Fragment fragment : fragmentList) {
if(fragment instanceof TartgetFragment){
targetFragment = (TargetFragment)fragment;
}else if(fragment instanceof HideFragment){
hideFragment = (HideFragment)fragment;
}
}
// 解决重叠问题
getFragmentManager().beginTransaction()
.show(targetFragment)
.hide(hideFragment)
.commit();
}else{ // 正常时
targetFragment = TargetFragment.newInstance();
hideFragment = HideFragment.newInstance();
// 这里add时,tag可传可不传
getFragmentManager().beginTransaction()
.add(R.id.container)
.add(R.id,container,hideFragment)
.hide(hideFragment)
.commit();
}
}
13、Android 屏幕适配
超文本传输协议HTTP协议被用于在Web浏览器和服务器之间传递信息。
-
HTTP协议以明文方式发送内容,不提供任何方式的数据加密,如果攻击者截取了Web浏览器和网站服务器之间的传输报文,就可以直接读懂其中的信息,因此HTTP协议不适合传输一些敏感信息,比如信用卡号、密码等。
-
为了解决HTTP协议的这一缺陷,需要使用另一种协议:安全套接字层超文本传输协议HTTPS。为了数据传输的安全,HTTPS在HTTP的基础上加入了SSL协议,
SSL依靠证书来验证服务器的身份,并为浏览器和服务器之间的通信加密。
区别
HTTPS和HTTP的区别主要为以下五点:
-
https 用的 443 端口, http 用的 80 端口
-
https协议需要到ca申请证书,一般免费证书很少,需要交费。
-
http是超文本传输协议,信息是明文传输,https 则是具有安全性的ssl加密传输协议。
-
http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
-
http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。
1.懒汉(加了锁,线程安全的懒汉)
缺点:当有多个线程频繁调用时,会造成很大的性能损失
//懒汉
public class Singleton {
private static Singleton singleton;
private Singleton() {
}
public static synchronized Singleton getSingleton() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
2.饿汉
缺点:性能差点,不是调用方法时才调用,类加载时就初始化了实例
//饿汉
public class Singleton {
private static final Singleton singleton = new Singleton();
private Singleton () {
}
public static Singleton getSingleton() {
return singleton;
}
}
3.静态内部类
相对于饿汉、懒汉,静态内部类方式实际上是结合了饿汉式和懒汉式的优点的一种方式,最好的方法
即保证了单一的唯一性,又线程安全,又是调用方法时才初始化实例
//静态内部类
public class Singleton {
private static class SingletonHolder {
private static final Singleton singleton = new Singleton();
}
private Singleton() {
}
public static Singleton getSingleton() {
return SingletonHolder.singleton;
}
}
4.枚举
//枚举
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}
5.double-lock(双重锁)
//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;
}
}
20、
Android APP内存优化之图片优化
优点
1:提高开发效率
2:减少代码量
缺点
1:代码可读性差
2:增加新人学习成本
3:加速触及65535方法数问
24、Android App 的设计架构:MVC、MVP、MVVM
详细:https://juejin.im/entry/56ebb4ad5bbb50004c440972在Android中,我们有三种方式来实现视频的播放:
1、使用其自带的播放器。指定Action为ACTION_VIEW,Data为Uri,Type为其MIME类型。
2、使用VideoView来播放。在布局文件中使用VideoView结合MediaController来实现对其控制。
3、使用MediaPlayer类和SurfaceView来实现,这种方式很灵活。
1、调用其自带的播放器:
Uri uri = Uri.parse(Environment.getExternalStorageDirectory().getPath()+"/Test_Movie.m4v");
//调用系统自带的播放器
Intent intent = new Intent(Intent.ACTION_VIEW);
Log.v("URI:::::::::", uri.toString());
intent.setDataAndType(uri, "video/mp4");
startActivity(intent);
2、使用VideoView来实现:
Uri uri = Uri.parse(Environment.getExternalStorageDirectory().getPath()+"/Test_Movie.m4v");
VideoView videoView = (VideoView)this.findViewById(R.id.video_view);
videoView.setMediaController(new MediaController(this));
videoView.setVideoURI(uri);
videoView.start();
videoView.requestFocus();
3、使用MediaPlayer: