关于AndroidManifest.xml
先看图吧!
这就是android的清单文件!
它有什么用呢?前面我们说了,它用于描述项目的对吧!在以前的eclipse项目里,androidmanifest.xml文件里还会有版本号,编译版本等信息。这里的话,我们可以看到有:
包名、Application、activity.
其实:不指这些!我们大前面的视频里学习数据存储的时候有外置存储卡读写的权限声明:
后面我们学习到的四大组件:Activity、Service、ContentProvider、BroadcastReceiver都需要在这里注册。具体的结构,先了解一下即可:
首先是Application,一个程序就一个Application,它里面可以包涵四大组件(当然啦,不一定全用上)。比如说只有Activity,或者只有Activity和Service。广播接收者的话,用得也算多。但是内容提供者的话就用得比较少了。我工作三年多了,只在做蓝牙电话上用到过,用于向外暴露数据内容。即使用得不多,后面我们也会详解的呢!
什么是Activity?
Activity是什么呢?同学们总是翻译为“活动”!城里人表示一脸蒙逼!之所以叫它Activity是因为它用于跟用户交互的,所以就有了同学们的“活动”,官方的解释如下:
An activity is a single, focused thing that the user can do. Almost all activities interact with the user, so the Activity class takes care of creating a window for you in which you can place your UI with setContentView(View).
Activity是单一独立的,它用于处理用户操作。几乎所有的Activity都与用户交互,所以Activity负责创建窗口来放你所设置的UI内容。
综上,我们可以理解Activity是一个容器,是一个窗口。
它有什么用呢?
Activity有什么用呢?用于承载UI内容,对吧!这是前面的英文说的,用于跟用户进行交互!
AndroidManifest.xml清单文件
首先Manifest是清单的意思,是送货单的意思。那我们要怎么理解呢?很简单,我们类比就可以了,既然是送货单,它是用于描述所送的货们物的嘛。因些,AndroidManifest.xml是用于描述应用程序的。它描述权限,描述这个应用的四大组件。
权限前面我们已经用到了,同学们不用去记。随着你代码写多了,就就知道什么时候需要获取权限了,写的时候不知道怎么写也没关系,因为会提示你的。这个不需要去记,实在不知道什么时候添加权限,你放心,会报错的嘛!报错会打出来告诉你需要什么权限的,OK!到这里的话,权限就搞定了!
One more thing.还有一点要注意的,就是权限的添加不要放错地方哦!
不要放到application里面去了呢。
好啦,到这里我们权限就搞定了!!
再到application,从上图我们可以看到有一个标签是application,也就是应用的意思。一个应用程序就有一个application,这里的话没有指明注册,默认就有一个application。如果大家要在application的生命周期里头做点什么东西,可以写一个application类,比如说BaseApplication并且继承Application这个类,复写application的生命周期方法,还有一点,就是要在Manifest里进行注册。如下:
先创建一个BaseApplication类:
在xml文件里进行注册,怎么注册呢?很简单,在application的标签属性里添加一个name属性:
好啦,到这里的话,当应用程序启动的时候,就会调用application的生命周期方法onCreate了。
我们继续去application别的属性。
android:allowBackup="true"
这个属性翻译过来就是允许备份的意思,true就是允许,false就是不允许。这里的备份是谁备份呢?
是系统备份呢。它的官方说明是怎么样的呢?
android:allowBackup
Whether to allow the application to participate in the backup and restore infrastructure. If this attribute is set to false, no backup or restore of the application will ever be performed, even by a full-system backup that would otherwise cause all application data to be saved via adb. The default value of this attribute is true.
翻译过来大概是:
是否参与备份以及恢复.如果这个属性设置为false,就不会被备份或者恢复到曾经使用的时候一样,即使是全包备份,也不会把整个应用程序的数据保存到via数据库中.这个属性的默认值是true.
下一个属性是设置应用的图标,这个图标累死于我们的Pc上的桌面图标或者说是快捷启动图标。
android:icon="@mipmap/ic_launcher"
从上面的这个属性值来看,它的名字是ic_launcher,我们可以从Android Studio的左边可以看到,有多个这们的文件ic_launcher.png
它们分别是不同尺寸的设备使用的。Android系统的硬件设备太多了,这不跟iOS一样,三套图片就可以了。而android的话,因为厂商不统一,没有统一的标准,所以的话屏幕的尺寸非常多。
回到我们说的这个图标上,这个图标的名字是可自己定的,对吧!你可以叫logo,也可以叫launcher_icon
直接替换原有的文件,或者修清单文件里的引用名称也可以的。
好的,我们来看下一下属性值:
android:label="@string/app_name"
这个是Label,是吧!label是什么意思呢?标记,标签的意思。学习xml就知道了!
这个Label属性在这里有什么用呢?它也就是我们看到的应用名称啦!
在那时修改?它其实在资源res文件下面的strings.xml文件里。
直接修改上面的app_name的内容即可。
这里面要注意的是:activity标签也可以配置label标签,假设在主程序入口的activity里配置,解释和application的不一样,那么也会显示activity的是一样的。这种配置信息的读取,是后来的复盖前面的内容。其实还有,嘻嘻!后面讲到多个activity入口再说。
下面这个属性是支持从右到左
android:supportsRtl="true"
可能做了两个开发的同学也不知道这个什么意思,Rtl其实是右到左的意思。这个属性很少用,但是我在做手表系统开发的时候用到过。什么意思呢?
先看这里:
在开发者选项里头,有这么一个选项:强制使用从右到左的布局方向。下面的summary说:强制将所有语言区域的屏幕布局方向改成从右到左。
第一种情况:也就是默认的状态,这个支持从右到左的值为true。
第二种情况:把属性的值设置为true并且,设置开发者选项里强制布局从右到左:
android:supportsRtl="true"
这样子设置的话,我们可以看到:
因为是允许的,是吧。所以真的是从右到左布局了。
第三种情况,我们把属性设置为false,一样的打开这个开发者选项里强制布局从右到左:
android:supportsRtl="false"
这个时候,它是这样子的,也就是跟我们默认的样子是一样的:
所以,可以通过设置这个属性值,来控制是否允许强制从右到左布局。我做手表是因为手表是圆的,你正看和反着看都行,对吧!!!
好啦,application还有一个常用的标签:
android:theme="@style/AppTheme"
这个标签用于控制样式,它在那里呢?在res文件夹下面的styles.xml文件里:
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
</resources>
这里可以看到parent这个单词,它的意思就是继承自Theme.AppCompat.NoActionBar,这javaWeb开发的struts的配置是一样的,都具有继承性,跟我们的java也是一样和道理。
就一个application竟然写了这么多东西,你以为完事了吗?其实我还没有写activity的配置呢!
这里面把application的属性列出来,给大家看看就OK了,不用去记的。
<application android:allowTaskReparenting=["true" | "false"]
android:allowBackup=["true" | "false"]
android:allowClearUserData=["true" | "false"]
android:backupAgent="string"
android:backupInForeground=["true" | "false"]
android:banner="drawable resource"
android:debuggable=["true" | "false"]
android:description="string resource"
android:directBootAware=["true" | "false"]
android:enabled=["true" | "false"]
android:extractNativeLibs=["true" | "false"]
android:fullBackupContent="string"
android:fullBackupOnly=["true" | "false"]
android:hasCode=["true" | "false"]
android:hardwareAccelerated=["true" | "false"]
android:icon="drawable resource"
android:isGame=["true" | "false"]
android:killAfterRestore=["true" | "false"]
android:largeHeap=["true" | "false"]
android:label="string resource"
android:logo="drawable resource"
android:manageSpaceActivity="string"
android:name="string"
android:networkSecurityConfig="xml resource"
android:permission="string"
android:persistent=["true" | "false"]
android:process="string"
android:restoreAnyVersion=["true" | "false"]
android:requiredAccountType="string"
android:resizeableActivity=["true" | "false"]
android:restrictedAccountType="string"
android:supportsRtl=["true" | "false"]
android:taskAffinity="string"
android:testOnly=["true" | "false"]
android:theme="resource or theme"
android:uiOptions=["none" | "splitActionBarWhenNarrow"]
android:usesCleartextTraffic=["true" | "false"]
android:vmSafeMode=["true" | "false"] >
. . .
</application
接下来,我们简单地看看activity标签吧!
<activity
android:name=".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>
这是比较常见的配置方式,这里的name和application里的name是一样的。都是用于配置目标的类。四大组件的静态注册,name都是指要注册的类。
这里也可以声明label的值。因为接下来我们的intent-filter,也就是意图过滤器,声明的是主入口,所以这里这个label值就会成为了这个入口的名称,当然啦,一个应用程序可以有多个入口,详情请看视频。
主入口的默认写法:
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
Android开发基础之组件之间的数据传输
如何把数据传到下一个界面。大家也不要局限于Activity之间的数据传递,也就是说,这是组件与组件之间的数据传递,也适用于后面我们学到的服务,广播接收者…
四大组件,对吧!
理论知识我们可以传递什么类型的数据呢:
主要分两大类:基本数据类型和引用数据类型,引用数据类型的话也就是我们所说的对象。
基本数据类型有那些呢?复习一下java基础吧:
怎么把基本数据类型的数据传输到下一个组件呢?
基本数据类型的传输
前面我们要跳转的话需要创建一个意图对象,也就是Intent。这个Intent其实就是我们数据的载体,我们把数据扔intent里面。
所以就有了:
前面的String name是key,也就是这里put,另外一边则是get了。get的时候需要传入key,这样才能获取到对应的值。
一般来说,这个key定义为一个常量,并且两个组件都能访问到。
另外一边则是获取数据是吧!
首先我们要拿到意图对象,也就是Intent
通过get类型(key)的方法来获取到对应的内容,这样子就完成了数据内容的传输了。
例子:
package com.sunofbeaches.componentdatadeliver;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
/**
* 虽然说我们这节课是组件之间的数据传递
* 我们常说的组件有Activity,BroadcastReceiver,Service,ContentProvider
* <p/>
* 这里的话我们只学习Activity之间的数据传输,其实其他组件之间的数据传输也是一样的。
*/
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
/**
* 第一个按钮被点击了
*
* @param view
*/
public void firstClick(View view) {
//这样子写也是可以的哦!
Intent intent = new Intent(this, SecondActivity.class);
intent.putExtra("booleanKey", true);
intent.putExtra("charKey", 'a');
intent.putExtra("byteKey", (byte) 1);
intent.putExtra("shortKey", (short) 2);
intent.putExtra("intKey", 3);
intent.putExtra("longKey", 4l);
intent.putExtra("floatKey", 0.5f);
intent.putExtra("doubleKey", 0.6d);
startActivity(intent);
}
/**
* 第二个按钮被点击了
*/
public void secondClick(View view) {
}
}
第二个Activity
package com.sunofbeaches.componentdatadeliver;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
/**
* Create by TrillGates 2017/11/23
* 这是第二个界面,我们就在这个界面获取一个内容吧:
*/
public class SecondActivity extends Activity {
private static final String TAG = "SecondActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/**
* 拿到启动这个Activity的意图对象
*/
Intent intent = getIntent();
if (intent != null) {
//前面的是key,后面的是默认值,假设获取不到的时候,就会返回默认值,也就是后面的那个值。
//比如说我们把key写错了,这样子就获取不到值了。
boolean booleanValue = intent.getBooleanExtra("booleanKey", false);
//
char charValue = intent.getCharExtra("charKey", '*');
//
byte byteValue = intent.getByteExtra("byteKey", (byte) 0);
//
short shortValue = intent.getShortExtra("shortKey", (short) 0);
//
int intValue = intent.getIntExtra("intKey", -1);
//
long longValue = intent.getLongExtra("longKey", 0l);
//
float floatValue = intent.getFloatExtra("floatKey", 0.0f);
//
double doubleValue = intent.getDoubleExtra("doubleKey", 0.0d);
Log.d(TAG, "booleanValue = " + booleanValue);
Log.d(TAG, "charValue = " + charValue);
Log.d(TAG, "byteValue = " + byteValue);
Log.d(TAG, "shortValue = " + shortValue);
Log.d(TAG, "intValue = " + intValue);
Log.d(TAG, "longValue = " + longValue);
Log.d(TAG, "floatValue = " + floatValue);
Log.d(TAG, "doubleValue = " + doubleValue);
}
}
}
执行结果是怎么样的呢?我们点击一下按钮如下:
引用数据类型的数据传输
前面已经说了如何传输基本数据类型,那么后面的话我们说一说怎么传递对象。
前面我们看到可以传String呢?String呢不是基本数据类型,它是引用数据类型。
String是已经实现了序列化的接口的:
Bitmap也就是位图,位图对象也是实现了序列化的接口的。
所以我们可以传位图对象,但是要注意的是它的大小 ,后面我们会讲到意图对象的传值的大小限制。
接下来,我们使用Intent来传一个对象:User
首先,我们要把User序列化。
package com.sunofbeaches.componentdatadeliver;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Created by TrillGates on 17/11/24.
* God bless my code!
*/
public class User implements Parcelable {
private String name;
private int age;
public User(String name, int age) {
this.age = age;
this.name = name;
}
protected User(Parcel in) {
name = in.readString();
age = in.readInt();
}
public static final Creator<User> CREATOR = new Creator<User>() {
@Override
public User createFromParcel(Parcel in) {
return new User(in);
}
@Override
public User[] newArray(int size) {
return new User[size];
}
};
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(age);
}
}
实现Parcelable也行,Serializable也行。后者是Java的,前者是google写的类。两者的不同是前都比较高效,它是写到内存里的,后者是写到持久化存储单元里的。
然后呢?看代码吧:
/**
* 第二个按钮被点击了
*/
public void secondClick(View view) {
User user = new User("TrillGates", 25);
Intent intent = new Intent(this, SecondActivity.class);
intent.putExtra("user", user);
startActivity(intent);
}
第二个Activity里的代码:
User user = intent.getParcelableExtra("user");
Log.d(TAG, "usr Name == " + user.getName());
Log.d(TAG, "usr age == " + user.getAge());
运行起来,结果如下:
除了这样传以外,还可以怎么传呢?其实我们可以以协议的形式来传数据的呢:
实际的列子有那些呢?比如说我们第三应用发短信,第三方应用要调用电话拨号器。这个时候就需要去看它的意图过滤器了:
<intent-filter>
<action android:name="android.intent.action.CALL" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="tel" />
</intent-filter>
我们可以看到,这其实就是约束,数据的约束,也就是说,我们要拨打电话的时候,是这样子的:
Intent intent = new Intent():
intent.addAction(“android.intent.action.CALL”);
intent.setCategory(“android.intent.category.DEFAULT”);
intent.setData(Uri.parse(“tel://10086”));
startActivity(intent);
这样能看明白吗?以上是纯手写的代码,没有开发工具也不知道单词有没写错。
OK,知道这个以后,我们就明白了,还可以通过setData来传数据,并且,在另外一个界面,通过getData来获取到。
获取到的是全部内容哦,包括约束:tel://10086
Intent封装数据的大小限制
有的时候,我们传的数据是挺大的。比如说,我们要传一张图片的时候,就很大了,是吧。那么它的现实是多大呢?我们可以看看官方的文档。
那怎么办呢?可以写到SD卡上,把路径传过去就可以。对于IPC来说,有好多种方式。IPC就是跨进程通迅啦,后面我们学习到服务的时候 ,我们也会学到AIDL,这也是IPC的一种方式。
从上面的文章我们可以知道,它限制的大小为1M,这块其实是共享内存来的。也就是Blundle.
Activity的数据回传
前面我们学习了怎么样把数据传递到下一个Activity,有些时间,数据是需要回传的,比如说,我们去拍照吧。
思路:点击一个按钮,然后跳转到拍照的界面,拍完以后,你是不是要显示拍到的内容呢?这个时候,就需要把数据从拍照界面传回来了吧。
或者大家可以想象这样一个使用场景:当你要发微信朋友圈的时候,是不是要选择拍摄或者相册选择呢?拍摄会跳转到拍摄的界面。然后拍完了再回来,回来的时候就已经显示了图片了。
其实这个过程就使用到了这种数据回传的技术了。那我们是不是要学习一下呢?
实现分析
首先,我们还是一样,要打开Activity,先创建意图对象。
- 如果是第三方的那么就用隐式意图,自己的就用显示意图。
- 但是不同的是,不用startActivity,而是用startActivityForResult这个方法。
- 好,我们来看一下,这个方法要传什么参数:
第一个参数如大家所愿是intent。 这个方法有两个重载的方法,一个是两个参数,一个是三个参数。 那我们就把三个参数说完吧: 第二个参数是requestCode,翻译过来就是请求码,这个请求码有什么用呢?其实就是暗号! 第三个参数
* @param options Additional options for how the Activity should be started.
这个参数的意思是设置Activity是如何启动的,在ActivityOptions这个类里面,就是封装Activity的启动参数的。这个Bundle可以理解为一个可以封装多种集合即可。
刚吃完饭回来,不知道写啥了,哈哈!!
话不多说,咱们开干吧!
举个栗子
怎么举呢?
大概是这样子的:我们从第一个界面,点击重置跳转到第二个界面,然后,充值完成以后,回到第一个界面提示!
怎么样着手呢?还是那样,是界面 的跳转,一样要创建意图对象。算了,直接看代码吧,里面已经有详细注释了,看完代码,我再把流程图画出来。
首先是第一个MainActivity的代码,它就长成这样子:
package com.sunofbeaches.activityforresultdemo;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
private static final int sRequestCode = 1;
/**
* 充值按钮被点击,跳转到充值界面,然后进行充值。
*
* @param view
*/
public void reCharge(View view) {
Intent intent = new Intent(this, PayActivity.class);
//如果要传输则在这之间封装,比如说有时候需要把用户的id传过去对吧。
//一般来说,这个唯一表示会保存成全应用能访问到的变量(超时了要修改的,不能是常量)
startActivityForResult(intent, sRequestCode);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
//在这里会返回充值界面返回的数据。
if (requestCode == sRequestCode) {
TextView resultText = (TextView) findViewById(R.id.result_text);
//说明是我们请求的数据结果,对得上请求码
//然后判断结果码,在支付界面 ,我们定义了-1为失败,1为成功。所以就有了:
if (resultCode == -1) {
//支付失败,可以是网络问题,也可以是取消充值
resultText.setText("充值失败!如果遇到问题请联系客服!");
} else if (requestCode == 1) {
//充值成功,给出提示吧!
resultText.setText("感谢您的支持!您已充值 :" + data.getStringExtra("charge") + "\n 您当前的余额是***");
}
}
}
}
这个是MainActivity的UI代码:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="reCharge"
android:text="充值"/>
<TextView
android:id="@+id/result_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
还有就是第二个Activity的代码啦,这个是支付界面 ,也就是PayActivity,要记得在Manifest里头注册一下
package com.sunofbeaches.activityforresultdemo;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
import java.util.regex.Pattern;
/**
* Create by TrillGates 2017/12/6
*/
public class PayActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_pay);
}
/**
* 确定充值被点击了,这里提示充值成功,并且把充值的金额返回给前一个界面。
*
* @param view
*/
public void confirmCharge(View view) {
EditText chargeInput = this.findViewById(R.id.charge_input);
//这里要对数字进行检查,对吧!
String targetChargeText = chargeInput.getText().toString().trim();
String reg = "/(^[1-9]([0-9]+)?(\\.[0-9]{1,2})?$)|(^(0){1}$)|(^[0-9]\\.[0-9]([0-9])?$)/";
if (TextUtils.isEmpty(targetChargeText)) {
//没有输入内容
Toast.makeText(this, "您没有输入充值金额!", Toast.LENGTH_SHORT).show();
} else {
//如果有输入,判断是否符合金额的格式
boolean matches = Pattern.matches(reg, targetChargeText);
if (matches) {
//TODO:向网络提交数据,然后充值
//等待返回结果再更新UI
//这里合法的话,那我们就给出提示,并且把数据设置回去即可
//把结果封装到Intent里
Intent resultIntent = new Intent();
resultIntent.putExtra("charge", targetChargeText);
setResult(1);
finish();
} else {
Toast.makeText(this, "您输入的金额不合法,请重新输入!", Toast.LENGTH_SHORT).show();
}
}
}
/**
* 取消充值
*
* @param view
*/
public void cancelCharge(View view) {
//这个是取消充值
setResult(-1);
finish();
}
}
把它的布局也弄上来看一下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="https://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
android:id="@+id/charge_input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入充值金额"
android:inputType="number"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="confirmCharge"
android:text="确认充值"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="cancelCharge"
android:text="取消充值"/>
</LinearLayout>
这些都上完了,我们看一下这个逻辑是怎么样的:
首先,我们点击充值按钮,跳转到充值界面。
点击充值,则会检查输入内容,给出响应的动作,提示输入不合法,或者充值成功,返回结果到第一个界面。
点击取消充值,则会结束当前界面,并且返回结果给第一个界面。
代码流程:
跳转到这个界面:
点击确认充值,其实这样的检测是有问题的,这正则表达式得处理一下。因为我们已经限定的输入为数字了,也就是说小数点是没法输入的:
点击取消充值:
然后,我们都看到了setResult的内容:
这个调用以后,就会回 调到:
这就是整个过程啦!
要注意的事
第一,要注意过程,不是用startActivity,而是用startActivityForResult
第二,要复写onActivityResult方法,这里会有返回值
第三,第一个Activity不可finished,否则就接收不到结果了。
什么是启动模式呢?
我们的Activity是由系统创建的,我们编写xxxActivity直接或者间接继承自Activity,然后我们是没有自己创建的,只是配置了一下即可。
Activity由系统创建启动的时候,有几种模式,我们把这几种模式叫做Activity的启动模式。
Activity启动模式有那些呢?
在android中,Activity的启动模式有四种,分别是:
– standard,标准模式 – singleTop,单一栈顶模式 – singleInstance,单例模式 – singleTask,单任务模式
接下来,我们会通过代码和图例的形式来学习一下启动模式,已及启动模式的应用场景。
正文开始
Activity从它的架构和设计上来说,是非常优秀的想法,流行于大多数的移动操作系统中。不管怎么样,Activity不仅仅是只跑界面这么简单,它是怎么加载的也是非常值得我们去研究一下的。
在这篇文章里头会有很多内容,其中最重要的就是加载模式(LaunchMode)这个就是我们今天重点讨论的话题了。
每个Activity获取它有不同的作用,多数情况下,我们使用标准的启动模式即可,但是有些特殊情况,比如说,我的主界面只能有一个,不管是谁启动我这个主界面,它在栈里就只有一个实例,这种情况下,我们就需要把这个Activity的启动模式设置成singleInstance了。
如何指定Activity的启动模式呢?
最基本最直接的方式就是直接修改AndroidManifest.xml里的属性配置即可,比如说:
前面我们知道,有且仅有四种启动模式,让我们一个一个地来看吧!
Standard标准模式
标准启动模式,也是默认的启动模式。
如果在AndroidManifest.xml文件里设置了这个启动模式(其实可以不设置,默认就是这个启动模式),每开启一个被设置的Activity,就会创建新的对象。
验证代码:
第一个Activity去启动一个新的Activity,这个Activity每1秒钟就启动一下新的Activity,启动5个就完事了。
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private int time = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final Handler handler = new Handler();
handler.post(new Runnable() {
@Override
public void run() {
if (time < 5) {
startActivity(new Intent(MainActivity.this, NewActivity.class));
Log.d(TAG, "start new activity...");
time++;
handler.postDelayed(this, 1000);
}
}
});
}
}
第二个界面的代码如下,做了一个小动作,就是每个界面的创建,就会设置一下随机的背景颜色。
/**
* Created by TrillGates on 2018/2/2.
*/
public class NewActivity extends Activity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_new);
RelativeLayout container = (RelativeLayout) this.findViewById(R.id.container);
Random random = new Random();
int colorValue = random.nextInt();
container.setBackgroundColor(colorValue);
}
}
把NewActivity的启动模式设置成standard,不设置也可以,因为默认就是这个启动模式。
<activity
android:name=".NewActivity"
android:launchMode="standard"
android:theme="@style/AppTheme"/>
然后我们把这个应用跑起来,看现象:
以上动图,需要重新开始看哈,是这样子的。首先是点击打开,从上面的代码我们知道。一打开以后,第一个Activity就会每一秒打开一个新的Activity,打开5个。然后我点击了返回键,也就是关闭当前的Activity,可以发现我们一直关闭了5个界面才回到开始的hello界面,这个过程的动作是怎么样的呢?
它是这么进入任务站的:
也就是逐个创建进入任务栈,当我们点击返回键的时候呢? 就变成这样子了:
基本的启动模式,是最简单的,也是最常用的。在栈里的顺序是先进后出,后进先出。由于画图的原因,我们应该理解为这个栈为一个箱子,只有顶部的出口。
SingleTop模式
具体演示请看视频吧,singleTop的意思则是如果要 启动的任务已经在栈的顶部了,则不会再去创建新的任务,也就是说,假设ActivityB是singleTop,当前栈顶已经是ActivityB了,则不会去创建ActivityB。
使用场景:如果这个任务是比较被动的,比如说,浏览器的收藏夹,可以被javaScript的代码控制,比如说通知,可以被拉起来的这些,比较被动的任务,则使用SingleTop模式,防止被多次创建。如果已经在顶部了,或者我们可以理解为已经聚焦了,就没必要再创建了。
SingTask模式
singTask,是会保证在这个栈里,只有一个目标任务。假设说,我们生命了ActivityB为singleTask,那么在任务栈里,只有一个ActivityB。当我们要打开ActivityB时有以下两种情况:
第一种情况:任务栈里没有,就创建,放在栈顶。
第二种情况:任务栈里有这个ActivityB,则不会创建,并且把ActivityB以上的任务,全部出栈,保证ActivityB在顶部。
使用场景:假设我们这个任务,要占比较多的内存开销,就会使用SingleTask的模式来保证它在栈里只有一个。
SingleInstance模式
这个SIngleInstance牛逼,比前面三种模式都牛逼。
SingleInstance模式怎么牛逼呢?前面三种模式,都是在同一个任务栈里的,但是这个SingleInstance自己独占一个任务栈呢!
标签:public,intent,Activity,组件,四大,android,我们,Intent From: https://www.cnblogs.com/cavange/p/16616118.html