深入四大组件之Activity

来源:互联网 发布:淘宝网店货源供应商 编辑:程序博客网 时间:2024/06/04 00:49

Activity是Android的四大组件之一,我是一个新手在刚刚开始学的时候并没有对其有更深入的了解,平时也就是会用。但是当别人问我activity的一些其他知识我还是一窍不通,所以就决定把android的四大组件再去重新的了解一下。这篇博客呢就是加深对Activity的理解。另外其他三大组件会陆续推出。

首先呢,我们来看一下如何深入的了解activity,下面是一张学习路线图


相信大家对Activity有了一定的认识,接下来就开始对这些内容,进行深入的了解

   一:Activity的本质是什么

我的理解是activity就是一个页面的载体,类似于我们的html、jsp、servlet这些,我们在这个载体中来实现对界面的控制,事件的响应,或者与其他activity进行沟通等等一些功能。

  二:Activity的生命周期

(1)单个activity的生命周期   

这里先给出我们的熟悉的生命周期图

  一个Activity在创建和显示的时候,先调用的是1.onCreate, onStart, onResume,方法2,按下back键的时候会调用:onPause,onStop,onDestory方法3.按下home键会调用:onPause onStop4.按下home键后再打开程序:onRestart onStart onResume
相信大家基本上有了了解了,对于这些生命周期我们现在是记住了,但是时间长了说不定我们就忘了(我就是一个笨人老是忘),我在一个视频里面看到过有位大神是这样理解这几种状态的的,我觉得还不错,就拿来用了。
一个Activity生命周期的三种状态:1.显示状态:onCreate,onStart,OnResume2.看不见状态:onPause,onStop3.销毁状态:onDestory

(2)多个Activity之间的生命周期

首先为了演示我们的生命周期,我们需要在我们的项目中定义两个Activity。这里我们分别命名为A(入口),B。在A中定义一个Button可以转入B。由于代码非常简单我们就不贴代码了。接下来运行我们的项目

   1、进入项目->进入A界面执行           onCreate(A)->onStart(A)->onResume(A) 

     2、点击A中的Button进入B              onPause(A)->onCreate(B)->onStart(B)->onResume(B)->onStop(A)  

   3、点击Back键从B进入A界面         onPause(B)->onRestart(A)->onStart(A)->onResume(A)->onStop(B)->onDestory(B)      

现在我们了解了多个Activity之间的生命周期之后可能就会有大量的疑问。下面就简单的把这些疑问解答一下

       1、为什么要先暂停onPause方法,再执行新的Activity的什么周期?答:为了解答这一个问题,我们举一个例子。例如我们在听歌的时候突然之间一个电话打了进来,那么我们的理想状态是什么呢,肯定就是我们的歌先暂停,接下来我们去接电话,接完电话之后,我们的歌重新播放。onPause就是为了解决这一问题的。当我们调用这一个方法的时候。我们的歌就会先暂停下来。否则的话,我们一边接着电话,一边放着歌。用户体验恐怕相当不好吧。      2、为什么不先执行onPause、onStop方法,在执行新的Activity的生命周期答:我们接着上面的例子。当我们接完电话的时候接下来就是回到播放歌曲的界面吧。如果我们先调用了onStop就会留下一个空档区。 什么空档区呢?就是我们电话界面回到我们的歌曲界面的时候。有可能会出现闪退(crush)的现象。

(3)切换横竖屏时的生命周期

     首先为了演示这个例子我们建一个项目。
     运行项目           onCreate->onStart->onResume (创建竖屏)
     执行竖屏切换    onPause->onSaveInstanceState->onStop->onDestroy(销毁横屏)    
                                onCreate->onStart->onResume (创建竖屏)
 整个流程大概就是先创建一个竖屏,然后销毁竖屏,再去重新创建一个横屏 。onSaveInstanceState这个方法是干嘛的呢?就是为了保存我们的横屏信息的一些状态。那我们如何保存呢?
protected void onSavaInstanceState(Bundle outState){
Log.i(TAG,"onSavaInstanceState");
super.onSavaInstanceState(outState);
outState.putString("name","zhangsan")
}
我们首先在这个重载方法里面把我们的状态去保存。然后在我们的onCreate方法里面去拿
if(savaInstanceState!=null){
textView.setText(savadInstanceState.getString("name"));}
但是需要说明的是 ,我们一般在开发的时候不去切换。这样会增大我们的开发难度。也没什么必要对吧。

(4)生命周期的应用场景

这里还是回到我们刚刚举得那个例子吧。我们播放一首歌,这时候就可以根据歌曲的生命周期在我们的Activity的生命周期进行不同的操作。例如在我们的onCreate方法中播放音乐,在onPause方法中暂停音乐,在onDestory方法中释放音乐等等。
只要我们能够理解生命周期,在我们的开发中活学活用。好了Activity的生命周期先告一段落了。

  三  启动Acyivity

      (1)启动Activity的两种方式

       1、隐式调用activity      在mainifest中配置被调用的activity的设置
<activity android:name="A">
<intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
</avtivity>
      2、在类中的onclick方法中        
          Intent intent=new Intent();          intent.setClass(A.this,B.class);表示从A->B          startActivity(intent);
由于这两种启动方式大家都比较熟悉了,代码在这就不做过多的解释了

    (2)启动系统的Activity

在我们Intent中有很多的常量,用于启动系统的Activity,当我们设置不同的常量值时,系统就会调用不同的Activity。在这里我们列举几个在我们开发中常用的几个。当然如果我们想要了解更多的启动系统Activity的常量值的设定的话我们可以到android的API文档中去查看,我们给出其链接地址官方Intent的使用。
1、启动系统浏览器Intent intent=new Intent();intent.setAction(Intent.ACTION_VIEW)Uri url=Uri.parse("http://www.baidu.com");intent.setData(url);startActivity(intent);2、启动系统图库Intent intent=new Intent();intent.setAction(Intent.ACTION_GET_CONTENT);intent.setType("image/*");startActivity(intent);3、启动系统短信Intent intent=new Intent();intent.setAction(Intent.ACTION_SEND);intent.setType("text/plain");intent.putExtra(Intent.EXTRA_TEXT,"hello world");startActivity(intent);4、启动拨号器Intent intent=new Intent();intent.setAction(Intent.ACTION_VIEW)Uri url=Uri.parse("tel:13766257357");intent.setData(url);startActivity(intent);

四、Activity之间的数据传递

我们知道在我们的开发当中,通常需要在两个Activity之间传递数据。但是他们之间如何传递的呢?听我细细道来。

(1)小批量的数据传递

 方式一、使用Intent     比较简单,
我们在A界面定义
        Intent intent = new Intent(A.this,B.class);
        intent.putExtra("username","zhangsan");
        intent.putExtra("password","123456");
        startActivity(intent);
我们在B界面接收
         Intent intent=getIntent();
         if(intent!=null){
               String username=intent.getStringExtra("username");
               String password = intent.getStringExtra("password");
       }
方式二、使用Bundle
我们在A界面定义
         Intent intent = new Intent(A.this,B.class);
        Bundle bundle=new Bundle();
         bundle.putString("name","lisi");
        startActivity(intent);
我们在B界面接收     
         Bundle bundle = this.getIntent().getExtras();
        String name = bundle.getString("name");
前面讲的这两种方式,我们发现基本上没有什么大的区别,但其实他们是有区别的(废话)。设一个数据由A到B再到C。
Intent传递的时候假设由A传递给B。在A中声明的键值对在B中必须要被接受。但在C中就接受不到了。也就是说Intent这种传递方式没有传递性。
Bundle传递的时候就不用考虑在B中接收了。我们在C中根据相应的键值对也可以拿到相应的数据。还可以在中途B中添加数据。

(2)传递大数据

        方式三、传递对象
             Intent intent =new Intent(ThreeActivity.this, FourActivity.class);
             Person person =new Person(1,"小明","北京");
            Bundle bundle =new Bundle();
            bundle.putSerializable("person", person);
            intent.putExtras(bundle);
             startActivity(intent);
这里面我们需要把对象序列化,那什么是序列化呢?Serializable 是序列化接口,对象如果实现这个接口的话,就是代表可以序列化,可以序列化的意思你可以这么理解,对象可以作为字节流进行传输或保存。不用它,是不能完成数据传递的,intent和budle都不能携带对象,但能携带实现序列化的对象
  方式四、传递图片
传递Bitmap对象、Intent intent = new Intent( );Bundle bundle = new Bundle();Bitmap bitmap = BitampFactory.decodeResource(getResoruces(),R.drawable.ic_launch);bundler.putParcelable("bitmap",btimap);intent.putExtras(bundle);startActivity(intent);接收数据Intent intent=getIntent();if(intent!=null){ Bitmap bitmap=intent.getParcelableExtra("bitmap"); imageView.setImageBitmap(bitmap);}
bundle传递数据大于0.5M会抛出传输数据过大异常;在传输大量数据的时候也有可能抛出TransactionTooLargeException异常,解决办法是减少bundle传输的数据量
但是如果我们碰到要传输大量数据的时候该用什么方法呢?那就太多了。我们可以使用File,SqLite数据库等等。IPC通讯机制也可以。传递大量数据会有点麻烦。

五、Activity栈

Task和BackStack概念,我们这里给出官方的介绍
file:///D:/software/Eclipse/adtbundlewindowsx8620140702/adtbundlewindowsx8620140702/sdk/android_sdk_docs_offline_20/docs/guide/components/tasks-and-back-stack.html
1. 启动一个app,framework都会为这个app分配一个Task,有对应的TaskID和stack,然后在这个APP中所有的activity都共用一个TaskID和stack。  
       2. 当从一个app的进程跳转到另一个app进程时,还是在同一个task中,这就是Task允许跨进程间调用。通过在manifest中声明activity "android:process=XXX"可以指定这个activity为另一个进程       
        3. Back Stack        
       一个Task就是一系列的activity的集合,这些activity以打开的顺序放入一个stack中,最后打开的activity最先出来。对于一个app来说,main activity就在栈顶的位置。在一个app中可能会调用其他app的activity,但即使两个activity不在同一个APP中,但是其TaskID是同一个,属于同一个Task Stack       
        4. android中的多任务         
         假设启动了APP1,对应创建了一个Task A,它有三个activity。然后回到home screen,启动APP2,对应创建了一个Task B。此时,Task A的activity虽然全部stop了,但是Task A的stack还是完整存在的。然后可以通过回到home screen或点击recent apps启动Task A,它的activity回到前台。     
        5. dumpsys activity            
          (1)running activities部分可以查看当前正在运行的activity             
          (2)mFocusedActivity可以查看正在获取焦点的activity            
          (3)Recent tasks可以查看最近的一些任务
      接下来我们给出一个例子看一下,到底是如何运行的(在一个Activity中打开一个新的Activity) 

重要提示:在Manifest文件中给要新打开的Activity配置一个Android:process属性,指定该属性后这个Activity就属于另外一个进程的Activity,就会在另外一个进程中创建该Activity。可以在上面两个Activity的onCreate方法中用Log日志打印一下当前Activity的task id使用getTaskid()方法。两个提示示例代码如下:

<activity android:name=".NewActivity" android:process=":NewActivity"/>
  • 1
  • 1
// 在MainActivity的onCreate方法中添加Log.i("Tag", "MainActivity taskId"+getTaskId());// 在NewActivity的onCreate方法中添加Log.i("Tag", "NewActivity taskId"+getTaskId());
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

写完后运行Demo,在MainActivity中点击按钮打开新的NewActivity。切换到eclipse的DBMS界面可以查看到这两个Activity的进程id是不同的 
这里写图片描述

在查看后台打印的Log他们的testId都是相同的所以他们是在同一个任务栈中 
这里写图片描述


我们也可以使用命令的方式去查看当前的任务栈。方法如下
步骤一:配置adb环境    http://jingyan.baidu.com/article/17bd8e52f514d985ab2bb800.html
        步骤二:打开我们的DOS,输入adb shell dumpsys activity
                       Running activities 就是当前活动的Activity,Recent tasks就是模拟器曾经运行过的。



六、启动模式

          第一种:standard   标准模式,可以重复启动同一个activity。也就是每次创建一个activity的时候,就会在我们的回退栈中添加一个Activity的实例。尽管创建的是同一个Activity。
          第二种:singleTop  只有在某activity为栈顶activity,在重复启动时会调用onNewInstance()方法,不会被重建。就是说当我们在创建Activity的时候,如果当前的回退栈中的栈顶就是我们要创建的activity,那么将不创建此activity实例了。直接使用栈顶的这个。但是如果栈顶的activity不是我们创建的activity的实例,那么就算栈中或者是栈低有,依然会创建这个实例。
          第三种:singleTask   首先会在任务栈中搜索是否包含该activity,如果有重新创建调用onNewInstance。其他的被Destroy掉。也就是说,在创建activity的时候会看整个回退栈中是否有次activity的实例。如果有就不创建了,直接使用回退栈里面的这个activity实例。在这个实例上面的其他activity的实例,全部清除。
          第四种:singleInstance    与singleTask类似,只是singleInstance定义的activity会重新分配一个栈地址,
如图所示。这里面有两个任务栈。启动顺序是Main1->FirstActivity->Main2->FirstActivity->Main3
当我们点击back键的时候是如何调用的呢?Main3->Main2->Main1->FirstActivity



  七、activity的应用实例

         我们在用其他一些软件的时候经常会看到这样一种情况,当我们点击back键的时候,然后弹出一个Toast说再次点击退出软件。但是根据前面的回退栈知识我们知道,如果我们点击back键的时候只会回到上一个activity。那么这种效果是如何实现的呢?其实答案也在这种回退栈中。假设有A,B,C,D四个Activity。由A-》B-》C-》D。我们此时在D。在D中点击back按钮实现退出软件的效果,而不是回到C。我们看一下效果图吧

        我们首先定义一个公共类
public class ActivityCollector {//声明一个List集public static List<Activity> activities=new ArrayList<Activity>();//将activity添加到List集中public static void addActivity(Activity activity){activities.add(activity);}//将某一个Activity移除public static void removeActivity(Activity activity){activities.remove(activity);}//结束所有添加进来的的Activitypublic static void finishAll(){for(Activity activity:activities){//如果activity没有销毁,那么销毁if(!activity.isFinishing()){activity.finish();}}}}
作用一看就明白了吧。我们再定义一个BaseActivity。让A,B,C,D全都继承它。
public class BaseActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);//将得到的Activity的名字在后台打印Log.i("info", getClass().getSimpleName());//将启动的Activity添加进来ActivityCollector.addActivity(this);}@Overrideprotected void onDestroy() {ActivityCollector.removeActivity(this);super.onDestroy();}}
这个类的作用就是,在A,B,C。D启动的时候将其实例添加到List集合中。退出的时候删除实例。
         然后我们再定义A,B,C,D.由于A,B,C相似这里只给出A。只有D稍有不同。
  
public class A extends BaseActivity {private Button bt1;@Overrideprotected void onCreate(Bundle savedInstanceState) {setContentView(R.layout.activity_a);super.onCreate(savedInstanceState);bt1=(Button) findViewById(R.id.button1);bt1.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {Intent intent=new Intent(A.this,B.class);startActivity(intent);}});}}
下面是D的只有一行不同。也特别简单
public class D extends BaseActivity {private Button bt1;@Overrideprotected void onCreate(Bundle savedInstanceState) {setContentView(R.layout.activity_d);super.onCreate(savedInstanceState);bt1=(Button) findViewById(R.id.button4);bt1.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {//结束所有的Activity退出程序.ActivityCollector.finishAll();}});}}
这就是所有的代码了。当我们点击D中的按钮之后,就能退出所有的Activity了。
  
   目前Activity就告一段落了,如果里面有错误,希望大家提出来帮忙改正。
2 0