2. 先從看得到的入手-探究活動(dòng)

1.活動(dòng)(Activity)

Activity是一種可以包含用戶界面的組件,主要用于和用戶進(jìn)行交互。
手動(dòng)創(chuàng)建活動(dòng),我們?cè)趧?chuàng)建的時(shí)候,選擇Add No Activity,這個(gè)時(shí)候app/src/main/java/com.example.activitytest目錄是空的。我們右鍵新建一個(gè)活動(dòng),但是不要勾選Generate Layout FileLauncher Activity.

image

Generate Layout File: 表示會(huì)自動(dòng)為FirstActivity創(chuàng)建一個(gè)對(duì)應(yīng)的布局文件
Launcher Activity: 表示會(huì)自動(dòng)將FirstActivity設(shè)置為當(dāng)前項(xiàng)目的主活動(dòng)。
項(xiàng)目中的任何活動(dòng)都應(yīng)該重寫ActivityonCreate()方法。
Android Studio為我們提供了可視化布局編輯器,在窗口的最下方有兩個(gè)切換卡,左邊是Design,是當(dāng)前的可視化布局編輯器,右邊是Text,通過XML文件的方式來編輯布局的。
setContentView()方法給當(dāng)前的活動(dòng)加載一個(gè)布局,在方法中我們一般會(huì)傳入一個(gè)布局文件的id.項(xiàng)目中添加的任何資源都會(huì)在R文件中生成一個(gè)相應(yīng)的資源id

image

上圖是手動(dòng)配置的主活動(dòng),其中android:label 指定活動(dòng)中標(biāo)題欄的內(nèi)容,標(biāo)題欄是顯示在活動(dòng)最頂部的,需要注意的是,給主活動(dòng)指定的label不僅會(huì)成為標(biāo)題欄中的內(nèi)容,還會(huì)成為啟動(dòng)器(Launcher)中應(yīng)用程序顯示的名稱。
如果你的應(yīng)用程序中沒有聲明任何一個(gè)活動(dòng)作為主活動(dòng),這個(gè)程序仍然是可以正常安裝的,只是你無法在啟動(dòng)器中看到或者打開這個(gè)程序,這種程序一般都是作為第三方服務(wù)供其它應(yīng)用在內(nèi)部進(jìn)行調(diào)用的,如支付寶快捷支付服務(wù)等。

2. Toast

image

在活動(dòng)中,可以通過findViewById()方法獲取到在布局文件中定義的元素,findViewById()方法,返回的是一個(gè)View對(duì)象,我們需要向下轉(zhuǎn)型將他轉(zhuǎn)成Button對(duì)象,調(diào)用setOnClickListener()方法為按鈕注冊(cè)一個(gè)監(jiān)聽器,點(diǎn)擊按鈕時(shí)就會(huì)執(zhí)行監(jiān)聽器中的onClick()方法。
通過靜態(tài)方法makeText()創(chuàng)建出一個(gè)Toast對(duì)象,然后調(diào)用show()方法將Toast顯示出來就可以了。makeText()方法需要傳入三個(gè)參數(shù),第一個(gè)參數(shù)是Context對(duì)象,也就是Toast要求的上下文,第二個(gè)參數(shù)是Toast顯示的文本內(nèi)容,第三個(gè)參數(shù)是Toast顯示的時(shí)長(zhǎng)。有兩個(gè)內(nèi)置常量可以選擇Toast.LENGTH_SHORTToast.LENGTH_LONG

3.在活動(dòng)中使用Menu

image

其中<item>標(biāo)簽就是用來創(chuàng)建具體的某一個(gè)菜單項(xiàng),然后通過android:id給這個(gè)菜單項(xiàng)指定一個(gè)唯一的標(biāo)識(shí)符,通過android:title給這個(gè)菜單項(xiàng)指定一個(gè)名稱。

@Override
    public boolean onCreateOptionsMenu(Menu menu)
    {
        getMenuInflater().inflate(R.menu.main,menu);
        return true;
    }

通過getMenuInflater()方法能夠得到MenuInflater對(duì)象,在調(diào)用它的inflater()方法,就可以給當(dāng)前的活動(dòng)創(chuàng)建菜單了,inflater()方法接受兩個(gè)參數(shù),第一個(gè)參數(shù)用于指定我們通過哪一個(gè)資源文件來創(chuàng)建菜單,第二個(gè)參數(shù)用于指定我們的菜單項(xiàng)將添加到哪一個(gè)Menu對(duì)象中去。這里直接使用onCreateOptionMenu()方法中傳入的Menu參數(shù),然后給這個(gè)方法換回true,表示允許創(chuàng)建的菜單顯示出來。如果返回false,創(chuàng)建的菜單將無法顯示。

菜單響應(yīng)事件

 @Override
    public boolean onOptionsItemSelected(MenuItem item)
    {
        switch (item.getItemId())
        {
            case R.id.add_item:
                Toast toast1 = Toast.makeText(FirstActivity.this,"You clicked Add",
                        Toast.LENGTH_LONG);
                toast1.show();
                break;
            case R.id.remove_item:
                Toast toast2 = Toast.makeText(FirstActivity.this,"You clicked Remove",
                        Toast.LENGTH_LONG);
                toast2.show();
                break;
            default:
        }
        return true;
    }

調(diào)用item.getItemId() 來判斷我們點(diǎn)擊的是哪一個(gè)菜單項(xiàng)。

4.Intent的使用

IntentAndroid程序中各組件之間進(jìn)行交互的一種重要方式,它不僅可以指明當(dāng)前組件想要執(zhí)行的動(dòng)作,還可以在不同組件之間傳遞數(shù)據(jù)。Intent一般可被用于啟動(dòng)活動(dòng),啟動(dòng)服務(wù)以及發(fā)送廣播等場(chǎng)景。
Intent大致可以分為兩種:顯式Intent和隱式Intent.
顯式Intent

Intent intent = new Intent(FirstActivity.this;SecondActivity.class);
startActicity(intent);

Intent有多個(gè)構(gòu)造函數(shù)的重載,其中一個(gè)是Intent(Context packageContext,Class<?> cls).這個(gè)構(gòu)造函數(shù)接受兩個(gè)參數(shù),第一個(gè)參數(shù)Context要求提供一個(gè)啟動(dòng)活動(dòng)的上下文,第二個(gè)參數(shù)Class則是指定想要啟動(dòng)的目標(biāo)活動(dòng),通過這個(gè)構(gòu)造函數(shù)就可以構(gòu)造出Intent的意圖.Activity類中提供了一個(gè)startActivity()方法,這個(gè)方法是專門用于啟動(dòng)活動(dòng)的,他接受一個(gè)Intent參數(shù),這里我們將構(gòu)建好的Intent傳入startActivity()方法就可以啟動(dòng)目標(biāo)活動(dòng)了。
使用這種方式來啟動(dòng)活動(dòng),Intent的意圖非常明顯,因此我們稱之為顯式Intent
隱式Intent
相比于顯式Intent,隱式Intent則含蓄了很多,他并不明確指出我們想要啟動(dòng)哪一個(gè)活動(dòng),而是制定了一系列更為抽象的action和category等信息,然后交于系統(tǒng)去分析這個(gè)Intent,并幫我們找出合適的活動(dòng)去啟動(dòng)。

        <activity 
            android:name=".SecondActivity">
            <intent-filter>
                <action android:name="com.example.activitytest.ACTION_START"/>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </activity>

在<action>標(biāo)簽中我們聲明了當(dāng)前活動(dòng)可以相應(yīng)com.example.activitytest.ACTION_START這個(gè)action,而<category>標(biāo)簽則包含了一些附加信息,更精確的指明了當(dāng)前活動(dòng)能夠響應(yīng)的Intent中還可能帶有category.只有<action>和<category>中的內(nèi)容同時(shí)能夠匹配上Intent中指定的action和category時(shí),這個(gè)活動(dòng)才能夠響應(yīng)該Intent.

Intent intent = new Intent("com.example.activitytest.ACTION_START");
startActivity(intent);

我們使用了Intent的另一個(gè)構(gòu)造函數(shù),直接將action的字符串傳了進(jìn)去,表明我們想要啟動(dòng)能夠響應(yīng)com.example.activitytest_ACTION_START這個(gè)action的活動(dòng),android.intent.category.DEFAULT是一種默認(rèn)的category,在調(diào)用startActivity()方法的時(shí)候,會(huì)自動(dòng)將這個(gè)category添加到Intent中。
每個(gè)Intent中只能指定一個(gè)action,但卻能指定多個(gè)category.

 Intent intent = new Intent("com.example.activitytest.ACTION_START");
 intent.addCategory("com.example.activitytest.MY_CATEGORY");
 startActivity(intent);

我們調(diào)用Intent中的addCategory()方法來添加一個(gè)category,我們指定了一個(gè)自定義的category,值為:com.example.activitytest.MY_CATEGORY.如果我們?cè)谶\(yùn)行的話會(huì)出現(xiàn)這樣的錯(cuò)誤:

Process: com.example.activitytest, PID: 12083    
android.content.ActivityNotFoundException: No Activity found to handle  
Intent { act=com.example.activitytest.ACTION_START  cat=[com.example.activitytest.MY_CATEGORY] }  

因?yàn)椴]有活動(dòng)去響應(yīng)我們新增加的category.

<activity
            android:name=".SecondActivity">
            <intent-filter>
                <action android:name="com.example.activitytest.ACTION_START"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <category android:name="com.example.activitytest.MY_CATEGORY"/>
            </intent-filter>
        </activity>

我們?cè)黾右粋€(gè)category就可以了。
更多隱式Intent的用法
使用隱式Intent,我們不僅可以啟動(dòng)我們自己程序向內(nèi)的活動(dòng),還可以啟動(dòng)其他程序的活動(dòng),這使得Android多個(gè)應(yīng)用程序之間的功能共享成為了可能。

 Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("http://www.baidu.com"));
startActivity(intent);

我們指定了Intent的action是Intent.ACTION_VIEW,這是一個(gè)Android系統(tǒng)內(nèi)置的活動(dòng),其常量值為android.intent.action.View.然后通過Uri.parse()方法,將一個(gè)網(wǎng)址字符串解析成一個(gè)Uri對(duì)象,在調(diào)用Intent的setData()方法將這個(gè)Uri對(duì)象傳遞進(jìn)去。setData()方法接受一個(gè)Uri對(duì)象,主要用于指定當(dāng)前Intent正在操作的數(shù)據(jù),而這些數(shù)據(jù)通常都是以字符串的形式傳入到Uri,parse()方法中解析產(chǎn)生的。
我們還可以在<intent-filter>標(biāo)簽中再配置一個(gè)<data>標(biāo)簽,用于更精確的指定當(dāng)前活動(dòng)能夠響應(yīng)什么類型的數(shù)據(jù)。<data>標(biāo)簽中主要可以配置一下內(nèi)容。

  • android:scheme :用于指定數(shù)據(jù)的協(xié)議部分,如上面的http部分。
  • android:host :用于指定數(shù)據(jù)的主機(jī)名部分,如上面的www.baidu.com
  • android:port :用于指定數(shù)據(jù)的端口部分,一般緊隨在主機(jī)名之后
  • android:mineType:用于指定可以處理的數(shù)據(jù)類型,允許使用通配符的方式指定。
    只有<data>標(biāo)簽中指定的內(nèi)容和Intent中攜帶的Data完全一致時(shí),當(dāng)前活動(dòng)才能夠響應(yīng)該Intent.不過一般在<data>中都不會(huì)指定過多的內(nèi)容。如上面的瀏覽器例子中,只需要指定android:scheme為http,就可以響應(yīng)所有的http協(xié)議的Intent了。
     <activity
            android:name=".ThirdActivity">
            <intent-filter>
                <action android:name="android.intent.action.VIEW"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <data android:scheme="http"/>
            </intent-filter>
        </activity>

我們添加一個(gè)活動(dòng),修改<intent-filter>的值,讓他也可以響應(yīng)瀏覽器的Intent.
除了http協(xié)議外,我們還可以指定很多其他協(xié)議,比如geo表示顯示地理位置,tel表示撥打電話。

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

這是調(diào)用系統(tǒng)撥號(hào)界面
指定了Intent的action是Intent.ACTION_DIAL,這又是系統(tǒng)的內(nèi)置動(dòng)作。然后data部分指定了協(xié)議是tel,號(hào)碼是10086.

向下一個(gè)活動(dòng)傳遞數(shù)據(jù)

Intent中提供了一系列putExtra()方法的重載。

String data = "hello SecondActivity";
Intent intent = new Intent(FirstActivity.this,SecondActivity.class);
intent.putExtra("extra_data",data);
startActivity(intent);

注意這里putExtra()方法接收兩個(gè)參數(shù),第一個(gè)參數(shù)是鍵,用于后面從Intent中取值,第二個(gè)參數(shù)才是真正要傳遞的數(shù)據(jù)。

Intent intent = getIntent();
String data = intent.getStringExtra("extra_data");
Log.d(TAG, data);

通過getIntent()方法獲取到用于啟動(dòng)SecondActivityIntent,然后調(diào)用getStringExtra()方法,傳入相應(yīng)的鍵值,就可以得到傳遞的數(shù)據(jù)了。如果傳遞的是整形數(shù)據(jù)使用getIntExtra()方法,如果傳遞的是布爾型數(shù)據(jù),則使用getBooleanExtra()方法,以此類推。

返回?cái)?shù)據(jù)給上一個(gè)活動(dòng)

Activity中還有一個(gè)startActivityForResult()方法也是用于啟動(dòng)活動(dòng)的,但這個(gè)方法期望在活動(dòng)銷毀的時(shí)候能夠返回一個(gè)結(jié)果給上一個(gè)活動(dòng)。
startActivityForResult()方法接收兩個(gè)參數(shù),第一個(gè)參數(shù)還是Intent,第二個(gè)參數(shù)是請(qǐng)求碼,用于在之后的回調(diào)中判斷數(shù)據(jù)的來源。

 Intent intent = new Intent(FirstActivity.this,SecondActivity.class);
 startActivityForResult(intent,1);  

使用startActivityForResult()來啟動(dòng)活動(dòng)。請(qǐng)求碼只要是一個(gè)唯一值就可以了。 ```

 Intent intent = new Intent();
 intent.putExtra("data_return","hello FirstActivity");
 setResult(RESULT_OK,intent);
 finish();

我們構(gòu)建了一個(gè)Intent,只不過這個(gè)Intent僅僅是用于傳遞數(shù)據(jù)而已,他沒有指定任何的意圖,接著我們把要傳遞的數(shù)據(jù)放入Intent中,然后調(diào)用setResult()方法,這個(gè)方法非常重要,是專門用于向上一個(gè)活動(dòng)返回?cái)?shù)據(jù)的。setResult()方法接收兩個(gè)參數(shù),第一個(gè)參數(shù)用于向上一個(gè)活動(dòng)返回處理的結(jié)果,一般只使用RESULT_OKRESULT_CANCELED兩個(gè)值,第二個(gè)參數(shù)則把帶有數(shù)據(jù)的Intent傳遞回去。

SecondActivity銷毀后會(huì)回調(diào)上一個(gè)活動(dòng)的onActivityResult()方法:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
 
   switch (requestCode){
       case 1:
           if (resultCode == RESULT_OK) {
               String returnedData = data.getStringExtra("data_return");
               Log.d(TAG, "onActivityResult: "+returnedData);
           }
           break;
       default:
   }
    
}

onActivityResult()方法帶有三個(gè)參數(shù),第一個(gè)參數(shù)requestCode,即我們啟動(dòng)活動(dòng)時(shí)的請(qǐng)求碼,第二個(gè)參數(shù)resultCode,即我們?cè)诜祷財(cái)?shù)據(jù)時(shí)傳入的處理結(jié)果,第三個(gè)參數(shù)data,即攜帶者返回?cái)?shù)據(jù)的Intent,由于在一個(gè)活動(dòng)中有可能調(diào)用startActivityForResult()方法去啟動(dòng)很多不同的活動(dòng),每一個(gè)活動(dòng)返回的數(shù)據(jù)都會(huì)回調(diào)到onActivityResult()這個(gè)方法,因此我們首先要做的就是通過檢查requestCode的值來判斷數(shù)據(jù)來源,然后再通過resultCode的值來判斷處理結(jié)果是否成功.

如果用戶在SecondActivity中并不是通過點(diǎn)擊按鈕,而是通過按下Back鍵回到FirstActivity,我們可以通過在SecondActivity中重寫onBackPressed()方法。

@Override
public void onBackPressed() {
   Intent intent = new Intent();
   intent.putExtra("data_return","hello FirstActivity2");
   setResult(RESULT_OK,intent);
   finish();
}

5.活動(dòng)的生命周期

Android是使用任務(wù)(Task)來管理活動(dòng)的,一個(gè)任務(wù)就是一組存放在棧里的活動(dòng)的集合,這個(gè)棧也被稱作返回棧(Back Stack).棧是一種后進(jìn)先出的數(shù)據(jù)結(jié)構(gòu),在默認(rèn)情況下,每當(dāng)我我們啟動(dòng)了一個(gè)新的活動(dòng),他會(huì)在返回棧中入棧,并處于棧頂?shù)奈恢茫慨?dāng)我們按下Back鍵或調(diào)用finish()方法去銷毀一個(gè)活動(dòng)的時(shí)候,處于棧頂?shù)幕顒?dòng)會(huì)出棧,這時(shí)前一個(gè)入棧的活動(dòng)就會(huì)重新處于棧頂?shù)奈恢谩O到y(tǒng)總是會(huì)顯示處于棧頂?shù)幕顒?dòng)給用戶。
返回棧的工作示意圖:

image

5.1活動(dòng)的狀態(tài)

每個(gè)活動(dòng)在其生命周期中最多可能會(huì)有4種狀態(tài)。
1.運(yùn)行狀態(tài)
當(dāng)一個(gè)活動(dòng)位于返回棧的棧頂時(shí),這時(shí)活動(dòng)就處于運(yùn)行狀態(tài)。
2.暫停狀態(tài)
當(dāng)一個(gè)活動(dòng)不再處于棧頂位置,但仍然可見時(shí),這是活動(dòng)就進(jìn)入暫停狀態(tài)。
3.停止?fàn)顟B(tài)
當(dāng)一個(gè)活動(dòng)不在處于棧頂位置,并且完全不可見的時(shí)候,就進(jìn)入停止?fàn)顟B(tài)。
4.銷毀狀態(tài)
當(dāng)一個(gè)活動(dòng)從返回棧中移除后就變成了銷毀狀態(tài)。

5.2活動(dòng)的生存期

Activity類中定義了7個(gè)回調(diào)方法,覆蓋了活動(dòng)生命周期的每一個(gè)環(huán)節(jié)。
onCreate(): 在活動(dòng)第一次被創(chuàng)建的時(shí)候調(diào)用,你應(yīng)該在這個(gè)方法中完成活動(dòng)的初始化操作,比如說加載布局,綁定事件等。
onStart(): 在活動(dòng)由不可見變?yōu)榭梢姷臅r(shí)候調(diào)用。
onResume(): 在活動(dòng)準(zhǔn)備好和用戶進(jìn)行交互的時(shí)候調(diào)用,此時(shí)的活動(dòng)一定位于返回棧的棧頂,并且處于運(yùn)行狀態(tài)。
onPause(): 在系統(tǒng)準(zhǔn)備去啟動(dòng)或恢復(fù)另一個(gè)活動(dòng)的時(shí)候調(diào)用,我們通常會(huì)在這個(gè)方法中將一些小號(hào)CPU的資源釋放掉,以及保存一些關(guān)鍵數(shù)據(jù),但這個(gè)方法的執(zhí)行速度一定要快,不然會(huì)影響到新的棧頂活動(dòng)的使用。
onStop(): 在活動(dòng)完全不可見的時(shí)候調(diào)用,他和onPause()方法的主要區(qū)別是如果啟動(dòng)的新活動(dòng)是一個(gè)對(duì)話框的活動(dòng),那么onPause()方法會(huì)得到執(zhí)行,而onStop()方法并不會(huì)得到執(zhí)行。
onDestory(): 在活動(dòng)貝銷毀之前調(diào)用,之后活動(dòng)的狀態(tài)將變?yōu)殇N毀狀態(tài)。
onRestart(): 在活動(dòng)有停止?fàn)顟B(tài)變?yōu)檫\(yùn)行狀態(tài)之前調(diào)用,也就是活動(dòng)被重新啟動(dòng)了。
完整生存期
活動(dòng)在onCreate()方法和onDestory()方法之間所經(jīng)歷的,就是完整生存期,一般情況下,一個(gè)活動(dòng)會(huì)在onCreate()方法中完成各種初始化操作,而在onDestory()方法中完成釋放內(nèi)存的操作。
可見生存期
活動(dòng)在onStart()方法和onStop()方法之間所經(jīng)歷的就是可見生存期。在onStart()方法中對(duì)資源進(jìn)行加載,而在onStop()方法中對(duì)資源進(jìn)行釋放,從而保證處于停止?fàn)顟B(tài)的活動(dòng)不會(huì)占用過多的內(nèi)存。
前臺(tái)生存期
活動(dòng)在onResume()方法和onPause()方法之間所經(jīng)歷的就是前臺(tái)生存期。

活動(dòng)的生命周期圖

image

 <activity 
            android:name=".DialogActivity"
            android:theme="@android:style/Theme.Dialog">
        </activity>

我們給他使用了一個(gè)android:theme屬性,這是用于給當(dāng)前活動(dòng)指定主題的,Android系統(tǒng)內(nèi)置有很多的主題可以選擇,當(dāng)然我們也可以定制自己的主題。而這里@android:style/Theme.Dialog,則毫無疑問是讓DialogActivity使用對(duì)話框式的主題。

當(dāng)MainActivity第一次被創(chuàng)建時(shí)會(huì)依次執(zhí)行onCreate(),onStart()和onResume()方法。
NormalActivity把MainActivity完全遮擋住,onPause()和onStop()方法會(huì)得到執(zhí)行。
按下Back鍵,之前的MainActivity已經(jīng)進(jìn)入停止?fàn)顟B(tài),所以onRestart()方法會(huì)得到執(zhí)行之后又會(huì)依次執(zhí)行onStart()和onResume()方法。
點(diǎn)擊進(jìn)入DialogActivity,只有onPause()方法得到了執(zhí)行,onStop()方法并沒有執(zhí)行,這是因?yàn)镈ialogActivity并沒有完全遮擋住MainActivity,此時(shí)MainActivity只是進(jìn)入了暫停狀態(tài),并沒有進(jìn)入停止?fàn)顟B(tài)。相應(yīng)的,按下Back鍵返回MainActivity也應(yīng)該只有onResume()方法會(huì)得到執(zhí)行。
活動(dòng)被回收了怎么辦?
Activity中還提供了一個(gè)onSaveInstanceState()回調(diào)方法,這個(gè)方法可以保證在活動(dòng)被回收之前一定會(huì)被調(diào)用。因此我們可以通過這個(gè)方法來解決活動(dòng)被回收時(shí)臨時(shí)數(shù)據(jù)得不到保存的問題。
onSaveInstanceState()方法會(huì)攜帶一個(gè)Bundle類型的參數(shù),Bundle提供了一系列的方法用于保存數(shù)據(jù),比如可以使用putString()方法保存字符串,使用putInt()方法保存整形數(shù)據(jù)。每個(gè)保存方法需要傳入兩個(gè)參數(shù),第一個(gè)參數(shù)是鍵,用于后面從Bundle中取值,第二個(gè)參數(shù)是真正要保存的內(nèi)容。

 @Override
    public void onSaveInstanceState(Bundle outState)
    {
        super.onSaveInstanceState(outState);
        String tempData = "Something you just typed";
        outState.putString("data_key",tempData);
    }

onCreate()方法其實(shí)也有一個(gè)Bundle類型的參數(shù)。這個(gè)參數(shù)在一般情況下都是null,但是如果在活動(dòng)被系統(tǒng)回收之前有通過onSaveInstanceState()方法來保存數(shù)據(jù)的話,這個(gè)參數(shù)就會(huì)帶有之前所保存的全部數(shù)據(jù)。

 @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        if (savedInstanceState != null)
        {
            String tempData = savedInstanceState.getString("data_key");
        }
        
    }

取出值后再做相應(yīng)的操作就可以了。
Intent還可以結(jié)合Bundle一起用于傳遞數(shù)據(jù),首先可以把需要傳遞的數(shù)據(jù)都保存在Bundle對(duì)象中,然后再將Bundle對(duì)象存放在Intent里,到了目標(biāo)活動(dòng)之后先從Intent中取出Bundle,在從Bundle中一一取出。

6.活動(dòng)的啟動(dòng)模式

在實(shí)際項(xiàng)目中我們應(yīng)該根據(jù)特定的需求為每個(gè)活動(dòng)指定恰當(dāng)?shù)膯?dòng)模式。啟動(dòng)模式一共有4種,分別是standard,singleTop,singleTask和singleInstance,可以在AndroidManifest.xml中通過給<activity>標(biāo)簽指定android:launchMode屬性來選擇啟動(dòng)模式。

6.1 standard

image

standard是活動(dòng)默認(rèn)的啟動(dòng)模式,在不進(jìn)行顯式指定的情況下,所有活動(dòng)都會(huì)自動(dòng)使用這種啟動(dòng)模式。Android是使用返回棧來管理活動(dòng)的在standard(即默認(rèn)情況下),每當(dāng)啟動(dòng)一個(gè)新的活動(dòng),他就會(huì)在返回棧中入棧,并處于棧頂?shù)奈恢茫瑢?duì)于使用standard面膜是的活動(dòng),系統(tǒng)不會(huì)在乎這個(gè)活動(dòng)是否已經(jīng)在返回棧中存在,每次啟動(dòng)都會(huì)創(chuàng)建該活動(dòng)的一個(gè)新的實(shí)例。

在代碼中實(shí)踐:

Log.d(TAG, this.toString());
Intent intent = new Intent(FirstActivity.this,FirstActivity.class);
                startActivity(intent);

連續(xù)點(diǎn)擊兩次,可以看到打印出當(dāng)前活動(dòng)的實(shí)例。

01-06 21:47:26.409 18937-18937/com.example.activitytest D/FirstActivity: com.example.activitytest.FirstActivity@f7397be
01-06 21:47:32.614 18937-18937/com.example.activitytest D/FirstActivity: com.example.activitytest.FirstActivity@6b88a94

6.2 singleTop

image

使用singleTop模式,當(dāng)活動(dòng)的啟動(dòng)模式指定為singleTop,在啟動(dòng)活動(dòng)時(shí),如果發(fā)現(xiàn)返回棧的棧頂已經(jīng)是該活動(dòng),則認(rèn)為可以直接使用它,不會(huì)再創(chuàng)建新的活動(dòng)實(shí)例。

android:name=".FirstActivity"
            android:launchMode="singleTop"
            android:label="This is FirstActivity">
01-06 22:12:07.813 15130-15130/? D/FirstActivity: com.example.activitytest.FirstActivity@f7c25bd

無論點(diǎn)擊多少次,都只會(huì)創(chuàng)建一次實(shí)例。

6.3 singleTask

image

使用singleTop模式可以很好地解決重復(fù)創(chuàng)建棧頂活動(dòng)的問題,當(dāng)活動(dòng)的啟動(dòng)模式指定為singleTask模式,每次啟動(dòng)該活動(dòng)時(shí)系統(tǒng)首先會(huì)在返回棧中檢查是否存在該活動(dòng)的實(shí)例,如果發(fā)現(xiàn)已經(jīng)存在則直接使用該實(shí)例,并把在這個(gè)活動(dòng)之上的所有活動(dòng)統(tǒng)統(tǒng)出棧,如果沒有發(fā)現(xiàn)就會(huì)創(chuàng)建一個(gè)新的活動(dòng)實(shí)例。

6.4 singleInstance

image

指定為singleInstance模式的活動(dòng)會(huì)啟用一個(gè)新的返回棧來管理這個(gè)活動(dòng)(其實(shí)如果singleTask模式指定了不同的taskAffinity,也會(huì)啟動(dòng)一個(gè)新的返回棧)。每個(gè)應(yīng)用程序都會(huì)有自己的返回棧,同一個(gè)活動(dòng)在不同的返回棧中入棧時(shí)必然是創(chuàng)建了新的實(shí)例,在singleInstance模式下,會(huì)有一個(gè)單獨(dú)的返回棧來管理這個(gè)活動(dòng)不管是哪個(gè)應(yīng)用程序來訪問這個(gè)活動(dòng),都共用的同一個(gè)返回棧,也就解決了共享活動(dòng)實(shí)例的問題。

7.活動(dòng)的最佳實(shí)踐

7.1 知曉當(dāng)前是在哪一個(gè)活動(dòng)?

新建一個(gè)類:BaseActivity

public class BaseActivity extends AppCompatActivity
{
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        Log.d("BaseActivity",getClass().getSimpleName());
    }
}

讓所有的Activity不在繼承AppCompatActivity,而是繼承BaseActivity,getClass().getSimpleName()可以得到當(dāng)前實(shí)例的類名。

7.2隨時(shí)隨地退出程序

我們需要用一個(gè)專門的集合類對(duì)所有的活動(dòng)進(jìn)行管理就可以了。

public class ActivityCollector
{
     //添加活動(dòng)
    public static List<Activity> activities = new ArrayList<>();
    
    public static void addActivity(Activity activity)
    {
        activities.add(activity);
    }
    
    //移除活動(dòng)
    public static void removeActivity(Activity activity)
    {
        activities.remove(activity);
    }
    
    //全部銷毀活動(dòng)
    public static void finishAll()
    {
        for (Activity activity : activities)
        {
            if (!activity.isFinishing())
            {
                activity.finish();
            }
        }
    }
}

通過一個(gè)List來暫存活動(dòng),然后提供了一個(gè)addActivity()方法用于向List中添加一個(gè)活動(dòng),提供了一個(gè)removeActivity()方法用于從List中移除活動(dòng),最后提供了一個(gè)finishAll()方法用于將List中存儲(chǔ)的活動(dòng)全部銷毀掉.

public class BaseActivity extends AppCompatActivity
{
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        Log.d("BaseActivity",getClass().getSimpleName());
        
        ActivityCollector.addActivity(this);
    }

    @Override
    protected void onDestroy()
    {
        super.onDestroy();
        ActivityCollector.removeActivity(this);
    }
}

在BaseActivity的onCreate()方法中調(diào)用了ActivityCollector的addActivity()方法,表明將當(dāng)前正在創(chuàng)建的活動(dòng)添加到活動(dòng)管理器里,然后在BaseActivity中重寫onDestroy()方法,并調(diào)用了ActivityCollector的removeActivity()方法,表明將一個(gè)馬上要銷毀的活動(dòng)從活動(dòng)管理器里移除。

從此以后,不管你想在什么地方退出程序,只需要調(diào)用ActivityCollector.finishAll()方法就可以了。

public class ThirdActivity extends BaseActivity
{

    Button button;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_third);

        button = (Button) findViewById(R.id.button_3);
        initEvent();
    }

    public void initEvent()
    {
        button.setOnClickListener(new View.OnClickListener()
        {
            @Override
            public void onClick(View v)
            {
                ActivityCollector.finishAll();
                android.os.Process.killProcess(android.os.Process.myPid());
            }
        });
    }
}

你還可以在銷毀所有活動(dòng)的代碼后面再加上殺掉當(dāng)前進(jìn)程的代碼,以保證程序完全退出,殺掉進(jìn)程的代碼:

 android.os.Process.killProcess(android.os.Process.myPid());

killProcess()方法用于殺掉一個(gè)進(jìn)程,它接受一個(gè)進(jìn)程ID的參數(shù),我們可以通過myPid()方法來獲得當(dāng)前程序的進(jìn)程ID,需要注意的是,killProcess()方法只能用于殺掉當(dāng)前程序的進(jìn)程,,我們不能使用這個(gè)方法去殺掉其他程序。

7.3 啟動(dòng)活動(dòng)的最佳寫法

在SecondActivity中寫入下面的方法:


    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);
        context.startActivity(intent);
    }

在FirstActivity里面調(diào)用這個(gè)方法:

 SecondActivity.actionStart(FirstActivity.this,"data1","data2");

這樣寫的好處是一目了然:SecondActivity所需要的數(shù)據(jù)在方法參數(shù)中全部體現(xiàn)出來了,這樣即使不用閱讀SecondActivity中的代碼,不去詢問負(fù)責(zé)編寫SecondActivity的同事,你也可以非常清晰地知道啟動(dòng)SecodeActivity需要傳遞哪些數(shù)據(jù),另外這樣寫還簡(jiǎn)化了啟動(dòng)活動(dòng)的代碼。0

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,251評(píng)論 25 708
  • 作為Android的四大組件之一,活動(dòng)最先走進(jìn)我們的視野,其重要性不言而喻,今天就抽出時(shí)間來專門對(duì)Android活...
    loser先生閱讀 910評(píng)論 0 0
  • 本篇文章主要介紹以下幾個(gè)知識(shí)點(diǎn):顯示、隱式 Intent 的相關(guān)內(nèi)容;活動(dòng) Activity 的生命周期;活動(dòng) A...
    開心wonderful閱讀 1,404評(píng)論 0 3
  • 1.什么是Activity?問的不太多,說點(diǎn)有深度的 四大組件之一,一般的,一個(gè)用戶交互界面對(duì)應(yīng)一個(gè)activit...
    JoonyLee閱讀 5,760評(píng)論 2 51
  • 當(dāng)我們?cè)谡務(wù)撈嚨臅r(shí)候,我們到底在交流什么 我是個(gè)新手車主,車齡2年,歷程3萬,車子是13款的標(biāo)致308乞...
    回歸安卡拉閱讀 378評(píng)論 0 1