Android面试系列文章2018之Android部分Activity篇

Android面试系列2018知识总结:
http://blog.csdn.net/ClAndEllen/article/details/79257663

Android知识体系总结之Android部分Activity篇_第1张图片

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的生命周期分析

正常情况下的生命周期:

  Activity启动–>onCreate()–>onStart()–>onResume()

  点击home键回到桌面–>onPause()–>onStop()

  再次回到原Activity时–>onRestart()–>onStart()–>onResume()

  退出当前Activity时–>onPause()–>onStop()–>onDestroy()

详细生命周期如下:

Android知识体系总结之Android部分Activity篇_第2张图片

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:资源相关的系统配置发生改变导致Activity被杀死并重新创建。

Android知识体系总结之Android部分Activity篇_第3张图片

可以从图中看出当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.3 一些特殊情况下的生命周期分析

1.3.1 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()方法。

1.3.2 什么时候Activity单独走onPause()不走onStop()?

  关于这个特殊情况,笔者在上面的生命周期图解析的时候,贴了一个链接,这里主要是检验你是否会了这个问题的答案,这里笔者就不贴答案了,答案全在那个链接里,你会了吗?

1.3.3 什么时候Activity不执行onDestory()

栈里面的第一个没有销毁的activity会执行ondestroy方法,其他的不会执行。

比如说:从mainactivity跳转到activity-A(或者继续从activity-A再跳转到activity-B),这时候,从后台强杀,只会执行mainactivity的onDestroy方法,activity-A(以及activity-B)的onDestroy方法都不会执行;

1.4 进程的优先级

  前台>可见>服务>后台>空

  前台:与当前用户正在交互的Activity所在的进程。

  可见:Activity可见但是没有在前台所在的进程。

  服务:Activity在后台开启了Service服务所在的进程。

  后台:Activity完全处于后台所在的进程。

  空:没有任何Activity存在的进程,优先级也是最低的。

2.Android任务栈

  任务栈与Activity的启动模式密不可分,它是用来存储Activity实例的一种数据结构,Activity的跳转以及回跳都与这个任务栈有关。详情请看下面的Activity的启动模式。

3.Activity的启动模式

  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所示:

Android知识体系总结之Android部分Activity篇_第4张图片

  如何指定活动的启动模式呢?在AndroidManifest.xml文件当注册活动的代码中去指定
比如:我要把MainActivity活动的启动模式指定为singleInstance模式

也可以在代码中指定:

Intent pack = new Inten(MCPersonalCenterActivity.this,MCGiftsCenterActivity.class);pack.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);startActivity(pack);

详情请查看

3.1 TaskAffinity

  TaskAffinity与启动模式的关系

4.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    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    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    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    protected void onDestroy() {       super.onDestroy();       unbindService(this);    }    //这个是服务绑定的时候调用    @Override    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数据");

5.scheme跳转协议

  Android中的scheme是一种页面内跳转协议,通过自定义scheme协议,可以非常方便的跳转到app中的各个页面,通过scheme协议,服务器可以定制化告诉app跳转到哪个页面,可以通过通知栏消息定制化跳转页面,可以通过H5页面跳转到相应页面等等。

  如果你之前没用过,那么请看以下链接进行学习吧:
  http://blog.csdn.net/qq_23547831/article/details/51685310

6.源码解析startActivity干了什么?

https://blog.csdn.net/pihailailou/article/details/78545391

更多相关文章

  1. C语言函数以及函数的使用
  2. Android activity四种启动模式及Flag
  3. Android滑动手势侦测方法介绍
  4. Activity背景色为透明的2种方法
  5. Android单例模式
  6. Android--Activity篇之启动模式
  7. 无法安装ADT(无法访问https://dl-ssl.google.com/android/eclipse
  8. Activity的启动模式
  9. android 几种发送短信的方法

随机推荐

  1. android 用editview 禁用输入法,涉及到的
  2. WebView的Java和javascript相互调用
  3. Android(安卓)fragment 获取id,findViewBy
  4. android点击返回键退出应用的实现
  5. Android(安卓)Adapter
  6. react native友盟app统计
  7. 【Android性能工具】腾讯开源工具APT
  8. android.app.Activity 的介绍(转)
  9. Android客户端与服务器之间传递json数据
  10. 【移动开发】Android相机、相册获取图片