Android組件系列----Activity組件詳解


【聲明】

歡迎轉載,但請保留文章原始出處→_→

生命壹號:http://www.cnblogs.com/smyhvae/

文章來源:http://www.cnblogs.com/smyhvae/p/3924567.html

 

【正文】

注:四大組件指的是應用組件:Activity、Service、BroadcastReceiver、ContentProvider;之前的控件指的是UI組件。

博文目錄:

  • 一、Activity簡介
  • 二、Activity的狀態和生命周期
  • 三、Activity的基本用法
  • 四、向下一個Activity傳遞數據
  • 五、返回數據給上一個Activity
  • 六、Activity運行時屏幕方向與顯示方式
  • 七、Activity的現場保存
  • 八、Activity通過SharedPreferences保存數據

一、Activity簡介:

Activity組件是四大組件之一,在應用中一個Activity可以用來表示一個界面, 中文意思也可以理解為“活動” ,即一個活動開始,代表Activity組件啟動;活動結束,代表一個Activity的生命周期結束。

一個android應用必須通過Activity來運行和啟動,和J2ME 的MIDlet 一樣,在android中,Activity的生命周期統一交給系統管理。與MIDlet 不同的是安裝在android 中的所有的Activity 都是平等的。

理解以下四個基本概念,將有助於我們更好的了解Activity:

• Application(APP)

• Activity

• Activity棧

• Task

每個Application均占有獨立的內存空間。需要注意的是:Application之間雖然相互獨立,但APP_1中的Activity與APP_2中的Activity之間可以進行通信(調用、訪問等)。

二、Activity的狀態和生命周期

1、Activity的狀態:

(1)Resumed:Activity對象出於運行狀態。一個新Activity 啟動入棧后,它在屏幕最前端,處於棧的最頂端,此時它處於可見並可以與用戶交互的激活狀態。

(2)Paused:另一個Activity位於前端,但是本Activity還可見。

        Paused狀態常用於:當Activity被另一個透明或者Dialog樣式的Activity覆蓋時的狀態。此時它依然與窗口管理器保持連接,系統繼續維護其內部狀態,所以它仍然可見,但它已經失去了焦點故不可與用戶交互。注:一個Activity出於paused狀態時,系統並不會釋放資源。釋放資源你的操作要靠開發者來完成。

(3)Stopped:另一個Activity位於前端,完全遮擋本Activity。

(4)killed:Activity被系統殺死回收或者沒有被啟動時。

 繪制表格如下:

生命周期函數

調用時機

舉例

onCreate

在Activity對象被第一次創建時調用

買車

onStart

當Activity變得可見時調用

打火,啟動

onResume

當Activity開始准備和用戶交互時調用

踩油門,驅動汽車前進

onPause

當系統即將啟動另外一個Activity之前調用

松開油門

onStop

當前Activity變得不可見時調用

熄火

onDestroy

當前Activity被銷毀之前調用

車輛報廢

onRestart

當一個Activity再次啟動之前調用

 

 

 

 

 

 

 

 

 

 

 

 

 

 

注:on開頭的一般是事件的方法。(引申知識:觀察者的設計模式

2、Activity的生命周期:

詳情請見本人的另外一篇博客:Activity的生命周期

 

生命周期的完整代碼如下:

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

public class MainActivity extends Activity {

    private static final String TAG = "smyhvae";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.d(TAG, "onCreate");
    }

    @Override
    protected void onStart() {
        super.onStart();
        Log.d(TAG, "onStart");
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.d(TAG, "onResume");
    }


    @Override
    protected void onPause() {
        super.onPause();
        Log.d(TAG, "onPause");
    }


    @Override
    protected void onStop() {
        super.onStop();
        Log.d(TAG, "onStop");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy");
    }

    @Override
    protected void onRestart() {
        super.onRestart();
        Log.d(TAG, "onRestart");
    }
}

 

 

三、Activity的啟動模式:(面試注意)

 

Activity有四種啟動模式:standard、singleTop、singleTask、singleInstance。可以在AndroidManifest.xml中activity標簽的屬性android:launchMode中設置該activity的加載模式。

 

  • standard模式:默認的模式,以這種模式加載時,每當啟動一個新的活動,必定會構造一個新的Activity實例放到返回棧(目標task)的棧頂,不管這個Activity是否已經存在於返回棧中;
  • singleTop模式:如果一個以singleTop模式啟動的activity的實例已經存在於返回桟的桟頂,那么再啟動這個Activity時,不會創建新的實例,而是重用位於棧頂的那個實例,並且會調用該實例的onNewIntent()方法將Intent對象傳遞到這個實例中;

 

注:如果以singleTop模式啟動的activity的一個實例已經存在於返回桟中,但是不在桟頂,那么它的行為和standard模式相同,也會創建多個實例;

 

  • singleTask模式:這種模式下,每次啟動一個activity時,系統首先會在返回棧中檢查是否存在該活動的實例,如果存在,則直接使用該實例(會調用實例的onNewIntent()方法),並把這個活動之上的所有活動統統清除;如果沒有發現就會創建一個新的活動實例;

 

  • singleInstance模式:總是在新的任務中開啟,並且這個新的任務中有且只有這一個實例,並讓多個應用共享該棧中的該Activity實例。一旦該模式的Activity的實例存在於某個棧中,任何應用再激活該Activity時都會重用該棧中的實例。其效果相當於多個應用程序共享一個應用,不管誰激活該Activity都會進入同一個應用中。

注:也就是說被該實例啟動的其他activity會自動運行於另一個任務中。當再次啟動該activity的實例時,會重新調用已存在的任務和實例。並且會調用這個實例的onNewIntent()方法,將Intent實例傳遞到該實例中。和singleTask相同,同一時刻在系統中只會存在一個這樣的Activity實例。(singleInstance即單實例)

 

注:前面三種模式中,每個應用程序都有自己的返回棧,同一個活動在不同的返回棧中入棧時,必然是創建了新的實例。而使用singleInstance模式可以解決這個問題,在這種模式下會有一個單獨的返回棧來管理這個活動,不管是哪一個應用程序來訪問這個活動,都公用同一個返回棧,也就解決了共享活動實例的問題。(此時可以實現任務之間的切換,而不是單獨某個棧中的實例切換)

 

1、singleInstance模式詳解:

singleInstance模式從字面上看比較難理解,下面通過代碼舉例來分析。代碼如下:

(1)新建三個Activity:FirstActivity、SecondActivity、ThirdActivity。同時,將SecondActivity的啟動模式設置為singleInstance

(2)三個Activity的代碼如下:

FirstActivity.java:

 1 import android.app.Activity;
 2 import android.content.Intent;
 3 import android.os.Bundle;
 4 import android.util.Log;
 5 import android.view.View;
 6 import android.widget.Button;
 7 
 8 
 9 public class FirstActivity extends Activity {
10 
11     private Button button1;
12 
13     @Override
14     protected void onCreate(Bundle savedInstanceState) {
15         super.onCreate(savedInstanceState);
16         Log.d("--->FirstActivity", "返回棧的id是" + getTaskId());  //打印當前返回棧的id
17         setContentView(R.layout.activity_main);
18         button1 = (Button) findViewById(R.id.button1);
19         button1.setOnClickListener(new View.OnClickListener() {
20             @Override
21             public void onClick(View v) {
22                 startActivity(new Intent(FirstActivity.this, SecondActivity.class)); 23 
24             }
25         });
26     }
27 
28 }

上方代碼中,在onCreate()方法中打印當前返回棧的id。點擊按鈕,跳轉到SecondActivity。

 

SecondActivity.java:

 1 import android.app.Activity;
 2 import android.content.Intent;
 3 import android.os.Bundle;
 4 import android.util.Log;
 5 import android.view.View;
 6 import android.widget.Button;
 7 
 8 
 9 public class SecondActivity extends Activity {
10 
11     private Button button2;
12 
13     @Override
14     protected void onCreate(Bundle savedInstanceState) {
15         super.onCreate(savedInstanceState);
16         Log.d("--->SecondActivity", "返回棧的id是" + getTaskId());  //打印當前返回棧的id
17         setContentView(R.layout.activity_second);
18         button2 = (Button) findViewById(R.id.button2);
19         button2.setOnClickListener(new View.OnClickListener() {
20             @Override
21             public void onClick(View v) {
22                 startActivity(new Intent(SecondActivity.this, ThirdActivity.class)); 23 
24             }
25         });
26     }
27 
28 }

 

上方代碼中,在onCreate()方法中打印當前返回棧的id。點擊按鈕,跳轉到ThirdActivity。

ThirdActivity.java:

 1 import android.app.Activity;
 2 import android.os.Bundle;
 3 import android.util.Log;
 4 import android.widget.Button;
 5 
 6 
 7 public class ThirdActivity extends Activity {
 8 
 9     private Button button3;
10 
11     @Override
12     protected void onCreate(Bundle savedInstanceState) {
13         super.onCreate(savedInstanceState);
14         Log.d("--->ThirdActivity", "返回棧的id是" + getTaskId());  //打印當前返回棧的id
15         setContentView(R.layout.activity_third);
16     }
17 
18 }

 

運行程序,在FirstActivity中點擊按鈕進入SecondActivity中,然后在SecondActivity中點擊按鈕進入ThirdActivity。后台打印日志如下:

上方日志可以看到:SecondActivity的Task id不同於FirstActivity和ThirdActivity,這說明SecondActivity確實是存放在一個單獨的返回棧中的,而且這個返回棧中只有SecondActivity這一個活動。

然后,我們按下Back鍵進行返回,你會發現ThirdActivity竟然直接返回到了FirstActivity,再按下Back鍵又會返回到SecondActivity,再按下Back鍵才會退出程序。解釋如下:

FirstActivity和ThirdActivity存放在同一個返回棧里,當在ThirdActivity中按下Back鍵,ThirdActivity出棧,那么FirstActivity就成為了棧頂活動顯示在界面上;然后在FirstActivity界面再次按下Back鍵,這是當前的返回棧已經空了,於是就顯示了另一個返回棧的棧頂活動,即SecondActivity。最后按下Back鍵,這時,所有的返回棧都已經空了,自然也就退出了程序。 

 

三、Activity的基本用法:

1、隱藏標題欄:

requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.activity_main);  

注:第一行代碼一定要在第二行代碼之前執行。

2、在活動當中使用Toast:

例如點擊按鈕時,彈出吐司:

1          button.setOnClickListener(new OnClickListener() { 2  @Override 3              public void onClick(View v) { 4 Toast.makeText(MainActivity.this, "You clicked the Button",Toast.LENGTH_SHORT).show();  5  } 6          });

3、啟動一個Activity的方法:即在默認啟動的Activity中啟動另一個Activity

核心代碼如下:

Intent intent = new Intent(); intent.setClass(MainActivity.this, SecondActivity.class); 

具體過程請參考本人的另一篇博客: 當前Activity跳轉到另一個Activity的詳細過程

 

4、隱式Intent的用法:

使用隱式Intent,我們不僅可以啟動自己程序內的活動,還可以啟動其他程序的活動,這使得Android多個應用程序之間的功能共享成為了可能。比如應用程序中需要展示一個網頁,沒有必要自己去實現一個瀏覽器(事實上也不太可能),而是只需要條用系統的瀏覽器來打開這個網頁就行了。

【實例】:打開指定網頁。

監聽器部分的核心代碼如下:

1         button.setOnClickListener(new OnClickListener() { 2  @Override 3             public void onClick(View v) { 4 Intent intent = new Intent(Intent.ACTION_VIEW); 5 intent.setData(Uri.parse("http://www.baidu.com")); 6  startActivity(intent); 7  } 8         }); 

第4行代碼:指定了Intent的action是 Intent.ACTION_VIEW,這是一個Android系統內置的動作;

第5行代碼:通過Uri.parse()方法,將一個網址字符串解析成一個Uri對象,再調用intent的setData()方法將這個Uri對象傳遞進去。

詳見:《android第一行代碼》P48頁。

如果要打電話的話,可以使用下面的代碼:

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

  

四、向下一個Activity傳遞數據:

不同的Activity 實例可能運行在一個進程中,也可能運行在不同的進程中。因此,我們需要一種特別的機制幫助我們在Activity 之間傳遞消息。Android 中通過Intent 對象來表示一條消息,一個Intent 對象不僅包含有這個消息的目的地,還可以包含消息的內容,這好比一封Email,其中不僅應該包含收件地址,還可以包含具體的內容。對於一個Intent 對象,消息“目的地 ”是必須的,而內容則是可選項。

在上面的實例中通過Activity. startActivity(intent)啟動另外一個Activity的時候,我們在Intent類的構造器中指定了“收件人地址”。

Activity傳遞數據有以下兩種方式:

1、【方式一】使用Intent自帶的bundle對象

傳遞數據,代碼如下:

這里,我們采用另一種綁定監聽時間的方法,即在布局文件中,對Button按鈕做如下設置:

 

1     <Button
2         android:id="@+id/button1"
3         android:layout_width="wrap_content"
4         android:layout_height="wrap_content"
5         android:layout_below="@+id/textView1"
6         android:layout_marginTop="22dp"
7  android:onClick="gotoSecondActivity"
8         android:text="啟動第二個Activity" /> 

上方第7行代碼就是我們綁定的監聽事件,點擊按鈕,將觸發gotoSecondActivity()函數中的代碼。緊接着做下面的操作。

在MainActivity中發送數據:

1     public void gotoSecondActivity(View view){
2         //創建一個意圖
3         Intent intent = new Intent(MainActivity.this,SecondActivity.class);
4         
5         //第一種方式:使用Intent自帶的bundle對象
6         intent.putExtra("name", "smyhvae");//方法:public Intent putExtra (String name, boolean value) 
7  startActivity(intent); 8     }

 

在SecondActivity中接收數據:

 1     protected void onCreate(Bundle savedInstanceState) {
 2         // TODO Auto-generated method stub
 3         super.onCreate(savedInstanceState);
 4         setContentView(R.layout.second);
 5         
 6         Intent intent = getIntent();
 7         String name = intent.getStringExtra("name");          
 8 
 9         
10         TextView textView = (TextView)findViewById(R.id.textView1);
11         textView.setText("name="+name);       
1112     } 

 

或者傳遞一個對象

新建一個Student.java的類文件,作為傳遞的對象:

 1 import java.io.Serializable;
 2 //讓這個類序列化
 3 public class Student implements Serializable{
 4     int grade ;
 5     String school;
 6     String address;
 7     
 8     //將這些變量 變成字符串,方便輸出
 9     @Override
10     public String toString() {
11         return "Student [grade=" + grade + ", school=" + school + ", address="
12                 + address + "]";
13     }
14     
15 }

 

在MainActivity中發送數據:

 1     public void gotoSecondActivity(View view){
 2         //創建一個意圖
 3         Intent intent = new Intent(MainActivity.this,SecondActivity.class);
 4         
 5         //傳遞自定義類型(對象)
 6         Student student = new Student();
 7         student.grade = 2;
 8         student.school = "UESTC";
 9         student.address = "chengdu";
10         intent.putExtra("student", student);//方法:public Intent putExtra (String name, Serializable value) 
11         
12         startActivity(intent);
13     }

 

在SecondActivity中接收數據:

 1     protected void onCreate(Bundle savedInstanceState) {
 2         // TODO Auto-generated method stub
 3         super.onCreate(savedInstanceState);
 4         setContentView(R.layout.second);               
 5 
 6 
 7       Intent intent = getIntent();  8        Student student = (Student) intent.getSerializableExtra("student");  9         
10         TextView textView = (TextView)findViewById(R.id.textView1);
11         textView.setText(student);         
12         
13         System.out.println("SecondActivity-onCreate");
14     } 

​【工程文件】

鏈接:http://pan.baidu.com/s/1jGvEc6q

密碼:ic6c

 

2、【方式二】創建Bundle對象來傳遞

通過按鈕監聽事件。核心代碼如下:

在MainActivity中發送數據:

 1     //通過這個方法跳轉到SecondActivity界面     
 2     public void gotoSecondActivity(View view){
 3         //創建一個意圖
 4         Intent intent = new Intent(MainActivity.this,SecondActivity.class);
 5         
 6         //第二種傳值方式:創建Bundle對象來傳遞
 7         Bundle bundle = new Bundle();  //創建bundle的內容
 8         bundle.putString("name", "smyhvae");//編寫bundle的內容
 9         bundle.putInt("age", 22); 10         bundle.putLong("id", 20132224); 11         
12         intent.putExtra("person", bundle);//封裝bundle。方法:public Intent putExtra (String name, Bundle value) 
13 
14         startActivity(intent);
15     }

 

在SecondActivity中接收數據:

 1     protected void onCreate(Bundle savedInstanceState) {
 2         // TODO Auto-generated method stub
 3         super.onCreate(savedInstanceState);
 4         setContentView(R.layout.second);
 5         
 6         //獲取上一個Activity傳遞過來的參數
 7         Intent intent = getIntent();  8         Bundle bundle = intent.getBundleExtra("person");  9         String name = bundle.getString("name"); 10         int age = bundle.getInt("age"); 11         
12         //獲取上一個Activity傳遞過來的參數,將接收到的數據輸出到TextView當中
13         TextView textView = (TextView)findViewById(R.id.textView1);
14         textView.setText("name="+name+";"+"age="+age);         
15         
16         System.out.println("SecondActivity-onCreate");
17     }

 

【工程文件】

鏈接:http://pan.baidu.com/s/1ntLqzfN

密碼:xzn7

 

五、返回數據給上一個Activity:

步驟如下:

  • 啟動帶返回結果的MainActivity:

        startActivityForResult(Intent intent, int requestCode)

        第二個參數為請求碼,用於在之后的回調中判斷數據的來源

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

 

  • 在SecondActivity中通過putExtra放入數據,然后調用以下方法:(非常重要)

        setResult(int resultCode, Intent data)

         resultCode一般只使用RESULT_OK 或RESULT_CANCELED這兩個值,第二個參數則把帶有數據的Intent傳遞回去

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

 

  • SecondActicity被銷毀之前,會調用上MainActivity的 onActivityResult()方法,所以要重寫這個方法:

public void onActivityResult(int requestCode, int resultCode,Intent data)

 1     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
 2         switch (requestCode) {
 3         case 1:
 4             if (resultCode == RESULT_OK) {  5                 String returnedData = data.getStringExtra("data_return");  6                 Log.d("FirstActivity", returnedData);  7  }  8             break;
 9         default:
10         }
11     } 

舉例略。

 

六、Activity運行時屏幕方向與顯示方式

1、鎖定屏幕方向:橫屏/ 豎屏

Android 內置了方向感應器的支持。Android 會根據所處的方向自動在豎屏和橫屏間切換。但是有時我們的應用程序僅能在橫屏/ 豎屏時運行,比如某些游戲,此時我們需要鎖定該Activity 運行時的屏幕方向,<activity>節點的android:screenOrientation屬性可以完成該項任務,示例代碼如下:

【方法一】在清單文件中配置:

<activity android:name=".EX01"
    android:label="@string/app_name"
    android:screenOrientation="portrait"> 
    // 值為portrait時強制為豎屏, 值為landscape時強制為橫屏
</activity>

 

【方法二】通過代碼實現(一般放在onCreate方法中的前面),如下:

setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATTION_LANDSCAPE); 
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATTION_PORTRAIT); 

這里提一個小知識,Android模擬器中,快捷鍵"Ctrl+F11/F12"可以實現轉屏

 

2、全屏顯示:

可以在其onCreate()方法中添加如下代碼實現:

        //設置全屏模式
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
        //去除標題欄
        requestWindowFeature(Window.FEATURE_NO_TITLE);

 

3、以對話框形式顯示Activity:

在清單文件中配置:

        <activity 
            android:name="com.example.smyh004activity02.SecondActivity"
            android:label="SecondActivity" android:theme="@android:style/Theme.DeviceDefault.Dialog">            
        </activity> 

特別關注:Activity的啟動模式。見《Android第一行代碼》P68頁

 

七、Activity的現場保存:

程序在運行時,一些設備的配置可能會改變,如:橫豎屏的切換、鍵盤的可用性等。這種事情一旦發生,Activity會重新創建。

重新創建的過程如下:

  • 在銷毀之前,會調用onSaveInstanceState()去保存應用中的一些數據,保存在系統當中;
  • 然后調用onDestroy()銷毀之前的Activity;
  • 最后調用 onCreate()或onRestoreInstanceState()方法去重新創建一個Activity。

現場保存的步驟如下:

(1)在MainActivity中,調用onSaveInstanceState(),即添加如下代碼就可以將臨時數據保存:

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        String tempData = "Something you want to save";
 outState.putString("data_key", tempData);
        Log.i(LOG, "onSaveInstanceState..");
    }

 

(2)數據保存之后,修改MainActivity的onCreate()方法:

 1     @Override
 2     protected void onCreate(Bundle savedInstanceState) {
 3         super.onCreate(savedInstanceState);
 4         setContentView(R.layout.activity_main);
 5         Log.i(LOG, "onCreate...");
 6         //步驟2:還原當前activity的狀態
 7         if (savedInstanceState != null) {  8             String tempData = savedInstanceState.getString("data_key");  9  Log.i(LOG, tempData); 10         }
11     }

 

完整代碼如下:

 1 package com.example.smyh004activity03;
 2 import android.os.Bundle;
 3 import android.app.Activity;
 4 import android.content.res.Configuration;
 5 import android.util.Log;
 6 import android.view.Menu;
 7 public class MainActivity extends Activity {
 8     private static final String LOG = "Activity";
 9     @Override
10     protected void onCreate(Bundle savedInstanceState) {
11         super.onCreate(savedInstanceState);
12         setContentView(R.layout.activity_main);
13         Log.i(LOG, "onCreate...");
14         //步驟2:還原當前activity的狀態
15         if (savedInstanceState != null) {
16             String tempData = savedInstanceState.getString("data_key");
17             Log.i(LOG, tempData);
18         }
19     }
20     // 步驟1:活動被銷毀之前(如在橫豎屏切換時),會觸發該方法來保存activity數據
21     @Override
22     protected void onSaveInstanceState(Bundle outState) {
23         super.onSaveInstanceState(outState);
24         String tempData = "open";
25         outState.putString("data_key", tempData);
26         Log.i(LOG, "onSaveInstanceState..");
27     }
28 
29 }

 當手動旋轉屏幕后,后台輸出結果如下:

上圖的日志中,如果把生命周期寫完整一點,打印的日志如下:

 

在旋轉屏幕時,如果不想重新創建Activity,我們可以通過清單文件AndroidManifest.xml中android:configChanges來指定的某些屬性不發生變化,然后通知程序去調用onConfiguratonChanged()方法主動去改變一些設置(當旋轉屏幕的時候)。

清單文件中,指定的常見屬性有:

  • "keyboard" 鍵盤發生了改變----例如用戶用了外部的鍵盤 
  • "keyboardHidden" 鍵盤的可用性發生了改變
  • "orientation" 屏幕方向改變
  •   "screenSize" 屏幕大小改變

設置代碼舉例如下:

        <activity
            android:name="com.example.smyh004activity03.MainActivity"
            android:label="@string/app_name"
            android:configChanges="orientation|screenSize" >
        </activity> 

注:符號“|”表示“並且”的意思,這行代碼在實際應用中很常見。

接着在上面的java代碼的基礎之上,添加如下代碼:

1     @Override
2     public void onConfigurationChanged(Configuration newConfig) {
3         super.onConfigurationChanged(newConfig);
4         Log.i(LOG, "onConfigurationChanged..");
5     }

 

最終,當手動旋轉屏幕后,后台輸出結果如下:

  

可以看到,onSaveInstanceState()方法並沒有被調用,也就是說,旋轉屏幕時,當前Activity並沒有被銷毀。

 

八、Activity通過Shared Preferences保存數據:

通常情況下會發生這樣的問題,我們在編輯短信的同時有電話打進來,那么接電話肯定是要啟動另一個Activiy,那么當前編輯短信的Activity所編輯的信息我們想暫時保存下來,等接完電話后回到該Activity時,可以繼續編輯短信。該功能需要如何去實現呢?

其實,SharedPreferences使用xml格式為Android應用提供一種永久的數據存貯方式。對於一個Android應用,它存貯在文件系統的/data/ data/your_app_package_name/shared_prefs/目錄下,可以被處在同一個應用中的所有Activity 訪問。Android 提提供了相關的API來處理這些數據而不需要程序員直接操作這些文件或者考慮數據同步的問題。

現在就用代碼來實現這個功能:

首先使用SharedPreferences這個工具類: 

 1     private EditText etMsg ;
 2     private Button sendButton;
 3     private SharedPreferences sp;  4     
 5     @Override
 6     protected void onCreate(Bundle savedInstanceState) {
 7         super.onCreate(savedInstanceState);
 8         setContentView(R.layout.activity_main);
 9         
10         etMsg = (EditText)findViewById(R.id.editText1);
11         sendButton = (Button)findViewById(R.id.button1);
12         
13         // 獲取共享屬性操作的工具(文件名,操作模式)
14         sp = This.getSharedPreferences("data", 0); 15     } 

上方第14行代碼中,調用的方法是:public SharedPreferences getSharedPreferences (String name, int mode)

其中,第一個參數代表XML文件,如果有這個文件,就會操作這個文件,如果沒有這個文件,就會創建這個文件;第二個參數代表一種操作模式,0代表私有。

然后,我們要在onPause()方法里保存數據,之所以在onPause()方法里保存,是因為在所有可能會被內存銷毀的生命周期函數中,而onPause()方法最先執行。代碼如下: 

1     //在onPause()方法中保存數據
2     @Override
3     protected void onPause() {
4         super.onPause();
5         String msg = etMsg.getText().toString();
6         Editor editor = sp.edit(); 7         editor.putString("msg", msg); //執行方法:public abstract SharedPreferences.Editor putString (String key, String value) 
8  editor.commit();        
9     } 

將數據保存在msg變量中,然后拿到Editor這個編輯器,給它put進去。當然,這些只是在內存中操作,如果要反映到文件當中,還要執行 commit()方法。

緊接着,我們要在onResume()方法中重新還原數據:(為什么要在這個方法中還原數據,不用我多解釋) 

1     @Override
2     protected void onResume() {
3         super.onResume();
4         etMsg.setText(sp.getString("msg", ""));      
5     }

當程序中第一次啟動的時候,並沒有保存數據,所以返回一個默認的空值。將這個返回的數據放到etMsg控件中就行了。

現在我們運行程序,是可以執行的。

例如,現在編輯內容,然后去別的程序,再回來的時候(就算我們把程序退出了),編輯的內容還依然存在。這個時候,我們打開文件瀏覽器,發現數據是保存在data-data-android工程的文件夾-shared-prefs目錄的data.xml文件當中的,而且是永久保存;所以,當在onResume()方法還原數據之后,我們還要加一部分代碼,來刪掉這個文件里的內容(無法刪除文件本身),不然就會永久保存本地成為垃圾了。代碼如下: 

1     protected void onResume() {
2         super.onResume();
3         etMsg.setText(sp.getString("msg", ""));    
4         Editor editor = sp.edit(); 5  editor.clear(); 6  editor.commit(); 7     } 

 

總結之后,最終的完整版代碼如下:

activity_main.xml文件代碼: 

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >
    <EditText
        android:id="@+id/editText1"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:gravity="top"
        android:layout_weight="1"
        android:ems="10" />
    <Button
        android:id="@+id/button1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Button" />
</LinearLayout> 

 

MainActivity.java的代碼如下: 

 1 package com.example.smyh001;
 2 import android.app.Activity;
 3 import android.content.SharedPreferences;
 4 import android.content.SharedPreferences.Editor;
 5 import android.os.Bundle;
 6 import android.view.Menu;
 7 import android.widget.Button;
 8 import android.widget.EditText;
 9 public class MainActivity extends Activity {
10     private EditText etMsg ;
11     private Button sendButton;
12     private SharedPreferences sp;
13     
14     @Override
15     protected void onCreate(Bundle savedInstanceState) {
16         super.onCreate(savedInstanceState);
17         setContentView(R.layout.activity_main);
18         
19         etMsg = (EditText)findViewById(R.id.editText1);
20         sendButton = (Button)findViewById(R.id.button1);
21         
22         // 獲取共享屬性操作的工具
23         sp = getSharedPreferences("data", 0);
24     }
25     //在onPause()方法中保存數據
26     @Override
27     protected void onPause() {
28         super.onPause();
29         String msg = etMsg.getText().toString();
30         Editor editor = sp.edit();
31         editor.putString("msg", msg);//執行方法:public abstract SharedPreferences.Editor putString (String key, String value) 
32         editor.commit();        
33     }
34     
35     //在onResume()方法中還原數據
36     @Override
37     protected void onResume() {
38         super.onResume();
39         etMsg.setText(sp.getString("msg", ""));    
40         Editor editor = sp.edit();
41         editor.clear();
42         editor.commit();
43     }    
44 } 

 運行程序之后,我們在編輯框輸入一些文字:

退出程序,然后導出data.xml文件,打開后顯示如下:

說明輸入的文本被保存在了data.xml文件當中。當我們再回到程序,之前輸入的文字會被保留在界面上,而data.xml文件中的文本則會被清空。

 

 代碼優化:

上方代碼中如果我們在第40行代碼的后面加下面這一行代碼:

        etMsg.setSelection((sp.getString("msg", "")).length());

當返回到原程序時,setSelection方法可將輸入光標移動到文本的末尾位置以便繼續輸入。里面的參數sp.getString("msg", "")是之前所輸入的字符串。

 

到這里為止,Android的基礎知識就講完了,以后會不斷完善補充的。

 

我的公眾號

 

想學習代碼之外的軟技能?不妨關注我的微信公眾號:生命團隊(id:vitateam)。

 

掃一掃,你將發現另一個全新的世界,而這將是一場美麗的意外:

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM