实例、运行-Android实现程序前后台切换效果-by小雨

文章结束给大家来个序程员笑话:[M]

    本文演示如在何Android中实现序程台前后换切果效。

    在绍介序程实现之前,我们先看下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主界面。

    先看下本示例实现果效图:

    

 

    在上图第二个图中,我们点击时将会回返到的来原的Activity中。 

 

    当我们的序程进入台后运作时,在我们的模拟器顶部将以标图情势涌现,如下图:

      

    对于这类果效一般的做法是在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



 * @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() {

        // 建创一个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>

    这里要需意注的是:

    <activity>下的launchMode属性可以设置四种动启方法: 

    standard (认默模式) 

    singleTop 

    singleTask 

    singleInstance 

 

    这四个模式有以下的几个不同点:

    1. 响应Intent时Activity将被装入哪个task。

    对于standard和singleTop模式,由发生该Intent(调用startActivity())的task持有该Activity——除非Intent对象里含有FLAG_ACTIVITY_NEW_TASK志标,那么就会找寻一个新的task。 

    相反的,singTask和singleInstance模式,是总志标Activity为task的root Activity,开启这样的活动会建新一个task,而不是装入某个正在运行的任务。 

 

    2. 一个Activity是不是可以有多个实例。

    一个standard或者singleTop属性的Activity可以实例化多次,他们可以属于多个不同的task,而且一个task也可以含有雷同Activity的多个实例。 

    相反的,singleTask或者singleInstance属性的Activity只能有一个实例(单例),因为这些Activity是位于task的底部,这类制限意味着统一设备的统一刻时该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可以出当初task的任何置位。 

 

    4. 是不是建创一个新的Activity实例来理处一个新的Intent。

    对于认默的standard方法,将会生成新的实例来理处每个新的Intent。每个实例理处一个新的Intent。

 

    对singleTop模式,如果一个已存在的实例在目标task的栈顶,那么就重用这个实例来理处这个新的Intent,如果这个实例存在但是不在栈顶,那就不重用他,而是从建新创一个实例来理处这个新的Intent并且将这个实例压入栈。 

    例如当初有一个task堆栈ABCD,A是root Activity,D是栈顶Activity,当初有一个动启D的Intent来了,如果D是认默的standard方法,那么就会建创一个新的实例来理处这个Intent,所以这个堆栈就为变ABCDD,然而如果D是singleTop方法,这个已存在的栈顶的D就会来理处这个Intent,所以堆栈还是ABCD。 

 

    如果另外一种情况,到来的Intent是给B的,不管B是standard还是singleTop(因为当初B不在栈顶),会都建创一个新的实例,所以堆栈为变ABCDB。 

    如上所述,一个"singleTask"或"singleInstance"模式的activity只会有一个实例,这样它们的实例就会理处全部的新intent。一个"singleInstance" activity是总在栈里的最面上

    (因为它是task里的一唯的activity), 这样它是总可以理处一个intent。而一个"singleTask" activity在栈里可以有或没有其他activity在它面上。如果有的话,它就不能对新到的intent停止理处,intent将被抛弃。(即使intent被抛弃,它的到来将使task离开台前,并维持在那里。) 

 

    当一个已有的Activity被请求去理处一个新的Intent时,Intent对象会通过onNewIntent()的调用传递给这个活动。(传递进来的原始的Intent对象可以通过调用getIntent()取获)。  

    意注,当建创一个新的Activity的实例来理处一个新收到的Intent时,用户可以按BACK键回到上一个状态(上一个Activity)。但是用使一个已有的Activity实例作操新收到的Intent时,用户不能通过按下BACK键回到这个实例在接受到新Intent之前的状态。 

 

    呵呵,不好意思,扯得有点多了,我们续继看我们的序程。

 

    在这里,如果是对一个Activity实现时可以这样实现,如果有多个Activity,我们就要需在每个Activity里重写onKeyDown事件并捉捕用户是不是按下HOME键。

    为了实现便方,我们可以用使一个Service专门于用监听序程是不是进入台后或台前任务,如果序程进入台后运行就表现Notification,这样不管序程中有多少个Activity可就以很便方的实现序程前后如换切。 

    为此,我在序程中新添加了一个AppStatusService 类,目标是监听序程是不是在台前后运行,如果在台后运行则表现信息示提。

    码代如下:

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;

/**
 * 监听序程是不是在台前后运行Service
 * @Description: 监听序程是不是在台前后运行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("动启务服");
        
        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("终止务服");
        isStop = true;
    }
    
    // 表现Notification
    public void showNotification() {
        // 建创一个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;
                
        // 设置通知的事件消息
        CharSequence contentTitle = "阅读器表现信息"; // 通知栏标题
        CharSequence contentText = "推送信息表现,请看查……"; // 通知栏内容
        
        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。

 

    最后,希望转载的友朋可以重尊作者的劳动成果,加上转载地址:

     http://blog.csdn.net/by317966834/article/details/8803794

    谢谢。 

文章结束给大家分享下程序员的一些笑话语录: 问路
有一个驾驶热气球的人发现他迷路了。他降低了飞行的高度,并认出了地面 上的一个人。他继续下降高度并对着那个人大叫,“打扰一下,你能告诉我我 在哪吗?”
下面那个人说:“是的。你在热气球里啊,盘旋在 30 英尺的空中”。
热气球上的人说:“你一定是在 IT 部门做技术工作”。
“没错”,地面上的人说到,“你是怎么知道的?”
“呵呵”,热气球上的人说,“你告诉我的每件事在技术上都是对的,但对都没 有用”。
地面上的人说,“你一定是管理层的人”。
“没错”,热气球上的人说,“可是你是怎么知道的?”
“呵呵”,地面上的那人说到,“你不知道你在哪里,你也不知道你要去哪,你 总希望我能帮你。你现在和我们刚见面时还在原来那个地方,但现在却是我 错了”。

posted @ 2013-04-15 21:24  坚固66  阅读(304)  评论(0编辑  收藏  举报