Android 之activity

本文主要内容

Activity的生命周期、通信方式、Intent Filter


【Activity】
一个Activity是一个应用程序组件,提供一个屏幕,用户可以用来交互为了完成某项任务。每个activity被给予一个窗口,在上面可以绘制用户接口。

一个应用程序通常由多个Activity组成,他们通常是松耦合关系。通常,一个应用程序中的activity被指定为“main activity”。

【Activity生命周期】
在android.app.Activity类中,Android定义了一系列与生命周期相关的方法,在我们自己的Activity中,只是根据需要覆写需要方法,java的多态性会保证我们自己的方法被虚拟机调用。

protected void onCreate(Bundle savedInstanceState);一个Activity的实例被启动时调用的第一个方法。一般情况下,我们都覆盖该方法作为应用程序的一个入口点,在这里做一些初始化数据,设置用户界面等工作。大多数情况下,我们都要在这里从XML中加载设计好的用户界面。例如:
setContentView(R.layout.main);
当然,也可从savedInstanceState中读我们保存到存储设备中的数据,但是需要判断savedInstanceState是否为null,因为Activity第一次启动时并没有数据被存贮在设备中:

if(savedInstanceState ! = null){
savedInstanceState.get("key");
}

protected void onStart()该方法在onCreate方法之后被调用,或者在Activity从Stop状态转换为Active状态时被调用。
protected void onResume()在Activity从Pause状态转换到Active状态时被调用
protected void onPause()在Activity从Active状态转换到Pause状态时被调用。
protected void onStop()在Activity从Active状态转换到Stop状态时被调用。一般我们在这里保存Activity的状态信息。
protected void onDestroy()在Activity被结束时调用,它是被结束时调用的最后一个方法,这里一般做些释放资源,清理内存等工作。

此外,Android还定义了一些不常用的与生命周期相关的方法可用:

protected void onPostCreate(Bundle savedInstanceState);
protected void onRestart();
protected void onPostResume();

【Create an Activity】
创建一个activity,你必须创建一个Activity的子类(或者一个Activity的子类的子类)。编写一个继承自 android.app.Activity的 Java 类并在 AndroidManifest.xml声明即可。在你的子类中,你需要实现系统回调的回调方法。当activity在它的生命周期的多种状态中转换的时候,例如当activity被创建、停止、恢复或摧毁。两个最重要的回调方法是:

onCreate()
onCreate()函数是在Activity初始化的时候调用的,通常情况下,我们需要在onCreate()中调用setContentView(int)函数填充屏幕的UI,一般通过findViewById(int)返回XML中定义的视图或组件的ID。子类在重写onCreate()方法的时候,必须调用父类的onCreate()方法,即super.onCreate(),否则会抛出异常。

onCreate()里面耗时长的话可能导致极差的用户体验。需要注意:
(1)在Activity启动前,尽量少做。
(2)对于布局比较复杂的时候,可以考虑不要一次性全部加载上,动态加载是一个好的办法。
(3)对于及时需要的数据,加载起来耗时的又有异常危险的,一定记得开辟一个线程来做这些动作,千万记得不要做阻塞主线程(UI线程)的任何事情。
(4)对于特殊情况下,Activity启动确实需要大量工作时候,可以考虑先加载一个简单的布局(或是Activity)来过渡.。
(5)所有的目的都是让你要启动的组件尽快上场,而不是以画好妆为主,这样的话客人会等不及的,顾客就是上帝。

onPause()
系统调用这个方法当用户离开你的activity。这通常是你应该提交任何变化。

Implementing a user interface

一个activity的用户接口被一个层次化的视图提供——继承于View类的对象。每个View控制activity窗口中的一个特定矩形区域并且能影响应用用户交互。

Android提供了大量预定义的view,你可以用来设计和组建你的布局。你可以子类化View和ViewGroup类来创建自己的widget.

最普通的方法是定义一个布局使用view加上XML布局文件保存在你的程序资源里。这样,你可以单独维护你的用户接口设计,而与定义activity行为的代码无关。你可以设置布局作为UI使用setContentView(),传递资源布局的资源ID。可以,你也可以创建新Views在你的activity代码,并且创建一个view层次通过插入新Views到ViewGroup,然后使用那个布局通过传递到根ViewGroup给setContentView().

Declaring the activity in the manifest

你必须声明你的activity在manifest文件为了它可以被系统访问。要声明你的activity,打开你的manifest文件,添加一个元素作为元素的子元素。

【using intent filters】
一个元素也能指定多种intent filters——使用元素——为了声明其它应用程序可以激活它。

当你创建一个新的应用程序使用Android SDK工具,存根activity自动为你创建,包含一个intent filter,声明了activity响应"main"动作,并且应该被放置在“launcher”分类。Intent filter看起来像这个样子。

 
  
      
      
  
 

元素指定这是一个"main"入口点对这个应用程序。元素指定,这个activity应该被列入系统应用程序列表中(为了允许用户启动这个activity)。

【启动另外一个Activity】
Activity.startActivity()方法可以根据传入的参数启动另外一个Activity:

Intent intent = new Intent(CurrentActivity.this, OtherActivity.class);
startActivity(intent);

当然,OtherActivity同样需要在AndroidManifest.xml中定义

Activity之间的通信

使用Intent通信

在Android中,不同的Activity实例可能运行在一个进程中,也可能运行在不同的进程中。因此我们需要一种特别的机制帮助我们在Activity之间传递消息。Android中通过Intent对象来表示一条消息,一个Intent对象不仅包含有这个消息的目的地,还可以包含消息的内容。对于一个Intent对象,消息的“目的地”是必须的,而内容则是可选的。

在上面的实例中通过Activity.startActivity(intent)启动另外一个Activity的时候,我们在Intent类的构造器中指定了“目的地”。

如果需要添加消息,可通过如下方式:

Intent intent = new Intent(CurrentActivity.this, OtherActivity.class);
//创建一个带“收件人地址”的email
Bundle = bundle = new Bundle();//创建email内容
bundle.putBoolean("boolean_key",true);//编写内容
bundle.putString("string_key","string_value");
intent.putExtra("key",bundle); //封装email
startActivity(intent);//启动新的Activity

那么“收件人”该如何收信呢?在OtherActivity类的onCreate()或者其它任何地方使用下面的代码

Intent = intent = getIntent();//收取email
Bundle bundle = intent.getBundleExtra("key");//打开email
bundle.getBoolean("boolean_key");//读取内容
bundle.getString("String_key");

上面我们通过 bundle对象来传递信息,bundle维护了一个 HashMap对象,将我们的数据存贮在这个 HashMap 中来进行传递。但是像上面这样的代码稍显复杂,因为 Intent 内部为我们准备好了一个 bundle,所以我们也可以使用这种更为简便的方法:

Intent intent =new Intent(EX06.this,OtherActivity.class); 
 intent.putExtra("boolean_key", true); 
 intent.putExtra("string_key", "string_value"); 
 startActivity(intent);

接收:

 Intent intent=getIntent(); 
 intent.getBooleanExtra("boolean_key",false); 
 intent.getStringExtra("string_key");
使用SharedPreferences

SharedPreferences 使用 xml 格式为 Android 应用提供一种永久的数据存贮方式。对于一个 Android 应用,它存贮在文件系统的 /data/ data/your_app_package_name/shared_prefs/目录下,可以被处在同一个应用中的所有 Activity 访问。Android 提供了相关的 API 来处理这些数据而不需要程序员直接操作这些文件或者考虑数据同步问题。

// 写入 SharedPreferences 
 SharedPreferences preferences = getSharedPreferences("name", MODE_PRIVATE); 
 Editor editor = preferences.edit(); 
 editor.putBoolean("boolean_key", true); 
 editor.putString("string_key", "string_value"); 
 editor.commit(); 
    
 // 读取 SharedPreferences 
 SharedPreferences preferences = getSharedPreferences("name", MODE_PRIVATE); 
 preferences.getBoolean("boolean_key", false); 
 preferences.getString("string_key", "default_value");
其它方式

Android 提供了包括 SharedPreferences 在内的很多种数据存贮方式,比如 SQLite,文件等,程序员可以通过这些 API 实现 Activity 之间的数据交换。如果必要,我们还可以使用 IPC 方式。

Activity的Intent Filter

Intent Filter描述了一个组件愿意接收什么样的Intent对象,Android将其抽样为android.content.IntentFilter类。在Android的AndrdoiManifest.xml配置文件中可以通过节点为一个Activity指定其Intent Filter,以便告诉系统该Activity可以响应什么类型的Intent。

当程序员使用startActivity(intent)来启动另外一个Activity时,如果直接指定intent对象的Component属性,那么Activity Manager将试图启动其Component属性指定的Activity。否则Android将通过Intent的其他属性从安装在系统中的所有Activity中查找与之最匹配的一个启动,如果没有找到合适的Activity,应用程序会得到一个系统抛出的异常。这个匹配的过程如下:

  • 加载安装的所有Intent Filter到一个列表中
  • 剔除Action匹配失效的Intent Filter
  • 剔除URI数据匹配失效的Intent Filter
  • 剔除Category匹配失效的Intent Filter
    余下的Intent Filter数量是否为0
    如果不为0,将匹配成功的Intent Filter按优先级排序,返回最高优先级的Intent Filter;否则查找失败,抛出异常。
Action匹配

Action是一个用户定义的字符串,用于描述一个Android应用程序组件,一个Intent Filter可以包含多个Action。Activity定义时可以在其节点指定一个Action列表用于标示Activity所能接受的“动作”,例如:

 
  
  
……
 

如果我们在启动一个Activity时使用中这样的Intent对象:

Intent intent = new Intent();
intent.setAction("com.zy.myaction");

那么所有的Action列表中包含了“com.zy.myaction”的Activity都将会匹配成功。

Android 预定义了一系列的Action分别表示特定的系统动作。这些Action通过常量的方式定义在android.content.Intent中,以“ACTION_”开始,我们可以在Android提供的文档中找到他们的详细说明。

URI匹配

一个Intent可以通过URI携带外部数据给目标组件。在节点中,通过节点匹配外部数据。

mimeType 属性指定携带外部数据的数据类型,scheme指定协议,host,port,path指定数据的位置、端口、和路径。如下:
android:host = "host" android:port = "port" android:path = "path"/>

如果在Intent filter指定了这些属性,那么只有所有的属性都匹配成功时URI数据匹配才会成功。

Category 类别匹配

节点中可以为组件定义一个 Category 类别列表,当 Intent 中包含这个列表的所有项目时 Category 类别匹配才会成功。


一些关于 Activity 的技巧

锁定 Activity 运行时的屏幕方向

Android 内置了方向感应器的支持。在 G1 中,Android 会根据 G1 所处的方向自动在竖屏和横屏间切换。但是有时我们的应用程序仅能在横屏 / 竖屏时运行,比如某些游戏,此时我们需要锁定该 Activity 运行时的屏幕方向,节点的 android:screenOrientation属性可以完成该项任务,示例代码如下:
// 竖屏 , 值为 landscape 时为横屏…………

全屏的 Activity

要使一个 Activity 全屏运行,可以在其 onCreate()方法中添加如下代码实现:
// 设置全屏模式 getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); // 去除标题栏 requestWindowFeature(Window.FEATURE_NO_TITLE);

在 Activity 的 Title 中加入进度条

为了更友好的用户体验,在处理一些需要花费较长时间的任务时可以使用一个进度条来提示用户“不要着急,我们正在努力的完成你交给的任务”。如下图:
在 Activity 的标题栏中显示进度条不失为一个好办法,下面是实现代码:
// 不明确进度条 requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); setContentView(R.layout.main); setProgressBarIndeterminateVisibility(true); // 明确进度条 requestWindowFeature(Window.FEATURE_PROGRESS); setContentView(R.layout.main); setProgress(5000);


Android入门-关于Activity下的onCreate方法

在AndroidManifest.xml文件中的元素中有这么两句





1、一个应用程序可以有多个Activity,每个Activity是同级别的,那么在启动程序时,最先启动哪个Activity呢?
有些程序可能需要显示在程序列表里,有些不需要。怎么定义呢?android.intent.action.MAIN决定应用程序最先启动的Activityandroid.intent.category.LAUNCHER决定应用程序是否显示在程序列表里
2、因为你的程序可能有很多个activity只要xml配置文件中有这么一个intent-filter,而且里面有这个launcher,那么这个activity就是点击程序时最先运行的那个activity。
3、现在只有一个activity,那么加不加就没有关系了。用于模拟器启动时设置为默认打开为的activity。

当写好的应用发布到手机上之后,当双击该应用的图标时,系统会将这个点击事件包装成一个Intent,该Intent包含两个参数,如上所述的两个参数被传递给应用之后,在应用的功能清单文件中寻找与该意图匹配的意图过滤器,如果匹配成功,找到相匹配的意图过滤器所在的Activity元素,再根据元素的“name”属性来寻找其对应的Activity类。接着Android操作系统创建该Activity类的实例对象,对象创建完成之后,会执行到该类的onCreate()方法,此onCreate()方法是重写父类Activity的onCreate方法而实现的。onCreate方法用来初始化Activity实例对象。

public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceSate);
setContentView(R.layout.main);
}

其中super.onCreate(savedInstanceState)的作用是调用其父类Activity的onCreate方法来实现对界面的图画绘制工作。在实现自己定义的Activity子类的onCreate方法时一定要记得调用该方法,以确保能够绘制界面。

setContentView(R.layout.main)的作用是加载一个界面。该方法中传入的参数是“R.layout.main”,其含义为R.java类中静态内部类layout的静态常量main的值,而该值是一个指向res目录下的layout子目录下的main.xml文件的标识符。因此代表着显示main.xml所定义的画面。

onCreate(Bundle savedInstanceState)参数Bundle

onCreate方法是在Activity创建时被系统调用,是一个Activity生命周期的开始。可是有一点容易被忽视,就是onCreate方法的参数savedInstanceState.因为在一般的程序开发中,很少用到这个参数。
onCreate方法的完整定义如下:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
从上面的代码可以看出,onCreate方法的参数是一个Bundle类型的参数。Bundle类型的数据与Map类型的数据相似,都是以key-value的形式存储数据的。

从字面上看savedInstanceState,是保存实例状态的。实际上,savedInstanceState也就是保存Activity的状态的。那么,savedInstanceState中的状态数据从何处而来的呢?下面我们介绍Activity的另一个方法saveInstanceState.

onsaveInstanceState方法是用来保存Activity状态的。当一个Activity在生命周期结束前,会调用该方法保存状态。这个方法有一个参数名称与onCreate方法参数名称相同。如下所示:

public void onSaveInstanceState(Bundle saveInstanceState) {
super.onSaveInstanceState(saveInstanceState);
}

在实际应用中,当一个Activity结束前,如果需要保存状态,就在onsaveInstanceState中,将状态数据以key-value的形式放入到saveInstanceState中。这样,当一个Activity被创建时,就能从onCreate的参数saveInstanceState中获得状态数据。

可以重写onSaveInstanceState,以在Activity结束前保存一些数据。
重写onCreate,然后可以获得savedInstanceState

可以用于保存一些进度数据。


Android中的Bundle的具体使用

  1. API文档说明
    用于不同Activity之间的数据传递;它保存的数据,是以key-value(键值对)的形式存在的。

我们经常使用Bundle在Activity之间传递数据,传递的数据可以是boolean,byte,int,long,float,double,string等基本类型或它们对应的数组,也可以是对象或对象数组。当Bundle传递的是对象或对象数组时,必须实现Serializable或Parcelable接口。下面分别介绍Activity之间如何传递基本数据类型、传递对象。

  • 实例化Bundle一个对象,用putString(标记,数据)来将数据导入到Bundle对象中;
  • 然后将Bundle对象导入到Intent对象中;
  • Intent启动另一个Activity,从intent中读出需要的数据;
  • Bundle对象可以从activity.getIntent().getExtras()中返回。可以,启动当前Activity的Intent对象是由getIntent()来找到的。
  • 通过Bundle的getString()方法,就可以读出所要的数据。

这就是Bundle的经典用法,包裹数据放入Intent中,目的在于传输数据。

eg.
第一个活动的代码如下:

package com.ray.test;
import android.app.Activity; 
import android.content.Intent; 
import android.os.Bundle; 
import android.view.MotionEvent; 
public class TestBundle extends Activity {     
public void onCreate(Bundle savedInstanceState) {                 
super.onCreate(savedInstanceState);         
setContentView(R.layout.main);     
}     
public boolean onTouchEvent(MotionEvent event) { 
Intent intent = new Intent(); 
intent.setClass(TestBundle.this, Target.class); 
Bundle mBundle = new Bundle(); 
mBundle.putString("Data", "ray'blog");//压入数据 
intent.putExtras(mBundle); 
startActivity(intent); 
finish(); 
return super.onTouchEvent(event); 
} 
} 

将要跳转的活动代码

package com.ray.test; 
import android.app.Activity; 
import android.os.Bundle; 
public class Target extends Activity{     
public void onCreate(Bundle savedInstanceState) {         
super.onCreate(savedInstanceState);         
setContentView(R.layout.main);         
Bundle bundle = getIntent().getExtras();         
String data=bundle.getString("Data");//读出数据         
setTitle(data);     
} 
}
传递Serializable类型的对象。

Serializable是一个对象序列化的接口。一个类只有实现了Serializable接口,它的对象才是可序列化的。因此如果要序列化某些类的对象,这些类就必须实现Serializable接口。而实际上,Serializable是一个空接口,没有什么具体内容,它的目的只是简单的标识一个类的对象可以被序列化。

Serializable接口的实现方法:很简单,只要implements Serializable接口就可以了

Parcelable接口说明

public interface Parcelable{
//内容描述接口,基本不用管
public int describeContents();
//写入接口函数,打包
public void writeToParcel(Parcel dest, int flags);
//读取接口,目的是要从Parcel中构造一个实现了Parcelable的类的实例处理。因为实现类在这里还是不可知的,所以需要用到模板的方式,继承类名通过模板参数传入。
//为了能够实现模板参数的传入,这里定义Creator嵌入接口,内含两个接口函数分别返回单个和多个继承类实例。
public interface Creator{
public T createFromParcel(Parcel source);
public T[] newArray(int size);
}
}

Parcelable接口的实现方法

从parcelable接口定义中,我们可以看到,实现parcelable接口,需要我们实现下面几个方法:

  • describeContents方法。内容接口描述,默认返回0就可以;
  • writeToParcel(Parcel dest, int flags)
  • 静态的Parcelable.Creator接口,本接口有两个方法:
    createFromParcel(Parcel in)从Parcel容器中读取传递数据值,封装成Parceable对象返回逻辑层。
    newArray(int size)创建一个类型为T,长度为size的数据,仅一句话(return new T[size])即可。方法是供外部类反序列化本类数组使用。

你可能感兴趣的:(Android 之activity)