• 安卓四大组件之——Activity




    初识Activity

    什么是Activity

    这是官网给出的概念

    Activity是一个应用程序的组件,他在屏幕上提供了一个区域,允许用户在上面做一些交互性的操作, 比如打电话,照相,发送邮件,或者显示一个地图!Activity可以理解成一个绘制用户界面的窗口, 而这个窗口可以填满整个屏幕,也可能比屏幕小或者浮动在其他窗口的上方!

    总的来说就是Activity是一个应用程序中为提供交互界面的组件,同时一个APP中可以存在多个Activity

    Activity的生命周期

    在这里插入图片描述

    回调方法描述
    onCreate()这是第一个回调,在活动第一次创建时调用
    onStart()这个回调在活动为用户可见时被调用
    onResume() 这个回调在应用程序与用户开始可交互的时候调用
    onPause()被暂停的活动无法接受用户输入,不能执行任何代码。当前活动将要被暂停,上一个活动将要被恢复时调用
    onStop() 当活动不在可见时调用
    onDestroy() 当活动被系统销毁之前调用
    onRestart() 当活动被停止以后重新打开时调用

    注意:

    1. onPause()和onStop()被调用的前提是: 打开了一个新的Activity!而前者是旧Activity还可见的状态;后者是旧Activity已经不可见!
    2. 另外,亲测:AlertDialog和PopWindow是不会触发上述两个回调方法的~

    Activity的使用流程

    创建
    1.自定义Activity类名,继承Activity类或者它的子类。例如,在一个Activity中,只想实现一个列表, 那么就可以让该Activity继承ListActivity; 如果只想实现选项卡效果,那么就可以让该Activity继承TabActivity。创建一个名为MainAcrivity的继承Activity类的Activity,具体代码如下:

    import android. app. Activity;
    public class MyActivity extends Activity {
    }
    
    • 1
    • 2
    • 3

    2.重写需要的回调方法。通常情况下,都需要重写onCreate()方法,并且在该方法中调用setContentView()方法设置要显示的视图。

    @Override
    public void onCreate (Bundle savedInstanceState) {
    	super .onCreate ( savedInstanceState) ;
    	setContentView(R. layout .main);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    配置
    3. 创建Activity 后,还需要在AndroidManifest.xml文件中进行配置,如果没有配置,而又在程序中启动了该Activity,那么将抛出异常信息。

    <activity
    android :icon= "@drawable/图标文件名"
    android :name="类名"
    android :labe1="Activity显示的标题"
    android:theme= "要应用的主题"
    ...>
    </activity>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    android:icon 属性用于为Activity指定对应的图标,其中的图标文件名不包括扩展名;
    android:name 属性用子指是对应的Activity实现类;
    android:label 用于为该Activity 指定标签;
    android:theme属性用于设置要应用的主题

    注意:
    如果该Activity类在标记指定的包中,则android:name属性的属性值可以直接写类名,也可以加一个“.”点号;如果在标记指定包的子包中,则属性值需要设置为“.子包序列.类名”或者是完整的类名(包括包路径)。

    启动
    4.在一个Android项目中,如果只有一个Activity, 那么只需要在AndroidManifet.xml文件中对其进行配置,并且将其设置为程序的入口。这样,当运行该项目时,将自动启动该Activity。否则,需要应用startActivity()方法来启动需要的Activity。startActivity()方法的语法格式如下:

    public void startActivity (Intent intent)
    
    • 1

    该方法没有返回值,只有一个Intent类型的入口参数,Intent 是Android应用里各组件之间的通信方式,一个Activity通过Intent 来表达自己的“意图”。在创建Intent对象时,需要指定想要被启动的Activity.
    例如,要启动一个名称为DetailActivity的Activity,可以使用下面的代码:

    Intent intent = new Intent (MyActivity.this, DetailActivity.class);
    startActivity(intent);
    
    • 1
    • 2

    关闭
    5.在Android中,如果想要关闭当前的Activity, 可以使用Activity 类提供的finish()方法。该方法的使用比较简单,既没有入口参数,也没有返回值,只需要在Activity中相应的事件中调用该方法即可。

    public void finish ()
    
    • 1

    我们可以把他写到启动第二个Activity的方法中,当启动第二个Activity时,第一个Activity就会被关闭finish();

    注意:
    Android中的四大组件,只要你定义了,无论你用没用,都要在AndroidManifest.xml对 这个组件进行声明,不然运行时程序会直接退出,报ClassNotFindException

    onCreate()函数一个参数和两个参数的区别:

    一个参数

     @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    两个参数

    @Override
        public void onCreate(@Nullable Bundle savedInstanceState, @Nullable PersistableBundle persistentState) {
            super.onCreate(savedInstanceState, persistentState);
            setContentView(R.layout.activity_main);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    这个包含两个参数的方法是安卓5.0提供的新方法使用它时需要在配置清单文件中声明一个属性:

    android:persistableMode="persistAcrossReboots"
    
    • 1

    这样拥有该属性的Activity就拥有了持久化的能力了,增加的这个PersistableBundle参数令这些方法拥有了系统关机后重启的数据恢复能力!!而且不影响我们其他的序列化操作,一般我们会搭配另外两个方法来使用:

    public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState)
    public void onRestoreInstanceState(Bundle savedInstanceState, PersistableBundle persistentState)
    
    • 1
    • 2

    第一个方法可以在一下情景中使用:

    1. 点击home键回到主页或长按后选择运行其他程序
    2. 按下电源键关闭屏幕
    3. 动新的Activity
    4. 横竖屏切换时,肯定会执行,因为横竖屏切换的时候会先销毁Act,然后再重新创建 重要原则:当系统"未经你许可"时销毁了你的activity,则onSaveInstanceState会被系统调用, 这是系统的责任,因为它必须要提供一个机会让你保存你的数据(你可以保存也可以不保存)。

    第二个方法:

    和onCreate同样可以从前者取出保存的数据: 一般是在onStart()和onResume()之间执行! 之所以有两个可以获取到保存数据的方法,是为了避免Act跳转而没有关闭, 然后不走onCreate()方法,而你又想取出保存数据

    启动Activity的几种方式

    显式启动

    1. 常用:
    startActivity(new Intent(当前Act.this,要启动的Act.class));
    
    • 1
    1. 通过Intent的ComponentName:
    ComponentName cn = new ComponentName("当前Act的全限定类名","启动Act的全限定类名") ;
    Intent intent = new Intent() ;
    intent.setComponent(cn) ;
    startActivity(intent) ;
    
    • 1
    • 2
    • 3
    • 4
    1. 初始化Intent时指定包名:
    Intent intent = new Intent("android.intent.action.MAIN");
    intent.setClassName("当前Act的全限定类名","启动Act的全限定类名");
    startActivity(intent);
    
    • 1
    • 2
    • 3

    隐式启动:

    通过Intent-filter的Action,Category或data来实现 这个是通过Intent的 intent-filter**来实现的

    		<activity
                android:name=".SecondActivity"
                >
                <intent-filter>
                    <action android:name="my_action"/>
                    <category android:name="my_category"/>
                    <category android:name="android.intent.category.DEFAULT"/>
                </intent-filter>
            </activity>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
     		Intent intent = new Intent();
            intent.setAction("my_action");
            intent.addCategory("my_category");
            startActivity(intent);
    
    • 1
    • 2
    • 3
    • 4

    全包名启动

    Intent intent = getPackageManager().getLaunchIntentForPackage
    ("apk第一个启动的Activity的全限定类名") ;
    if(intent != null) startActivity(intent) ;
    
    • 1
    • 2
    • 3

    横竖屏切换与状态保存的问题

    前面也也说到了App横竖屏切换的时候会销毁当前的Activity然后重新创建一个,你可以自行在生命周期 的每个方法里都添加打印Log的语句,来进行判断,又或者设一个按钮一个TextView点击按钮后,修改TextView 文本,然后横竖屏切换,会神奇的发现TextView文本变回之前的内容了! 横竖屏切换时Act走下述生命周期:onPause-> onStop-> onDestory-> onCreate->onStart->onResume

    1. 先说下如何禁止屏幕横竖屏自动切换吧,很简单,在AndroidManifest.xml中为Act添加一个属性: android:screenOrientation, 有下述可选值:
    • unspecified:默认值 由系统来判断显示方向.判定的策略是和设备相关的,所以不同的设备会有不同的显示方向。
    • landscape:横屏显示(宽比高要长)
    • portrait:竖屏显示(高比宽要长)
    • user:用户当前首选的方向
    • behind:和该Activity下面的那个Activity的方向一致(在Activity堆栈中的)
    • sensor:有物理的感应器来决定。如果用户旋转设备这屏幕会横竖屏切换。
    • nosensor:忽略物理感应器,这样就不会随着用户旋转设备而更改了("unspecified"设置除外)。
    1. 横竖屏时想加载不同的布局:

    1)准备两套不同的布局,Android会自己根据横竖屏加载不同布局: 创建两个布局文件夹:layout-land横屏,layout-port竖屏 然后把这两套布局文件丢这两文件夹里,文件名一样,Android就会自行判断,然后加载相应布局了!

    2 )自己在代码中进行判断,自己想加载什么就加载什么:

    我们一般是在onCreate()方法中加载布局文件的,我们可以在这里对横竖屏的状态做下判断,关键代码如下:

    if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE){  
         setContentView(R.layout.横屏);
    }  
    
    else if (this.getResources().getConfiguration().orientation ==Configuration.ORIENTATION_PORTRAIT) {  
        setContentView(R.layout.竖屏);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    1. 状态保存
    onCreate(Bundle savedInstanceState);
    onSaveInstanceState(Bundle outState);
    onRestoreInstanceState(Bundle savedInstanceState);
    
    • 1
    • 2
    • 3

    然后重写onSaveInstanceState()方法,往这个bundle中写入数据,比如:

    outState.putInt(“num”,1);

    这样,然后你在onCreate或者onRestoreInstanceState中就可以拿出里面存储的数据,不过拿之前要判断下是否为null

    savedInstanceState.getInt(“num”);

    这样保存数据之后,当切换Activity后,即使无法回到原有的Activity也能保存数据。

    系统自带的常用Activity

    由于一些众所周知的原因,关于谷歌的一些操作是无法完成的

    //1.拨打电话
    // 给移动客服10086拨打电话
    Uri uri = Uri.parse("tel:10086");
    Intent intent = new Intent(Intent.ACTION_DIAL, uri);
    startActivity(intent);
    
    //2.发送短信
    // 给10086发送内容为“Hello”的短信
    Uri uri = Uri.parse("smsto:10086");
    Intent intent = new Intent(Intent.ACTION_SENDTO, uri);
    intent.putExtra("sms_body", "Hello");
    startActivity(intent);
    
    //3.发送彩信(相当于发送带附件的短信)
    Intent intent = new Intent(Intent.ACTION_SEND);
    intent.putExtra("sms_body", "Hello");
    Uri uri = Uri.parse("content://media/external/images/media/23");
    intent.putExtra(Intent.EXTRA_STREAM, uri);
    intent.setType("image/png");
    startActivity(intent);
    
    //4.打开浏览器:
    // 打开Google主页
    Uri uri = Uri.parse("http://www.baidu.com");
    Intent intent  = new Intent(Intent.ACTION_VIEW, uri);
    startActivity(intent);
    
    //5.发送电子邮件:(阉割了Google服务的没戏!!!!)
    // 给someone@domain.com发邮件
    Uri uri = Uri.parse("mailto:someone@domain.com");
    Intent intent = new Intent(Intent.ACTION_SENDTO, uri);
    startActivity(intent);
    // 给someone@domain.com发邮件发送内容为“Hello”的邮件
    Intent intent = new Intent(Intent.ACTION_SEND);
    intent.putExtra(Intent.EXTRA_EMAIL, "someone@domain.com");
    intent.putExtra(Intent.EXTRA_SUBJECT, "Subject");
    intent.putExtra(Intent.EXTRA_TEXT, "Hello");
    intent.setType("text/plain");
    startActivity(intent);
    // 给多人发邮件
    Intent intent=new Intent(Intent.ACTION_SEND);
    String[] tos = {"1@abc.com", "2@abc.com"}; // 收件人
    String[] ccs = {"3@abc.com", "4@abc.com"}; // 抄送
    String[] bccs = {"5@abc.com", "6@abc.com"}; // 密送
    intent.putExtra(Intent.EXTRA_EMAIL, tos);
    intent.putExtra(Intent.EXTRA_CC, ccs);
    intent.putExtra(Intent.EXTRA_BCC, bccs);
    intent.putExtra(Intent.EXTRA_SUBJECT, "Subject");
    intent.putExtra(Intent.EXTRA_TEXT, "Hello");
    intent.setType("message/rfc822");
    startActivity(intent);
    
    //6.显示地图:
    // 打开Google地图中国北京位置(北纬39.9,东经116.3)
    Uri uri = Uri.parse("geo:39.9,116.3");
    Intent intent = new Intent(Intent.ACTION_VIEW, uri);
    startActivity(intent);
    
    //7.路径规划
    // 路径规划:从北京某地(北纬39.9,东经116.3)到上海某地(北纬31.2,东经121.4)
    Uri uri = Uri.parse("http://maps.google.com/maps?f=d&saddr=39.9 116.3&daddr=31.2 121.4");
    Intent intent = new Intent(Intent.ACTION_VIEW, uri);
    startActivity(intent);
    
    //8.多媒体播放:
    Intent intent = new Intent(Intent.ACTION_VIEW);
    Uri uri = Uri.parse("file:///sdcard/foo.mp3");
    intent.setDataAndType(uri, "audio/mp3");
    startActivity(intent);
    
    //获取SD卡下所有音频文件,然后播放第一首=-= 
    Uri uri = Uri.withAppendedPath(MediaStore.Audio.Media.INTERNAL_CONTENT_URI, "1");
    Intent intent = new Intent(Intent.ACTION_VIEW, uri);
    startActivity(intent);
    
    //9.打开摄像头拍照:
    // 打开拍照程序
    Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); 
    startActivityForResult(intent, 0);
    // 取出照片数据
    Bundle extras = intent.getExtras(); 
    Bitmap bitmap = (Bitmap) extras.get("data");
    
    //另一种:
    //调用系统相机应用程序,并存储拍下来的照片
    Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); 
    time = Calendar.getInstance().getTimeInMillis();
    intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(Environment
    .getExternalStorageDirectory().getAbsolutePath()+"/tucue", time + ".jpg")));
    startActivityForResult(intent, ACTIVITY_GET_CAMERA_IMAGE);
    
    //10.获取并剪切图片
    // 获取并剪切图片
    Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
    intent.setType("image/*");
    intent.putExtra("crop", "true"); // 开启剪切
    intent.putExtra("aspectX", 1); // 剪切的宽高比为1:2
    intent.putExtra("aspectY", 2);
    intent.putExtra("outputX", 20); // 保存图片的宽和高
    intent.putExtra("outputY", 40); 
    intent.putExtra("output", Uri.fromFile(new File("/mnt/sdcard/temp"))); // 保存路径
    intent.putExtra("outputFormat", "JPEG");// 返回格式
    startActivityForResult(intent, 0);
    // 剪切特定图片
    Intent intent = new Intent("com.android.camera.action.CROP"); 
    intent.setClassName("com.android.camera", "com.android.camera.CropImage"); 
    intent.setData(Uri.fromFile(new File("/mnt/sdcard/temp"))); 
    intent.putExtra("outputX", 1); // 剪切的宽高比为1:2
    intent.putExtra("outputY", 2);
    intent.putExtra("aspectX", 20); // 保存图片的宽和高
    intent.putExtra("aspectY", 40);
    intent.putExtra("scale", true);
    intent.putExtra("noFaceDetection", true); 
    intent.putExtra("output", Uri.parse("file:///mnt/sdcard/temp")); 
    startActivityForResult(intent, 0);
    
    //11.打开Google Market 
    // 打开Google Market直接进入该程序的详细页面
    Uri uri = Uri.parse("market://details?id=" + "com.demo.app");
    Intent intent = new Intent(Intent.ACTION_VIEW, uri);
    startActivity(intent);
    
    //12.进入手机设置界面:
    // 进入无线网络设置界面(其它可以举一反三)  
    Intent intent = new Intent(android.provider.Settings.ACTION_WIRELESS_SETTINGS);  
    startActivityForResult(intent, 0);
    
    //13.安装apk:
    Uri installUri = Uri.fromParts("package", "xxx", null);   
    returnIt = new Intent(Intent.ACTION_PACKAGE_ADDED, installUri);
    
    //14.卸载apk:
    Uri uri = Uri.fromParts("package", strPackageName, null);      
    Intent it = new Intent(Intent.ACTION_DELETE, uri);      
    startActivity(it); 
    
    //15.发送附件:
    Intent it = new Intent(Intent.ACTION_SEND);      
    it.putExtra(Intent.EXTRA_SUBJECT, "The email subject text");      
    it.putExtra(Intent.EXTRA_STREAM, "file:///sdcard/eoe.mp3");      
    sendIntent.setType("audio/mp3");      
    startActivity(Intent.createChooser(it, "Choose Email Client"));
    
    //16.进入联系人页面:
    Intent intent = new Intent();
    intent.setAction(Intent.ACTION_VIEW);
    intent.setData(People.CONTENT_URI);
    startActivity(intent);
    
    //17.查看指定联系人:
    Uri personUri = ContentUris.withAppendedId(People.CONTENT_URI, info.id);//info.id联系人ID
    Intent intent = new Intent();
    intent.setAction(Intent.ACTION_VIEW);
    intent.setData(personUri);
    startActivity(intent);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155

    Activity间的数据传递

    Bundle传递数据

    当在一个Activity中启动另一个Activity时,经常需要传递一-些数据。这时就可以通过Intent来实现,因为Intent通常被称为是两个Activity之间的信使,通过将要传递的数据保存在Intent中,就可以将其传递到另一个Activity中了。
    在Android中,可以将要保存的数据存放在Bundle对象中,然后通过Intent提供的putExtras()方法将要携带的数据保存到Intent中。下面通过一个具体的实例介绍如何使用Bundle在Activity 之间交换数据。

    说明: Bundle 是一个字符串值到各种Parcelable 类型的映射,用于保存要携带的数据包。

    一次传递一个
    存数据

     		Intent intent = new Intent(A.this, B.class);
            intent.putExtra("key",value);
            startActivity(intent);
    
    • 1
    • 2
    • 3

    取数据

    Intent it2 = getIntent();
    it2.getStringExtra("key");//不同类型数据使用不同的函数
    
    • 1
    • 2

    一次传递多个
    存数据

    Intent intent = new Intent(A.this, B.class);
    Bundle bundle = new Bundle();
    bundle.putInt("num",1);
    bundle.putString("detail","示例");
    intent.putExtras(bundle);
    startActivity(intent);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    取数据

    Intent it2 = getIntent();
    Bundle bd = it2.getExtras();
    int num = bd.getInt("num");
    String detail = bd.getString("detail");
    
    • 1
    • 2
    • 3
    • 4

    在使用Bundle传递数据时,要注意,Bundle的大小是有限制的 < 0.5MB,如果大于这个值 是会报TransactionTooLargeException异常的!!!

    调用另一个Activity返回结果

    在Android应用开发时,有时需要在一个Activity中调用另一个Activity,当用户在第二个Activity中选择完成
    后,程序自动返回到第一个 Activity中,第一 个Activity 必须能够获取并显示用户在第二个Activity中选择的结果;或者,在第一个Activity中将一些 数据传递到第二个Activity,由于某些原因,又要返回到第一个 Activity中,并显示传递的数据,如程序中经常出现的“返回上一步"功能。这时,也可以通过Intent 和Bundle来实现。与在两个Acitivity之间交换数据不同的是,此处需要使用startActivityForResult()方法来启动另一个 Activity。

    ①使用startActivityForResult(Intent intent, int requestCode)来启动一个Activity

    @Override
    protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_main);
         //
         startActivityForResult(intent,CODE);
    }
    		
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    ②在启动的Activity中重写onActivityResult(int requestCode, int resultCode, Intent data)
    requestCode是用于区分在该Activity中不同的启动方式,比如有两个不同的按钮,启动的是同一一个Activity,传递的数据可能不同,这里就可以用这个requestCode来区别,resultCode:子Activity通过setResult()返回的码

     @Override
        protected void onActivityResult(int requestCode,int resultCode,Intent data) {
            super.onActivityResult(requestCode, resultCode, data);
            if (requestCode==CODE&&resultCode==CODE){
                ((EditText) findViewById(R.id.pwd)).setText("");//清空”密码“的编辑框
                ((EditText) findViewById(R.id.repwd)).setText("");//清空”确认密码“的编辑框
            }
            
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    ③在子Activity重写:setResult(int resultCode, Intent data)

     setResult(0x717,intent);//设置返回的结果码
    
    • 1

    随时获取当前是哪个Activity

    让所有Activity继承-一个自定义的BaseActivity类,在OnCreate()方法中添加下述语句即可:

    Log.d(" BaseActivity". getClassQ.getSimpleName0);
    
    • 1

    一次性关闭所有Activity

    1.创建一个Activity管理器类:ActivityCollector定义三个共有静态的方法:定义存储Activity的list集合,方法如下
    ①addActivity:往集合添加Activity对象
    ②removeActivity:移除Activity中的对象
    ③finishAll:增强for循环遍历所有Activity调用:if(!activity.isFinishing())activityfinish();

    public class ActivityCollector {  
        public static LinkedList<Activity> activities = new LinkedList<Activity>();  
        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();  
                }  
            }  
        }  
    }  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    2.BaseActivity中
    ①onCreate( )方法添加ActivityCollector.addActivity(this);
    ②onDestory( )方法添加: ActivityCollector.removeActivity(this);
    ③可以在任意一个Activity中调用:ActivityCollector.finishAll( );
    从而关闭所有Activity,退出app!

    完全退出App

    上面说的是关闭所有Activity的,但是有些时候我们可能想杀死整个App,连后台任务都杀死 杀得一干二净的话,可以使用搭配着下述代码使用:

    /** 
     * 退出应用程序 
     */  
    public void AppExit(Context context) {  
        try {  
            ActivityCollector.finishAll();  
            ActivityManager activityMgr = (ActivityManager) context  
                    .getSystemService(Context.ACTIVITY_SERVICE);  
            activityMgr.killBackgroundProcesses(context.getPackageName());  
            System.exit(0);  
        } catch (Exception ignored) {}  
    }  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    再按一次退出

    1.定义一个变量,来标识是否退出

    // 定义一个变量,来标识是否退出
    private static boolean isExit = false;
    Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            isExit = false;
        }
    };
    
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            if (!isExit) {
                isExit = true;
                Toast.makeText(getApplicationContext(), "再按一次退出程序",
                        Toast.LENGTH_SHORT).show();
                // 利用handler延迟发送更改状态信息
                mHandler.sendEmptyMessageDelayed(0, 2000);
            } else {
                exit(this);
            }
            return false;
        }
    return super.onKeyDown(keyCode, event);}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    1. 保存点击时间
    //保存点击的时间
    private long exitTime = 0;
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            if ((System.currentTimeMillis() - exitTime) > 2000) {
                Toast.makeText(getApplicationContext(), "再按一次退出程序",
                        Toast.LENGTH_SHORT).show();
                exitTime = System.currentTimeMillis();
            } else {
                            exit();
                          }
            return false;
        }
            return super.onKeyDown(keyCode, event);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    Activity动画

    所谓的过场动画就是切换到另外的Activity时加上一些切换动画,比如淡入淡出,放大缩小,左右互推等
    将动画文件放在res/anim下即可引用
    这里有两种方式为Activity设置过场动画

    方法一:
    A跳转到B,在startActivity(intent)后面加上overridePendingTransition(R.anim.anim _in,R.anim.anim out);

    B返回A,要在finish()后面加上overridePendingTransition(R.anim.anim in,R.anim.anim out);

    anim in是进入的Activity的动画,anim out是退出的Activity的动画

    方法二:
    ①在style.xml中自定义style:

    <!-- 默认Activity跳转动画 -->
    <style name="default_animation" mce_bogus="1" parent="@android:style/Animation.Activity">
        <item name="android:activityOpenEnterAnimation">@anim/default_anim_in</item><!--Activity A跳转到Activity B时Activity B进入动画-->
        <item name="android:activityOpenExitAnimation">@anim/anim_stay</item><!--Activity A跳转到Activity B时Activity A退出动画-->
        <item name="android:activityCloseEnterAnimation">@anim/anim_stay</item><!--Activity B返回Activity A时Activity A的进入动画-->
        <item name="android:activityCloseExitAnimation">@anim/default_anim_out</item><!--Activity B返回Activity A时ActivityB的退出动画-->
    </style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    ②然后修改下AppTheme:

    <style name="AppTheme" mce_bogus="1" parent="@android:style/Theme.Light">
            <item name="android:windowAnimationStyle">@style/default_animation</item>
            <item name="android:windowNoTitle">true</item>
    </style>
    
    • 1
    • 2
    • 3
    • 4

    ③最后在appliction设置下:

    <application
       android:icon="@drawable/logo"
       android:label="@string/app_name"
       android:theme="@style/AppTheme" >
    
    • 1
    • 2
    • 3
    • 4

    方法三:
    除了上面两种方法以外,还可以使用TransitionManager来实现,但是需求版本是API 19以上的, 另外还有一种addOnPreDrawListener的转换动画

    Activity全屏效果

    方法一:代码隐藏ActionBar(这个就是每个Activity的标题栏)

    在Activity的onCreate方法中调用getActionBar.hide();即可

    方法二:通过requestWindowFeature设置

    requestWindowFeature(Window.FEATURE_NO_TITLE);

    把 requestWindowFeature(Window.FEATURE_NO_TITLE);放在super.onCreate(savedInstanceState);前面就可以隐藏ActionBar而不报错。

    方法三:通过AndroidManifest.xml的theme

    在需要全屏的Activity的标签内设置 theme = @android:style/Theme.NoTitleBar.FullScreen

    对话框风格的Activity

    在某些情况下,我们可能需要将Activity设置成对话框风格的,Activity一般是占满全屏的, 而Dialog则是占据部分屏幕的!实现起来也很简单!

    直接设置下Activity的theme:

    android:theme="@android:style/Theme.Dialog"
    
    • 1

    这样就可以实现了,当然也可以再设置下标题,小图标

    //设置左上角小图标
    requestWindowFeature(Window.FEATURE_LEFT_ICON);
    setContentView(R.layout.main);
    getWindow().setFeatureDrawableResource(Window.FEATURE_LEFT_ICON, android.R.drawable.ic_lion_icon);
    //设置文字:
    setTitle(R.string.actdialog_title);  //XML代码中设置:android:label="@string/activity_dialog"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    深入理解Activity

    Activity、Window与View的关系

    在这里插入图片描述
    流程解析: Activity调用startActivity后最后会调用attach方法,然后在PolicyManager实现一个Ipolicy接口,接着实现一个Policy对象,接着调用makenewwindow(Context)方法,该方法会返回一个PhoneWindow对象,而PhoneWindow 是Window的子类,在这个PhoneWindow中有一个DecorView的内部类,是所有应用窗口的根View,即View的老大, 直接控制Activity是否显示,接着里面有一个LinearLayout,里面又有两个FrameLayout他们分别拿来装ActionBar和CustomView,而我们setContentView()加载的布局就放到这个CustomView中!

    总结下这三者的关系: 打个牵强的比喻: 我们可以把这三个类分别堪称:画家,画布,画笔画出的东西; 画家通过画笔( LayoutInflater.infalte)画出图案,再绘制在画布(addView)上! 最后显示出来(setContentView)

    Activity,Task和Back Stack的一些概念

    这里主要涉及一些Activity的管理机制,首先来介绍一下Task和Back Stack
    我们的APP一般都是由多个Activity构成的,而在Android中给我们提供了一个Task(任务)的概念, 就是将多个相关的Activity收集起来,然后进行Activity的跳转与返回!当然,这个Task只是一个 frameworker层的概念,而在Android中实现了Task的数据结构就是Back Stack(回退堆栈)

    栈这个数据结构的特点为:

    后进先出(LIFO),常用操作入栈(push),出栈(pop),处于最顶部的叫栈顶,最底部叫栈底

    那使用了栈来管理Activity的原理也很好理解

    当切换到新的Activity,那么该Activity会被压入栈中,成为栈顶! 而当用户点击Back键,栈顶的Activity出栈,紧随其后的Activity来到栈顶!

    在这里插入图片描述
    流程解析:
    应用程序中存在A1,A2,A3三个activity,当用户在Launcher或Home Screen点击应用程序图标时, 启动主A1,接着A1开启A2,A2开启A3,这时栈中有三个Activity,并且这三个Activity默认在 同一个任务(Task)中,当用户按返回时,弹出A3,栈中只剩A1和A2,再按返回键, 弹出A2,栈中只剩A1,再继续按返回键,弹出A1,任务被移除,即程序退出!

    而Task与Activity是什么关系呢?
    在这里插入图片描述
    Task是Activity的集合,是一个概念,实际使用的Back Stack来存储Activity,可以有多个Task,但是 同一时刻只有一个栈在最前面,其他的都在后台!那栈是如何产生的呢?

    当我们通过主屏幕,点击图标打开一个新的App,此时会创建一个新的Task!举个例子:
    我们通过点击通信录APP的图标打开APP,这个时候会新建一个栈1,然后开始把新产生的Activity添加进来,可能我们在通讯录的APP中打开了短信APP的页面,但是此时不会新建一个栈,而是继续添加到栈1中,这是 Android推崇一种用户体验方式,即不同应用程序之间的切换能使用户感觉就像是同一个应用程序, 很连贯的用户体验,官方称其为seamless (无缝衔接)! ——这个时候假如我们点击Home键,回到主屏幕,此时栈1进入后台,我们可能有下述两种操作:
    1)点击菜单键(正方形那个按钮),点击打开刚刚的程序,然后栈1又回到前台了! 又或者我们点击主屏幕上通信录的图标,打开APP,此时也不会创建新的栈,栈1回到前台!
    2)如果此时我们点击另一个图标打开一个新的APP,那么此时则会创建一个新的栈2,栈2就会到前台, 而栈1继续呆在后台;
    3) 后面也是这样…以此类推!

    Task的管理(重要)

    ManagingTasks
    如上面所述,Android会将新成功启动的Activity添加到同一个Task中并且按照以"先进先出"方式管理多个Task 和Back Stack,用户就无需去担心Activites如何与Task任务进行交互又或者它们是如何存在于Back Stack中! 或许,你想改变这种正常的管理方式。比如,你希望你的某个Activity能够在一个新的Task中进行管理; 或者你只想对某个Activity进行实例化,又或者你想在用户离开任务时清理Task中除了根Activity所有Activities。你可以做这些事或者更多,只需要通过修改AndroidManifest.xml中 < activity >的相关属性值或者在代码中通过传递特殊标识的Intent给startActivity( )就可以轻松的实现 对Actvitiy的管理了。

    < activity >中可以使用的属性如下:

    • taskAffinity
    • launchMode
    • allowTaskReparenting
    • clearTaskOnLaunch
    • alwaysRetainTaskState
    • finishOnTaskLaunch

    可选的Intent标志有:

    • FLAG_ACTIVITY_NEW_TASK
    • FLAG_ACTIVITY_CLEAR_TOP
    • FLAG_ACTIVITY_SINGLE_TOP

    taskAffinity和allowTaskReparenting

    默认情况下,一个应用程序中的所有activity都有一个Affinity,这让它们属于同一个Task。 你可以理解为是否处于同一个Task的标志,然而,每个Activity可以通过 < activity>中的taskAffinity属性设置单独的Affinity。 不同应用程序中的Activity可以共享同一个Affinity,同一个应用程序中的不同Activity 也可以设置成不同的Affinity。

    总的来说,每个Activity都是独立的,它们能否装入同一个Task完全取决于是否拥有相同的Affinity,而与它们是否在同一个应用程序无关。

    Affinity属性在2种情况下起作用:

    1)当启动 activity的Intent对象包含FLAG_ACTIVITY_NEW_TASK标记: 当传递给startActivity()的Intent对象包含 FLAG_ACTIVITY_NEW_TASK标记时,系统会为需要启动的Activity寻找与当前Activity不同Task。如果要启动的 Activity的Affinity属性与当前所有的Task的Affinity属性都不相同,系统会新建一个带那个Affinity属性的Task,并将要启动的Activity压到新建的Task栈中;否则将Activity压入那个Affinity属性相同的栈中。

    2)allowTaskReparenting属性设置为true 如果一个activity的allowTaskReparenting属性为true, 那么它可以从一个Task(Task1)移到另外一个有相同Affinity的Task(Task2)中(Task2带到前台时)。 如果一个.apk文件从用户角度来看包含了多个"应用程序",你可能需要对那些 Activity赋不同的Affinity值。

    清空栈

    当用户长时间离开Task(当前task被转移到后台)时,系统会清除task中栈底Activity外的所有Activity 。这样,当用户返回到Task时,只留下那个task最初始的Activity了。我们可以通过修改下面这些属性来 改变这种行为!

    alwaysRetainTaskState: 如果栈底Activity的这个属性被设置为true,上述的情况就不会发生。 Task中的所有activity将被长时间保存。

    clearTaskOnLaunch 如果栈底activity的这个属性被设置为true,一旦用户离开Task, 则 Task栈中的Activity将被清空到只剩下栈底activity。这种情况刚好与 alwaysRetainTaskState相反。即使用户只是短暂地离开,task也会返回到初始状态 (只剩下栈底acitivty)。

    finishOnTaskLaunchclearTaskOnLaunch相似,但它只对单独的activity操 作,而不是整个Task。它可以结束任何Activity,包括栈底的Activity。 当它设置为true时,当前的Activity只在当前会话期间作为Task的一部分存在, 当用户退出Activity再返回时,它将不存在。

    launchMode(核心)

    Activity有四种加载模式,分别为:standard(默认)singleTopsingleTasksingleInstance

    Standard模式
    标准启动模式,也是activity的默认启动模式。在这种模式下启动的activity可以被多次实例化,即在同一个任务中可以存在多个activity的实例,每个实例都会处理一个Intent对象。如果Activity A的启动模式为standard,并且A已经启动,在A中再次启动Activity A,即调用startActivity(new Intent(this,A.class)),会在A的上面再次启动一个A的实例,即当前的桟中的状态为A—>A。

    singleTop模式:
    如果一个以singleTop模式启动的Activity的实例已经存在于任务栈的栈顶, 那么再启动这个Activity时,不会创建新的实例,而是重用位于栈顶的那个实例, 并且会调用该实例的onNewIntent()方法将Intent对象传递到这个实例中。 举例来说,如果A的启动模式为singleTop,并且A的一个实例已经存在于栈顶中, 那么再调用startActivity(new Intent(this,A.class))启动A时, 不会再次创建A的实例,而是重用原来的实例,并且调用原来实例的onNewIntent()方法。 这时任务栈中还是这有一个A的实例。如果以singleTop模式启动的activity的一个实例 已经存在与任务栈中,但是不在栈顶,那么它的行为和standard模式相同,也会创建多个实例。

    singleTask模式:
    只允许在系统中有一个Activity实例。如果系统中已经有了一个实例, 持有这个实例的任务将移动到顶部,同时intent将被通过onNewIntent()发送。 如果没有,则会创建一个新的Activity并置放在合适的任务中。

    但是这里要注意以下

    官方对于这种模式有这样的设想:系统会创建一个新的任务,并将这个Activity实例化为新任务的根部(root),而要实现这样,则需要我们对taskAffinity进行设置了,使用taskAffinity后则会出现。

    singleInstance模式
    保证系统无论从哪个Task启动Activity都只会创建一个Activity实例,并将它加入新的Task栈顶 也就是说被该实例启动的其他activity会自动运行于另一个Task中。 当再次启动该activity的实例时,会重用已存在的任务和实例。并且会调用这个实例 的onNewIntent()方法,将Intent实例传递到该实例中。和singleTask相同, 同一时刻在系统中只会存在一个这样的Activity实例。

    在这里插入图片描述

    常用的Activity管理类(工具类)

    public class AppManager {
        
        private static Stack<Activity> activityStack;
        private static AppManager instance;
        
        private AppManager(){}
        /**
         * 单一实例
         */
        public static AppManager getAppManager(){
            if(instance==null){
                instance=new AppManager();
            }
            return instance;
        }
        /**
         * 添加Activity到堆栈
         */
        public void addActivity(Activity activity){
            if(activityStack==null){
                activityStack=new Stack<Activity>();
            }
            activityStack.add(activity);
        }
        /**
         * 获取当前Activity(堆栈中最后一个压入的)
         */
        public Activity currentActivity(){
            Activity activity=activityStack.lastElement();
            return activity;
        }
        /**
         * 结束当前Activity(堆栈中最后一个压入的)
         */
        public void finishActivity(){
            Activity activity=activityStack.lastElement();
            finishActivity(activity);
        }
        /**
         * 结束指定的Activity
         */
        public void finishActivity(Activity activity){
            if(activity!=null){
                activityStack.remove(activity);
                activity.finish();
                activity=null;
            }
        }
        /**
         * 结束指定类名的Activity
         */
        public void finishActivity(Class<?> cls){
            for (Activity activity : activityStack) {
                if(activity.getClass().equals(cls) ){
                    finishActivity(activity);
                }
            }
        }
        /**
         * 结束所有Activity
         */
        public void finishAllActivity(){
            for (int i = 0, size = activityStack.size(); i < size; i++){
                if (null != activityStack.get(i)){
                    activityStack.get(i).finish();
                }
            }
            activityStack.clear();
        }
        /**
         * 退出应用程序
         */
        public void AppExit(Context context) {
            try {
                finishAllActivity();
                ActivityManager activityMgr= (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
                activityMgr.restartPackage(context.getPackageName());
                System.exit(0);
            } catch (Exception e) {    }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81

    小结

    • 按Home键,将之前的Task切换到后台
    • 长按Home键,会显示出最近执行过的Task列表
    • 在Launcher或HomeScreen点击app图标,开启一个新Task,或者是将已有的Task调度到前台
    • 启动singleTask模式的Activity时,会在系统中搜寻是否已经存在一个合适的Task,若存在,则会将这个Task调度到前台以重用这个Task。如果这个Task中已经存在一个要启动的Activity的实例,则清除这个实例之上的所有Activity,将这个实例显示给用户。如果这个已存在的Task中不存在一个要启动的Activity的实例,则在这个Task的顶端启动一个实例。若这个Task不存在,则会启动一个新的Task,在这个新的Task中启动这个singleTask模式的Activity的一个实例。
    • 启动singleInstance的Activity时,会在系统中搜寻是否已经存在一个这个Activity的实例,如果存在,会将这个实例所在的Task调度到前台,重用这个Activity的实例(该Task中只有这一个Activity),如果不存在,会开启一个新任务,并在这个新Task中启动这个singleInstance模式的Activity的一个实例。
  • 相关阅读:
    用三智者交易策略澳福加减仓轻松盈利,就是这么厉害
    【Verilog】时序逻辑电路 -- 有限同步状态机[补充]
    STC8H_硬件IIC
    SaaS 架构实现理论(二)多租户/高性能多租户
    【JVM】一文带你了解JVM中的垃圾回收机制(GC)——内含图解
    python中如何实现多进程,用进程的优缺点有啥?
    vue 功能:点击增加一项,点击减少一项
    Mozilla Firefox侧边栏和垂直标签在131 Nightly版本中开始试用
    grafana graphite statsd搭建安装部署 实时监控_亲测成功
    MySQL 索引
  • 原文地址:https://blog.csdn.net/weixin_44524687/article/details/125514287