Android02--认识Activity

标签: Android


undefined

新建一个Activity #

使用Android Studio新建一个Empty Activity,例如Main2Activity。在activity_main.xml中新增一个普通按钮:

<Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="打开新页面"
    android:id="@+id/OpenNewAty"
    android:layout_below="@+id/textView"
    android:layout_toRightOf="@+id/textView"
    android:layout_toEndOf="@+id/textView"
    android:layout_marginLeft="38dp"
    android:layout_marginStart="38dp"
    android:layout_marginTop="54dp" />

注意ID是OpenNewAty。在MainActivity中新增事件绑定:

//打开新的页面
findViewById(R.id.OpenNewAty).setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        startActivity(new Intent(MainActivity.this, Main2Activity.class));
    }
});

AndroidManifest.xml中新增一行:

<activity android:name=".Main2Activity"></activity>

运行即可。

同理,打开一个网页:

findViewById(R.id.OpenWeb).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("http://52fhy.com")));
            }
        });

Activity生命周期 #

共有:onCreate、onStart、onResume、onPause、onStop、onRestart、onDestroy。

Activity生命周期
Activity生命周期

单个Activity #

默认执行onCreate、onStart、onResume。
当点击home键返回首页,会执行onPause、onStop;
当从首页返回应用,会执行onRestart、onStart、onResume;
当点击退出按钮,会执行onPause、onStop、onDestroy;
退出后进入应用,会执行onCreate、onStart、onResume;

锁屏会执行onPause、onStop;
开屏会执行onRestart、onStart、onResume;

多个Activity #

假如有A Activity,B Activity:
默认执行A onCreate、A onStart、A onResume。
进入B后,会执行A onPause、B onCreate、B onStart、B onResume、A onStop;

com.a52fhy.activitylifecircle I/System.out: A onCreate
com.a52fhy.activitylifecircle I/System.out: A onStart
com.a52fhy.activitylifecircle I/System.out: A OnResume

com.a52fhy.activitylifecircle I/System.out: A onPause
com.a52fhy.activitylifecircle I/System.out: B onCreate
com.a52fhy.activitylifecircle I/System.out: B onStart
com.a52fhy.activitylifecircle I/System.out: B OnResume
com.a52fhy.activitylifecircle I/System.out: A onStop

返回,会执行B onPause、A onRestart、A onStart、A onResume、B onStop、B onDestroy:

com.a52fhy.activitylifecircle I/System.out: B onPause
com.a52fhy.activitylifecircle I/System.out: A onRestart
com.a52fhy.activitylifecircle I/System.out: A onStart
com.a52fhy.activitylifecircle I/System.out: A OnResume
com.a52fhy.activitylifecircle I/System.out: B onStop
com.a52fhy.activitylifecircle I/System.out: B onDestroy

传递数据到另一个Activity #

传递简单数据 #

MainActivity:

findViewById(R.id.btnNewAty).setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Intent i = new Intent(MainActivity.this, Main2Activity.class);

        TextView tv = (TextView)findViewById(R.id.textGetData);
        String s = (String) tv.getText();//注意类型转换,也可以使用tv.getText().toString();
        s = s + " 欢迎学习安卓";

        //传递数据到新的Activity
        //i.putExtra("name", "欢迎学习安卓");
        i.putExtra("name", s);
        startActivity(i);
    }
});

MainActivity2:

Intent i = getIntent();
TextView tv = (TextView) findViewById(R.id.textShowData); //将View转TextView
tv.setText(i.getStringExtra("name")); //获取并设置值

传递Bundle #

可以通过Intent传递Bundle类型数据。

MainActivity:

findViewById(R.id.btnNewBundleAty).setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Intent i = new Intent(MainActivity.this, Main3Activity.class);

        //实例化Bundle
        Bundle b = new Bundle();
        b.putString("name", "fhy");
        b.putInt("age", 20);
        b.putDouble("money", 1000.99);

        // i.putExtras(b);//直接传递Bundle
        i.putExtra("data", b); //也可以通过键值对形式传过去

        startActivity(i);
    }
});

MainActivity3:
使用getExtras()或者getBundleExtra()接收Intent传递过来的Bundle。

Intent i = getIntent();
//Bundle data = i.getExtras();//从Intent中获取到Bundle
Bundle data = i.getBundleExtra("data");

TextView tv = (TextView) findViewById(R.id.textShowBundleData);
tv.setText(String.format("name=%s\nage=%d\nmoney=%.2f",
        data.getString("name"),
        data.getInt("age"),
        data.getDouble("money", 0) //参数2是缺省值
));//使用String.format格式化显示

传递自定义类型数据 #

Serializable类型 #

User.java

package com.a52fhy.activityputargs;

import java.io.Serializable;

/**
 * User
 * Created by YJC on 2016/9/11 011.
 * 实现序列化接口
 */
public class User implements Serializable{

    private String name;
    private Integer age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public User(String name, Integer age){
        this.name = name;
        this.age = age;
    }
}

MainActivity:

//使用Intent传递自定义类型数据
findViewById(R.id.btnNewObjectAty).setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Intent i = new Intent(MainActivity.this, Main4Activity.class);

        //实例化User
        User userinfo = new User("yjc", 22);

        i.putExtra("userinfo", userinfo); //传递User类型数据

        startActivity(i);
    }
});

Main4Activity:

Intent i = getIntent();

User user = (User) i.getSerializableExtra("userinfo");//获取传递过来的序列化数据

TextView tv = (TextView)findViewById(R.id.textShowObjectData);
tv.setText(String.format("name=%s\nage=%d",user.getName(), user.getAge()));

Parcelable类型 #

User2.java

package com.a52fhy.activityputargs;

import android.os.Parcel;
import android.os.Parcelable;

import java.io.Serializable;

/**
 * Created by YJC on 2016/9/11 011.
 */
public class User2 implements Parcelable{

    private String name;
    private Integer age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public User2(String name, Integer age){
        this.name = name;
        this.age = age;
    }

    /**
     * Describe the kinds of special objects contained in this Parcelable's
     * marshalled representation.
     *
     * @return a bitmask indicating the set of special object types marshalled
     * by the Parcelable.
     */
    @Override
    public int describeContents() {
        return 0;
    }

    /**
     * Flatten this object in to a Parcel.
     *
     * @param dest  The Parcel in which the object should be written.
     * @param flags Additional flags about how the object should be written.
     *              May be 0 or {@link #PARCELABLE_WRITE_RETURN_VALUE}.
     */
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(getName());
        dest.writeInt(getAge());

    }

    /**
     * 泛型
     */
    public static final Creator<User2> CREATOR = new Creator<User2>(){
        @Override
        public User2 createFromParcel(Parcel source){
            return new User2(source.readString(),source.readInt());
        }

        @Override
        public User2[] newArray(int size){
            return new User2[0];
        }
    };
}

MainActivity:
仅需改下类名:User改为User2

//使用Intent传递自定义类型数据(Parcelable)
findViewById(R.id.btnNewObject2Aty).setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Intent i = new Intent(MainActivity.this, Main5Activity.class);

        //实例化User
        User2 userinfo = new User2("yjc", 22);

        i.putExtra("userinfo", userinfo); //传递User类型数据

        startActivity(i);
    }
});

Main5Activity:
需要使用Intent的getParcelableExtra()方法。

Intent i = getIntent();

User2 user = (User2) i.getParcelableExtra("userinfo");//获取传递过来的Parcelable数据

TextView tv = (TextView)findViewById(R.id.textShowObjectData);
tv.setText(String.format("name=%s\nage=%d",user.getName(), user.getAge()));

获取子页面的返回值 #

MainActivity:

@Override
protected void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	setContentView(R.layout.activity_main);

	//点击获取子页面的返回值
	findViewById(R.id.btnNewAtyGetRes).setOnClickListener(new View.OnClickListener() {
		@Override
		public void onClick(View v) {
		    //启动并获取返回值
			startActivityForResult(new Intent(MainActivity.this,Main6Activity.class), 1);
		}
	});
}

//获取子页面返回值
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    TextView btn = (TextView) findViewById(R.id.btnNewAtyGetRes);
    btn.setText(data.getStringExtra("name"));
}

Mai6nActivity:

findViewById(R.id.btnSubmitName).setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        EditText name = (EditText)findViewById(R.id.etName);

        Intent i = new Intent();
        i.putExtra("name", name.getText().toString());
        setResult(1, i);

        finish();
    }
});

activity_main6.xml:

<EditText
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text=""
    android:hint="请输入您的姓名"
    android:id="@+id/etName"
    android:layout_alignParentTop="true"
    android:layout_centerHorizontal="true" />

<Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="提 交"
    android:id="@+id/btnSubmitName"
    android:layout_below="@+id/etName"
    android:layout_centerHorizontal="true"
    android:layout_marginTop="50dp" />

Android 中 Activity 启动模式 #

共有4种:

Activity有四种加载模式:   
standard  标准模式,默认  
singleTop 栈顶模式  
singleTask 单任务模式  
singleInstance 单实例模式 

standard模式下启动多个相同或者不同的Activity,会依次在同一栈里增加,相同的也会增加。例如有A,B两个Activity,启动顺序:A->B->B->A,最终栈的内容:A->B->B->A。只会存在一个TaskID。

singleTop模式下启动多个相同或者不同的Activity,会依次在同一栈里增加,连续相同的不会增加而是复用。例如有A,B两个Activity,启动顺序:A->B->B->A,最终栈的内容:A->B->A。只会存在一个TaskID。

singleTask模式下启动多个相同或者不同的Activity,会依次在同一栈里增加,只要是相同的不会增加而是复用。例如有A,B两个Activity,启动顺序:A->B->B,栈的内容:A->B;启动顺序:A->B->B->A,栈的内容:A。只会存在一个TaskID。

singleInstance模式下启动多个相同或者不同的Activity,会在不同的栈里增加,每个栈里只保存一份Activity。例如有A,B两个Activity,启动顺序:A->B->B->A,会有2个栈,分别是A,B。会存在多个TaskID。

示例代码:

使用getTaskId()显示当前TaskId,toString()显示当前Activity信息。

MainActivity:

TextView tv = (TextView)findViewById(R.id.tv);
tv.setText(String.format("TaskId:%d\nCurrent Activity Id:%s", getTaskId(), toString()));

findViewById(R.id.btnLaunchMain).setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        startActivity(new Intent(MainActivity.this, MainActivity.class));
    }
});

findViewById(R.id.btnLaunchB).setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        startActivity(new Intent(MainActivity.this, BActivity.class));
    }
});

BActivity:

TextView tv = (TextView)findViewById(R.id.tv);
tv.setText(String.format("TaskId:%d\nCurrent Activity Id:%s", getTaskId(), toString()));

findViewById(R.id.btnLaunchMain).setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        startActivity(new Intent(BActivity.this, MainActivity.class));
    }
});

findViewById(R.id.btnLaunchB).setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        startActivity(new Intent(BActivity.this, BActivity.class));
    }
});

AndroidManifest.xml里每个Activity可以修改launchMode:

<activity android:name=".BActivity" android:launchMode="singleInstance"></activity>

显式Intent和隐式Intent #

默认的,我们在Activity代码里指明了要打开哪个Activity:

startActivity(new Intent(MainActivity.this, Bty.class));

这种称为显式Intent调用。

Android还支持隐式Intent。这时候可以在AndroidManifest.xml指明Activity相关信息。

现在有MainActivity、Bty两个Activity,我们想在MainActivity里隐式调用Bty,需要在AndroidManifest.xml中声明如下内容:

<activity
    android:name=".Bty"
    android:exported="true"
    android:label="Bty">
    <intent-filter>
        <category android:name="android.intent.category.DEFAULT" />
        <action android:name="com.a52fhy.activityfilter.intent.action.bty" />
    </intent-filter>
</activity>

其中android:name指明了该Activity被调用时的名称;category申明为android.intent.category.DEFAULT。另外android:label可选,指明该该Activity标题显示名称。android:name名称约定俗称是包名+.intent.action+Activity名

现在我们在MainActivity里隐式调用Bty:

startActivity(new Intent("com.a52fhy.activityfilter.intent.action.bty"));

为了抒写方便,我们一般会在Bty里申明常量:

 //定义ACTION常量
public final static String ACTION = "com.a52fhy.activityfilter.intent.action.bty";

这样,在MainActivity里隐式调用Bty:

 startActivity(new Intent(Bty.ACTION));//隐式调用

如果存在两个Activity,其action android:namecategory android:name一模一样,且android:label不相同,当我们隐式调用时,安卓会让我们选择调用哪个Activity:

<activity
    android:name=".Bty"
    android:exported="true"
    android:label="Bty">
    <intent-filter>
        <category android:name="android.intent.category.DEFAULT" />
        <action android:name="com.a52fhy.activityfilter.intent.action.bty" />
    </intent-filter>
</activity>

<activity
    android:name=".Bty2"
    android:exported="true"
    android:label="Bty2">
    <intent-filter>
        <category android:name="android.intent.category.DEFAULT" />
        <action android:name="com.a52fhy.activityfilter.intent.action.bty" />
    </intent-filter>
</activity>

隐式调用的好处就是,我们可以指明Activity的很多属性,且支持使用action android:name打开另一个APP的Activity。显然,这是显式调用做不到的。

使用scheme #

通过申明scheme,我们可以使用scheme协议打开一个Activity。同样的,也支持打开另一个APP的Activity。

新建一个AppAty,配置其activity属性:

<activity
    android:name=".AppAty"
    android:exported="true"
    android:label="AppAty">
    <intent-filter>
        <category android:name="android.intent.category.DEFAULT" />
        <action android:name="com.a52fhy.activityfilter.intent.action.appaty" />
        <data android:scheme="myapp" />
    </intent-filter>
</activity>

这里增加了data android:scheme,名称是myapp。我们在新建一个APP,在另一个APP的MainActivity里编写如下代码:

 //根据隐式Activity启动
try{
    startActivity(new Intent("com.a52fhy.activityfilter.intent.action.appaty", Uri.parse("myapp://test")));//隐式调用
    
    Toast.makeText(AppTest.this, "myapp://test", Toast.LENGTH_SHORT).show();
    
}catch (Exception e){
    Toast.makeText(AppTest.this, "禁止被调用", Toast.LENGTH_SHORT).show();
}

其中try...catch用于捕获异常,防止调用出错出现崩溃,可选的。这里startActivity()支持第2个参数,我们出入了一个scheme,并附带参数test

Toast.makeText()用于弹出提示信息。

分别安装这两个APP,我们发现在新的APP里启动了之前APP的AppAty页面。

如何在AppAty接收传过来的参数呢?可以使用下面方法:

//打印获取到的信息
Uri uri = getIntent().getData();
Log.i("log", uri.toString());

Logcat里捕捉到的信息:

com.a52fhy.activityfilter I/log: myapp://test

通过浏览器打开APP #

上一节我们通过一个APP打开了另一个APP,那么,能不能通过浏览器打开APP呢?答案是可以的。scheme是协议,和我们常见的http其实是一样的,也支持主机、端口等配置。下面,我们新建一个Activity名为SchemeAty,配置activity属性为:

<activity
    android:name=".SchemeAty"
    android:exported="true"
    android:label="SchemeAty">
    <intent-filter>
        <category android:name="android.intent.category.BROWSABLE" />
        <category android:name="android.intent.category.DEFAULT" />
        <action android:name="android.intent.action.VIEW" />
        <data android:scheme="app" />
    </intent-filter>
</activity>

增加了几行属性,指明scheme是app。接着,我们编写html:

<a href="app://helloSchemeAty">helloSchemeAty</a>

在手机浏览器打开,点击链接,发现打开了我们写的APP。

Build by Loppo 0.6.6