2. 先從看得到的入手-探究活動

1.活動(Activity)

Activity是一種可以包含用戶界面的組件轩勘,主要用于和用戶進行交互。
手動創(chuàng)建活動,我們在創(chuàng)建的時候丘逸,選擇Add No Activity,這個時候app/src/main/java/com.example.activitytest目錄是空的。我們右鍵新建一個活動掀宋,但是不要勾選Generate Layout FileLauncher Activity.

image

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

image

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

2. Toast

image

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

3.在活動中使用Menu

image

其中<item>標簽就是用來創(chuàng)建具體的某一個菜單項暂殖,然后通過android:id給這個菜單項指定一個唯一的標識符价匠,通過android:title給這個菜單項指定一個名稱。

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

通過getMenuInflater()方法能夠得到MenuInflater對象呛每,在調(diào)用它的inflater()方法踩窖,就可以給當前的活動創(chuàng)建菜單了,inflater()方法接受兩個參數(shù)晨横,第一個參數(shù)用于指定我們通過哪一個資源文件來創(chuàng)建菜單毙石,第二個參數(shù)用于指定我們的菜單項將添加到哪一個Menu對象中去廉沮。這里直接使用onCreateOptionMenu()方法中傳入的Menu參數(shù),然后給這個方法換回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() 來判斷我們點擊的是哪一個菜單項滤灯。

4.Intent的使用

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

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

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

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

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

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

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

我們調(diào)用Intent中的addCategory()方法來添加一個category,我們指定了一個自定義的category,值為:com.example.activitytest.MY_CATEGORY.如果我們在運行的話會出現(xiàn)這樣的錯誤:

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ī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>

我們增加一個category就可以了库北。
更多隱式Intent的用法
使用隱式Intent爬舰,我們不僅可以啟動我們自己程序向內(nèi)的活動们陆,還可以啟動其他程序的活動,這使得Android多個應(yīng)用程序之間的功能共享成為了可能情屹。

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

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

  • android:scheme :用于指定數(shù)據(jù)的協(xié)議部分锨亏,如上面的http部分痴怨。
  • android:host :用于指定數(shù)據(jù)的主機名部分,如上面的www.baidu.com
  • android:port :用于指定數(shù)據(jù)的端口部分屯伞,一般緊隨在主機名之后
  • android:mineType:用于指定可以處理的數(shù)據(jù)類型腿箩,允許使用通配符的方式指定。
    只有<data>標簽中指定的內(nèi)容和Intent中攜帶的Data完全一致時劣摇,當前活動才能夠響應(yīng)該Intent.不過一般在<data>中都不會指定過多的內(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>

我們添加一個活動,修改<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)撥號界面
指定了Intent的action是Intent.ACTION_DIAL,這又是系統(tǒng)的內(nèi)置動作艺栈。然后data部分指定了協(xié)議是tel,號碼是10086.

向下一個活動傳遞數(shù)據(jù)

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

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

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

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

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

返回數(shù)據(jù)給上一個活動

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

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

使用startActivityForResult()來啟動活動壁畸。請求碼只要是一個唯一值就可以了。 ```

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

我們構(gòu)建了一個Intent茅茂,只不過這個Intent僅僅是用于傳遞數(shù)據(jù)而已捏萍,他沒有指定任何的意圖,接著我們把要傳遞的數(shù)據(jù)放入Intent中空闲,然后調(diào)用setResult()方法令杈,這個方法非常重要,是專門用于向上一個活動返回數(shù)據(jù)的碴倾。setResult()方法接收兩個參數(shù)逗噩,第一個參數(shù)用于向上一個活動返回處理的結(jié)果,一般只使用RESULT_OKRESULT_CANCELED兩個值跌榔,第二個參數(shù)則把帶有數(shù)據(jù)的Intent傳遞回去异雁。

SecondActivity銷毀后會回調(diào)上一個活動的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()方法帶有三個參數(shù),第一個參數(shù)requestCode,即我們啟動活動時的請求碼僧须,第二個參數(shù)resultCode纲刀,即我們在返回數(shù)據(jù)時傳入的處理結(jié)果,第三個參數(shù)data担平,即攜帶者返回數(shù)據(jù)的Intent,由于在一個活動中有可能調(diào)用startActivityForResult()方法去啟動很多不同的活動示绊,每一個活動返回的數(shù)據(jù)都會回調(diào)到onActivityResult()這個方法,因此我們首先要做的就是通過檢查requestCode的值來判斷數(shù)據(jù)來源暂论,然后再通過resultCode的值來判斷處理結(jié)果是否成功.

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

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

5.活動的生命周期

Android是使用任務(wù)(Task)來管理活動的盆耽,一個任務(wù)就是一組存放在棧里的活動的集合蹋砚,這個棧也被稱作返回棧(Back Stack).棧是一種后進先出的數(shù)據(jù)結(jié)構(gòu)扼菠,在默認情況下摄杂,每當我我們啟動了一個新的活動,他會在返回棧中入棧循榆,并處于棧頂?shù)奈恢梦龌郑慨斘覀儼聪翨ack鍵或調(diào)用finish()方法去銷毀一個活動的時候,處于棧頂?shù)幕顒訒鰲Q硪@時前一個入棧的活動就會重新處于棧頂?shù)奈恢糜彻摇O到y(tǒng)總是會顯示處于棧頂?shù)幕顒咏o用戶。
返回棧的工作示意圖:

image

5.1活動的狀態(tài)

每個活動在其生命周期中最多可能會有4種狀態(tài)盗尸。
1.運行狀態(tài)
當一個活動位于返回棧的棧頂時柑船,這時活動就處于運行狀態(tài)。
2.暫停狀態(tài)
當一個活動不再處于棧頂位置泼各,但仍然可見時鞍时,這是活動就進入暫停狀態(tài)。
3.停止狀態(tài)
當一個活動不在處于棧頂位置扣蜻,并且完全不可見的時候逆巍,就進入停止狀態(tài)。
4.銷毀狀態(tài)
當一個活動從返回棧中移除后就變成了銷毀狀態(tài)莽使。

5.2活動的生存期

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

活動的生命周期圖

image

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

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

當MainActivity第一次被創(chuàng)建時會依次執(zhí)行onCreate()哮肚,onStart()和onResume()方法。
NormalActivity把MainActivity完全遮擋住广匙,onPause()和onStop()方法會得到執(zhí)行允趟。
按下Back鍵,之前的MainActivity已經(jīng)進入停止狀態(tài)鸦致,所以onRestart()方法會得到執(zhí)行之后又會依次執(zhí)行onStart()和onResume()方法潮剪。
點擊進入DialogActivity,只有onPause()方法得到了執(zhí)行,onStop()方法并沒有執(zhí)行分唾,這是因為DialogActivity并沒有完全遮擋住MainActivity,此時MainActivity只是進入了暫停狀態(tài)抗碰,并沒有進入停止狀態(tài)。相應(yīng)的绽乔,按下Back鍵返回MainActivity也應(yīng)該只有onResume()方法會得到執(zhí)行弧蝇。
活動被回收了怎么辦?
Activity中還提供了一個onSaveInstanceState()回調(diào)方法折砸,這個方法可以保證在活動被回收之前一定會被調(diào)用看疗。因此我們可以通過這個方法來解決活動被回收時臨時數(shù)據(jù)得不到保存的問題。
onSaveInstanceState()方法會攜帶一個Bundle類型的參數(shù)睦授,Bundle提供了一系列的方法用于保存數(shù)據(jù)两芳,比如可以使用putString()方法保存字符串,使用putInt()方法保存整形數(shù)據(jù)去枷。每個保存方法需要傳入兩個參數(shù)怖辆,第一個參數(shù)是鍵是复,用于后面從Bundle中取值,第二個參數(shù)是真正要保存的內(nèi)容竖螃。

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

onCreate()方法其實也有一個Bundle類型的參數(shù)斑鼻。這個參數(shù)在一般情況下都是null坚弱,但是如果在活動被系統(tǒng)回收之前有通過onSaveInstanceState()方法來保存數(shù)據(jù)的話荒叶,這個參數(shù)就會帶有之前所保存的全部數(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對象中促煮,然后再將Bundle對象存放在Intent里菠齿,到了目標活動之后先從Intent中取出Bundle,在從Bundle中一一取出绳匀。

6.活動的啟動模式

在實際項目中我們應(yīng)該根據(jù)特定的需求為每個活動指定恰當?shù)膯幽J浇笫俊幽J揭还灿?種陋桂,分別是standard,singleTop,singleTask和singleInstance,可以在AndroidManifest.xml中通過給<activity>標簽指定android:launchMode屬性來選擇啟動模式宣渗。

6.1 standard

image

standard是活動默認的啟動模式痕囱,在不進行顯式指定的情況下鞍恢,所有活動都會自動使用這種啟動模式。Android是使用返回棧來管理活動的在standard(即默認情況下)窒典,每當啟動一個新的活動涩搓,他就會在返回棧中入棧昧甘,并處于棧頂?shù)奈恢眉膊悖瑢τ谑褂胹tandard面膜是的活動痛黎,系統(tǒng)不會在乎這個活動是否已經(jīng)在返回棧中存在湖饱,每次啟動都會創(chuàng)建該活動的一個新的實例。

在代碼中實踐:

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

連續(xù)點擊兩次仅仆,可以看到打印出當前活動的實例垢袱。

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模式咳榜,當活動的啟動模式指定為singleTop畔柔,在啟動活動時靶擦,如果發(fā)現(xiàn)返回棧的棧頂已經(jīng)是該活動玄捕,則認為可以直接使用它寂纪,不會再創(chuàng)建新的活動實例。

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

無論點擊多少次,都只會創(chuàng)建一次實例庄涡。

6.3 singleTask

image

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

6.4 singleInstance

image

指定為singleInstance模式的活動會啟用一個新的返回棧來管理這個活動(其實如果singleTask模式指定了不同的taskAffinity,也會啟動一個新的返回棧)贪绘。每個應(yīng)用程序都會有自己的返回棧税灌,同一個活動在不同的返回棧中入棧時必然是創(chuàng)建了新的實例菱涤,在singleInstance模式下粘秆,會有一個單獨的返回棧來管理這個活動不管是哪個應(yīng)用程序來訪問這個活動,都共用的同一個返回棧昔搂,也就解決了共享活動實例的問題摘符。

7.活動的最佳實踐

7.1 知曉當前是在哪一個活動?

新建一個類: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()可以得到當前實例的類名逛裤。

7.2隨時隨地退出程序

我們需要用一個專門的集合類對所有的活動進行管理就可以了。

public class ActivityCollector
{
     //添加活動
    public static List<Activity> activities = new ArrayList<>();
    
    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();
            }
        }
    }
}

通過一個List來暫存活動洽糟,然后提供了一個addActivity()方法用于向List中添加一個活動炉菲,提供了一個removeActivity()方法用于從List中移除活動,最后提供了一個finishAll()方法用于將List中存儲的活動全部銷毀掉.

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()方法坤溃,表明將當前正在創(chuàng)建的活動添加到活動管理器里拍霜,然后在BaseActivity中重寫onDestroy()方法,并調(diào)用了ActivityCollector的removeActivity()方法薪介,表明將一個馬上要銷毀的活動從活動管理器里移除祠饺。

從此以后,不管你想在什么地方退出程序汁政,只需要調(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());
            }
        });
    }
}

你還可以在銷毀所有活動的代碼后面再加上殺掉當前進程的代碼,以保證程序完全退出,殺掉進程的代碼:

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

killProcess()方法用于殺掉一個進程骇两,它接受一個進程ID的參數(shù)速种,我們可以通過myPid()方法來獲得當前程序的進程ID,需要注意的是,killProcess()方法只能用于殺掉當前程序的進程黔姜,纳寂,我們不能使用這個方法去殺掉其他程序猾浦。

7.3 啟動活動的最佳寫法

在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)用這個方法:

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

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末洽议,一起剝皮案震驚了整個濱河市礼旅,隨后出現(xiàn)的幾起案子龄坪,更是在濱河造成了極大的恐慌雳旅,老刑警劉巖型豁,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件飘言,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人勾给,你說我怎么就攤上這事烂叔。” “怎么了固歪?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵蒜鸡,是天一觀的道長。 經(jīng)常有香客問我,道長逢防,這世上最難降的妖魔是什么康聂? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮胞四,結(jié)果婚禮上恬汁,老公的妹妹穿的比我還像新娘。我一直安慰自己辜伟,他們只是感情好氓侧,可當我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著导狡,像睡著了一般约巷。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上旱捧,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天独郎,我揣著相機與錄音,去河邊找鬼枚赡。 笑死氓癌,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的贫橙。 我是一名探鬼主播贪婉,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼卢肃!你這毒婦竟也來了疲迂?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤莫湘,失蹤者是張志新(化名)和其女友劉穎尤蒿,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體幅垮,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡腰池,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了军洼。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片巩螃。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖匕争,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情爷耀,我是刑警寧澤甘桑,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響跑杭,放射性物質(zhì)發(fā)生泄漏铆帽。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一德谅、第九天 我趴在偏房一處隱蔽的房頂上張望爹橱。 院中可真熱鬧,春花似錦窄做、人聲如沸愧驱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽组砚。三九已至,卻和暖如春掏颊,著一層夾襖步出監(jiān)牢的瞬間糟红,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工乌叶, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留盆偿,地道東北人。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓准浴,卻偏偏與公主長得像陈肛,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子兄裂,可洞房花燭夜當晚...
    茶點故事閱讀 42,802評論 2 345

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