Activity是Android四大组件之一,用于显示View。Activity是一个应用程序组件,提供一个用户交互的接口,其本身是没有界面的,Activity类创建一个窗口,在上面可以绘制用户接口。窗口通常充满屏幕,也可以小于屏幕而悬浮于其他窗口之上。
开发者可以通过Activity类提供的setContentView(View)接口将View放到Activity创建的窗口上。一个程序一般由多个Activity组成,他们通常是松耦合关系。一个Activity可以启动另外一个Activity。每次一个Activity启动,前一个Activity就停止,但是Android系统会保留Activity在一个栈上,当用户完成当前Activity然后点击back按钮时,它就会被弹出栈并被销毁。所有的Activity类必须在AndroidManifest.xml文件中注册,不注册会报错。例如:
<activity android:name=".BaseActivity" />
<activity android:name=".activity.AboutCSDNActivity" />
<activity android:name=".activity.BlogOsphereActivity" />
<activity android:name=".activity.FeedBackActivity" />
<activity android:name=".activity.WebActivity" />
<activity android:name=".activity.AllBloggerActivity" />
<activity android:name=".activity.BlogDetailActivity" />
下图是Android api中所提供的Activity生命周期图
由图可以看出,Activity有如下生命周期方法,分别是:
public class Activity extends ApplicationContext {
protected void onCreate(Bundle savedInstanceState);
protected void onStart();
protected void onResume();
protected void onPause();
protected void onStop();
protected void onDestroy();
}
一个Activity启动,执行生命周期方法顺序是:onCreate -> onStart -> onResume;当一个Activity被Kill掉时候,执行生命周期方法顺序是:onPause -> onStop -> onDestroy。这是一个完整的生命周期循环。当一个应用正在运行,由于其它应用中断,比如电话来了等,执行的生命周期方法是onPause -> onStop,挂断电话恢复到当前应用,执行的生命周期方法是onStart -> onResume;如果当前应用的Activity是Theme为Translucent(半透明)或者Dialog时,那么中断就是onPause ,恢复的时候onResume。
onPause,onstop, onDestroy,三种状态下 activity都有可能被系统kill掉
Activity有四种状态分别是:
生命周期方法在各个状态被调用时机如下图:
由上图可以看出:
在 Android 中,不同的 Activity 实例可能运行在一个进程中,也可能运行在不同的进程中。因此我们需要一种特别的机制帮助我们在 Activity 之间传递消息。Android 中通过 Intent 对象来表示一条消息,一个 Intent 对象不仅包含有这个消息的目的地,还可以包含消息的内容,这好比一封 Email,其中不仅应该包含收件地址,还可以包含具体的内容。对于一个 Intent 对象,消息“目的地”是必须的,而内容则是可选项。
Intent负责对操作的动作、动作涉及数据、附加数据进行描述,Android则根据此Intent的描述,负责找到对应的组件,将 Intent传递给调用的组件,并完成组件的调用。因此,Intent在这里起着一个媒体中介的作用,专门提供组件互相调用的相关信息,实现调用者与被调用者之间的解耦。
在应用中,我们可以通过两种形式来使用Intent:
直接Intent:指定了component属性的Intent(setClass(Context, Class)来指定)。通过指定具体的组件类,通知应用启动对应的组件。
间接Intent:没有指定comonent属性的Intent。这些Intent需要包含足够的信息,这样系统才能根据这些信息,在在所有的可用组件中,确定满足此Intent的组件。
对于直接Intent,Android不需要去做解析,因为目标组件已经很明确。
Android需要解析的是那些间接Intent,通过解析,将 Intent映射给可以处理此Intent的Activity、IntentReceiver或Service。Intent解析机制主要是通过查找已注册在AndroidManifest.xml中的所有IntentFilter及其中定义的Intent,最终找到匹配的Intent。
通过setClass传递消息:
Intent intent =new Intent(CurrentActivity.this,OtherActivity.class);
Bundle bundle =new Bundle();
bundle.putBoolean("boolean_key", true);
bundle.putString("string_key", "string_value");
intent.putExtra("key", bundle);
startActivity(intent);
另一个Activity解析消息:
Intent intent =getIntent();
Bundle bundle =intent.getBundleExtra("key");
bundle.getBoolean("boolean_key");
bundle.getString("string_key");
除了使用intent之外还可以使用sharedpreference传递消息:
// 写入 SharedPreferences
SharedPreferences preferences = getSharedPreferences("name", MODE_PRIVATE);
Editor editor = preferences.edit();
editor.putBoolean("boolean_key", true);
editor.putString("string_key", "string_value");
editor.commit();
// 读取 SharedPreferences
SharedPreferences preferences = getSharedPreferences("name", MODE_PRIVATE);
preferences.getBoolean("boolean_key", false);
preferences.getString("string_key", "default_value");
一个Activity向另外一个Activity传递消息,后一个Activity传回给前一个
例如:A.Activity向B.Activity传递一些消息,当B.Activity关闭的时候回传一些消息给A.Activity
//在A.Activity中加入:
button.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View arg0) {
Intent intent = new Intent();
intent.setClass(a.this, b.class);
Bundle bundle = new Bundle();
bundle.putString("v_id",i_id);
intent.putExtras(bundle);
startActivityForResult(intent,0);
overridePendingTransition(R.anim.zoomin, R.anim.zoomout);
}
});
//在B.Activity中加入:
String v_id = this.getIntent().getExtras().getString("v_id");
//在B.Activity 中加入。当关闭b时,传递数据name给a:
b_close.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View arg0) {
Intent intent = getIntent();
Bundle bundle = new Bundle();
bundle.putString("name",name);
intent.putExtras(bundle);
setResult(RESULT_OK,intent);
finish();
}
});
//在A.Activity中加入,接受回调信息name
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data_intent){
super.onActivityResult(requestCode, resultCode, data_intent);
if(resultCode == RESULT_CANCELED)
setTitle("cancel");
else if (resultCode == RESULT_OK){
Bundle bundle = data_intent.getExtras();
if(bundle != null){
//获取b传送的数据
String name = bundle.getString("name");
}
}
}
Android 提供了包括 SharedPreferences 在内的很多种数据存贮方式,比如 SQLite,文件等,程序员可以通过这些 API 实现 Activity 之间的数据交换。如果必要,我们还可以使用 IPC 方式。
Activity有四种加载模式,分别是:standard模式、singleTop模式、singleTask模式、singleInstance模式(其中standard模式和singleTop模式是一组、singleTask模式和singleInstance模式是一组),默认加载模式是standard模式。
四种加载模式的区别:
1、standard:标准模式(默认模式),一旦调用startActivity()方法就会产生一个新的Activity实例。 不需要在launchMode属性配置
/**
* 默认(standard)加载模式
*
*/
public class ActivityOne extends Activity implements OnClickListener
{
private TextView textView;// 显示文本
private Button button;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
textView = (TextView) findViewById(R.id.textView);
button = (Button) findViewById(R.id.button);
button.setText("go to ActivityOne");
button.setOnClickListener(this);
textView.setText(this + "");
}
@Override
public void onClick(View v)
{
Intent intent = new Intent();
intent.setClass(ActivityOne.this, ActivityOne.class);// 跳转
startActivity(intent);
}
}
AndroidManifset.xml文件配置:
<activity android:name="ActivityOne"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
2、singleTop:如果Activity实例位于栈顶,就不产生新的实例,直接使用栈顶的实例,否则,就会产生一个新的实例。
例如:现在Task栈元素为A-B-C-D(D在栈顶),这时候给D发一个启动Intent,如果D是 “standard”模式的,则生成D的一个新实例,栈状态为A-B-C-D-D。如果D是singleTop模式的话,则不会生成D的新实例,栈状态仍为A-B-C-D。如果这时候给B发Intent的话,不管B的launchMode是“standard”还是“singleTop”,都会生成B的新实例,栈状态变为A-B-C-D-B。
/**
* singleTop模式
*/
public class ActivityOne extends Activity implements OnClickListener
{
private TextView textView;// 显示文本
private Button button;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
textView = (TextView) findViewById(R.id.textView);
button = (Button) findViewById(R.id.button);
button.setText("go to ActivityOne");
button.setOnClickListener(this);
textView.setText(this + "");
}
@Override
public void onClick(View v)
{
Intent intent = new Intent();
intent.setClass(ActivityOne.this, ActivityOne.class);// 跳转
startActivity(intent);
}
}
AndroidManifset.xml文件配置:
<activity
android:name="ActivityOne"
android:label="@string/app_name"
android:launchMode="singleTop">
<intent-filter>
<action
android:name="android.intent.action.MAIN" />
<category
android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
3、singleTask:每次调用都会使用这个实例,不会去产生新的实例了。
AndroidManifset.xml文件配置:
<activity
android:name="ActivityOne"
android:label="@string/app_name"
android:launchMode="singleTask">
<intent-filter>
<action
android:name="android.intent.action.MAIN" />
<category
android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="ActivityTwo"
android:label="@string/app_name">
</activity>
4、singleInstance:跟singleTask模式基本上是一样,只有一个区别:在这个模式下的Activity实例与其他Activity处在不同的Task中,此实例所处的Task中只能有这个Activity实例,不能有其他的实例。
AndroidManifaset.xml文件配置:
<activity
android:name="ActivityOne"
android:label="@string/app_name">
<intent-filter>
<action
android:name="android.intent.action.MAIN" />
<category
android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="ActivityTwo"
android:label="@string/app_name"
android:launchMode="singleInstance">
</activity>
Activity创建需要经历如下的步骤:
(1) 自定义Activity类名,继承Activity类或者其子类,使用AS开发,一般继承AppCompatActivity类
public class BaseActivity extends AppCompatActivity {}
(2) 重写onCreate方法,并在该方法中调用setContentView方法
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(View);
}
(3) 在AndroidManiFest.xml文件中注册Activity,必须要注册,不注册会报错
<application
android:name=".BaseApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".SplashActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".MainActivity" />
<activity android:name=".BaseActivity" />
<activity android:name=".activity.AboutCSDNActivity" />
<activity android:name=".activity.BlogOsphereActivity" />
<activity android:name=".activity.FeedBackActivity" />
<activity android:name=".activity.WebActivity" />
<activity android:name=".activity.AllBloggerActivity" />
<activity android:name=".activity.BlogDetailActivity" />
</application>
(4) 启动Activity,通过调用startActivity(Intent)启动。
Intent intent = new Intent(getApplicationContext(), BlogDetailActivity.class);
intent.putExtra("username", blogOspheres.get(position).getUserName());
intent.putExtra("name", blogOspheres.get(position).getName());
startActivity(intent);
(5) 销毁Activity,通过调用Finish()方法
@Override
public void onClick(View v) {
finish();
}
第一种:显示启动
startActivity(new Intent(当前Activity,要启动的Activity.class));
//通过intent的ComponentName启动
ComponentName componentName = new ComponentName("当前Activity的全限定类名","启动Activity的全限定类名") ;
Intent intent = new Intent() ;
intent.setComponent(componentName) ;
startActivity(intent) ;
Intent intent = new Intent("android.intent.action.MAIN");
intent.setClassName("当前Activity的全限定类名","启动Activity的全限定类名");
startActivity(intent);
第二种:隐式启动
<activity
android:name="com.example.android.tst.SecondActivity"
android:label="@string/title_activity_main" >
<intent-filter>
<action android:name="com.example.android.tst.SecondActivity"/>
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
Intent intent=new Intent("com.example.android.tst.SecondActivity");
startActivity(intent);
第三种:启动APK
Intent intent = getPackageManager().getLaunchIntentForPackage
("apk第一个启动的Activity的全限定类名") ;
if(intent != null) startActivity(intent) ;
以下是Android系统给我们提供的常用的Activity
拨打电话
Uri uri = Uri.parse("tel:10010");
Intent intent = new Intent(Intent.ACTION_DIAL, uri);
startActivity(intent);
发送短信
Uri uri = Uri.parse("smsto:10010");
Intent intent = new Intent(Intent.ACTION_SENDTO, uri);
intent.putExtra("sms_content", "Hello");
startActivity(intent);
发送彩信
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);
打开浏览器:
Uri uri = Uri.parse("http://www.baidu.com");
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
发送电子邮件
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[] mailTos = {"[email protected]", "[email protected]"}; // 收件人
String[] copyTos = {"[email protected]", "[email protected]"}; // 抄送
String[] secretTos = {"[email protected]", "[email protected]"}; // 密送
intent.putExtra(Intent.EXTRA_EMAIL, mailTos);
intent.putExtra(Intent.EXTRA_CC, copyTos);
intent.putExtra(Intent.EXTRA_BCC, secretTos);
intent.putExtra(Intent.EXTRA_SUBJECT, "Subject");
intent.putExtra(Intent.EXTRA_TEXT, "Hello");
intent.setType("message/rfc822");
startActivity(intent);
显示地图:
Uri uri = Uri.parse("geo:23.20,113.30");
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
多媒体播放:
Intent intent = new Intent(Intent.ACTION_VIEW);
Uri uri = Uri.parse("file:///sdcard/music.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);
打开摄像头拍照:
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);
获取并剪切图片
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);
进入手机设置界面:
Intent intent = new Intent(android.provider.Settings.ACTION_WIRELESS_SETTINGS);
startActivityForResult(intent, 0);
安装apk应用:
Uri installUri = Uri.fromParts("package", "xxx", null);
returnIt = new Intent(Intent.ACTION_PACKAGE_ADDED, installUri);
卸载apk应用:
Uri uri = Uri.fromParts("package", packageName, null);
Intent it = new Intent(Intent.ACTION_DELETE, uri);
startActivity(it);
打开联系人:
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(People.CONTENT_URI);
startActivity(intent);
查看指定联系人:
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);
两种方法:
第一种:写一个BaseActivity,项目中所有的Activity都继承于这个BaseActivity,在BaseActivity中记录每一个Activity,退出应用的时候将所有的Activity销毁
//用一个集合管理所有的activity
public final static LinkedList<BaseActivity> mBaseActivities = new LinkedList<BaseActivity>();
public static BaseActivity mBaseActivity;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
synchronized (mBaseActivities) {
mBaseActivities.add(this);
}
}
@Override
protected void onResume() {
super.onResume();
mBaseActivity = this;
}
@Override
protected void onPause() {
super.onPause();
mBaseActivity = null;
}
@Override
protected void onDestroy() {
super.onDestroy();
synchronized (mBaseActivities) {
mBaseActivities.remove(this);
}
}
//安全退出程序
public static void exitApp() {
LinkedList<BaseActivity> activityCopy;
//复制一份activity
synchronized (mBaseActivities) {
activityCopy = new LinkedList<BaseActivity>(mBaseActivities);
}
for (BaseActivity baseActivity : activityCopy) {
baseActivity.finish();
}
//杀死当前进程
android.os.Process.killProcess(android.os.Process.myPid());
}
第二种方法:退出的时候发送广播,销毁所有的Activity
在Activity切换的时候设置切换动画有两种方式:
第一种:在Activity跳转的时候设置,即是在startActivity方法之后设置,代码如下:
A.Activity跳转到B.Activity时,在startActivity()方法后面设置
//第一个参数是activity离开时的动画,第二个参数是activity进入时的动画
overridePendingTransition(R.anim.push_left_in, R.anim.push_left_out);
B.Activity回转到A.Activity时,在Finish()方法后面设置
finish();
overridePendingTransition(R.anim.push_left_in, R.anim.push_left_out);
优化思路:可以将整个应用里面Activity相同的操作抽取到一个类里面,在BaseActivity中实例化,每一次设置动画或者进行Activity相关操作(比如,Activity跳转、传递参数等),直接调用方法即可,代码如下:
public class Opration {
private Intent mIntent = new Intent();
private Activity mContext = null;
private BaseApplication application = null;
public Opration(Activity mContext) {
this.mContext = mContext;
application = (BaseApplication) this.mContext.getApplicationContext();
}
/**
* 跳转Activity
*
* @param activity 需要跳转至的Activity
*/
public void forward(Class<? extends Activity> activity) {
mIntent.setClass(mContext, activity);
mContext.startActivity(mIntent);
mContext.overridePendingTransition(R.anim.base_slide_right_in, R.anim.base_slide_remain);
}
/**
* 设置传递参数
*
* @param key 参数key
* @param value 数据传输对象
*/
public void addParameter(String key, Bundle value) {
mIntent.putExtra(key, value);
}
/**
* 设置传递参数
*
* @param key 参数key
* @param value 数据传输对象
*/
public void addParameter(String key, String value) {
mIntent.putExtra(key, value);
}
}
第二种设置切换动画的方法:在样式文件里面设置
<!-- 默认Activity跳转动画 -->
<style name="change_animation" parent="@android:style/Animation.Activity">
<!-- 从A.Activity跳转到B.Activity时B.Activity的进入动画 -->
<item name="android:activityOpenEnterAnimation">@anim/default_anim_in</item>
<!-- 从A.Activity跳转到B.Activity时A.Activity的退出动画 -->
<item name="android:activityOpenExitAnimation">@anim/anim_stay</item>
<!-- 从B.Activity跳转到B.Activity时A.Activity的进入动画 -->
<item name="android:activityCloseEnterAnimation">@anim/anim_stay</item>
<!-- 从B.Activity跳转到B.Activity时B.Activity的退出动画 -->
<item name="android:activityCloseExitAnimation">@anim/default_anim_out</item>
</style>
修改Theme
<style name="AppTheme" parent="@android:style/Theme.Light">
<item name="android:windowAnimationStyle">@style/change_animation</item>
<item name="android:windowNoTitle">true</item>
</style>
最后在AndroidManifest.xml文件中设置:
<application
android:icon="@drawable/logo"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
在Android中,默认状态下APP横竖屏切换的时候会销毁当前的Activity实例,然后重新创建Activity实例,横竖屏切换时Activity的生命周期方法顺序是:onPause-> onStop-> onDestory-> onCreate->onStart->onResume。在一些特殊情况下,我们需要对横竖屏切换进行一些处理,以下是常用的处理:
在AndroidManifest.xml中为Activity添加一个属性: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.竖屏);
}
通过一个Bundle savedInstanceState参数即可完成,有三个核心方法:
onCreate(Bundle savedInstanceState);
onSaveInstanceState(Bundle outState);
onRestoreInstanceState(Bundle savedInstanceState);
在onSaveInstanceState(Bundle outState);里面将需要保存的数据放入到Bundle里面,然后在 onCreate(Bundle savedInstanceState);或者onRestoreInstanceState(Bundle savedInstanceState);中将数据取出。
注意:取出数据的时候要进行判空操作。