这是官网给出的概念
Activity是一个应用程序的组件,他在屏幕上提供了一个区域,允许用户在上面做一些交互性的操作, 比如打电话,照相,发送邮件,或者显示一个地图!Activity可以理解成一个绘制用户界面的窗口, 而这个窗口可以填满整个屏幕,也可能比屏幕小或者浮动在其他窗口的上方!
总的来说就是Activity是一个应用程序中为提供交互界面的组件,同时一个APP中可以存在多个Activity
回调方法 | 描述 |
---|---|
onCreate() |
这是第一个回调,在活动第一次创建时调用 |
onStart() |
这个回调在活动为用户可见时被调用 |
onResume() |
这个回调在应用程序与用户开始可交互的时候调用 |
onPause() |
被暂停的活动无法接受用户输入,不能执行任何代码。当前活动将要被暂停,上一个活动将要被恢复时调用 |
onStop() |
当活动不在可见时调用 |
onDestroy() |
当活动被系统销毁之前调用 |
onRestart() |
当活动被停止以后重新打开时调用 |
注意:
1. onPause()和onStop()被调用的前提是: 打开了一个新的Activity!而前者是旧Activity还可见的状态;后者是旧Activity已经不可见!
2. 另外,亲测:AlertDialog和PopWindow是不会触发上述两个回调方法的~
创建
1.自定义Activity类名,继承Activity类或者它的子类。例如,在一个Activity中,只想实现一个列表, 那么就可以让该Activity继承ListActivity; 如果只想实现选项卡效果,那么就可以让该Activity继承TabActivity。创建一个名为MainAcrivity的继承Activity类的Activity,具体代码如下:
import android. app. Activity;
public class MyActivity extends Activity {
}
2.重写需要的回调方法。通常情况下,都需要重写onCreate()方法,并且在该方法中调用setContentView()方法设置要显示的视图。
@Override
public void onCreate (Bundle savedInstanceState) {
super .onCreate ( savedInstanceState) ;
setContentView(R. layout .main);
}
配置
3. 创建Activity 后,还需要在AndroidManifest.xml文件中进行配置,如果没有配置,而又在程序中启动了该Activity,那么将抛出异常信息。
<activity
android :icon= "@drawable/图标文件名"
android :name="类名"
android :labe1="Activity显示的标题"
android:theme= "要应用的主题"
...>
activity>
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)
该方法没有返回值,只有一个Intent类型的入口参数,Intent 是Android应用里各组件之间的通信方式,一个Activity通过Intent 来表达自己的“意图”。在创建Intent对象时,需要指定想要被启动的Activity.
例如,要启动一个名称为DetailActivity的Activity,可以使用下面的代码:
Intent intent = new Intent (MyActivity.this, DetailActivity.class);
startActivity(intent);
关闭
5.在Android中,如果想要关闭当前的Activity, 可以使用Activity 类提供的finish()方法。该方法的使用比较简单,既没有入口参数,也没有返回值,只需要在Activity中相应的事件中调用该方法即可。
public void finish ()
我们可以把他写到启动第二个Activity的方法中,当启动第二个Activity时,第一个Activity就会被关闭finish();
注意:
Android中的四大组件,只要你定义了,无论你用没用,都要在AndroidManifest.xml对 这个组件进行声明,不然运行时程序会直接退出,报ClassNotFindException
一个参数
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
两个参数
@Override
public void onCreate(@Nullable Bundle savedInstanceState, @Nullable PersistableBundle persistentState) {
super.onCreate(savedInstanceState, persistentState);
setContentView(R.layout.activity_main);
}
这个包含两个参数的方法是安卓5.0提供的新方法使用它时需要在配置清单文件中声明一个属性:
android:persistableMode="persistAcrossReboots"
这样拥有该属性的Activity就拥有了持久化的能力了,增加的这个PersistableBundle参数令这些方法拥有了系统关机后重启的数据恢复能力!!而且不影响我们其他的序列化操作,一般我们会搭配另外两个方法来使用:
public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState)
public void onRestoreInstanceState(Bundle savedInstanceState, PersistableBundle persistentState)
第一个方法可以在一下情景中使用:
- 点击home键回到主页或长按后选择运行其他程序
- 按下电源键关闭屏幕
- 动新的Activity
- 横竖屏切换时,肯定会执行,因为横竖屏切换的时候会先销毁Act,然后再重新创建 重要原则:当系统"未经你许可"时销毁了你的activity,则onSaveInstanceState会被系统调用, 这是系统的责任,因为它必须要提供一个机会让你保存你的数据(你可以保存也可以不保存)。
第二个方法:
和onCreate同样可以从前者取出保存的数据: 一般是在onStart()和onResume()之间执行! 之所以有两个可以获取到保存数据的方法,是为了避免Act跳转而没有关闭, 然后不走onCreate()方法,而你又想取出保存数据
startActivity(new Intent(当前Act.this,要启动的Act.class));
ComponentName cn = new ComponentName("当前Act的全限定类名","启动Act的全限定类名") ;
Intent intent = new Intent() ;
intent.setComponent(cn) ;
startActivity(intent) ;
Intent intent = new Intent("android.intent.action.MAIN");
intent.setClassName("当前Act的全限定类名","启动Act的全限定类名");
startActivity(intent);
通过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>
Intent intent = new Intent();
intent.setAction("my_action");
intent.addCategory("my_category");
startActivity(intent);
Intent intent = getPackageManager().getLaunchIntentForPackage
("apk第一个启动的Activity的全限定类名") ;
if(intent != null) startActivity(intent) ;
前面也也说到了App横竖屏切换的时候会销毁当前的Activity然后重新创建一个,你可以自行在生命周期 的每个方法里都添加打印Log的语句,来进行判断,又或者设一个按钮一个TextView点击按钮后,修改TextView 文本,然后横竖屏切换,会神奇的发现TextView文本变回之前的内容了! 横竖屏切换时Act走下述生命周期:onPause-> onStop-> onDestory-> onCreate->onStart->onResume
android:screenOrientation
, 有下述可选值: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.竖屏);
}
onCreate(Bundle savedInstanceState);
onSaveInstanceState(Bundle outState);
onRestoreInstanceState(Bundle savedInstanceState);
然后重写onSaveInstanceState()方法,往这个bundle中写入数据,比如:
outState.putInt(“num”,1);
这样,然后你在onCreate或者onRestoreInstanceState中就可以拿出里面存储的数据,不过拿之前要判断下是否为null
savedInstanceState.getInt(“num”);
这样保存数据之后,当切换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服务的没戏!!!!)
// 给[email protected]发邮件
Uri uri = Uri.parse("mailto:[email protected]");
Intent intent = new Intent(Intent.ACTION_SENDTO, uri);
startActivity(intent);
// 给[email protected]发邮件发送内容为“Hello”的邮件
Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_EMAIL, "[email protected]");
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 = {"[email protected]", "[email protected]"}; // 收件人
String[] ccs = {"[email protected]", "[email protected]"}; // 抄送
String[] bccs = {"[email protected]", "[email protected]"}; // 密送
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);
当在一个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);
取数据
Intent it2 = getIntent();
it2.getStringExtra("key");//不同类型数据使用不同的函数
一次传递多个
存数据
Intent intent = new Intent(A.this, B.class);
Bundle bundle = new Bundle();
bundle.putInt("num",1);
bundle.putString("detail","示例");
intent.putExtras(bundle);
startActivity(intent);
取数据
Intent it2 = getIntent();
Bundle bd = it2.getExtras();
int num = bd.getInt("num");
String detail = bd.getString("detail");
在使用Bundle传递数据时,要注意,Bundle的大小是有限制的 < 0.5MB,如果大于这个值 是会报TransactionTooLargeException异常的!!!
在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);
}
②在启动的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("");//清空”确认密码“的编辑框
}
}
③在子Activity重写:setResult(int resultCode, Intent data)
setResult(0x717,intent);//设置返回的结果码
让所有Activity继承-一个自定义的BaseActivity类,在OnCreate()方法中添加下述语句即可:
Log.d(" BaseActivity". getClassQ.getSimpleName0);
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();
}
}
}
}
2.BaseActivity中
①onCreate( )方法添加ActivityCollector.addActivity(this);
②onDestory( )方法添加: ActivityCollector.removeActivity(this);
③可以在任意一个Activity中调用:ActivityCollector.finishAll( );
从而关闭所有Activity,退出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.定义一个变量,来标识是否退出
// 定义一个变量,来标识是否退出
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);}
//保存点击的时间
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);
}
所谓的过场动画就是切换到另外的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:
<style name="default_animation" mce_bogus="1" parent="@android:style/Animation.Activity">
- "android:activityOpenEnterAnimation"
>@anim/default_anim_in
- "android:activityOpenExitAnimation"
>@anim/anim_stay
- "android:activityCloseEnterAnimation">@anim/anim_stay
- "android:activityCloseExitAnimation">@anim/default_anim_out
style>
②然后修改下AppTheme:
<style name="AppTheme" mce_bogus="1" parent="@android:style/Theme.Light">
- "android:windowAnimationStyle"
>@style/default_animation
- "android:windowNoTitle"
>true
style>
③最后在appliction设置下:
<application
android:icon="@drawable/logo"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
方法三:
除了上面两种方法以外,还可以使用TransitionManager来实现,但是需求版本是API 19以上的, 另外还有一种addOnPreDrawListener的转换动画
方法一:代码隐藏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一般是占满全屏的, 而Dialog则是占据部分屏幕的!实现起来也很简单!
直接设置下Activity的theme:
android:theme="@android:style/Theme.Dialog"
这样就可以实现了,当然也可以再设置下标题,小图标
//设置左上角小图标
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"
流程解析: 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
我们的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) 后面也是这样…以此类推!
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
默认情况下,一个应用程序中的所有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)。
finishOnTaskLaunch
与clearTaskOnLaunch
相似,但它只对单独的activity操 作,而不是整个Task。它可以结束任何Activity,包括栈底的Activity。 当它设置为true时,当前的Activity只在当前会话期间作为Task的一部分存在, 当用户退出Activity再返回时,它将不存在。
Activity有四种加载模式,分别为:standard(默认)
,singleTop
,singleTask
,singleInstance
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实例。
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) { }
}
}