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

首頁 > 系統(tǒng) > Android > 正文

Android開發(fā)中實現(xiàn)應(yīng)用的前后臺切換效果

2020-04-11 10:45:36
字體:
供稿:網(wǎng)友

在介紹程序?qū)崿F(xiàn)之前,我們先看下Android中Activities和Task的基礎(chǔ)知識。

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


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


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


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

 
  以上是關(guān)于Activity和Task的描述。
 

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

 

  先看下本示例實現(xiàn)效果圖:

2016223154411266.jpg (416×686)

2016223154441113.jpg (419×693)

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

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

2016223154500745.jpg (416×164)

對于這種效果一般的做法是在Activity中的onStop()方法中編寫相應(yīng)代碼,因為當Activity進入后臺時將會調(diào)用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 * @Author Hanyonglu * @Date 2012-4-13 下午02:00:44 * @Version V1.0 */public class NotificationExtend { private Activity context;  public NotificationExtend(Activity context) {  // TODO Auto-generated constructor stub  this.context = context; }  // 顯示Notification public void showNotification() {  // 創(chuàng)建一個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;      // 設(shè)置通知的事件消息  CharSequence contentTitle = "閱讀器顯示信息"; // 通知欄標題  CharSequence contentText = "推送信息顯示,請查看……"; // 通知欄內(nèi)容    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); }}

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

 

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

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

           

   這里需要注意的是:

  <activity>下的launchMode屬性可以設(shè)置四種啟動方式:

  • standard (默認模式)
  • singleTop
  • singleTask
  • singleInstance 

  這四個模式有以下的幾個不同點:

  1. 響應(yīng)Intent時Activity將被裝入哪個task。

  對于standard和singleTop模式,由產(chǎn)生該Intent(調(diào)用startActivity())的task持有該Activity――除非Intent對象里含有FLAG_ACTIVITY_NEW_TASK標志,那么就會尋找一個新的task。

  相反的,singTask和singleInstance模式,總是標志Activity為task的root Activity,開啟這樣的活動會新建一個task,而不是裝入某個正在運行的任務(wù)。

 

  2. 一個Activity是否可以有多個實例。

  一個standard或者singleTop屬性的Activity可以實例化多次,他們可以屬于多個不同的task,而且一個task也可以含有相同Activity的多個實例。


  相反的,singleTask或者singleInstance屬性的Activity只能有一個實例(單例),因為這些Activity是位于task的底部,這種限制意味著同一設(shè)備的同一時刻該task只能有一個實例。
 

  3. 實例是否能允許在它的task里有其他的Activity。

  一個singleInstance屬性的Activity是它所在的task里僅有的一個Activity,如果他啟動了另一個Activity,那個Activity會被加載進一個不同的task而無視它的啟動模式――就如Intent里有FLAG_ACTIVITY_NEW_TASK標識一樣。在其他的方面,singleInstance和singleTask一樣的。
 

  其他三個模式允許有多個Activity在一個task里,一個singleTask屬性的Activity總是一個task里的root Activity,但是他可以啟動另外的Activity并且將這個新的Activity裝進同一個task里,standard和singleTop屬性的Activity可以出現(xiàn)在task的任何位置。
 

  4. 是否創(chuàng)建一個新的Activity實例來處理一個新的Intent。

  對于默認的standard方式,將會生成新的實例來處理每一個新的Intent。每個實例處理一個新的Intent。
 

  對singleTop模式,如果一個已經(jīng)存在的實例在目標task的棧頂,那么就重用這個實例來處理這個新的Intent,如果這個實例存在但是不在棧頂,那就不重用他,而是重新創(chuàng)建一個實例來處理這個新的Intent并且將這個實例壓入棧。


  例如現(xiàn)在有一個task堆棧ABCD,A是root Activity,D是棧頂Activity,現(xiàn)在有一個啟動D的Intent來了,如果D是默認的standard方法,那么就會創(chuàng)建一個新的實例來處理這個Intent,所以這個堆棧就變?yōu)锳BCDD,然而如果D是singleTop方式,這個已經(jīng)存在的棧頂?shù)腄就會來處理這個Intent,所以堆棧還是ABCD。
 

  如果另外一種情況,到來的Intent是給B的,不管B是standard還是singleTop(因為現(xiàn)在B不在棧頂),都會創(chuàng)建一個新的實例,所以堆棧變?yōu)锳BCDB。


  如上所述,一個"singleTask"或"singleInstance"模式的activity只會有一個實例,這樣它們的實例就會處理所有的新intent。一個"singleInstance" activity總是在棧里的最上面

(因為它是task里的唯一的activity), 這樣它總是可以處理一個intent。而一個"singleTask" activity在棧里可以有或沒有其他activity在它上面。如果有的話,它就不能對新到的intent進行處理,intent將被丟棄。(即使intent被丟棄,它的到來將使task來到前臺,并維持在那里。)
 

  當一個已有的Activity被請求去處理一個新的Intent時,Intent對象會通過onNewIntent()的調(diào)用傳遞給這個活動。(傳遞進來的原始的Intent對象可以通過調(diào)用getIntent()獲取)。 


  注意,當創(chuàng)建一個新的Activity的實例來處理一個新收到的Intent時,用戶可以按BACK鍵回到上一個狀態(tài)(上一個Activity)。但是使用一個已有的Activity實例操作新收到的Intent時,用戶不能通過按下BACK鍵回到這個實例在接受到新Intent之前的狀態(tài)。
 

  呵呵,不好意思,扯得有點多了,我們繼續(xù)看我們的程序。

 

  在這里,如果是對一個Activity實現(xiàn)時可以這樣實現(xiàn),如果有多個Activity,我們就需要在每個Activity里重寫onKeyDown事件并捕捉用戶是否按下HOME鍵。

  為了實現(xiàn)方便,我們可以使用一個Service專門用于監(jiān)聽程序是否進入后臺或前臺工作,如果程序進入后臺運行就顯示Notification,這樣不管程序中有多少個Activity就可以很方便的實現(xiàn)程序前后如切換。

  為此,我在程序中新添加了一個AppStatusService 類,目的是監(jiān)聽程序是否在前后臺運行,如果在后臺運行則顯示信息提示。

  代碼如下:

package com.test.service;

import java.util.List;import com.test.background.MainActivity;import com.test.background.NotificationExtend;import com.test.background.R;import com.test.util.AppManager;import android.app.ActivityManager;import android.app.Notification;import android.app.NotificationManager;import android.app.PendingIntent;import android.app.ActivityManager.RunningAppProcessInfo;import android.app.Service;import android.content.Context;import android.content.Intent;import android.graphics.Color;import android.os.IBinder;import android.util.Log;/** * 監(jiān)聽程序是否在前后臺運行Service * @Description: 監(jiān)聽程序是否在前后臺運行Service * @FileName: AppStatusService.java  * @Package com.test.service * @Author Hanyonglu * @Date 2012-4-13 下午04:13:47  * @Version V1.0 */public class AppStatusService extends Service{ private static final String TAG = "AppStatusService";  private ActivityManager activityManager;  private String packageName; private boolean isStop = false;  @Override public IBinder onBind(Intent intent) {  // TODO Auto-generated method stub  return null; }  @Override public int onStartCommand(Intent intent, int flags, int startId) {  // TODO Auto-generated method stub  activityManager = (ActivityManager) this.getSystemService(Context.ACTIVITY_SERVICE);   packageName = this.getPackageName();   System.out.println("啟動服務(wù)");    new Thread() {    public void run() {     try {      while (!isStop) {       Thread.sleep(1000);             if (isAppOnForeground()) {        Log.v(TAG, "前臺運行");      } else {        Log.v(TAG, "后臺運行");       showNotification();      }      }     } catch (Exception e) {      e.printStackTrace();     }    }   }.start();     return super.onStartCommand(intent, flags, startId); }  /**  * 程序是否在前臺運行  * @return  */ public boolean isAppOnForeground() {   // Returns a list of application processes that are running on the device   List<RunningAppProcessInfo> appProcesses = activityManager.getRunningAppProcesses();   if (appProcesses == null) return false;     for (RunningAppProcessInfo appProcess : appProcesses) {    // The name of the process that this object is associated with.    if (appProcess.processName.equals(packageName)      && appProcess.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {     return true;    }   }     return false;  }   @Override public void onDestroy() {  // TODO Auto-generated method stub  super.onDestroy();  System.out.println("終止服務(wù)");  isStop = true; }  // 顯示Notification public void showNotification() {  // 創(chuàng)建一個NotificationManager的引用  NotificationManager notificationManager = (    NotificationManager)getSystemService(      android.content.Context.NOTIFICATION_SERVICE);    // 定義Notification的各種屬性  Notification notification = new Notification(    R.drawable.icon,"閱讀器",     System.currentTimeMillis());  // 將此通知放到通知欄的"Ongoing"即"正在運行"組中  notification.flags |= Notification.FLAG_ONGOING_EVENT;  // 點擊后自動清除Notification  notification.flags |= Notification.FLAG_AUTO_CANCEL;  notification.flags |= Notification.FLAG_SHOW_LIGHTS;  notification.defaults = Notification.DEFAULT_LIGHTS;  notification.ledARGB = Color.BLUE;  notification.ledOnMS = 5000;      // 設(shè)置通知的事件消息  CharSequence contentTitle = "閱讀器顯示信息"; // 通知欄標題  CharSequence contentText = "推送信息顯示,請查看……"; // 通知欄內(nèi)容    Intent notificationIntent = new Intent(AppManager.context,AppManager.context.getClass());  notificationIntent.setAction(Intent.ACTION_MAIN);  notificationIntent.addCategory(Intent.CATEGORY_LAUNCHER);  PendingIntent contentIntent = PendingIntent.getActivity(    AppManager.context, 0, notificationIntent,PendingIntent.FLAG_UPDATE_CURRENT);  notification.setLatestEventInfo(    AppManager.context, contentTitle, contentText, contentIntent);  // 把Notification傳遞給NotificationManager  notificationManager.notify(0, notification); }}

   在這里為了在信息提示欄里點擊后能夠返回到原來的Activity,需要在AppManager里記下我們當前的Activity。
 

PS:監(jiān)聽程序是否進入后臺
方法一:

/**  *判斷當前應(yīng)用程序處于前臺還是后臺  *   * @param context   * @return   */ public static boolean isApplicationBroughtToBackground(final Context context) {  ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);  List<RunningTaskInfo> tasks = am.getRunningTasks(1);  if (!tasks.isEmpty()) {   ComponentName topActivity = tasks.get(0).topActivity;   if (!topActivity.getPackageName().equals(context.getPackageName())) {    return true;   }  }  return false;  }

這段代碼是需要一個權(quán)限的:

<uses-permission android:name="android.permission.GET_TASKS" />

方法二:

/*** * @param context* @return*/public static boolean isBackground(Context context) {  ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);List<RunningAppProcessInfo> appProcesses = activityManager.getRunningAppProcesses();for (RunningAppProcessInfo appProcess : appProcesses) {if (appProcess.processName.equals(context.getPackageName())) {if (appProcess.importance == RunningAppProcessInfo.IMPORTANCE_BACKGROUND) {Log.i(String.format("Background App:", appProcess.processName));return true;}else{Log.i(String.format("Foreground App:", appProcess.processName));return false;}}}return false;}

方法三:
使用api提供的

@Override protected void onUserLeaveHint() { //當用戶按Home鍵等操作使程序進入后臺時即開始計時  super.onUserLeaveHint();  if(!isLeave){   isLeave=true;   saveStartTime();  } }

這個需要需要加一個intent flag
public static final int FLAG_ACTIVITY_NO_USER_ACTION
不然從一個activity依次調(diào)用startActivity,finish關(guān)閉自己,啟動一個新的activity時,onUserLeaveHint也會被調(diào)用

發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 治多县| 玉树县| 海原县| 防城港市| 揭阳市| 商城县| 二连浩特市| 杂多县| 大同县| 普洱| 延安市| 漯河市| 北碚区| 金昌市| 莎车县| 五台县| 遵化市| 拉萨市| 江孜县| 旌德县| 农安县| 隆子县| 武安市| 芜湖市| 团风县| 泾川县| 新巴尔虎右旗| 徐州市| 南城县| 封开县| 疏附县| 灵武市| 开阳县| 永兴县| 上蔡县| 宁海县| 马尔康县| 彭泽县| 桂林市| 鹤庆县| 朔州市|