号外号外,一只Android菜鸟的第一篇博客面世了。
享受着各种大牛前辈博客的恩惠,我也开始将自己所学所想分享出去,同时也提醒自己不断进步,早日成为一只老鸟!
作为一个“developer”,其学习过程正如同古代剑客要想成为绝世高手一样分为四个阶段:
- 手里有剑:学会使用某个工具,了解其方方面面
- 心中有剑:如何用好工具,理解其独特特性
- 人剑合一:你掌握的够深入吗?剖析其本质
十年磨剑:你是否可以创造工具?
好了,话有点多,下面进入正文,开始练贱吧。~~
Activity作为四大组件之首,是使用最为频繁的一种组件,那么不得不将其放在开篇来讲。
1 、Activity使用—手里有剑
活动(Activity),将其理解为“界面”会更方便一些,在创建一个Android程序时,IDE会要求输入一个活动名,之后会自动生成一个MainActivity。打开MainActivity即可以看到如下主要代码:
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setContentView(R.layout.activity_main);为我们指定了此Activity的布局文件。打开AndroidManifest.xml文件,可以看到MainActivity已被注册,主要代码如下:
<activity
android:name="com.example.demo1.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
其中 intent-filter 标签中的两行表明了此Activity会被当做启动页面,即点击桌面App icon之后第一个显示的页面。
1.1 启动Acivity
1.1.1 使用显式Intent
注意:这里仅仅是为了方便所以采用Eclipse截图的,实际开发中墙裂建议大家用AS!
Intent有多个构造函数的重载,我们使用的那一个如上图所示,传入的第一个参数为启动活动的上下文,第二个参数则是要启动的目标活动。只需两行代码即可调转Activity:
Intent intent = new Intent(当前活动.this,目标活动.class);
startActivity(intent);
注意:一定要在AndroidManifest.xml文件中注册将要启动的Activity,不过现在IDE已经可以帮我们完成,但仍然需要记住。
1.1.2 使用隐式Intent:
你一定注意到过:当我们进行某个操作时,可能会跳出一个页面供你选择要打开此操作的App,如进行分享操作。这里就用到了隐式Intent,它指定了一系列更为抽象的action和category,再由系统去分析这个Intent并找出合适的Activity去启动。
通过在< activity >标签下配置 < intent-filter >可以指定当前Activity能够响应的action和category。创建SecondActivity,部分代码片段如下:
<activity
android:name="com.example.demo1.SecondActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="com.example.demo1.SecondActivity.ACTION_TEST" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
修改MainActivity当中的点击按钮的事件:
注意:一些简单的步骤被省略掉了,如创建布局、添加按钮等等,请自行添加
Intent intent = new Intent("com.example.demo1.SecondActivity.ACTION_TEST");
startActivity(intent);
点击Button之后会跳转至SecondActivity,这说明隐式Intent已经起了作用。
同样,我们也可以在代码中添加category,并在要启动的活动的< intent-filter >中注册,只要此活动当中有一个category匹配成功即可。
更多隐式Intent的用法:
在很多情况下我们需要调用系统的Activity,比如从应用中调用手机自带 App拨打电话、发送短信等(注意添加响应权限),这是我们就要用到隐式的Intent以及系统内置的action。如以下示例:
//拨打电话
Intent intent = new Intent(Intent.ACTION_DIAL);
intent.setData(Uri.parse("110"));
startActivity(intent);
//加载网页
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("Http://www.google.com"));
startActivity(intent);
关于内置的Intent有很多,大家可以参考官方文档实现不同的效果。
1.2 Activity之间传递数据
1.2.1 向下一个Activity传递数据
向下一个Activity传递数据的思路非常简单,用Intent的一系列putExtra()方法的重载即可。部分代码如下:
Intent intent = new Intent(MainActivity.this,SecondActivity.class);
intent.putExtra("extra_data","");//传递不同的参数
startActivity(intent);
在SecondActivity的onCreate()方法中使用
Intent intent=getIntent();
Xxx data=intent.getXxxExtra("extra_data");
即可将数据取出。如果传递的是int型数值则用getIntExtra,等等以此类推。
1.2.2 向上一个Activity返回数据
返回上一个活动时只需按一下Back键即可,并没有一个Intent来传递数据,那我们怎么传递数据呢?通过查看官方文档我们可知Activity有一个startActivityResult方法用来启动活动,并且在活动注销时会返回一个结果给上一个活动。如下所示,首先在MainActivity中启动SecondActivity:
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
startActivityResult(intent, 1);
其中第二个参数是请求码,用于在后面的回调中判断数据的来源。
接着,在SecondActivity中返回事件的逻辑如下(比如点击按钮事件等):
Intent intent = new Intent();
intent.putExtra("data_return","data returned to last activity");
setResult(RESULT_OK,intent); //向上一个Activity返回数据
finish();
最后,在MainActivity当中重写onActivityResult方法来处理返回的数据,如下所示:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case 1:
if (resultCode == RESULT_OK) {
String returnData = data.getStringExtra("data_return");
}
break;
default:
}
}
1.3 Activity的生命周期
Activity的生命周期可以分为两部分:
- 典型情况下:指的是有用户参与的情况下,正常的生命周期
- 异常情况下:Activity被系统回收或由于当前设备的Configuration(如:旋转屏幕、更改系统设置等)发生改变所导致的Activity被销毁重建
两种情况下的生命周期关注点略有差别。
1.3.1 典型情况下的生命周期
正常情况下的生命周期如下所示:
- onCreate():表示Activity正在被创建,这是生命周期的第一个方法。通常在里面进行一些初始化工作,如加载布局、绑定事件等。这个阶段Activity处于不可见状态。
- onRestart():表示Activity当前正在重新启动,当前Activity从不可见变为可见状态时会调用。一般是由用户操作造成的,比如返回桌面后又回到App中。此阶段Activity由不可见变为可见(位于后台),但是并未获取焦点,无法和用户交互。可以将其看做一个短暂的加载阶段。
- onStart():表示Activity正在启动,还并未出现在前台,处于不可见状态。(可认为是一个从不可见转态变为可见状态的短暂阶段)
- onResume():当前Activity可见并且位于前台,可以和用户交互。
- onPause():表示Activity正在暂停中。当跳转到另一个Activity时,会首先执行当前活动的onPause(),当另一个活动onResume()调用后,才会调用当前Activity的onStop()方法。因此,为了下一个Activity流畅显式,在当前Activity的onPause方法中不能执行耗时操作,仅仅保存关键数据!
- onStop():Activity正在停止,可以做一些稍微重量级的回收工作。Activity变为完全不可见状态。
- onDestroy():表示Activity即将销毁,这是最后一个回调。应该做完回收工作和释放资源。
根据以上分析,我们很容易知道:
- 针对一个特定的Activity,第一次启动,回调如下:onCreate()->onStart()->onResume()
- 当用户启动一个新Activity或返回桌面时,当前活动回调如下:onPause()->onStop,特别的:是当新的Activity为透明主题时,第一个活动不会回调onStop(为什么呢?如果之前的Activity 调用onStop了,那么第二个活动的背景会是什么)
- 当用户返回Activity时,回调为onRestart()->onStart()->onResume()
- 当按下back键时,回调为onPause()->onStop()->onDestroy()
1.3.2 异常情况下的生命周期
资源相关的系统配置发生改变导致Activity被杀死并重新创建
当Activity被异常终止的话,系统会调用onSaveInstanceState()来保存当前Activity的状态,这个方法的调用时机是在onStop之前,和onPause无固定的时序关系。
当Activity重建时,系统会调用onRestoreInstanceState(),并且把Activity销毁时onSaveInstanceState()方法所保存的Bundle对象作为参数同时传递给onRestoreInstanceState()和onCreate()方法。onRestoreInstanceState()的调用时机在onStart()之后。资源内存不足导致低优先级的Activity被杀死
数据存储和数据恢复同情况1一样,会杀死低优先级的Activity。
前台活动-优先级最高;可见但非前台活动-中级;后台活动(如执行了onStop的)-优先级最低
系统配置改变,不重新创建Activity的方法:
给Activity指定configChanges属性,如:android:configChanges=”oriention”可用|组合多个属性。设置此属性后,Activity不会重新创建,即不会调用生命周期、数据存储恢复,取而代之会调用onConfigurationChanged()方法,在此方法中进行其他处理!
1.4 Activity的启动模式
说到启动模式(Launch mode)就不得不讲讲返回栈(back stack),Android是使用任务(task)来管理Activity的,一个任务就是一组存放在栈里的Activity的集合,这个栈被称为返回栈。
启动模式一共有4中,下面一一进行分析:
- standard:标准模式,也是默认模式。每当启动一个Activity时,系统都会重建一个新的实例
- singleTop:栈顶复用,解决了标准模式重复创建栈顶Activity实例的问题。如果新的Activity已经位于任务栈的栈顶,那么不会重建此Activity的实例,同时它的onNewIntent()方法会被回调
- singleTask:栈内复用,单实例模式,突破了栈顶复用模式只能在栈顶复用Activity实例的限制,使整个Activity在返回栈中只有一个实例。只要Activity在这个栈中,那么就不会重新创建此Activity的实例,系统也会调用其onNewIntent()方法
- singleInstance:单实例模式。可以认为是加强型的singleTask,具有此模式的Activity系统会为其创建一个新的返回栈。(可用于多个应用共享一个Activity)
在具体的开发中,我们应该根据具体的情景合适的选用相关的启动模式。
1.5 Activity使用小技巧
1.5.1 使用BaseActivity&获取当前Activity名称
在大多数的开发当中,App当中并不止一个Activity,有一些可以复用的代码(如设置样式、加载资源、判断网络等)如果写入每一个Activity中会很麻烦也不便于修改维护。所以创建一个BaseActivity继承于Activity或AppCompatActivity 等等,并让其他所有Activity继承BaseActivity就很有必要了。
public class BaseActivity extends Activity {
...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("BaseActivity",getClass().getSimpleName());
}
...
}
1.5.2 退出程序(清除所有Activities)
如果你的应用在某一个页面时,你想退出App,此时点击Home键App会在后台挂起,并未真正退出。那么应该怎样做,使得可以注销所有Activity后退出呢?
答案就是:我们可以新建一个类ActivityCollector,并在其内部维护一个List、增加删除Activity的方法、清空List的方法(即finish所有Activity)。当新建一个Activity时在onCreate中调用List的add方法,在onDestroy中调用其remove方法,要退出程序是调用List的清空方法即可!部分代码如下:
public class ActivityCollector{
public static List<Activity> activities =new ArrayList<Activity>();
public static void addActivity(Activity activity ){
activities.add(activity);
}
public static void removeActivity(Activity activity){
activities.remove(activity);
}
public static void finishAllActivities()
for(Activity activity:activities){
if(!activities.isFinishing()){
activity.finish();
}
}
}
接下来修改BaseActivity中的代码,如下所示:
public class BaseActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("BaseActivity",getClass().getSimpleName());
ActivityCollector.addActivity(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
ActivityCollector.removeActivity(this);
}
最后,在需要退出App的地方调用清除所有活动的方法即可:
ActivityCollector.finishAllActivities();
1.5.3 启动活动的最佳写法-传参
假如从MainActivity跳转进入SecondActivity时需要传递一些参数,我想我们会很容易写出以下代码:
intent.putExtra("param1","data1");
intent.putExtra("param2","data2");
startActivity(intent);
这样写完全没有问题,但是试想一下如果是在实际项目当中有很多的Activity,有的我们并不知道它需要传递什么数据(甚至是不久之前自己写的),如果去阅读代码会很费事。又没有什么改进的办法呢?请看下面的代码片段:
public class SecondActivity extends BaseActivity {
public static void actionStart(Context context, String data1, String data2) {
Intent intent = new Intent(context, SecondActivity.class);
intent.putExtra("param1", data1);
intent.putExtra("param2", data2);
context.startActivity(intent);
}
我们可以在每个Activity中添加一个actionStart方法,指明需要传进来的数据,然后在要跳转至此的Activity中调用即可:
SecondActivity.actionStart(FirstActivity.this, "data1", "data2");
如此一来,跳转需要传入的数据就很清晰了,我们可以大量的节约去阅读代码的时间,就可以多码几块砖了。哈哈···
写了这么长时间,第一篇博文终于好了。由于是第一次写,还是感觉不太容易,虽然写的自己都很熟悉了。鄙人才疏学浅,欢迎各位读者拍砖、指点。