android学习四(Activity的生命周期)

   要学好活动(Activity),就必须要了解android中Activity的声明周期,灵活的使用生命周期,可以开发出更好的程序,在android中是使用任务来管理活动的,一个任务就是一组存放在栈里的活动的集合,这个栈也被称作返回栈。栈的特性是后进先出,在默认的情况下,每当我们启动了一个新的活动,它会在返回栈中入栈,并处于栈顶的位置。而每当我们按下Back键或调用finish方法去销毁一个活动时,处于栈顶的活动会出栈,这时前一个入栈的活动就会重新处于栈顶的位置。系统总是会显示处于栈顶的活动给用户。

1.活动的四种状态:

运行状态:当一个活动位于返回栈的栈顶时,这是活动就处于运行状态。系统不会回收处于运行状态的活动。

暂停状态:当一个活动不再处于栈顶位置,但仍然可见时,这时活动就进入了暂停状态。

停止状态:当一个活动不在处于栈顶位置,并且完全不可见的时候,就进入了停止状态

销毁状态:当一个活动从返回栈中移除后就变成了销毁状态。


2.活动的生命周期,活动中定义了7个回调方法

onCreate方法在活动第一次被创建的时候调用,在这个方法中,应该完成活动的初始话操作,比如加载布局,绑定事件等。

onStart方法在活动由不可见变为可见的时候调用

onResume在活动准备好和用户进行交互的时候调用,此时活动处于返回栈的栈顶。

onPause方法在系统准备去启动或者恢复另一个活动的时候调用。通常会在这个方法中将一些销毁cpu的资源释放掉,以及保存一些关键数据,但这个方法的执行速度一定要快,不然会影响新的栈顶活动的使用。

onStop这个方法在活动完全不可见的时候调用。它和onPause方法的主要区别在于,如果启动的新活动是一个对话框式的活动,那么onPause方法会得到执行,而onStop方法并不会执行。

onDestory方法在活动被销毁之前调用,之后活动的状态将变为销毁状态。

onRestart方法在活动由停止状态变为运行状态之前调用,也就是活动的重启了。


3.活动的3个周期

完整生命周期:活动在onCreate方法和onDestroy方法之间所经历的,就是完整生存周期。一般情况下一个活动会在onCreate方法中完成各种初始话操作,而在onDestory方法中完成释放操作。

可见生存周期:活动在onStart方法和onStop方法之间所经历的,就是可见生存周期。在可见生存周期内,活动对于用户总是可见的,即便有可能无法和用户进行交互。我们可以通过这两个方法,合理的管理那些对用户可见的资源。比如在onStart方法中对资源进行加载,而在onStop方法中对资源进行释放,从而保证处于停止状态的活动不会占用过多的内存。

前台生存期:活动在onResume方法和onPause方法之间所经历的,就是前台生存期。在前台生存期内,活动总是处于运行状态,此时活动是可以和用户进行交互的。



上面说了那么多的理论知识,下面就让我们开始编程测试下活动的生命周期吧!!!

1.先建一个android项目,项目名为ActivityLifeCycleTest,采用默认的设置,填写好包名。

    下面还需要创建2个活动,进行生命周期的测试。

2.建立一个布局文件,文件名为normal_layout.xml,代码如下

<span style="font-size:18px;"><?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
    
    <TextView 
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="this is a normal activity"/>
    

</LinearLayout></span>


3.在建立一个布局文件,文件名为dialog_layout.xml代码如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
    

    <TextView 
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="this is a dialog activity"/>
    
</LinearLayout>

4.新建一个活动,类名为NormalActivity,继承Activity代码如下:

package com.wj.activitylifecycletest;

import android.app.Activity;
import android.os.Bundle;
import android.view.Window;

public class NormalActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		setContentView(R.layout.normal_layout);
	}

	
}

5.在新建一个活动,活动名为DialogActivity代码如下:

package com.wj.activitylifecycletest;

import android.app.Activity;
import android.os.Bundle;
import android.view.Window;

public class DialogActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		setContentView(R.layout.dialog_layout);
	}

	
}


6.给活动进行注册,代码如下:

<activity android:name=".NormalActivity">
            
        </activity>
        
        <!--android:theme="@android:style/Theme.Dialog"是让活动以对话框的新式显示  -->
        <activity android:name=".DialogActivity"
            android:theme="@android:style/Theme.Dialog">
            
        </activity>

7.修改主布局文件acitvity_main.xml代码如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
    
    <Button 
        android:id="@+id/start_normal_activity"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="start NomalActivity"/>
    <Button 
        android:id="@+id/start_dialog_activity"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="start DialogActivity"/>
    
</LinearLayout>


上面定义了2个按钮,用来启动一个正常的活动,和一个以对话框形式显示的活动。



8.修改MainActivity里面的方法,重写activity的生命周期方法,对按钮进行监听,代码如下:

package com.wj.activitylifecycletest;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.widget.Button;

public class MainActivity extends Activity {

	public static final String TAG="MainActivity";
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		Log.d(TAG, "onCreate");
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		setContentView(R.layout.activity_main);
		
		//获得组件
		Button startNormalActivity=(Button) findViewById(R.id.start_normal_activity);
		Button startDialogActivity=(Button) findViewById(R.id.start_dialog_activity);
		
		startNormalActivity.setOnClickListener(new OnClickListener(){

			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				Intent intent =new Intent(MainActivity.this,NormalActivity.class);
				startActivity(intent);
			}
			
		});
		
		
		startDialogActivity.setOnClickListener(new OnClickListener(){

			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				Intent intent =new Intent(MainActivity.this,DialogActivity.class);
				startActivity(intent);
			}
			
		});
		
		
	}

	
	
	
	
	
	
	@Override
	protected void onDestroy() {
		// TODO Auto-generated method stub
		super.onDestroy();
		Log.d(TAG, "onDestroy()");
	}







	@Override
	protected void onPause() {
		// TODO Auto-generated method stub
		super.onPause();
		Log.d(TAG, "onPause()");
	}







	@Override
	protected void onRestart() {
		// TODO Auto-generated method stub
		super.onRestart();
		Log.d(TAG, "onRestart()");
	}







	@Override
	protected void onResume() {
		// TODO Auto-generated method stub
		super.onResume();
		Log.d(TAG, "onResume()");
	}







	@Override
	protected void onStart() {
		// TODO Auto-generated method stub
		super.onStart();
		Log.d(TAG, "onStart()");
	}







	@Override
	protected void onStop() {
		// TODO Auto-generated method stub
		super.onStop();
		Log.d(TAG, "onStop()");
	}







	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

}

代码比较的简单,就不啰嗦了,下面我们运行观察结果吧!!!!



9.运行程序,观察Logcat的输出

   运行程序:


可以看到执行了onCreate,onStart,onResume方法。

在点击start nomalActivity按钮




启动新的活动后,调用了onPause和onStop方法。NormalActivity显示在界面了。

在按back键进行返回



可以观察到调用了onRestart,onStart和onResume方法,在点击start DailogActivity按钮




可以看到,指调用了onPause方法,因为MainActivity只有部分被遮盖了。在按back键,观察只调用了onResume方法。


在按back键观察如下输出:


可见程序调用了onPause,onStop,onDestory方法后就销毁了活动了

通过上面的演示,相信你对活动的生命周期有了一定的了解了吧,有不懂的,还可以交流啊!!!




下面我们来学习,保存用户的数据的activity。在Activity中提供了一个onSaveInstanceState方法,这个方法会保证一定在活动被回收之前调用,因此我们可以通过这个方法来解决活动被回收时零时数据得不到保存的问题。

在活动中重写onSaveInstanceState(Bundle outState)方法,代码如下:

@Override
	protected void onSaveInstanceState(Bundle outState) {
		// TODO Auto-generated method stub
		super.onSaveInstanceState(outState);
		String tempData="something you just typed";
		/*
		 * onSaveInstanceState方法携带了一个Bundle类型的参数,Bundle提供了一系列的
		 * 方法用于保存数据,具体的可以查看api,主要方法都有key-value参数,来保存数据的。
		 * */
		outState.putString("data_key", tempData);
	}

这样的话,在活动被回收的时候,onStop方法被调用之前,使用了onSaveInstanceState(Bundle outState)进行数据的保存了,那么该在哪里进行数据的恢复了?观察onCreate方法的参数有一个Bunndle的参数,所以就应该在onCreate函数中进行数据的恢复,如下:

if(savedInstanceState!=null){
			String tempData=savedInstanceState.getString("data_key");
			Log.d(TAG, tempData);
		}

onSaveInstanceState(Bundle outState)方法在活动暂停的时候调用了onPause方法后也会调用onSaveInstanceState(Bundle outState)方法,onSaveInstanceState(Bundle outState)方法在onStop方法之前得到调用了,在onCreate方法中进行恢复。


活动的启动模式

活动的启动模式一共有四种,在实际的项目中,可以根据特定的需求为每个活动指定恰当的启动模式。四种模式分别是standard,singleTop,singleTask,singleInstance,可以在AndroidManifest.xml中通过给<activity>标签指定android:launchMode属性来选择启动模式。


在这里我们使用ActivityTest这个项目进行模式的测试,ActivityTest这个项目的源码我已经在前面给出来了,所以这里就不在给出完整的代码了。

standard是活动默认的启动模式,在不进行显示指定的情况下,所有活动都会自动使用这种模式。因此,到目前为止我们写过的所有活动都是使用的standard模式。我们知道android中是使用返回栈来管理活动的,在standard模式下,每当启动一个新的活动,它就会在返回栈中入栈,并处于栈顶的位置。对于使用standard模式的活动,系统不会在乎这个活动释放已经在返回栈中,每次启动都会创建该活动的一个新实例。

修改FirstActivity中的onCreate方法里面的代码。

package com.wj.test;

import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.widget.Button;
import android.widget.Toast;

public class FirstActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		Log.d("FirstActivity", this.toString());
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		setContentView(R.layout.first_layout);
		Button button1=(Button) findViewById(R.id.button1);
		button1.setOnClickListener(new OnClickListener(){

			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
/*				Toast.makeText(FirstActivity.this, "you clicked button1",
					Toast.LENGTH_SHORT).show();*/
				/*
				 * Intent的使用分为显示的Intent和隐式的Intent,下面的是显示的Intent
				 * Intent有多个构造函数,我使用的是一个简单的的,第一个参数Context要求提供一个启动
				 * 活动的上下文,第二个参数Class则是指定想要启动的目标活动,通过这个构造函数构建出Intent
				 * 的“意图”,然后使用startActivity(intent);启动目标活动。
				 * Intent是android程序中个组件之间进行交互的一种重要的方式,它不紧可以指明当前组想要执行
				 * 的动作,还可以在不同组件之间传递数据。Intent一般可被用于启动活动,服务,以及发送广播等。
				 * */
				/*Intent intent=new Intent(FirstActivity.this,
						SecondActivity.class);
				startActivity(intent);*/
				
				//使用隐式的Intent
				/*Intent intent=new Intent("com.wj.test.activitytest.ACTION_START");
				intent.addCategory("com.wj.activitytest.MY_CATEGORY");
				startActivity(intent);*/
				
				//更多隐式的Intent的用法
				//打开浏览器
				/*
				 * 这里我们指定了Intent的action是Intent.ACTION_VIEW,这是一个android系统内置的动作,
				 * 其常量为android.intent.action.VIEW,然后通过Uri.parse方法,将一个网址字符串解析成一个
				 * Uri对象,在调用Intent的setData()方法将这个对象传递进去。
				 * */
				/*Intent intent=new Intent(Intent.ACTION_VIEW);
				intent.setData(Uri.parse("http://www.baidu.com"));
				startActivity(intent);*/
				
				
				//实现拨打电话
				/*Intent intent=new Intent(Intent.ACTION_DIAL);
				intent.setData(Uri.parse("tel:10086"));
				startActivity(intent);*/
				
				
				//向下一个活动传递数据
				/*
				 * Intent中提供了一系列putExtra()方法的重载,可以把我们想要传递的数据暂存在Intent中
				 * ,启动了另一个活动后,只需把这些数据再从Intent中取出就可以了。
				 * */
				/*String data="Hello SecondActivity";
				Intent intent =new Intent(FirstActivity.this,SecondActivity.class);
				intent.putExtra("extra_data", data);
				startActivity(intent);*/
				
				
				//返回数据给上一个活动
				//Intent intent=new Intent(FirstActivity.this,SecondActivity.class);
				/*
				 * startActivityForResult方法接收2个参数,一个参数是Intent,第二个参数是请求码
				 * ,用于在之后的的回调中判断数据的来源
				 * */
				//startActivityForResult(intent, 1);
				
				
				
				//测试android活动的standard模式
				Intent intent=new Intent(FirstActivity.this,FirstActivity.class);
				startActivity(intent);
			}
			
		});
	}

	
	
	
	
	//重写onActivityResult方法获取返回的结果数据
	//@Override
	/*protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		// TODO Auto-generated method stub
		switch(requestCode){
		case 1:
			if(resultCode==RESULT_OK){
				String returnedData=data.getStringExtra("data_return");
				Log.d("FirstActivity", returnedData);
			}
			break;
		default:break;
		}
		
	}*/






	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// TODO Auto-generated method stub
		getMenuInflater().inflate(R.menu.main, menu);
		/**
		 * getMenuInflater()方法能够得到MenuInflater对象,在调用他的inflate方法就可以给
		 * 当前活动创建菜单了。inflate方法接收两个参数,第一个参数用于指定我们通过哪一个资源文件来创建
		 * 菜单,这里当然传入R.menu.main,第二个参数用于指定我们的菜单将添加到哪一个Menu对象中,这里直接
		 * 使用onCreateOptionsMenu(Menu menu)传入的menu参数。然后给这个方法返回true,表。示
		 * 允许创建的菜单显示出来,如果返回false,创建的菜单将无法显示
		 *
		 */
	
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		// TODO Auto-generated method stub
		/*
		 * 通过调用item.getItemId()来判断我们点击的是哪一个菜单项。
		 * */
		switch(item.getItemId()){
		case R.id.add_item:
			Toast.makeText(FirstActivity.this, "you clicked add",
					Toast.LENGTH_SHORT).show();
			break;
		case R.id.remove_item:
			Toast.makeText(FirstActivity.this, "you clicked remove",
					Toast.LENGTH_SHORT).show();
			break;
		default:break;
		}
		//return super.onOptionsItemSelected(item);
		return true;
	}

	
	
	
	
}



运行程序,点击button1出现下面的输出,可以知道,每次点击都重写创建了一个活动了,因为他们的地址不一样了。
10-29 14:10:25.231: D/FirstActivity(3961): com.wj.test.FirstActivity@40cf61d8

10-29 14:10:34.391: D/FirstActivity(3961): com.wj.test.FirstActivity@40cfd590

10-29 14:11:59.410: D/FirstActivity(3961): com.wj.test.FirstActivity@40d045e0

每次点击都会创建一个新的FirstActivity实例,返回时,你创建了几个实例就需要按几次back键才能退出程序。



singleTop:当活动的启动模式指定是singleTop,在启动活动时,如果发现返回栈的栈顶已经是该活动,则认为可以直接使用它,不会在创建新的活动实例。

我们修改下AndroidManifest.xml中的FirstActivity的启动模式,如下所示:

<activity 
            android:name="com.wj.test.FirstActivity"
            android:launchMode="singleTop"
            android:label="this is firstactivity">
            <intent-filter >
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>

再次运行程序,你会看到已经创建了一个FirstActivity的实例了,之后不管你点击多少下,都不会有新的信息出现了,因为目前FirstActivity已经处于返回栈的栈顶了,每当想要启动一个FirstActivity时都会直接使用栈顶的活动,因为FirstActivity也只会有一个实例,仅按一次back键就可以退出程序了。

不过当FirstActivity并为处于栈顶的位置时,这时启动FirstActivity,还是会创建新的实例的。


singleTask:当活动的启动模式指定为singleTask,每次启动该活动时系统首先会在返回栈中检查是否存在该活动的实例,如果发现已经存在则直接使用该实例,并把在这个活动之上的活动统统出栈,如果没有发现就会创建一个新的活动。


singleInstance:指定为singleInstance模式的活动会启用一个新返回栈来管理这个活动。假设我们的程序中有一个活动是允许其他程序调用的,如果我们想实现其他程序和我们的程序可以共享这个活动的实例,其他的三种模式就不能实现这中共享了,因为每个应用程序都会有自己的返回栈的。而我们使用singleInstance模式就可以解决这个问题,在这种模式下会有一个单独的返回栈来管理这个活动,不管是哪个应用程序来访问这个活动,都共用一个返回栈,也就解决了共享活动的实例的问题。



活动的最佳实践。


知晓当前是哪对应一个活动:这主要是为了解决看别人的代码的时候,不知道当前的界面对应的是哪个活动的问题。

建立一个基类,继承Activity代码如下:

package com.wj.test;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;

public class BaseActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		Log.d("BaseActivity", getClass().getSimpleName());
	}

	
}


我们在onCreatey方法中获取了当前实例的类名,并通过Log打印出来,以后只要让BaseActivity成为所有活动的父类,这样当我们每进入一个活动的界面,该活动的类名就会被打印出来了,这样我们就可以知道当前界面对应哪一个活动了。



随时随地的退出程序:当我们在程序中接连启动几个程序时,需要按几次back键才能退出程序,按home键只能把程序挂起,并没有退出。那么我们该怎么实现注销或者退出的功能了?必须随时随地都能退出程序的方案才行。解决思路:用一个专门的集合类对所有的活动进行管理就可以了,下面实现部分代码。

首先来一个管理活动的类代码如下:

package com.wj.test;

import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
/*
 * 在活动的管理中我们,通过一个List来暂存活动,然后提供了一个addActivity(Activity activity)
 * 方法用于向List中添加活动,提供了一个removeActivity(Activity activity)方法用于从List中
 * 移除活动,提供了一个finishAll()方法用于将存储的活动全部都销毁掉。
 * */
public class ActivityCollector {

	public static List<Activity> activities=new ArrayList<Activity>();
	//添加活动
	public static void addActivity(Activity activity){
		activities.add(activity);
	}
	
	//删除活动
	public static void removeActivity(Activity activity){
		activities.remove(activity);
	}
	
	public static void finishAll(){
		for(Activity activity:activities){
			if(!activity.isFinishing()){
				activity.finish();
			}
		}
	}
	
	
}


然后在修改BaseActivity类的代码:

package com.wj.test;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;

public class BaseActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		Log.d("BaseActivity", getClass().getSimpleName());
		ActivityCollector.addActivity(this);
	}

	@Override
	protected void onDestroy() {
		// TODO Auto-generated method stub
		super.onDestroy();
		ActivityCollector.removeActivity(this);
	}

	
	
	
	
}

在BaseActivity的onCreate方法中调用了ActivityCollector的addActivity方法,表明将当前正在创建的活动添加到活动管理器中,然后在onDestory方法中调用ActivityCollector的removeActivity方法,表明将一个马上要销毁的活动从活动管理器里移除。从此,不管想在什么地方退出程序,只需要调用ActivityCollector的finishAll方法就行了。你可以在销毁所有活动后,在加上杀掉当前进程的代码。



启动活动的最佳实践:解决对接的问题,我们不知道启动活动,需要哪些参数的情况下

我们在一个活动中加入下面的一个函数:

public static void actionStart(Context context,
			String data1,String data2){
		Intent intent =new Intent(context,SecondActivity.class);
		intent.putExtra("param1", data1);
		intent.putExtra("param2", data2);
	}


当我们要启动SecondActivity这个活动的时候,只需要在在其他的活动中调用actionStart方法,并传入相应的参数即可了。


活动的使用就写到着把,以后有什么更好的例子了,我在进行补充。

我总结的内容都来至第一行代码android,学了后感觉收获很多,写的很容易理解,在此感谢第一行代码android的作者,郭神


转载请注明来至:http://blog.csdn.net/j903829182/article/details/40555119






郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。