国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Android > 正文

深入解析Android系統中應用程序前后臺切換的實現要點

2019-12-12 06:28:40
字體:
來源:轉載
供稿:網友

在介紹程序實現之前,我們先看下Android中Activities和Task的基礎知識。
我們都知道,一個Activity 可以啟動另一個Activity,即使這個Activity是定義在別一個應用程序里的,比如說,想要給用戶展示一個地圖的信息,現在已經有一個Activity可以做這件事情,那么現在你的Activity需要做的就是將請求信息放進一個Intent對象里,并且將這個Intent對象傳遞給startActivity(),那么地圖就可顯示出來了,但用戶按下Back鍵之后,你的Activity又重新出現在屏幕上。

對用戶來講,顯示地圖的Activity和你的Activity好像在一個應用程序中的,雖然是他們是定義在其他的應用程序中并且運行在那個應有進程中。Android將你的Activity和借用的那個Activity被放進一個Task中以維持用戶的體驗。那么Task是以棧的形式組織起來一組相互關聯的Activity,棧中底部的Activity就是開辟這個Task的,通常是用戶在應用程序啟動器中選擇的Activity。棧的頂部的Activity是當前正在運行的Activity--用戶正在交互操作的Activity。

當一個Activity啟動另一個Activity時,新啟動的Activity被壓進棧中,成為正在運行的Activity。舊的Activity仍然在棧中。當用戶按下BACK鍵之后,正在運行的Activity彈出棧,舊的Activity恢復成為運行的Activity。棧中包含對象,因此如果一個任務中開啟了同一個Activity子類的的多個對象――例如,多個地圖瀏覽器――則棧對每一個實例都有一個單獨的入口。棧中的Activity不會被重新排序,只會被、彈出。Task是一組Activity實例組成的棧,不是在manifest文件里的某個類或是元素,所以無法設定一個Task的屬性而不管它的Activity,一個Task的所有屬性值是在底部的Activity里設置的,這就需要用于Affinity。關于Affinity這里不再詳述,大家可以查詢文檔。

一個Task里的所有Activity作為一個整體運轉。整個Task(整個Activity堆棧)可以被推到前臺或被推到后臺。假設一個正在運行的Task中有四個Activity――正在運行的Activity下面有三個Activity,這時用戶按下HOME鍵,回到應有程序啟動器然后運行新的應用程序(實際上是運行了一個新的Task),那么當前的Task就退到了后臺,新開啟的應用程序的root Activity此時就顯示出來了,一段時間后,用戶又回到應用程序器,又重新選擇了之前的那個應用程序(先前的那個Task),那么先前的那個Task此時又回到了前臺了,當用戶按下BACK鍵時,屏幕不是顯示剛剛離開的那個新開啟的那個應用程序的Activity,而是被除回到前臺的那個Task的棧頂Activity,將這個Task的下一個Activity顯示出來。 上述便是Activity和Task一般的行為,但是這個行為的幾乎所有方面都是可以修改的。Activity和Task的關系,以及Task中Activity的行為,是受啟動該Activity的Intent對象的標識和在manifest文件中的Activity的<Activity>元素的屬性共同影響的。

以上是關于Activity和Task的描述。

在開發Android項目時,用戶難免會進行程序切換,在切換過程中,程序將進入后臺運行,需要用時再通過任務管理器或是重新點擊程序或是通過點擊信息通知欄中的圖標返回原來的界面。這種效果類似于騰訊QQ的效果,打開QQ后顯示主界面,在使用其他的程序時,QQ將以圖標的形式顯示在信息通知欄里,如果再用到QQ時再點擊信息通知欄中的圖標顯示QQ主界面。
先看下本示例實現效果圖:

2016428144551137.jpg (416×686)

2016428144627452.jpg (419×693)

在上圖第二個圖中,我們點擊時將會返回到的原來的Activity中。

當我們的程序進入后臺運作時,在我們的模擬器頂部將以圖標形式出現,如下圖:

2016428144647679.jpg (416×164)

對于這種效果一般的做法是在Activity中的onStop()方法中編寫相應代碼,因為當Activity進入后臺時將會調用onStop()方法,我們可以在onStop()方法以Notification形式顯示程序圖標及信息,其中代碼如下所示:

@Override   protected void onStop() {   // TODO Auto-generated method stub     super.onStop();     Log.v("BACKGROUND", "程序進入后臺");     showNotification();   } 

以上的showNotification()方法就是Notification。
然后點擊信息通知欄的Notification后再返回到原來的Activity。
當然,我們也可以捕捉HOME鍵,在用戶按下HOME鍵時顯示Notification, 以下是代碼示例:

// 點擊HOME鍵時程序進入后臺運行    @Override    public boolean onKeyDown(int keyCode, KeyEvent event) {      // TODO Auto-generated method stub      // 按下HOME鍵      if(keyCode == KeyEvent.KEYCODE_HOME){        // 顯示Notification        notification = new NotificationExtend(this);        notification.showNotification();        moveTaskToBack(true);               return true;      }      return super.onKeyDown(keyCode, event);    } 

這里的NotificationExtend是對顯示Notification的一個封裝,類中的代碼如下:

package com.test.background; import android.app.Activity;  import android.app.Notification;  import android.app.NotificationManager;  import android.app.PendingIntent;  import android.content.Intent;  import android.graphics.Color; /**  * Notification擴展類  * @Description: Notification擴展類  * @File: NotificationExtend.java  * @Package com.test.background  */  public class NotificationExtend {    private Activity context;   public NotificationExtend(Activity context) {      // TODO Auto-generated constructor stub      this.context = context;    }    // 顯示Notification    public void showNotification() {      // 創建一個NotificationManager的引用      NotificationManager notificationManager = (          NotificationManager)context.getSystemService(              android.content.Context.NOTIFICATION_SERVICE);      // 定義Notification的各種屬性      Notification notification = new Notification(          R.drawable.icon,"閱讀器",           System.currentTimeMillis());      // 將此通知放到通知欄的"Ongoing"即"正在運行"組中      notification.flags |= Notification.FLAG_ONGOING_EVENT;      // 表明在點擊了通知欄中的"清除通知"后,此通知自動清除。      notification.flags |= Notification.FLAG_AUTO_CANCEL      notification.flags |= Notification.FLAG_SHOW_LIGHTS;      notification.defaults = Notification.DEFAULT_LIGHTS;      notification.ledARGB = Color.BLUE;      notification.ledOnMS = 5000;     // 設置通知的事件消息      CharSequence contentTitle = "閱讀器顯示信息"; // 通知欄標題      CharSequence contentText = "推送信息顯示,請查看……"; // 通知欄內容     Intent notificationIntent = new Intent(context,context.getClass());      notificationIntent.setAction(Intent.ACTION_MAIN);     notificationIntent.addCategory(Intent.CATEGORY_LAUNCHER);     PendingIntent contentIntent = PendingIntent.getActivity(     context, 0, notificationIntent,PendingIntent.FLAG_UPDATE_CURRENT);     notification.setLatestEventInfo(     context, contentTitle, contentText, contentIntent);     // 把Notification傳遞給NotificationManager     notificationManager.notify(0, notification);    }   // 取消通知    public void cancelNotification(){      NotificationManager notificationManager = (          NotificationManager) context.getSystemService(              android.content.Context.NOTIFICATION_SERVICE);      notificationManager.cancel(0);    }  } 

這里需要在配置文件中設置每個Activity以單任務運行,否則,每次返回原Activity時會新增加一個Activity,而不會返回到原Activity。

在使用FLAG_ACTIVITY_NEW_TASK控制標識時也會出現不會返回到原Activity的現象。如果該標識使一個Activity開始了一個新的Task,然后當用戶按了HOME鍵離開這個Activity,在用戶按下BACK鍵時將無法再返回到原Activity。一些應用(例如Notification)總是在一個新的Task里打開Activity,而從來不在自己的Task中打開,所以它們總是將包含FLAG_ACTIVITY_NEW_TASK的Intent傳遞給startActivity()。所以如果有一個可以被其他的東西以這個控制標志調用的Activity,請注意讓應用程序有獨立的回到原Activity的方法。 代碼如下:

<activity android:name="ShowMessageActivity"        android:launchMode="singleTask"></activity>  

Android應用前后臺切換的判斷
Android中沒有提供一個應用前后臺切換的回調或廣播,這個功能只能我們自己來處理。以前遇到這個問題的處理方式是,實現一個BaseActivity,然后讓其他所有Activity都繼承自它,然后在生命周期函數中做相應的檢測。具體檢測方法如下:
       在Activity的onStart和onStop方法中進行計數,計數變量為count,在onStart中將變量加1,onStop中減1,假設應用有兩個Activity,分別為A和B。
       情況一、首先啟動A,A再啟動B:啟動A,count=1,A啟動B,生命周期的順序為B.onStart->A.onStop,count的計數仍然為1。
       情況二、首先啟動A,然后按Home鍵返回桌面:啟動A,count=1,按Home鍵返回桌面,會執行A.onStop,count的計數變位0。
       從上面的兩種情況看出,可以通過對count計數為0,來判斷應用被從前臺切到了后臺。同樣的,從后臺切到前臺也是類似的道理。具體實現看后面的代碼。
       但是如果項目中不是所有的Activity都繼承自同一個BaseActivity,就無法實現這個功能了。幸運的是,Android在API 14之后,在Application類中,提供了一個應用生命周期回調的注冊方法,用來對應用的生命周期進行集中管理,這個接口叫registerActivityLifecycleCallbacks,可以通過它注冊自己的ActivityLifeCycleCallback,每一個Activity的生命周期都會回調到這里的對應方法。其實這個注冊方法的本質和我們實現BaseActivity是一樣的,只是將生命周期管理移到了Activity本身的實現中。
 具體使用方法如下:

public class MyApplication extends Application{   public int count = 0;   @Override   public void onCreate() {     super.onCreate();      registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {        @Override       public void onActivityStopped(Activity activity) {         Log.v("viclee", activity + "onActivityStopped");         count--;         if (count == 0) {           Log.v("viclee", ">>>>>>>>>>>>>>>>>>>切到后臺 lifecycle");         }       }        @Override       public void onActivityStarted(Activity activity) {         Log.v("viclee", activity + "onActivityStarted");         if (count == 0) {           Log.v("viclee", ">>>>>>>>>>>>>>>>>>>切到前臺 lifecycle");         }         count++;       }        @Override       public void onActivitySaveInstanceState(Activity activity, Bundle outState) {         Log.v("viclee", activity + "onActivitySaveInstanceState");       }        @Override       public void onActivityResumed(Activity activity) {         Log.v("viclee", activity + "onActivityResumed");       }        @Override       public void onActivityPaused(Activity activity) {         Log.v("viclee", activity + "onActivityPaused");       }        @Override       public void onActivityDestroyed(Activity activity) {         Log.v("viclee", activity + "onActivityDestroyed");       }        @Override       public void onActivityCreated(Activity activity, Bundle savedInstanceState) {         Log.v("viclee", activity + "onActivityCreated");       }     });   } } 

除此之外,有沒有其他方法可以實現這個功能呢?
當應用切到后臺的時候,運行在前臺的進程由我們的app變成了桌面app,依據這一點,我們可以實現檢測應用前后臺切換的功能。在Activity的onStop生命周期中執行檢測代碼,如果發現當前運行在前臺的進程不是我們自己的進程,說明應用切到了后臺。
想想為什么要在onStop中檢測,而不是onPause?這是由于A啟動B時,生命周期的執行順序如下:A.onPause->B.onCreate->B.onStart->B.onResume->A.onStop,也就是說,在A的onPause方法中,B的生命周期還沒有執行,進程沒有進入前臺,當然是檢測不到的。我們把代碼移到onPause生命周期中,發現確實沒有效果。
具體實現代碼如下:

//用來控制應用前后臺切換的邏輯  private boolean isCurrentRunningForeground = true;  @Override  protected void onStart() {    super.onStart();    if (!isCurrentRunningForeground) {      Log.d(TAG, ">>>>>>>>>>>>>>>>>>>切到前臺 activity process");    }  }   @Override  protected void onStop() {    super.onStop();    isCurrentRunningForeground = isRunningForeground();    if (!isCurrentRunningForeground) {      Log.d(TAG,">>>>>>>>>>>>>>>>>>>切到后臺 activity process");    }  }   public boolean isRunningForeground() {    ActivityManager activityManager = (ActivityManager) this.getSystemService(Context.ACTIVITY_SERVICE);    List<ActivityManager.RunningAppProcessInfo> appProcessInfos = activityManager.getRunningAppProcesses();    // 枚舉進程    for (ActivityManager.RunningAppProcessInfo appProcessInfo : appProcessInfos) {      if (appProcessInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {        if (appProcessInfo.processName.equals(this.getApplicationInfo().processName)) {          Log.d(TAG,"EntryActivity isRunningForeGround");          return true;        }      }    }    Log.d(TAG, "EntryActivity isRunningBackGround");    return false;  } 

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 墨玉县| 甘泉县| 八宿县| 马公市| 浙江省| 楚雄市| 佛坪县| 苏州市| 武城县| 越西县| 根河市| 新乡县| 洛宁县| 锦州市| 区。| 万全县| 乌鲁木齐县| 同德县| 石阡县| 依安县| 旬邑县| 黎城县| 广州市| 万安县| 南充市| 南陵县| 田阳县| 会宁县| 扎兰屯市| 个旧市| 班戈县| 佛坪县| 婺源县| 股票| 石渠县| 临夏县| 专栏| 平安县| 石渠县| 阳西县| 资源县|