初级~中级
0.Activity是什么?
Activity实际上只是一个与用户交互的接口而已。
1.Activity生命周期
1.1 Activity的4种状态
Active/Paused/Stopped/Killed
Activie:当前Activity正处于运行状态,指的是当前Activity获取了焦点。
paused:当前Activity正处于暂停状态,指的是当前Activity失去焦点,此时的Activity并没有被销毁,内存里面的成员变量,状态信息等仍然存在,当然这个Activity也仍然可见,但是焦点却不在它身上,比如被一个对话框形式的Activity获取了焦点,或者被一个透明的Activity获取了焦点,这都能导致当前的Activity处于paused状态。
stopped:与paused状态相似,stopped状态的Activity是完全不可见的,但是内存里面的成员变量,状态信息等仍然存在,但是也没有被销毁。
killed:已经被销毁的Activity才处于killed状态,它的内存里面的成员变量,状态信息等都会被一并回收。
1.2 Activity的生命周期分析
1.2.1 正常情况下的生命周期
Activity启动–>onCreate()–>onStart()–>onResume()
点击home键回到桌面–>onPause()–>onStop()
再次回到原Activity时–>onRestart()–>onStart()–>onResume()
退出当前Activity时–>onPause()–>onStop()–>onDestroy()
详细生命周期如下:
1.启动了一个Activity,通常是Intent来完成。启动一个Activity首先要执行的回调函数是onCreate(),通常在代码中你需要在此函数中绑定布局,绑定控件,初始化数据等做一些初始化的工作。
2.即将执行Activity的onStart()函数,执行之后Activity已经可见,但是还没有出现在前台,无法与用户进行交互。这个时候通常Activity已经在后台准备好了,但是就差执行onResume()函数出现在前台。
3.即将执行Activity的onResume()函数,执行之后Activity不止可见而且还会出现在前台,可以与用户进行交互啦。
4.由于Activity执行了onResume()函数,所以Activity出现在了前台。也就是Activity处于运行状态。
5.处于运行状态的Activity即将执行onPause()函数,什么情况下促使Activity执行onPause()方法呢?
[1]启动了一个新的Activity
[2]返回上一个Activity
可以理解为当需要其他Activity,当前的Activity必须先把手头的工作暂停下来,再来把当前的界面空间交给下一个需要界面的Activity,而onPause()方法可以看作是一个转接工作的过程,因为屏幕空间只有那么一个,每次只允许一个Activity出现在前台进行工作。通常情况下onPause()函数不会被单独执行,执行完onPause()方法后会继续执行onStop()方法,执行完onStop()方法才真正意味着当前的Activity已经退出前台,存在于后台。
6.Activity即将执行onStop()函数,在“5”中已经说得很清楚了,当Activity要从前台切换至后台的时候会执行,比如:用户点击了返回键,或者用户切换至其他Activity等。
7.当前的Activity即将执行onDestory()函数,代表着这个Activity即将进入生命的终结点,这是Activity生命周期中的最后一次回调生命周期,我们可以在onDestory()函数中,进行一些回收工作和资源的释放工作,比如:广播接收器的注销工作等。
8.执行完onDestory()方法的Activity接下来面对的是被GC回收,宣告生命终结。
9.很少情况下Activity才走“9”,网上一些关于对话框弹出后Activity会走“9”的说法,经过笔者验证,在某个Activity内弹出对话框并没有走“9”,所以网上大部分这样说法的文章要么是没验证,要么直接转载的,这个例子说明,实验出真知,好了,不废话了,那么什么情况下,Activity会走“9”呢?看看下面这位博主才是真的懂得“实验出真知”的人:
http://blog.csdn.net/a872822645/article/details/62217965
10.当用户在其他的Activity或者桌面回切到这个Activity时,这个Activity就会先去执行onRestart()函数,Restart有“重新开始”的意思,然后接下来执行onStart()函数,接着执行onResume()函数进入到运行状态。
11.在“10”中讲的很清楚了。
12.高优先级的应用急需要内存,此时处于低优先级的此应用就会被kill掉。
13.用户返回原Activity。
下面来着重说明一下Activity每个生命周期函数:
onCreate():
表示Activity正在被创建,这是Activity生命周期的第一个方法。通常我们程序员要在此函数中做初始化的工作,比如:绑定布局,控件,初始化数据等。
onStart():
表示Activity正在被启动,这时候的Activity已经被创建好了,完全过了准备阶段,但是没有出现在前台,需要执行onResume()函数才可以进入到前台与用户进行交互。
onResume():
表示Activitiy已经可见了,并且Activity处于运行状态,也就是Activity不止出现在了前台,而且还可以让用户点击,滑动等等操作与它进行交互。
onPause():
表示Activity正在暂停,大多数情况下,Activity执行完onPause()函数后会继续执行onStop()函数,造成这种函数调用的原因是当前的Activity启动了另外一个Activity或者回切到上一个Activity。还有一种情况就是onPause()函数被单独执行了,并没有附带执行onStop()方法,造成这种函数调用的原因很简单,就是当前Activity里启动了类似于对话框的东东。
onStop():
表示Activity即将停止,我们程序员应该在此函数中做一些不那么耗时的轻量级回收操作。
onRestart():
表示Activity正在重新启动。一般情况下,一个存在于后台不可见的Activity变为可见状态,都会去执行onRestart()函数,然后会继续执行onStart()函数,onResume()函数出现在前台并且处于运行状态。
onDestory():
表示Activity要被销毁了。这是Activity生命中的最后一个阶段,我们可以在onDestory()函数中做一些回收工作和资源释放等,比如:广播接收器的注销等。
1.2.2 异常情况下的生命周期
什么是异常情况呢?
情况1:资源相关的系统配置发生改变导致Activity被杀死并重新创建。
可以从图中看出当Activity发生意外的情况的时候,这里的意外指的就是系统配置发生改变,Activity会被销毁,其onPause,OnStop,onDestory函数均会被调用,同时由于Actiivty是在异常情况下终止的,系统会调用onSaveInstanceState来保存当前Activity状态。调用onSaveInstanceState的时机总会发生在onStop之前,至于会不会调用时机发生在onPause方法之前,那就说不定了,这个没有固定的顺序可言,正常情况下一般onSaveInstanceState不会被调用。当Activity被重新创建后,系统会调用onRestoreInstanceState,并且把Actiivty销毁时onSaveInstanceState方法所保存的Bundle对象作为参数传递给onRestoreInstanceState和onCreate方法。所以我们可以通过onRestoreInstanceState和onCreate方法来判断Actiivty是否被重建了,如果被重建了,那么我们就可以取出之前保存的数据并恢复,从时序上来看,onRestoreInstanceState的调用时机发生在onStart之后。
同时,在onSaveInstanceState和onRestoreInstanceState方法中,系统自动为我们做了一定的恢复工作。当Activity在异常情况下需要重新创建时,系统会默认为我们保存当前Activity的视图结构。当Activity在异常情况下需要重新创建时,系统会默认为我们保存当前Activity的视图结构,并且在Activity重启后为我们恢复这些数据,比如:文本框中用户输入的数据,ListView滚动的位置等,这些View相关的状态系统都能够默认为我们恢复。具体针对某一个特定的View系统 能为我们恢复哪些数据,我们可以查看View的源码。和Activity一样,每个View都有onSaveInstanceState和onRestoreInstanceState这两个方法,看一下它们的具体实现,就能知道系统能够自动为每个View恢复哪些数据。
关于保存和恢复View层次结构,系统的工作流程是这样的:
首先Activity被意外终止时,Activity会调用onSaveInstanceState去保存数据,然后Activity会委托Window去保存数据,接着Window在委托它上面的顶级容器去保存数据。顶级容器是一个ViewGroup,一般来说它很可能是DecorView。最后顶层容器再去一一通知它的子元素来保存数据,这样整个数据保存过程就完成了。可以发现,这是一个典型的委托思想,上层委托下层,父容器去委托子元素去处理一件事情,这种思想在Android中有很多应用,比如:View的绘制过程,事件分发等都是采用类似的思想。至于数据恢复过程也是类似的,这样就不再重复介绍了。
情况2:资源内存不足导致低优先级的Activity被杀死。
首先,Activity有优先级?你肯定怀疑,代码中都没设置过啊!优先级从何而来,其实这里的Activity的优先级是指一个Activity对于用户的重要程度,比如:正在与用户进行交互的Activity那肯定是最重要的。我们可以按照重要程度将Activity分为以下等级:
优先级最高: 与用户正在进行交互的Activity,即前台Activity。
优先级中等:可见但非前台的Activity,比如:一个弹出对话框的Activity,可见但是非前台运行。
优先级最低:完全存在与后台的Activity,比如:执行了onStop。
当内存严重不足时,系统就会按照上述优先级去kill掉目前Activity所在的进程,并在后续通过onSaveInstanceState和onRestoreInstanceState来存储和恢复数据。如果一个进程中没有四大组件的执行,那么这个进程将很快被系统杀死,因此,一些后台工作不适合脱离四大组件独立运行在后台中,这样进程更容易被杀死。比较好的方法就是将后台工作放入Service中从而保证进程有一定的优先级,这样就不会轻易地被系统杀死。
总结:
上面分析了系统的数据存储和恢复机制,我们知道,当系统配置发生改变之后,Activity会被重新创建,那么有没有办法不重新创建呢?答案是有的,接下来我们就来分析这个问题。系统配置中有很多内容,如果某项内容发生了该变后,我们不想系统重新创建Activity可以给Activity指定configChanges属性。比如我们不想让Actiivty在屏幕旋转的时候重新创建,就可以给configChanges属性添加一些值,请继续往下看。
1.2.3 一些特殊情况下的生命周期分析
Activity的横竖屏切换
与横竖屏生命周期函数有关调用的属性是"android:configChanges",关于它的属性值设置影响如下:
- orientation:消除横竖屏的影响
- keyboardHidden:消除键盘的影响
- screenSize:消除屏幕大小的影响
当我们设置Activity的android:configChanges属性为orientation或者orientation|keyboardHidden或者不设置这个属性的时候,它的生命周期会走如下流程:
刚刚启动Activity的时候:
onCreate
onStart
onResume
由竖屏切换到横屏:
onPause
onSaveInstanceState //这里可以用来横竖屏切换的保存数据
onStop
onDestroy
onCreate
onStart
onRestoreInstanceState//这里可以用来横竖屏切换的恢复数据
onResume
横屏切换到竖屏:
onPause
onSaveInstanceState
onStop
onDestroy
onCreate
onStart
onRestoreInstanceState
onResume
当我们设置Activity的android:configChanges属性为orientation|screenSize或者orientation|screenSize|keyboardHidden
刚刚启动Activity的时候:
onCreate
onStart
onResume
由竖屏切换到横屏:
什么也没有调用
由横屏切换到竖屏:
什么也没有调用
而且需要注意一点的是设置了orientation|screenSize属性之后,在进行横竖屏切换的时候调用的方法是onConfigurationChanged(),而不会回调Activity的各个生命周期函数;
当然在显示中我们可以屏蔽掉横竖屏的切换操作,这样就不会出现切换的过程中Activity生命周期重新加载的情况了,具体做法是,在Activity中加入如下语句:
android:screenOrientation="portrait" 始终以竖屏显示
android:screenOrientation="landscape" 始终以横屏显示
如果不想设置整个软件屏蔽横竖屏切换,只想设置屏蔽某个Activity的横竖屏切换功能的话,只需要下面操作:
Activity.this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);以竖屏显示
Activity.this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);以横屏显示
最后提一点,当你横竖屏切换的时候,如果走了销毁Activity的流程,那么需要保存当前和恢复当前Activity的状态的话,我们可以灵活运用onSaveInstanceState()方法和onRestoreInstanceState()方法。
什么时候Activity单独走onPause()不走onStop()?
关于这个特殊情况,笔者在上面的生命周期图解析的时候,贴了一个链接,这里主要是检验你是否会了这个问题的答案,这里笔者就不贴答案了,答案全在那个链接里,你会了吗?
什么时候Activity不执行onDestory()
栈里面的第一个没有销毁的activity会执行ondestroy方法,其他的不会执行。
比如说:从mainactivity跳转到activity-A(或者继续从activity-A再跳转到activity-B),这时候,从后台强杀,只会执行mainactivity的onDestroy方法,activity-A(以及activity-B)的onDestroy方法都不会执行,注意笔者这里说的是当前应用为焦点应用时才会如此,如果切换到后台去了,当前此应用不再是焦点应用,如果再强杀该应用,那么栈中所有的Activity都会执行onDestory,注意区别,这里也是感谢网友的测试验证出的结论;
1.3 进程重要等级和Activity的关系
前台>可见>服务>后台>空
前台:与当前用户正在交互的Activity所在的进程。
可见:Activity可见但是没有在前台所在的进程。
服务:Activity在后台开启了Service服务所在的进程。
后台:Activity完全处于后台所在的进程。
空:没有任何Activity存在的进程,优先级也是最低的。
2.Activity的启动模式
2.1 Android任务栈
任务栈与Activity的启动模式密不可分,它是用来存储Activity实例的一种数据结构,Activity的跳转以及回跳都与这个任务栈有关。详情请看下面的Activity的启动模式。
2.2 启动模式
Activity的启动模式,你在初学期间一定很熟悉了吧!不管你是否熟悉还是不熟悉,跟随笔者的思路把Activity的启动模式整理一遍:
问题1:Activity为什么需要启动模式?
问题2:Activity的启动模式有哪些?特性如何
问题3:如何给Activity选择合适的启动模式
问题1:Activity为什么需要启动模式?
我们都知道启动一个Activity后,这个Activity实例就会被放入任务栈中,当点击返回键的时候,位于任务栈顶层的Activity就会被清理出去,当任务栈中不存在任何Activity实例后,系统就回去回收这个任务栈,也就是程序退出了。这只是对任务栈的基本认识,深入学习,笔者会在之后文章中提到。那么问题来了,既然每次启动一个Activity就会把对应的要启动的Activity的实例放入任务栈中,假如这个Activity会被频繁启动,那岂不是会生成很多这个Activity的实例吗?对内存而言这可不是什么好事,明明可以一个Activity实例就可以应付所有的启动需求,为什么要频繁生成新的Activity实例呢?杜绝这种内存的浪费行为,所以Activity的启动模式就被创造出来去解决上面所描述的问题。
问题2:Activity的启动模式有哪些?特性如何
Activity的启动模式有4种,分别是:standard,singleTop,singleTask和singleInstance。下面一一作介绍:
1.系统默认的启动模式:Standard
标准模式,这也是系统的默认模式。每次启动一个Activity都会重新创建一个新的实例,不管这个实例是否存在。被创建的实例的生命周期符合典型情况下的Activity的生命周期。在这种模式下,谁启动了这个Activity,那么这个Activity就运行在启动它的那个Activity的任务栈中。比如Activity A启动了Activity B(B是标准模式),那么B就会进入到A所在的任务栈中。有个注意的地方就是当我们用ApplicationContext 去启动standard模式的Activity就会报错,这是因为standard模式的Actiivty默认会进入启动它的Activity所属的任务栈中,但是由于非Activity类型的Context(如ApplicationContext)并没有所谓的任务栈,所以这就会出现错误。解决这个问题的方法就是为待启动的Activity指定FLAG_ACTIVITY_NEW_TASK标记位,这样启动的时候就会为它创建一个新的任务栈,这个时候启动Activity实际上以singleTask模式启动的,读者可以自己仔细体会。
2.栈顶复用模式:SingleTop
在这种模式下,如果新的Activity已经位于任务栈的栈顶,那么此Activity不会被重新创建,同时它的onNewIntent方法被回调,通过此方法的参数我们可以取出当前请求的信息。需要注意的是,这个Activity的onCreate,onStart不会被系统调用,因为它并没有发生改变。如果新的Activity已经存在但不是位于栈顶,那么新的Activity仍然会重新重建。举个例子,假设目前栈内的情况为ABCD,其中ABCD为四个Activity,A位于栈低,D位于栈顶,这个时候假设要再次启动D,如果D的启动模式为singleTop,那么栈内的情况依然为ABCD;如果D的启动模式为standard,那么由于D被重新创建,导致栈内的情况为ABCDD。
3.栈内复用模式:SingTask
这是一种单例实例模式,在这种模式下,只要Activity在一个栈中存在,那么多次启动此Activity都不会重新创建实例,和singleTop一样,系统也会回调其onNewIntent。具体一点,当一个具有singleTask模式的Activity请求启动后,比如Activity A,系统首先寻找任务栈中是否已存在Activity A的实例,如果已经存在,那么系统就会把A调到栈顶并调用它的onNewIntent方法,如果Activity A实例不存在,就创建A的实例并把A压入栈中。举几个栗子:
- 比如目前任务栈S1的情况为ABC,这个时候Activity D以singleTask模式请求启动,其所需的任务栈为S2,由于S2和D的实例均不存在,所以系统会先创建任务栈S2,然后再创建D的实例并将其投入到S2任务栈中。
- 另外一种情况是,假设D所需的任务栈为S1,其他情况如同上面的例子所示,那么由于S1已经存在,所以系统会直接创建D的实例并将其投入到S1。
- 如果D所需的任务栈为S1,并且当前任务栈S1的情况为ADBC,根据栈内复用的原则,此时D不会重新创建,系统会把D切换到栈顶并调用其onNewIntent方法,同时由于singleTask默认具有clearTop的效果,会导致栈内所有在D上面的Activity全部出栈,于是最终S1中的情况为AD。
通过以上3个例子,你应该能比较清晰地理解singleTask的含义了。
4.单实例模式:SingleInstance
这是一种加强的singleTask模式,它除了具有singleTask模式所有的特性外,还加强了一点,那就是具有此种模式的Activity只能单独位于一个任务栈中,换句话说,比如Activity A是singleInstance模式,当A启动后,系统会为它创建一个新的任务栈,然后A独自在这个新的任务栈中,由于栈内复用的特性,后续的请求均不会创建新的Activity,除非这个独特的任务栈被系统销毁了。
对于SingleInstance,面试时你有说明它的以下几个特点:
(1)以singleInstance模式启动的Activity具有全局唯一性,即整个系统中只会存在一个这样的实例。
(2)以singleInstance模式启动的Activity在整个系统中是单例的,如果在启动这样的Activiyt时,已经存在了一个实例,那么会把它所在的任务调度到前台,重用这个实例。
(3)以singleInstance模式启动的Activity具有独占性,即它会独自占用一个任务,被他开启的任何activity都会运行在其他任务中。
(4)被singleInstance模式的Activity开启的其他activity,能够在新的任务中启动,但不一定开启新的任务,也可能在已有的一个任务中开启。
换句话说,其实SingleInstance就是我们刚才分析的SingleTask中,分享Activity为栈底元素的情况。
- 总结
上面介绍了4种启动模式,这里需要指出一种情况,我们假设目前有2个任务栈,前台任务栈的情况为AB,而后台任务栈的情况为CD,这里假设CD的启动模式均为singleTask。现在请求启动D,那么整个后台任务栈都会被切换到前台,这个时候整个后退列表变成了ABCD。当用户按back键的时候,列表中的Activity会一一出栈,如下图1所示:
注意:
前台任务栈:就是指和用户正在交互的应用程序所在的任务栈。
后台任务栈:就是指处于后台的应用程序所在的任务栈。
如果不是请求的D而是请求的C,那么情况就不一样了,如下图2所示:
如何指定活动的启动模式呢?在AndroidManifest.xml文件当注册活动的代码中去指定
比如:我要把MainActivity活动的启动模式指定为singleInstance模式
也可以在代码中指定:
Intent pack = new Inten(MCPersonalCenterActivity.this,MCGiftsCenterActivity.class);
pack.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(pack);
2.3 TaskAffinity
3.Activity通信问题
1.Activity->Activity
[1]Intent/Bundle
这种方式多用于Activity之间传递数据。示例代码如下:
//首先创建一个Bundle对象
Bundle bundle = new Bundle();
bundle.putString("data_string","数据");
bundle.putInt("data_int",10);
bundle.putChar("da_char",'a');
//然后创建一个Intent对象
Intent intent = new Intent(FirstActivity.this,SecondActivity.class);
intent.putExtras(bundle);
startActivity(intent);
[2]类静态变量
在Activity内部定义静态的变量,这种方式见于少量的数据通信,如果数据过多,还是使用第一种方式。示例代码如下:
public class FirstActivity extends AppCompatActivity {
//声明为静态
static boolean isFlag = false;
[@Override](https://my.oschina.net/u/1162528)
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_first);
//首先创建一个Bundle对象
Bundle bundle = new Bundle();
bundle.putString("data_string","数据");
bundle.putInt("data_int",10);
bundle.putChar("da_char",'a');
//然后创建一个Intent对象
Intent intent = new Intent(FirstActivity.this,SecondActivity.class);
intent.putExtras(bundle);
startActivity(intent);
}
}
[3]全局变量
创建一个类,里面定义一批静态变量,Activity之间通信都可以访问这个类里面的静态变量,这就是全局变量。这种方式笔者就不给代码了。
2.Activity->Service
[1]绑定服务的方式,利用ServiceConnection这个接口。
首先我们需要在要绑定的服务中声明一个Binder类
public class MyService1 extends Service {
public String data = "";
public MyService1() {
}
[@Override](https://my.oschina.net/u/1162528)
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return new Binder();
}
public class Binder extends android.os.Binder{
public void sendData(String data){
MyService1.this.data = data;
}
}
}
然后我们让Activity实现ServiceConnection这个接口,并且在onServiceConnected方法中获取到Service提供给Activity的Binder实例对象,通过这个对象我们就可以与Service进行通信可以通过上述代码的Binder类中的sendData()方法进行通信。
public class ServiceBindActivity extends AppCompatActivity implements ServiceConnection,View.OnClickListener {
private Button bt0,bt1,bt2;
public MyService1.Binder binder = null;
[@Override](https://my.oschina.net/u/1162528)
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_service_bind);
bt0 = findViewById(R.id.bt0);
bt1 = findViewById(R.id.bt1);
bt2 = findViewById(R.id.bt2);
bt0.setOnClickListener(this);
bt1.setOnClickListener(this);
bt2.setOnClickListener(this);
}
[@Override](https://my.oschina.net/u/1162528)
protected void onDestroy() {
super.onDestroy();
unbindService(this);
}
//这个是服务绑定的时候调用
[@Override](https://my.oschina.net/u/1162528)
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
binder = (MyService1.Binder) iBinder;
}
//这个是服务解绑的时候调用
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.bt0:
//绑定服务
Intent intent = new Intent(ServiceBindActivity.this,MyService1.class);
bindService(intent,this, Context.BIND_AUTO_CREATE);
break;
case R.id.bt1:
//通过binder对象来和Service进行通信
if(binder != null)
binder.sendData("bt1");
break;
case R.id.bt2:
//通过binder对象来和Service进行通信
if(binder != null)
binder.sendData("bt2");
break;
}
}
}
也不一定非要笔者这种写法,你也可以有自己的写法,但核心部分都一样。
[2]Intent
这种方式很简单,我们在启动和停止Service时所调用的方法都需要传入一个Intent实例对象,通过这个传入的Intent对象,我们就可以与Service进行通信。示例代码如下:
Activity代码是这样的:
public class ServiceStartActivity extends AppCompatActivity implements View.OnClickListener {
private Button bt0,bt1;
private Intent intent ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_service_start);
intent = new Intent(this, MyService2.class);
bt0 = findViewById(R.id.bt0);
bt1 = findViewById(R.id.bt1);
bt0.setOnClickListener(this);
bt1.setOnClickListener(this);
}
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.bt0:
//开启服务并且传递数据
intent.putExtra("data_stirng","string数据");
startActivity(intent);
break;
case R.id.bt1:
//结束服务
stopService(intent);
break;
}
}
}
Service中的代码是这样的:
public class MyService2 extends Service {
public String data = "";
public MyService2() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
//得到Activity传递过来的数据
data = intent.getStringExtra("data_string");
return super.onStartCommand(intent, flags, startId);
}
}
这种通信方式的缺点显而易见,那就是只能传递少量的数据。
[3]CallBack + Handler,监听服务的进程变化
Service中的代码:
public class MyService3 extends Service {
//在Service中如果要进行耗时任务,可以通过CallBack接口提供的方法与Activity进行通信
public Callback callback;
public MyService3() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return new Binder();
}
public void setCallBack(CallBack callBack){
this.callback = callback;
}
public Callback getCallback() {
return callback;
}
public interface CallBack{
void onDataChange(String data);
}
public class Binder extends android.os.Binder{
public MyService3 getMyService3(){
return MyService3.this;
}
}
}
Activity中的代码:
public class ServiceBind2Activity extends AppCompatActivity implements ServiceConnection{
public MyService3.Binder binder = null;
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Bundle bundle = msg.getData();
String data_string = bundle.getString("data_string");
//接下来就是更新ui
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_service_bind2);
}
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
binder = (MyService3.Binder) iBinder;
binder.getMyService3().setCallBack(new MyService3.CallBack() {
//此方法提供给MyService3在子线程中调用
@Override
public void onDataChange(String data) {
Message message = new Message();
Bundle bundle = new Bundle();
bundle.putString("data_string","String数据");
message.setData(bundle);
//通过Handler进行异步通信,不过耗时操作放在MyService3中
handler.sendMessage(message);
}
});
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
}
可能第一次看到这段代码的你很懵逼吧,其实很简单,当ServiceBind2Activity去绑定服务MyService3的时候,那么在Activity中的onServiceConnected()方法被调用,此时位于MySerivce3的CallBack接口引用被实例化,并且onDataChange()方法被实现,可以看到里面是一段Handler通信的代码,不错,这个方法是为MyService3做耗时操作调用的,笔者没有在MyService3中写耗时操作的代码,不过说到这里你应该明白了这种通信方式的好处了吧,也印证了标题:监听服务的进程变化。
3.Activity->Fragment
[1]Bundle
在创建Fragment实例的时候,调用方法setArguments将一个Bundle对象传递给Fragment,然后在Fragment中先去判断是否和当前Activity绑定上了,如果绑定上了,就可以拿出这个Bundle中的数据啦。示例代码如下:
在Activity中代码是这样的:
//首先创建一个Bundle对象
Bundle bundle = new Bundle();
bundle.putString("data_string","数据");
bundle.putInt("data_int",10);
bundle.putChar("da_char",'a');
Fragment fragment = new MyFragment1();
fragment.setArguments(bundle);
在MyFragment1中代码是这样的:
if(isAdded()){//这里判断是否Fragment和Activity进行了绑定
Bundle bundle = getArguments();
String data_string = bundle.getString("data_string");
String data_int = bundle.getInt("data_int");
String data_char = bundle.getChar("data_char");
}
对于这个isAdded()方法笔者还需要提出一点,为什么要这么写呢?因为如果这个Fragment没有和Activity绑定的话,那么那个Bundle对象是无法从Activity传递给Fragment的,因此这种写法是必须的。
[2]直接进行方法调用
在Activity里通过Fragment的引用,可以直接调用Framgent中的定义的任何方法。示例代码如下:
MyFragment1 myFragment1 = new MyFragment1();
myFragment.toString("传送的string数据");
4.scheme跳转协议
Android中的scheme是一种页面内跳转协议,通过自定义scheme协议,可以非常方便的跳转到app中的各个页面,通过scheme协议,服务器可以定制化告诉app跳转到哪个页面,可以通过通知栏消息定制化跳转页面,可以通过H5页面跳转到相应页面等等。
如果你之前没用过,那么请看以下链接进行学习吧:
http://blog.csdn.net/qq_23547831/article/details/51685310
高级
1.setContentView干了什么
2.ActivityThread是什么?
3.startActivity()干了什么?
4.如何启动一个AndroidManifest.xml中没有注册的Activity?
面试题(检测自己学的怎么样)
- 1.Activity是什么?(校招&实习)
- 2.典型情况下的Activity生命周期?(校招&实习)
- 3.异常情况下的Activity的生命周期 & 数据如何保存和恢复? (校招&实习)
- 4.从Activity A跳转到Activity B之后,然后再点击back建之后,它们的生命周期调用流程是什么?(校招&实习)
- 5.如何统计Activity的工作时间?(校招&实习)
- 6.给我说说Activity的启动模式 & 使用场景。(校招&实习)
- 7.如何在任意位置关掉应用所有Activity & 如何在任意位置关掉指定的Activity?(校招&实习)
- 8.Activity的启动流程(从源码角度解析)?
- 9.启动一个其它应用的Activity的生命周期分析。
- 10.Activity任务栈是什么?在项目中有用到它吗?说给我听听
- 11.什么情况下Activity不走onDestory?
- 12.什么情况下Activity会单独执行onPause?(校招&实习)
- 13.a->b->c界面,其中b是SingleInstance的,那么c界面点back返回a界面,为什么?
- 14.如果一个Activity弹出一个Dialog,那么这个Acitvity会回调哪些生命周期函数呢?
- 15.Activity之间如何通信 & Activity和Fragment之间通信 & Activity和Service之间通信?
- 16.说说Activity横竖屏切换的生命周期。(校招&实习)
- 17.前台切换到后台,然后在回到前台时Activity的生命周期。
- 18.下拉状态栏时Activity的生命周期?
- 19.Activity与Fragment的生命周期比较?
- 20.了解哪些Activity常用的标记位Flags?
- 21.谈谈隐式启动和显示启动Activity的方式?
- 22.Activity用Intent传递数据和Bundle传递数据的区别?为什么不用HashMap呢?
- 23.在隐式启动中Intent可以设置多个action,多个category吗 & 顺便讲讲它们的匹配规则?
- 24.Activity可以设置为对话框的形式吗?(校招&实习)
- 25.如何给Activity设置进入和退出的动画?
- 26.Activity使用Intent传递数据是否有限制 & 如果传递一个复杂的对象,例如一个复杂的控件对象应该怎么做?
- 27.在Activity中可以多次调用setContentView方法吗?说说不同时机第二次调用setContentView会发生什么?
- 28.说说分别在Activity里每一个生命周期函数里调用finish方法后,接下来Activity的生命周期如何回调?
- 29.有什么方法可以启动一个没有在AndroidManifest.xml中注册过的Activity?
- 30.在Activity进行配置时,catrgory和action的区别是什么?
- 31.activity中分别在onCreate,onStart,onResume中调用finish后的生命周期如何回调?
- 32.锁定屏与解锁屏幕,Activity 生命周期?
- 33.如何设置Activity进入和退出的动画?
- 34.谈谈你对Activity中onNewIntent()方法的认识?
- 35.如果一个Activity启动比较慢,需要优化,你觉得可以从哪些方面入手?
- 36.Activity之间传递数据的方式Intent是否有大小限制,如果传递的数据量偏大,有哪些方案?
- 37.了解scheme跳转协议吗?谈一谈
- 38.谈谈你对Activity的Context的认识?
- 39.如何在Application中获取当前Activity实例?
- 40.Activity进程优先级?
- 41.Activity出现ANR的条件有哪些 & 解决方案?
注意:文章末尾面试题来自于笔者自己总结,想求答案或者交流,start 笔者GitHub项目AndroidFaceInterview
更新时间:2020-01-15