Android入门——详解Activity及Activity间的传值(一)

引言

在前一篇总结了下Android 入门——项目结构目录、文件详解,从这一篇正式开始了总结和学习Android开发之旅,Web应用最基本的UI是网页、Winform应用最基本的UI就是Form,那么Activity就是Android最基本的UI,只要有UI都会涉及到Activity。换言之,一个Activity是一个应用程序组件,提供一个屏幕,用户可以用来交互为了完成某项任务,例如拨号、拍照、发送email、看地图。每一个activity被给予一个窗口,在上面可以绘制用户接口。窗口通常充满屏幕,但也可以小于屏幕而浮于其它窗口之上。(设置样式即可,或者代码设置大小、位置等等)

一Android几个重要的术语

1 上下文对象Context

Context类是所有Android应用程序的核心,绝大部分的应用程序和底层交互的功能都通过Context访问,其设的目的就是为了更加便捷于开发者与底层交互。其实质只是一个抽象类,定义了很多方法,比如获取系统服务、得到SharedPreferences、启动Activity等等。

public abstract class Context {  
     ...  
     public abstract Object getSystemService(String name);  //获得系统级服务  
     public abstract void startActivity(Intent intent);     //通过一个Intent启动Activity  
     public abstract ComponentName startService(Intent service);  //启动Service  
     //根据文件名得到SharedPreferences对象  
     public abstract SharedPreferences getSharedPreferences(String name,int mode);  
     ...  
} 

2 活动Activity

Activity继承Context,Activity是用来与用户交互的主要UI,一个普通的app由若干个业务(task)构成,可以把每一个提供用户交互的业务理解为一个Activity,比如登录页面是一个Activity。一般来说一个Activity的目的在于处理单个屏幕的UI效果展示。Android本身也预定义了很多的Activity,开发的很多工作我们都可以直接调用就行了,比如说:拨号界面,设置界面,拍照界面我们能看到的一切界面都是Activity。

3 意图 Intent

Intent 本身是定义为一个类,一个Intent实例按照一定的格式把自己要表达一个意图或目的封装起来。Android则根据此Intent对实例,找出相配的组件,然后将 Intent对象传递给所找到的组件,此时他的传话工作就完成了。说得直白点,Intent 其实就是各组件交互的的跑腿角色,比如你想打电话的时候,你是不是希望那个拨打电话的Activity启动?那么你要做的就是把你要做点事(打电话)封装成对应的Intent 实例,然后Android系统接收到了Intent实例之后自动找到对应的接收者。

二使用Activity

1 Activity 的三个状态

  1. 当它在屏幕前台时(位于当前任务堆栈的顶部),它是激活运行状态。只要是响应用户当前操作的Activity就是激活运行状态。
  2. 当它失去焦点但仍然对用户可见时(被部分覆盖时也算),它处于暂停状态。即在它之上有另外一个Activity。这个Activity也许是透明的,或者没有完全覆盖全屏,所以被暂停的Activity仍对用户可见。暂停的Activity仍然是存活状态(它保留着所有的状态和成员信息并保持和窗口管理器的连接),但系统处于极低内存时仍然可以杀死这个Activity。
  3. 完全被另一个Activity覆盖时则处于停止状态。它仍然保留所有的状态和成员信息。然而对用户是不可见的,所以它的窗口将被隐藏,如果其它地方需要内存,则系统经常会杀死这个Activity。

2 Activity的生命周期

当Activity从一种状态转变到另一种状态时,系统会自动调用对应的方法来通知这种变化
Android入门——详解Activity及Activity间的传值(一)_第1张图片

  1. onCreate:当Activity被创建(Activity对象从无到有,但如果只是被暂时隐藏,并没有被销毁时,重新变成激活状态的不会触发)时,就会执行,且在一个生命周期内只执行一次。所以在这里完成一些初始化工作,比如说调用setContentView(id)设置在资源文件的id; 使用findViewById(id) 获得所有的组件;绑定注册对应的监听器、数据绑定等等。
  2. onStart:Activity处于可见状态的时候,即Activity处于栈顶
  3. onRestart:当Activity由完全被覆盖但没有被销毁时,重新回到前台
  4. onResume:当Activity**可以得到用户焦点时,此时Activity获得输入焦点。这个方法内部仍然比较适合获取运行所需资源,尤其适用于启动音频、视频和动画**。
  5. onPause:当Activity 部分被遮盖时,此时的Activity不能获得输入焦点,但是部分可见。一般来说,在onResume方法中获取的资源,比如手动管理的Cursor对象,均应该在onPause里释放,否则如果线程被终止,就有可能造成有些资源是放不完全。
  6. onStop :当activity被完全摭盖时被调用
  7. onDestory:在activity销毁之前被调用。这是activity能收到的最后一个调用。调用的原因可能是别人在这个activity上调用了finish(),也可能是系统为了更多的内存空间而把它所在的进程处死了。在这个方法中,可以调用isFinishing()来判断自己属于哪一种死法。

Activity生命周期小结

  1. Activity的完整生命周期自第一次调用onCreate()开始,直至调用onDestroy()为止。Activity在onCreate()中设置所有“全局”状态以完成初始化,而在onDestroy()中释放所有系统资源。例如,如果Activity有一个线程在后台运行从网络上下载数据,它会在onCreate()创建线程,而在onDestroy()销毁线程。
  2. Activity的可视生命周期自onStart()调用开始直到相应的onStop()调用结束。在此期间,用户可以在屏幕上看到Activity,尽管它也许并不是位于前台或者也不与用户进行交互。在这两个方法之间,我们可以保留用来向用户显示这个Activity所需的资源。例如,当用户不再看见我们显示的内容时,我们可以在onStart()中注册一个BroadcastReceiver来监控会影响UI的变化,而在onStop()中来注消。onStart() 和 onStop() 方法可以随着应用程序是否为用户可见而被多次调用。
  3. Activity的前台生命周期自onResume()调用起,至相应的onPause()调用为止。在此期间,Activity位于前台最上面并与用户进行交互。Activity会经常在暂停和恢复之间进行状态转换——例如当设备转入休眠状态或者有新的Activity启动时,将调用onPause() 方法。当Activity获得结果或者接收到新的Intent时会调用onResume() 方法。

如果按照Activity的活动状态区分,还可以把Activity的生命周期归纳为:启动ActivityActivity退居后台Activity返回前台锁屏、解锁时的生命周期方法变化

  • 启动Activity: onCreate()—>onStart()—>onResume(),Activity进入运行状态。

  • Activity退居后台: 当前Activity转到新的Activity界面或按Home键回到主屏:onPause()—>onStop(),进入停滞状态。

  • Activity返回前台:onRestart()—>onStart()—>onResume(),再次回到运行状态。

  • Activity退居后台,且系统内存不足,系统会杀死这个后台状态的Activity(此时这个Activity引用仍然处在任务栈中,只是这个时候引用指向的对象已经为null),若再次回到这个Activity,则会走onCreate()–>onStart()—>onResume()(将重新走一次Activity的初始化生命周期)

  • 锁屏:onPause()->onStop()

  • 解锁:onStart()->onResume()

3 创建Activity

  1. 编写Java类继承自Activity或者Activity的子类(ListActivity、FragmentActivity、ActionBarActivity等等),并重写Activity的相关生命周期方法。
  2. 在manifest清单文件中,声明Activity(必须声明,因为Activity也是Android的一个组件,只有声明了Android才能找得到,内容提供者、服务、广播也是组件也同样需要先声明再使用)

4 Activity的跳转startActivity(intent)和startActivityForResult(intent)

Activity的跳转又分为显式跳转和隐式跳转,调用的方法大同小异,区别只是在于传递的intent对象不同,其实实质是Intent对象的两种匹配方式。

//显式跳转,适合用于启动同一APP中的Activity
  Intent intent = new Intent(LoginActivity.this, MainActivity.class);
  startActivity(intent);//无返回值的跳转

//隐式跳转
  Intent intent = new Intent();
  intent.setClass(LoginActivity.this, MainActivity.class);
  startActivity(intent);  
  /*startActivityForResult(intent,requestCode); 其中requestCode为请求码,请求码的值随便设置,但必须>=0 ,作用在于加入你有多个操作都可以跳转到MainActivity,你可以在LoginActivity的回调方法中onActivityResult(int requestCode, int resultCode, Intent data)得到你的返回值,区别处理,(从MainActivity回来的时候会执行这个方法)
  */

5 Activity的4种启动模式

通过Acitivty的xml标签来改变任务栈的默认行为,使用android:launchMode=”standard|singleInstance|singleTask|singleTop”来控制Acivity任务栈。(任务栈是一种后进先出的结构。位于栈顶的Activity处于焦点状态,当按下back按钮的时候,栈内的Activity会一个一个的出栈,并且调用其onDestory()方法。如果栈内没有Activity,那么系统就会回收这个栈,每个APP默认只有一个栈,以APP的包名来命名.)

  1. standard(默认): 标准模式,每次启动Activity都会创建一个新的Activity实例,并且将其压入任务栈栈顶,而不管这个Activity是否已经存在。Activity的启动三回调(onCreate()->onStart()->onResume())都会执行。
  2. singleTop: 栈顶复用模式,如果新Activity已经位于任务栈的栈顶,那么此Activity不会被重新创建,所以它的启动三回调就不会执行,同时Activity的onNewIntent()方法会被回调来重用,否则创建新的实例并放入栈顶(即使栈中已经存在该实例,只要不在栈顶都会创建新的实例)
  3. singleTask: 栈内复用模式,创建这样的Activity的时候,系统会先确认它所需任务栈已经创建,否则先创建任务栈.然后放入Activity,如果栈中已经有一个Activity实例,那么这个Activity就会被调到栈顶,onNewIntent(),并且singleTask会清理在当前Activity上面的所有Activity。如果任务栈中正好存在Activity该实例重用(会调用实例的onNewIntent())重用时会让该实例重回栈顶,因此在他上面的实例就会被移出栈,如果栈中不存在该实例,则创建新的实例并放入栈中
  4. singleInstance:加强版的singleTask模式,这种模式的Activity只能单独位于一个任务栈内,由于栈内复用的特性,后续请求均不会创建新的Activity,除非这个独特的任务栈被系统销毁了,并让多个应用共享该Activity的实例,一旦该模式的Activity实例已存在于某个栈中,任何应用再激活该Activity时都会重用该栈中的实例,其效果相当于多个应用共享一个应用,不管谁激活该Activity都会进入同一个应用中。在这个模式下的Activity实例所处的task中,只能有这个activity实例,不能有其他的实例。

Activity的堆栈管理以ActivityRecord为单位,所有的ActivityRecord都放在一个List里面.可以认为一个ActivityRecord就是一个Activity栈

6 Activity之间的传值

1 通过Intent对象传值(最常用)

Intent对象传值是Activity之间的传统方式(同样适用于Service、Broadcast Receiver),主要是通过Intent.putExtra()设置要传递的值,再通过getXXXExtra()方法获取传递过来的数据。但是只能传递Java简单类型的数据(包括简单类型的数组)和实现了java.io.Serializable的类的对象实例(类成员也必须是序列化的即实现了java.io.Serializable接口)

//传值:直接保存到Intent中
Intent toMain=new Intent(LoginActivity.this, ManageUserActivity.class);
toMain.putExtra("LoginUser", "userEdt");
toMain.putExtra("Phone", "phone");
startActivity(toMain);

// 接收
Intent intent = getIntent();  
String loginUser=intent.getStringExtra("LoginUser").toString();
String phone=intent.getStringExtra("Phone").toString();

/***********************传值:先保存到Bundle***************************/
intent = new Intent(); 
Bundle bundle = new Bundle();  
bundle.putString("USERNAME", et_username.getText().toString());  
intent.putExtras(bundle);
//接收
Bundle bundle = this.getIntent().getExtras();  
String str=bundle.getString("USERNAME");

2 通过静态变量对象传值

将类成员定义成public static ,就直接可以通过类成员传递数据,好处是在于可以传递任意类型的数据。

3 通过Android剪切板存取数据

只能直接传递文本数据和Intent对象支持的数据,不过还可以传递可序列化的数据、图像等复杂数据的,思路是用字符串传递二进制数据,将二进制数据转成字符串只需要对其进行编码(一般采用Base64),再传递,接收之后再解码还原。

ClipboardManager clipboard=(ClipboardManager)getSystemService(Context.CLIPBOARD_SERVICE);//得到剪切板对象
clipboard.setText(et_username.getText().toString());

//从剪切板中获取数据
ClipboardManager clipboard=(ClipboardManager)getSystemService(Context.CLIPBOARD_SERVICE);//得到剪切板对象
clipboard.getText();
/*传递复杂数据,可序列化的数据、图像等,Data为一个可序列化的类,有数据成员id,name……,clipData为已经初始化了Data的可序列化实例**/
ClipboardManager clipboard=(ClipboardManager)getSystemService(Context.CLIPBOARD_SERVICE);//得到剪切板对象
Data clipData=new Data();
...
//开始对可序列化实例进行Base64编码
ByteArrayOutputStream bouts=new ByteArrayOutputStream();
String strBase64="";
try{
    ObjectOutputStream oos=new ObjectOutputStream (bouts);
    oos.writeObject(clipData);
    //使用base64的将byte[]数据转成base64 字符串
    strBase64=Base64.encodeToString(bouts.toByteArray(),Base64.DEFAULT);
    oos.close();
}catch(){}
//向剪切板中写入Base64格式的字符串
clipboard.setText(strBase64);
……
/********从剪切板中获取Base64格式的字符串,并解码还原成Data对象********/
//获得剪切板Base64字符串
String reBase64Str=clipboard.getText().toString();
//将Base64格式的字符串还原成byte格式的数据
byte[] buff=Base64.decode(reBase64Str,Base64.DEFAULT);
ByteArrayInputStream bins=new ByteArrayInputStream (buff);
try{
    ObjectInputStream ois=new ObjectInputStream (bins);
    //将byte[]还原成data
    Data data=(data)ois.readObject();
    //输出原始的Base64格式的字符串
    System.out.println("Data.id"+data.getId()+"Data.name"+data.getName());
    ...
}catch(){}

4 通过全局对象

可以为每一个应用程序定义一个全局对象,该对象由系统创建,但是使用全局对象的类必须继承android.app.Application,并在该类中定义成员变量和方法

//1定义继承Application全局对象的类,并不会自动创建全局对象的,因为Android系统并不知道哪个类是全局类
public class DataTemp extends Application{
    public String userName();
    public Data d=new Data();//可不序列化
}
//2在manifest中通过application里的android:name属性定义该全局类

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.weima.amis"
    android:versionCode="1"
    android:versionName="1.0" >
        <application
        android:name=".DataTemp"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
       android:theme="@android:style/Theme.Black.NoTitleBar" >
       ...
//3指定全局类后,在程序运行时,全局对象会被自动创建,而且一直驻留在内存中知道app彻底退出内存可以在应用程序中任何地方获取DataTemp 的对象
DataTemp  dt=(dt)getApplicationContext();

Activity传值小结

根据官方建议,传递简单类型或可序列化的对象,宜采用Intent对象进行数据传递,如果是传递不序列化的对象则可以采用静态变量或全局的对象方式,最后采用全局对象。

7 Activity的关闭

对于单一Activity的应用来说,退出很简单,直接finish()即可。当然,也可以用killProcess()和System.exit()这样的方法,不过这些方法都无法完美的关闭应用程序,现提供几个方法,供参考:
1. 抛异常强制退出:该方法通过抛异常,使程序Force Close。验证可以,但是,需要解决的问题是,如何使程序结束掉,而不弹出Force Close的窗口。
2. 记录打开的Activity:每打开一个Activity,就记录下来。在需要退出时,关闭每一个Activity即可。
3. 发送特定广播:在需要结束应用时,发送一个特定的广播,每个Activity收到广播后,关闭即可。
4. 递归退出在打开新的Activity时使用startActivityForResult,然后自己加标志,在onActivityResult中处理,递归关闭。除了第一个,都是想办法把每一个Activity都结束掉,间接达到目的。但是这样做同样不完美。你会发现,如果自己的应用程序对每一个Activity都设置了nosensor,在两个Activity结束的间隙,sensor可能有效了。但至少,我们的目的达到了,而且没有影响用户使用。为了编程方便,最好定义一个Activity基类,把Activity放到栈中去处理这些共通问题。

你可能感兴趣的:(Android,入门,Android系统组件使用)