2.2 安卓活动基础 Activity跳转---Intent
Intent是安卓程序中各组件之间进行交互的重要方式,既可以指明当前组件要执行的动作,还可以在不同组件之间传递数据。
Intent一般可以被用于启动活动、启动服务以及发送广播等场景。
Intent可以分为两种:
1、显式Intent
2、隐式Intent
Intent有多个构造函数的重载, 重载的知识可以参照Java函数重载知识点,本文末尾附简单说明;
显式Intent的应用
其中一个是Intent(Context packageContext, Class<?>cls)
。这个构造函数接收了两个参数:
第一个参数:Context要求提供一个启动活动的上下文 , Context上下文可以参照安卓Context知识点,本文末尾附简单说明;
第二个参数:Class则是指定想要启动的目标活动
通过Intent(Context packageContext, Class<?>cls)
这个构造函数就可以构建出Intent的“意图”;
安卓的Activity提供了一个startActivity()的方法,我们可以利用这个方法启动活动,这个方法接收一个Intent参数,我们将构建好的Intent参数传入startActivity()方法就可以启动目标活动了。
比如我们通过主活动的一个按钮启动第二个活动。下面是示例代码:
button.setOnclickListener(new OnClickListener(){
@override
public void onClick(View v){
//通过这个构造函数构建出我们的意图
//Intent intent 新建一个Intent的引用,放在栈内存中
//new Intent()新建一个Intent的对象,放在堆内存中
//= 将Intent的对象赋值给Intent的引用intent
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
//调用startActivity()方法,启动第二个活动;
startActivity(intent);
}
});
由于这种方式启动活动,Intent的意图很明显,所以称之为显式Intent
隐式Intent
隐式Intent并不明确指出我们想要启动哪一个活动,而是通过指定IntentFilter意图过滤器的action和category等信息,然后由系统分析判断这个Intent,然后帮助我们启动对应个活动。
首先我们在AndroidMainfest.xml的<activity>
标签下配置<intent-filter>
的内容。指定当前这个activity能够响应的action和category,当Java代码中的程序刚好匹配到的是这个意图过滤器中的内容,则可以自动启动这个活动
代码如下:
<activity android:name=".SecondActivity">
<intent-filter>
//此处我们指明了当前活动可以响应com.example.activity.ACTION_START这个action
<action android:name="com.example.activity.ACTION_START"/>
//category标签中我们指明了当前活动可以响应Intent中带有的category
//必须加上category android:name="android.intent.category.DEFAULT",否则无法启动改活动
<category android:name="android.intent.category.DEFFAULT"/>
//下面一行代码为下面一段知识点使用,可以添加多个category,此处我们添加了自己的category
//<category android:name="com.example.activityTest.MY_CATEGORY"/>
</intent-filter>
</activity>
只有上述activity中的action和category中的内容同时匹配上Java代码的Intent中的action和category时,这个活动才能响应;android:name="android.intent.category.DEFFAULT
是一个默认的category,在调用startActivity()方法的时候会自动将这个category添加到Intent中,所以默认的话,我们也可以不填写。
隐式Intent启动活动的Intent代码如下:
使用Intent的另外一种构造函数;
button.setOnclickListener(new OnClickListener(){
@override
public void onClick(View v){
//通过这个构造函数构建出我们的意图,响应与com.example.activity.ACTION_START匹配的action
//Intent intent 新建一个Intent的引用,放在栈内存中
//new Intent()新建一个Intent的对象,放在堆内存中
//= 将Intent的对象赋值给Intent的引用intent
Intent intent = new Intent("com.example.activity.ACTION_START");
//这一行是category,默认的这种方式可以不写;且category可以指定多个
//比如我们调用Intent中的addCategory()方法来添加一个category;
//intent.addCategory("android.intent.category.DEFFAULT");
intent.addCategory("com.example.activityTest.MY_CATEGORY");
//调用startActivity()方法,启动第二个活动;
startActivity(intent);
}
});
隐式Intent还有更多的用法,这里暂不详细解释,可以参照第一行代码第二版46-49页
1、启动其他程序中的活动,比如在我们自己的程序中展示一个网页,可以利用隐式Intent调用系统浏览器来打开这个网页
Intent传递数据
活动中传递数据的思路是:
Intent中提供了一系列的putExtra()方法的重载,可以把我们想要传递的数据暂存在Intent中,到达另一个活动中将数据从Intent中取出来即可
1、Intent向下一个活动传递数据
我们在A活动中通过Intent的putExtra()方法把传递字符串
传递数据的代码如下:
//button1的点击事件访问hint,属于局部内部类(包括匿名局部内部类和普通内部类)中使用局部变量,那么这个局部变量必须使用final修饰
//hint.getText()作为一个局部变量,其生命周期应该在该方法运行结束后就完结,但是在此处被内部类所引用,会导致其作用域的扩大。
// 如果在此期间又对局部变量的值随意改变,将会造成混乱。因此要将该局部变量的属性硬性的修改为final,即可防止混乱的产生。
final EditText hint = findViewById(R.id.tips_edt);
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String data = String.valueOf(hint.getText());
if (data.equals("")){
ToastShow(MainActivity.this, "请输入传递的数据!");
}else {
//显式Intent启动活动2
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
//这里的putExtra()接收两个参数,键值对的方式,第一个参数是键,下一级活动通过这个键可以从Intent中取值
//第二个参数才是真正传递的数据
intent.putExtra("data", data);
startActivity(intent);
}
}
});
在下一级活动中,我们通过键值对中键名来获取值
获取数据的代码如下:
TextView data_tv = findViewById(R.id.data_tv);
//首先通过getInten()方法获取到用于启动SecondActivity的Intent
Intent intent = getIntent();
//然后调用getStringExtra()方法,传入键,即可得到键对应的值;
//拓展:如果传递的是整形数据,则用getIntExtra()获取数据
//如果传递的是布尔型数据,则用getBooleanExtra()获取数据
data_tv.setText(intent.getStringExtra("data"));
实际APP运行gif见文末:
2、返回数据给上一级活动
与传递数据给下一级不同的是,返回上一个活动只需要按一下虚拟按键的返回键,并没有一个用于启动活动的Intent来传递数据。怎么办呢?我们知道Activity中还有一个startActivityForResult()方法也是用于启动活动的,这个方法是期望在活动销毁时能够返回一个结果给上一个活动,这正是我们需要的。
startActivityForResult()方法接收两个参数,第一个参数是Intent,第二个参数是请求码,这个请求码用于在之后的回调中判断数据的来源。接下来我们还是贴上程序
主活动的按钮点击事件代码:
button3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent3 = new Intent(MainActivity.this, ForthActivity.class);
//startActivityForResult()方法用来启动活动4,这个方法接收两个参数
//第一个是Intent,从主活动跳转到活动4
//第二个是请求码,用于在之后的回调中判断数据来源,请求码只要传入一个唯一值就行了
startActivityForResult(intent3, 1);
}
});
这里我们使用startActivityForResult()
方法用来启动活动4,这个方法接收两个参数:
第一个是Intent,从主活动跳转到活动4;
第二个是请求码,用于在之后的回调中判断数据来源,请求码只要传入一个唯一值就行了;
接下来是活动4的代码:
public class ForthActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_forth);
final EditText data_edt = findViewById(R.id.data_edt);
Button button = findViewById(R.id.send_btn);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//构建一个Intent,用于数据传递
Intent intent = new Intent();
//将数据以键值对的方式放入Intent
intent.putExtra("data_return", String.valueOf(data_edt.getText()));
Log.i("ForthActivity", "" + String.valueOf(data_edt.getText()));
//调用setResult()方法,这个方法专门用于向上一级活动返回数据
//setResult()接收两个参数
//第一个参数:用于向上一级活动返回结果,一般只有RESULT_OK和RESULT_CANCELED这两个值
//第二个参数是把带有数据的Intent传递回去
setResult(RESULT_OK, intent);
//然后调用finish()方法来销毁当前活动
finish();
}
});
}
}
由于我们是使用startActivityForResult()
方法来启动活动4的,所以在活动4被销毁之后会回调上一个活动的onActivityResult()
方法,因此我们需要再主活动中重写这个方法来得到返回的数据
//onActivityResult()方法带有三个参数
//第一个参数requestCode,这是我们在启动活动是传入的请求码
//第二个参数resultCode,这个是我们返回数据时传入的处理结果
//第三个参数data,这个是携带返回数据的Intent,通过data.getStringExtra(键)我们可以获取到传递过来的数据
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode){
//由于在一个活动中调用startActivityForResult()方法启动很多不同的活动,每一个活动返回的数据都会回调到onActivityResult()这个方法中
//因此我们首先检查requestCode的值判断数据来源,确认数据是从哪个Activity返回的
//之后判断resultCode判断处理的结果是否成功
//最后从data中取出数据
case 1:
if (resultCode == RESULT_OK){
rec_tv.setText(String.valueOf(data.getStringExtra("data_return")));
}
break;
default:
break;
}
}
程序运行效果:
附件:
1、函数重载
什么是方法的重载呢?
如果同一个类中包含了两个或两个以上方法名相同、方法参数的个数、顺序或类型不同的方法,则称为方法的重载,也可以称方法被重载了。
public void show(); //无参方法
public void show(String name); //重载show方法,一个字符串参数
public void show(String name, int age); //重载show方法,两个参数
public void show(int age, String name); //重载show方法,两个参数顺序不同
如何区分调用的是哪个重载方法呢?
当调用被重载的方法时,Java会根据参数的个数和类型来判断应该调用哪个重载方法,参数完全匹配的方法将被执行。
判断方法重载的依据:
1、必须是在同一个类中
2、方法名相同
3、方法参数的个数、顺序或类型不同
4、与方法的修饰符或返回值没有关系
2、Context 上下文
Context在语文中,我们可以理解为语境,在程序中,我们可以理解为当前对象在程序中所处的一个环境
那Context到底是什么呢?一个Activity就是一个Context,一个Service也是一个Context。Android程序员把“场景”抽象为Context类,他们认为用户和操作系统的每一次交互都是一个场景,比如打电话、发短信,这些都是一个有界面的场景,还有一些没有界面的场景,比如后台运行的服务(Service)。
Context到底可以实现哪些功能呢?这个就实在是太多了,弹出Toast、启动Activity、启动Service、发送广播、操作数据库等等都需要用到Context。
在绝大多数场景下,Activity、Service和Application这三种类型的Context都是可以通用的。不过有几种场景比较特殊,比如启动Activity,还有弹出Dialog。出于安全原因的考虑,Android是不允许Activity或Dialog凭空出现的,一个Activity的启动必须要建立在另一个Activity的基础之上,也就是以此形成的返回栈。而Dialog则必须在一个Activity上面弹出(除非是System
Alert类型的Dialog),因此在这种场景下,我们只能使用Activity类型的Context,否则将会出错。
如何获取Context
通常我们想要获取Context对象,主要有以下四种方法
1:View.getContext,返回当前View对象的Context对象,通常是当前正在展示的Activity对象。
2:Activity.getApplicationContext,获取当前Activity所在的(应用)进程的Context对象,通常我们使用Context对象时,要优先考虑这个全局的进程Context。
3:ContextWrapper.getBaseContext():用来获取一个ContextWrapper进行装饰之前的Context,可以使用这个方法,这个方法在实际开发中使用并不多,也不建议使用。
4:Activity.this 返回当前的Activity实例,如果是UI控件需要使用Activity作为Context对象,但是默认的Toast实际上使用ApplicationContext也可以。
getApplication()和getApplicationContext()
上面说到获取当前Application对象用getApplicationContext,不知道你有没有联想到getApplication(),这两个方法有什么区别?相信这个问题会难倒不少开发者。
Application本身就是一个Context,所以这里获取getApplicationContext()得到的结果就是Application本身的实例。那么问题来了,既然这两个方法得到的结果都是相同的,那么Android为什么要提供两个功能重复的方法呢?实际上这两个方法在作用域上有比较大的区别。getApplication()方法的语义性非常强,一看就知道是用来获取Application实例的,但是这个方法只有在Activity和Service中才能调用的到。那么也许在绝大多数情况下我们都是在Activity或者Service中使用Application的,但是如果在一些其它的场景,比如BroadcastReceiver中也想获得Application的实例,这时就可以借助getApplicationContext()方法了。
如何正确使用Context
一般Context造成的内存泄漏,几乎都是当Context销毁的时候,却因为被引用导致销毁失败,而Application的Context对象可以理解为随着进程存在的,所以我们总结出使用Context的正确姿势:
1:当Application的Context能搞定的情况下,并且生命周期长的对象,优先使用Application的Context。
2:不要让生命周期长于Activity的对象持有到Activity的引用。
3:尽量不要在Activity中使用非静态内部类,因为非静态内部类会隐式持有外部类实例的引用,如果使用静态内部类,将外部实例引用作为弱引用持有。