什么是Activity

官方定义:Activity是Android应用程序提供交互界面的一个重要界面,也是Android最重要的组件之一

自定义:Activity是业务类,是承载应用程序的界面以及业务行为的基础。

包括UI、Service……类似于我们的JavaBean

”脸“的表现都靠Activity了


什么是Intent

认为Intent就是在不同组件之间传递值而设计的一个数据结构

Intent属性:

extras——加入附加信息

category——IntentFilter

Action——Data,动作相关的值

ComponentName——Context


使用StarUML画UML图


Activity的启动过程

1、建立Activity类及定义属性和内部方法

2、注册Activity到Manifesst

3、在启动函数中(onCreate())实现业务

3.1 界面的定义layout

3.2 界面的绑定setContentView()


查看Activity源码

Activit.java中setContentView方法调用的是getWindow()的相关方法

getWindow返回的是Window类,Window类是一个抽象类,其调用的具体实现是PhoneWindow

查看PhoneWindow.java源码,可以看到setContentView的下一步实现

android高级ui android高级应用开发_Android


那么Activity与Window是什么关系呢

Activity构造的时候,会调用一个叫做attach的方法

attach最大的作用是通过PolicyManager.makeNewWindow(this)创建一个新的Window

并且把这个Window与Activity本身关联

android高级ui android高级应用开发_ide_02


PolicyManager最终调用的是Policy类

查看Policy.java的源码,它的makeNewWindow()方法返回一个新的PhoneWindow的对象



整体的关系如下图所示:

android高级ui android高级应用开发_ide_03


结论:

1、Activity构造的时候,调用了attach,绑定了一个window

2、Activity setContentView(),实际上是Window setContentView()


既然Activity实际上是Window来呈现的视图,那么setContentView也可以用Window来实现

this.getWindow().setContentView(R.layout.***);

还可以使用Inflate的方法进行设置

this.getWindow().setContentView(this.getLayoutInflater().inflate(R.layout.**));




Intent

跳转时,两种方式效果相同:

// 方法1
Intent intent = new Intent(A.this, B.class);
startActivity(intent);

// 方法2
Intent intent = new Intent();
intent.setClass(A.this, B.class);
startActivity(intent);


源码讲解startActivity()的机制

从Activity.java中可以看到startActivity方法

android高级ui android高级应用开发_android_04

实际上startActivity()方法就是调用了startActivityForResult()方法,传了一个默认的-1作为requestCode

android高级ui android高级应用开发_Android_05

核心就是红框中的execStartActivity方法

具体的参数主要有:

this:上下文

mMainThread.getApplicationThread():主线程实例

mToken:Token是IBinder这样的一个接口,

IBinder是linux内核所支持的一套IPC进程间通讯的机制,

说明Activity之间的跳转是通过进程间的通信来完成的

intent:意图

requestCode:请求码


下面找到Instrumentation.java的源码

exeStartActivity方法中的核心部分如下:

android高级ui android高级应用开发_ide_06

getDefault方法返回的是一个IActivityManager的接口

android高级ui android高级应用开发_ide_07

其实实现这个接口的就是ActivityManager

android高级ui android高级应用开发_android_08


其实finish方法也是使用的ActivityManager

android高级ui android高级应用开发_ide_09



为什么会用到IBinder,为什么activity跳转要用到IPC进程间通信机制?

因为Activity有四种LaunchMode,Activity可以存在于不同的进程之中

由于不能确保Activity是否属于同一进程,所以需要使用IPC进程间通信机制

比如点击按钮,开启了一个拨号,属于不同进程之间的切换


IntentFilter

当Intent在组件间传递时,组件如果想告知Android系统自己能够响应和处理哪些Intent,那么就需要用到IntentFilter对象

IntentFilter对象负责过滤掉组件无法响应和处理的Intent,只将自己关心的Intent接收进来进行处理

IntentFilter实行“白名单”管理,即只列出组件乐意接受的Intent,但IntentFilter只会过滤隐式Intent,显式的Intent会直接传送到目标组件


自定义一个IntentFilter

<activity ... >
    <intent-filter>
        <action android:name="com.test.demo.MyAction" />
        <category android:name="android.intent.category.DEFAULT" />
 </intent-filter></activity>



然后再activity跳转时

Intent intent = new Intent();
intent.setAction("com.test.demo.MyAction");
startActivity(intent);



需要注意的是,intent-filter中的category标签是必须要设置的


如果要传递Data

Intent intent = new Intent();
intent.setAction("com.test.demo.MyAction");
intent.setData(Uri.parase("http://www.116329.com"));
startActivity(intent);



那么在intent-filter中

<activity ... >
    <intent-filter>
        <action android:name="com.test.demo.MyAction" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:scheme="http" />
    </intent-filter>
</activity>



这里的data表示只有以为http前缀的Data才会被接收

接收时

Intent intent = this.getIntent();
if(intent != null){
    String dataString = intent.getDataString();
}


注意,如果有重复的action的话,会弹出选框以选择

就像打开文件时跳出不同程序以供打开一样



拨打电话时的代码

Intent intent = new Intent();
intent.setAction(Intent.ACTION_DIAL);
intent.setData(Uri.parse("tel:12345678901"));
startActivity(intent);


Activity在MVC中的定位

M —— Window

V —— xml、UI

C —— Activity


Android SDK自带小工具HierarchyViewer

在Android SDK目录下tools目录中

能够查看程序View的层级关系

但是好像需要手机root才行(因为我的手机不能显示,但可以用模拟器)

经过查资料:出于安全考虑,Hierarchy Viewer只能连接Android开发版手机或是模拟器

商业手机可以通过某些手段开启查看,请参考http://www.dup2.org/node/1538


Activity生命周期


注意事项1:

一个Activity跳转时,首先是先暂停自己的activity(onPause),然后加载下一个activity(onStart,onCreated),

等下一个activity加载完毕以后,在停止自己的activity(onStop)


注意事项2:

在一个EditText中输入一些文字,切换到home,然后杀死这个进程,再返回程序发现输入的文字还在,但程序确实重新onStart了

这就说明有一套状态的保存于恢复机制

android高级ui android高级应用开发_android_10



我们可以看一下onSaveInstanceState()这个函数

@Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
    }



它在activity即将变为不可见的时候都会保存一个Bundle类型的数据(类似hash)

然后我们就明白onCreate()方法里的参数是干什么用的了

@Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
}



就是传入了那个保存了之前状态的Bundle


同理onRestoreInstanceState()也是一样

@Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
    }



说明:onRestoreInstanceState()较少被调用,只有有些程序被kill掉的又重新召回的时候才会被调用

因为有时候不会被调用,所以不建议使用


在onSaveInstanceState()方法中,保存数据采用如下方式:

@Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putString("save string", editText.getText().toString());
    }



在onCreate()中可以取出保存的值

@Override
    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        // 由于未存值时未空,所以需要判断
        if (savedInstanceState != null){
            editText.setText(savedInstanceState.getString("save string"));
        }
}



采用上面的保存与还原的方法,我们会发现自己保存的值没有被还原

原因是我们个人保存的数据与系统保存的数据key不相同

系统优先回复了自己的数据

解决方法有两种:

一种方法是屏蔽onSaveInstanceState()方法中的super.onSaveInstanceState(outState);方法,不让系统自动保存

另一中方法是将回复数据写到onRestoreInstaceState方法中去,但此种方法不推荐


设置改变ConfigurationChanges

(一)设置改变的时候回复一个大对象

onRetainNonConfigurationInstance与getLastNonConfigurationInstance配合

注意returnObject不要与Context对象关联,比如View,否则会引起内存泄露

使用方法

保存:

@Override
    public Object onRetainCustomNonConfigurationInstance() {
        final MyDataObject data = collectMyloadedData();
        return data;
    }



使用:

@Override
    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.main);

        final MyDataObject data = (MyDataObject)getLastCustomNonConfigurationInstance();
        if (data == null){
            data =  loadMyData();
        }
}



(二)

自己处理配置改变

配置改变:语言改变,横屏竖屏切换,输入法有效无效切换

常用到onConfigurationChanged()方法

在manifest中配置configureChanges,这个属性的英文解释如下:

Specify one or more configuration changes that the activity will handle itself

内容可以选多项,彼此之间用" | "隔开即可

<activity android:name=".MyActivity"
          android:configChanges="orientation|keyboardHidden"
/>



使用时

@Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);

        // check if the orientation of the screen
        if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE){
            Toast.makeText(this, "landscape", Toast.LENGTH_LONG).show();
        } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
            Toast.makeText(this, "portrait", Toast.LENGTH_LONG).show();
        }
    }




Android任务栈TaskStack

多个Activity按顺序组成的一个业务逻辑

可以在Activity中获得task id,从而证明任务栈的机制

Log.i("task id", getTaskId());





Acitivity在Androidmanifest.xml中的标签说明

<activity android:allowTaskReparenting=["true" | "false"]
          android:alwaysRetainTaskState=["true" | "false"]
          android:clearTaskOnLaunch=["true" | "false"]
          android:configChanges=["mcc", "mnc", "locale",
                                 "touchscreen", "keyboard", "keyboardHidden",
                                 "navigation", "screenLayout", "fontScale", "uiMode",
                                 "orientation", "screenSize", "smallestScreenSize"]
          android:enabled=["true" | "false"]
          android:excludeFromRecents=["true" | "false"]
          android:exported=["true" | "false"]
          android:finishOnTaskLaunch=["true" | "false"]
          android:hardwareAccelerated=["true" | "false"]
          android:icon="drawable resource"
          android:label="string resource"
          android:launchMode=["multiple" | "singleTop" |
                              "singleTask" | "singleInstance"]
          android:multiprocess=["true" | "false"]
          android:name="string"
          android:noHistory=["true" | "false"]  
          android:parentActivityName="string" 
          android:permission="string"
          android:process="string"
          android:screenOrientation=["unspecified" | "behind" |
                                     "landscape" | "portrait" |
                                     "reverseLandscape" | "reversePortrait" |
                                     "sensorLandscape" | "sensorPortrait" |
                                     "userLandscape" | "userPortrait" |
                                     "sensor" | "fullSensor" | "nosensor" |
                                     "user" | "fullUser" | "locked"]
          android:stateNotNeeded=["true" | "false"]
          android:taskAffinity="string"
          android:theme="resource or theme"
          android:uiOptions=["none" | "splitActionBarWhenNarrow"]
          android:windowSoftInputMode=["stateUnspecified",
                                       "stateUnchanged", "stateHidden",
                                       "stateAlwaysHidden", "stateVisible",
                                       "stateAlwaysVisible", "adjustUnspecified",
                                       "adjustResize", "adjustPan"] >   
    . . .
</activity>




allowTaskReparenting 允许任务重定向

比如在自己的程序中有一个网页链接,点击打开浏览器打开了这个页面

默认情况下程序运行完毕,重新打开浏览器(新的任务)会默认回到主页面

而将allowTaskReparenting设为true时则会打开浏览器时页面依旧存在


alwaysRetainTaskState

如果activity被杀死了,从launch切换回去的时候也会回到之前切换出去时候的状态


clearTaskOnLaunch

从launch进入程序时,清除掉root acitvity以上的所有的acitvity

记得是从launch进入,而不是home切换回去


finishOnTaskLaunch

当用户再次载入该task时(在主页屏幕上选择该task),一个既存的activity实例是否应该被关闭(结束)

true表示会关闭,false表示不是,默认值为false


hardwareAccelerated 硬件加速

与open gl有关


multiprocess

activity的实例能否能够被载入到启动它的组件所在的进程里

true表示可以,false表示不行,默认值为false

通常,一个acitivty的新实例被载入到定义它的应用程序进程里

这样一来,该acitvity的所有实例运行在同一个进程里

然而,如果本标识设为true的话,activity的实例就能够运行在多个进程里

允许系统只要使用它时就生成实例(提供的权限允许的话)

某些几乎根本不需要或根本不合适


noHistory

当用户离开它且它不再显示在屏幕上时(比如切换出去的时候),

activity是否应该被从activity stack里移除并且结束掉(调用它的finish方法)

true表示应该结束,false表示不,默认值是false


excludeFromRecents

不再最近的任务历史列表中包含它


process

可以直接设为一个值,表示修改进程别名(DDMS中的进程名为默认的包名)

还可以使用“:”+字符串,有可能会在应用内创建一个新的进程


stateNotNeeded

不自动保存状态,比如切换时不再保存已经输入的文字

相当于onSaveInstanceState()中保存了null


export

是否允许被其他进程使用,一般都是允许的


windowsSoftInputMode

输入法状态


LaunchMode

一共有4种执行模式:

standard

singleTop

singleTask

singleInstance


当launchMode为standard

正常的模式,调用一次,创建一个新实例

退出时也按进栈的顺序依次出栈

android高级ui android高级应用开发_android高级ui_11



当launchMode为singleTop

该activity不在栈顶时,仍然会创建新的实例

在栈顶时,请求启动该activity的Intent并没有触发创建新实例

而是触发了该activity的onNewIntent()方法

所以连续开启多个此种activity不会生成新的实例,按back时只需按一下就能返回上一个activity

android高级ui android高级应用开发_ide_12


当launchMode为singleTask

activity已经存在时,再次请求该activity回出发已经存在的实例,调用onNewIntent()

并且如果该activity所在的任务栈 位于该activity上方的所有activity都会被销毁

android高级ui android高级应用开发_Android_13



当launchMode为singleInstance

独立成任务栈,并且有且仅有自己,而且始终保持一个实例

离栈的时候,先退当前的任务栈,再退其他的task

android高级ui android高级应用开发_Android_14



Android提供的专有Activity

MapActivity

ListActivity

ExpandableListActivity

TabActivity