【android_温故知新】深入理解 Activity

  • Activity
  • 建立配置和使用 Activity
    • Activity
      • 实例用 LauncherActivity 开发启动 Activity 的列表
      • 实例使用 ExpandableListActivity 实现可展开的 Activity
    • 配置 Activity
    • 启动关闭 Activity
    • 使用 Bundle 在 Activity 之间交换数据
      • 温馨提示
      • 实例用第二个 Activity 处理注册信息
    • 启动其他 Activity 并返回结果
      • 实例用第二个 Activity 让用户选择信息
  • Activity 的回调机制
  • Activity 的生命周期与加载模式
    • Activity 的生命周期演示
    • Activity 的 4 种加载模式

Activity

Activity 是 android 应用的重要组成单元之一(另外三个是 Service、BroadcastReceiver 和 ContentProvider),而 Activity 又是 android 应用最常见的组件之一。前面看到的示例通常都只包含一个 Activity,但在实际应用中这个是不太可能的,往往包括多个 Activity,不同的 Activity 向用户呈现不同的操作界面。android 应用的多个 Activity 组成 Activity 栈,当前活动的 Activity 位于栈顶。

建立、配置和使用 Activity

Activity

建立自己的 Activity 也需要继承 Activity 基类。当然,在不同应用场景下,有时也要求继承 Activity 的子类。例如,如果应用程序界面只包括列表,则可以让应用程序继承 ListActivity;如果应用程序界面需要实现标签页效果,则可以让应用程序继承 TabActivity。

Activity 类间接或直接地继承了 Context、ContextWrapper、ContextThemeWrapper 等基类,因此 Activity 可以直接调用它们的方法。

创建一个 Activity 也需要实现一个或多个方法,其中最常见的就是实现 onCreate(Bundle status)方法,该方法将会在 Activity 创建时被回调,该方法调用 Activity 的 setContentView(View view)方法来显示要展现的 View。为了管理应用程序界面的各组件,调用 Activity 的 findViewById(int id)方法来获取程序界面中的组件,接下来修改各组件的属性和方法即可。

实例:用 LauncherActivity 开发启动 Activity 的列表

LauncherActivity 继承了 ListActivity,因此它本质上也是一个开发列表界面的 Activity,但它开发出来的列表界面与普通列表界面有所不同。它开发出来的列表界面中每个列表项都对应一个 Intent,因此当用户单击不同的列表项时,应用程序会自动启动对应的 Activity。

使用 LauncherActivity 的方法并不难,由于依然是一个 ListActivity,因此同样需要为它设置 Adapter—-即可使用简单的 ArrayAdapter,也可使用 SimpleAdapter,当然还可以扩展 BaseAdapter 来实现自己的 Adapter。与使用普通 ListActivity 不同的是,继承 LauncherActivity 时通常应该重写 Intent intentForPosition(int position)方法,该方法根据不同列表项返回不同的 Intent(用于启动不同的 Activity)

public class MainActivity extends LauncherActivity {
    //定义两个Activity的名称
    String[] names = {"设置程序参数", "查看星际兵种"};
    //定义两个Activity对应的实现类
    Class[] clazzs = {PreferenceActivityTest.class
            , ExpandableListActivityTest.class};

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ArrayAdapter adapter = new ArrayAdapter(this,
                android.R.layout.simple_list_item_1, names);
        // 设置该窗口显示的列表所需的Adapter
        setListAdapter(adapter);
    }

    //根据列表项返回指定Activity对应的Intent
    @Override
    public Intent intentForPosition(int position) {
        return new Intent(MainActivity.this, clazzs[position]);
    }
}

上面程序还用到了如下两个 Activity

1. ExpandableListActivityTest:它是 ExpandableListActivity 的子类,用于显示一个可展开的列表窗口。
2. PreferenceActivityTest:它是 PreferenceActivity 的子类,用于显示一个显示设置选项参数并进行保存的窗口。

实例:使用 ExpandableListActivity 实现可展开的 Activity

ExpandableListActivity 的用法与前面介绍的 ExpandableListView 的用法基本相似,只要为该 Activity 传入一个 ExpandableListAdapter 对象即可,接下来 ExpandableListActivity 将会生成一个显示可展开列表的窗口。

public class ExpandableListActivityTest extends ExpandableListActivity {
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ExpandableListAdapter adapter = new BaseExpandableListAdapter() {
            int[] logos = new int[]
                    {
                            R.drawable.p,
                            R.drawable.z,
                            R.drawable.t
                    };
            private String[] armTypes = new String[]
                    {"神族兵种", "虫族兵种", "人族兵种"};
            private String[][] arms = new String[][]
                    {
                            {"狂战士", "龙骑士", "黑暗圣堂", "电兵"},
                            {"小狗", "刺蛇", "飞龙", "自爆飞机"},
                            {"机枪兵", "护士MM", "幽灵"}
                    };

            //获取指定组位置、指定子列表项处的子列表项数据
            @Override
            public Object getChild(int groupPosition, int childPosition) {
                return arms[groupPosition][childPosition];
            }

            @Override
            public long getChildId(int groupPosition, int childPosition) {
                return childPosition;
            }

            @Override
            public int getChildrenCount(int groupPosition) {
                return arms[groupPosition].length;
            }

            private TextView getTextView() {
                AbsListView.LayoutParams lp = new AbsListView.LayoutParams(
                        ViewGroup.LayoutParams.MATCH_PARENT, 64);
                TextView textView = new TextView(ExpandableListActivityTest.
                        this);
                textView.setLayoutParams(lp);
                textView.setGravity(Gravity.CENTER_VERTICAL | Gravity.LEFT);
                textView.setPadding(36, 0, 0, 0);
                textView.setTextSize(20);
                return textView;
            }

            //该方法决定每个子选项的外观
            @Override
            public View getChildView(int groupPosition, int childPosition,
                                     boolean isLastChild, View convertView, ViewGroup parent) {
                TextView textView = getTextView();
                textView.setText(getChild(groupPosition, childPosition).
                        toString());
                return textView;
            }

            //获取指定组位置处的组数据
            @Override
            public Object getGroup(int groupPosition) {
                return armTypes[groupPosition];
            }

            @Override
            public int getGroupCount() {
                return armTypes.length;
            }

            @Override
            public long getGroupId(int groupPosition) {
                return groupPosition;
            }

            //该方法决定每个组选项的外观
            @Override
            public View getGroupView(int groupPosition, boolean isExpanded,
                                     View convertView, ViewGroup parent) {
                LinearLayout ll = new LinearLayout(
                        ExpandableListActivityTest.this);
                ll.setOrientation(LinearLayout.HORIZONTAL);
                ImageView logo = new ImageView(
                        ExpandableListActivityTest.this);
                logo.setImageResource(logos[groupPosition]);
                ll.addView(logo);
                TextView textView = getTextView();
                textView.setText(getGroup(groupPosition).toString());
                ll.addView(textView);
                return ll;
            }

            @Override
            public boolean isChildSelectable(int groupPosition,
                                             int childPosition) {
                return true;
            }

            @Override
            public boolean hasStableIds() {
                return true;
            }
        };
        // 设置该窗口显示列表
        setListAdapter(adapter);
    }
}

配置 Activity

android 应用要求所有应用程序组件(Activity、Service、ContentProvider、BroadcastReceiver)都必须显式进行配置。

只要为< application…/ >元素添加< activity… >子元素即可配置 Activity。例如如下的配置片段:

".SampleActivity"
    android:icon="@drawable/small_pic.png"
    android:label="@string/freneticLabel"
    android:exported="true"
    android:launchMode="singleInstance"
    ...

从上面的配置片段可以看出,配置 Activity 时通常指定如下几个属性。

1. name:指定该 Activity 的实现类的类名。
2. icon:指定该 Activity 对应的图标。
3. label:指定该 Activity 的标签。
4. exported:指定该 Activity 是否允许被其他应用调用。如果将该属性设为 true,那么该 Activity 将可以被其他应用调用。
5. launchMode:指定该 Activity 的加载模式,该属性支持 standard、singleTop、singleTask 和 singleInstance 这 4 种加载模式。

除此之外,配置 Activity 时通常还需要指定一个或多个< intent-filter…/ >元素,该元素用于指定 Activity 可响应的 Intent。

为了在 AndroidManifest.xml 文件中配置、管理上面的三个 Activity,可以在清单文件的< application…/ >元素中增加如下三个< activity…/ >子元素。


<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="org.yonga.app" >

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <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>
        
        <activity android:name=".ExpandableListActivityTest"
            android:label="查看星际兵种">
        activity>
        <activity android:name=".PreferenceActivityTest"
            android:label="设置程序参数">
        activity>
    application>

manifest>

上面的配置片段配置了三个 Activity,其中第一个 Activity 还配置了一个< intent-filter…/>元素,该元素指定该 Activity 作为应用程序的入口。

启动、关闭 Activity

正如前面所介绍的,一个 android 应用通常都会包含多个 Activity,但只有一个 Activity 会作为程序的入口—-当该 android 应用运行时将会自动启动并执行该 Activity。至于应用中得其他 Activity,通常都由入口 Activity 启动,或由入口 Activity 启动的 Activity 启动。

Activity 启动其他 Activity 有如下两个方法。

1. startActivity(Intent intent):启动其他 Activity。
2. startActivityForResult(Intent intent,int requestCode):以指定的请求码(requestCode)启动 Activity,而且程序将会获取新启动的 Activity 返回的结果(通过重写 onActivityResult(…)方法来获取)。

启动 Activity 时可指定一个 requestCode 参数,该参数代表了启动 Activity 的请求码,这个请求码的值由开发者根据业务自行设置,用于标识请求来源。

上面两个方法都用到了 Intent 参数,Intent 是 android 应用里各组件之间通信的重要方式。一个 Activity 通过 Intent 来表达自己“意图”—-想要启动哪个组件,被启动的组件即可是 Activity 组件,也可是 Service 组件。

android 为关闭 Activity 准备了如下两个方法。

1. finish():结束当前 Activity。
2. finishActivity(int requestCode):结束以 startActivityForResult(Intent intent,int requestCode)方法启动的 Activity。

public class MainActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        // 获取应用程序中的bn按钮
        Button bn = (Button) findViewById(R.id.bn);
        // 为bn按钮绑定事件监听器
        bn.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View source) {
                // 创建需要启动的Activity对应的Intent
                Intent intent = new Intent(MainActivity.this,
                        SecondActivity.class);
                // 启动intent对应的Activity
                startActivity(intent);
            }
        });
    }
}
public class SecondActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.second);
        // 获取应用程序中的previous按钮
        Button previous = (Button) findViewById(R.id.previous);
        // 获取应用程序中的close按钮
        Button close = (Button) findViewById(R.id.close);
        // 为previous按钮绑定事件监听器
        previous.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View source) {
                // 获取启动当前Activity的上一个Intent
                Intent intent = new Intent(SecondActivity.this,
                        MainActivity.class);
                // 启动intent对应的Activity
                startActivity(intent);
            }
        });
        // 为close按钮绑定事件监听器
        close.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View source) {
                // 获取启动当前Activity的上一个Intent
                Intent intent = new Intent(SecondActivity.this,
                        MainActivity.class);
                // 启动intent对应的Activity
                startActivity(intent);
                // 结束当前Activity
                finish();
            }
        });
    }
}

使用 Bundle 在 Activity 之间交换数据

当一个 Activity 启动另一个 Activity 时,常常会有一些数据需要传过去。因为两个 Activity 之间本来就有一个 Intent,因此我们主要将需要交换的数据放入 Intent 中即可。

Intent 提供了多个重载的方法来“携带”额外的数据。

1. putExtras(Bundle data):向 Intent 中放下需要“携带”的数据包。
2. Bundle getExtras():取出 Intent 中所“携带”的数据包。
3. putExtra(String key, Xxx value):向 Intent 中按 key-value 对的形式存入数据。
4. getXxxExtra(String key):从 Intent 中按 key 取出指定类型的数据。

上面方法中的 Bundle 就是一个简单地数据携带包,该 Bundle 对象包含了多个方法来存入数据。

1. putXxx(String key, Xxx value):向 Bundle 中放入 Int、Long 等各种类型的数据。
2. putSerializable(String key, Serializable value):向 Bundle 中放入一个可序列化的对象。
3. putParcelable(String key, Parcelable value):向 Bundle 中放入一个可序列化的对象。

为了取出 Bundle 数据携带暴力的数据,Bundle 提供了如下方法。

1. getXxx(String key):从 Bundle 中取出 Int、Long 等各种类型的数据。
2. getSerializable(String key):从 Bundel 中取出一个可序列化的对象。
3. getParcelable(String key):从 Bundel 中取出一个可序列化的对象。

从上面的介绍不难看出,Intent 主要通过 Bundle 对象来携带数据,因此 Intent 提供了 putExtras()和 getExtras()两个方法。除此之外,Intent 也提供了多个重载的 putExtra(String key,Xxx value)、getXxxExtra(String key)。其实 Intent 提供的 putExtra(String key,Xxx value)、getXxxExtra(String key)方法,只是两个便捷的方法,这些方法依然是存取 Intent 所携带的 Bundle 中的数据。

温馨提示

Intent 的 putExtra(String key, XXxx value)方法是“智能”的,当程序调用 Intent 的 putExtra(String key,Xxx value)方法向 Intent 中存入数据时,如果该 Intent 中已经携带了 Bundle 对象,则该方法直接向 Intent 所携带的 Bundle 中存入数据;如果 Intent 还没有携带 Bundle 对象,putExtra(String key,Xxx value)方法会优先为 Intent 创建一个 Bundle,再向 Bundle 中存入数据。

实例:用第二个 Activity 处理注册信息


<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="请输入您的注册信息"
        android:textSize="20sp"/>
    <TableRow>
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="用户名:"
            android:textSize="16sp"/>
        
        <EditText
            android:id="@+id/name"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="请填写想注册的账号"
            android:selectAllOnFocus="true"/>
    TableRow>
    <TableRow>
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="密码:"
            android:textSize="16sp"/>
        
        <EditText
            android:id="@+id/passwd"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:password="true"
            android:selectAllOnFocus="true"/>
    TableRow>
    <TableRow>
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="性别:"
            android:textSize="16sp"/>
        
        <RadioGroup
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">
            <RadioButton
                android:id="@+id/male"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="男"
                android:textSize="16sp"/>
            <RadioButton
                android:id="@+id/female"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="女"
                android:textSize="16sp"/>
        RadioGroup>
    TableRow>
    <Button
        android:id="@+id/bn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="注册"
        android:textSize="16sp"/>
TableLayout>
public class MainActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        Button bn = (Button) findViewById(R.id.bn);
        bn.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                EditText name = (EditText) findViewById(R.id.name);
                EditText passwd = (EditText) findViewById(R.id.passwd);
                RadioButton male = (RadioButton) findViewById(R.id.male);
                String gender = male.isChecked() ? "男 " : "女";
                Person p = new Person(name.getText().toString(), passwd
                        .getText().toString(), gender);
                // 创建一个Bundle对象
                Bundle data = new Bundle();
                data.putSerializable("person", p);
                // 创建一个Intent
                Intent intent = new Intent(MainActivity.this,
                        ResultActivity.class);
                intent.putExtras(data);
                // 启动intent对应的Activity
                startActivity(intent);
            }
        });
    }
}

下面是 ResultActivity 的界面布局文件。


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    
    <TextView
        android:id="@+id/name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="18sp"/>
    <TextView
        android:id="@+id/passwd"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="18sp"/>
    <TextView
        android:id="@+id/gender"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="18sp"/>
LinearLayout>
public class ResultActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.result);
        TextView name = (TextView) findViewById(R.id.name);
        TextView passwd = (TextView) findViewById(R.id.passwd);
        TextView gender = (TextView) findViewById(R.id.gender);
        // 获取启动该Activity的Intent
        Intent intent = getIntent();
        // 直接通过Intent取出它所携带的Bundle数据包中的数据
        Person p = (Person) intent.getSerializableExtra("person");
        name.setText("您的用户名为:" + p.getName());
        passwd.setText("您的密码为:" + p.getPasswd());
        gender.setText("您的性别为:" + p.getGender());
    }
}
public class Person implements Serializable {
    private Integer id;
    private String name;
    private String passwd;
    private String gender;

    public Person(String name, String passwd, String gender) {
        this.name = name;
        this.passwd = passwd;
        this.gender = gender;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public String getPasswd() {
        return passwd;
    }

    public void setPasswd(String passwd) {
        this.passwd = passwd;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }
}

启动其他 Activity 并返回结果

Activity 还提供了一个 startActivityForResult(Intent intent,int requestCode)方法来启动其他 Activity。该方法用于启动指定 Activity,而且期望获取指定 Activity 返回的结果。

为了获取被启动的 Activity 所返回的结果,需要从两个方面着手。

1. 当前 Activity 需要重写 onActivityResult(int requestCode, int resultCode, Intent intent),当被启动的 Activity 返回结果时,该方法将会被触发,其中 requestCode 代表请求码,而 resultCode 代表 Activity 返回的结果码,这个结果码也是由开发者根据业务自行设定的。
2. 被启动的 Activity 需要调用 setResult()方法设置处理结果。

实例:用第二个 Activity 让用户选择信息

public class MainActivity extends Activity {
    Button bn;
    EditText city;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        // 获取界面上的组件
        bn = (Button) findViewById(R.id.bn);
        city = (EditText) findViewById(R.id.city);
        // 为按钮绑定事件监听器
        bn.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View source) {
                // 创建需要对应于目标Activity的Intent
                Intent intent = new Intent(MainActivity.this,
                        SelectCityActivity.class);
                // 启动指定Activity并等待返回的结果,其中0是请求码,用于标识该请求
                startActivityForResult(intent, 0);
            }
        });
    }

    // 重写该方法,该方法以回调的方式来获取指定Activity返回的结果
    @Override
    public void onActivityResult(int requestCode
            , int resultCode, Intent intent) {
        // 当requestCode、resultCode同时为0时,也就是处理特定的结果
        if (requestCode == 0 && resultCode == 0) {
            // 取出Intent里的Extras数据
            Bundle data = intent.getExtras();
            // 取出Bundle中的数据
            String resultCity = data.getString("city");
            // 修改city文本框的内容
            city.setText(resultCity);
        }
    }
}

为了让当前 Activity 获取 SelectCityActivity 所返回的结果,开发者应该重写 onActivityResult()方法—-当被启动的 SelectCityActivity 返回结果时,onActivityResult()方法将会被调用。

因此还需要为上面 ActivityForResult 添加如下方法。

// 重写该方法,该方法以回调的方式来获取指定Activity返回的结果
    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent intent) {
        // 当requestCode、resultCode同时为0时,也就是处理特定的结果
        if (requestCode == 0 && resultCode == 0) {
            // 取出Intent里的Extras数据
            Bundle data = intent.getExtras();
            // 取出Bundle中的数据
            String resultCity = data.getString("city");
            // 修改city文本框的内容
            city.setText(resultCity);
        }
    }
public class SelectCityActivity extends ExpandableListActivity {
    // 定义省份数组
    private String[] provinces = new String[]
            {"广东", "广西", "湖南"};
    private String[][] cities = new String[][]
            {
                    {"广州", "深圳", "珠海", "中山"},
                    {"桂林", "柳州", "南宁", "北海"},
                    {"长沙", "岳阳", "衡阳", "株洲"}
            };

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ExpandableListAdapter adapter = new BaseExpandableListAdapter() {
            // 获取指定组位置、指定子列表项处的子列表项数据
            @Override
            public Object getChild(int groupPosition, int childPosition) {
                return cities[groupPosition][childPosition];
            }

            @Override
            public long getChildId(int groupPosition, int childPosition) {
                return childPosition;
            }

            @Override
            public int getChildrenCount(int groupPosition) {
                return cities[groupPosition].length;
            }

            private TextView getTextView() {
                AbsListView.LayoutParams lp = new AbsListView.LayoutParams(
                        ViewGroup.LayoutParams.MATCH_PARENT, 64);
                TextView textView = new TextView(SelectCityActivity.this);
                textView.setLayoutParams(lp);
                textView.setGravity(Gravity.CENTER_VERTICAL | Gravity.LEFT);
                textView.setPadding(36, 0, 0, 0);
                textView.setTextSize(20);
                return textView;
            }

            // 该方法决定每个子选项的外观
            @Override
            public View getChildView(int groupPosition, int childPosition,
                                     boolean isLastChild, View convertView, ViewGroup parent) {
                TextView textView = getTextView();
                textView.setText(getChild(groupPosition, childPosition)
                        .toString());
                return textView;
            }

            // 获取指定组位置处的组数据
            @Override
            public Object getGroup(int groupPosition) {
                return provinces[groupPosition];
            }

            @Override
            public int getGroupCount() {
                return provinces.length;
            }

            @Override
            public long getGroupId(int groupPosition) {
                return groupPosition;
            }

            // 该方法决定每个组选项的外观
            @Override
            public View getGroupView(int groupPosition, boolean isExpanded,
                                     View convertView, ViewGroup parent) {
                LinearLayout ll = new LinearLayout(SelectCityActivity.this);
                ll.setOrientation(LinearLayout.HORIZONTAL);
                ImageView logo = new ImageView(SelectCityActivity.this);
                ll.addView(logo);
                TextView textView = getTextView();
                textView.setText(getGroup(groupPosition).toString());
                ll.addView(textView);
                return ll;
            }

            @Override
            public boolean isChildSelectable(int groupPosition,
                                             int childPosition) {
                return true;
            }

            @Override
            public boolean hasStableIds() {
                return true;
            }
        };
        // 设置该窗口显示列表
        setListAdapter(adapter);
        getExpandableListView().setOnChildClickListener(
                new OnChildClickListener() {
                    @Override
                    public boolean onChildClick(ExpandableListView parent,
                                                View source, int groupPosition, int childPosition,
                                                long id) {
                        // 获取启动该Activity之前的Activity对应的Intent
                        Intent intent = getIntent();
                        intent.putExtra("city",
                                cities[groupPosition][childPosition]);
                        // 设置该SelectCityActivity的结果码,并设置结束之后退回的Activity
                        SelectCityActivity.this.setResult(0, intent);
                        // 结束SelectCityActivity。
                        SelectCityActivity.this.finish();
                        return false;
                    }
                });
    }
}

Activity 的回调机制

所谓回调,在实现具有通用性质的应用架构时非常常见:对于一个具有通用性质的程序架构来说,程序架构完成整个应用的通用功能、流程,但在某个特定的点上,需要一段业务相关的代码—-通用的程序架构无法实现这段代码,那么程序架构会在这个点上留一个“空”。

对于 Java 程序来说,程序架构在某个点上留的“空”,可以以如下两种方式存在。

1. 以接口形式存在:该接口由开发者实现,实现该接口时将会实现该接口的方法,那么通用的程序架构就会调用该方法来完成业务相关的处理。
2. 以抽象方法(也可以是非抽象方法)的形式存在:这就是 Activity 的实现形式。在这些特定的点上方法已经被定义了,如 onCreate、onActivityResult 等方法,开发者可以由选择性地重写这些方法,通用的程序架构就会回调该方法来完成业务相关处理。

前面介绍的事件处理也用到了回调机制:当开发者开发一个组件时,如果开发者需要该组件能响应特定的事件,则可以有选择性地实现该组件的特定方法—-当用户在该组件上激发某个事件时,该组件上特定的方法就会回调。

Activity 的回调机制也于此类似,当 Activity 被部署在 Android 应用中之后,随着应用程序的运行,Activity 会不断地在不同的状态之间切换,该 Activity 中特定的方法就会被回调—-开发者就可以有选择性地重写这些方法来加入业务相关处理。

Activity 的生命周期与加载模式

当 Activity 处于 android 应用中运行时,它的活动状态由 android 以 Activity 栈的形式管理,当前活动 Activity 位于栈顶。随着不同应用的运行,每个 Activity 都有可能从活动状态转入非活动状态,也可能从非活动状态转入活动状态。

Activity 的生命周期演示

归纳起来,Activity 大致会经过如下 4 种状态。

  1. 运行状态:当前 Activity 位于前台,用户可见,可以获得焦点。
  2. 暂停状态:当前 Activity 位于前台,该 Activity 依然可见,只是不能获得焦点。
  3. 停止状态:该 Activity 不可见,失去焦点。
  4. 销毁状态:该 Activity 结束,或 Activity 所在的进程被结束。

在 Activity 的生命周期中,如下方法会被系统回调。

  1. onCreate(Bundle savedStatus):创建 Activity 时被回调。该方法只会被调用一次。
  2. onStart():启动 Activity 时被回调。
  3. onRestart():重新启动 Activity 时被回调。
  4. onResume():恢复 Activity 时被回调。在 onStart()方法后一定会回调 onResume()方法。
  5. onPause():暂停 Activity 时被回调。
  6. onStop():停止 Activity 时被回调。
  7. onDestroy():销毁 Activity 时被回调。该方法只会被调用一次。
public class MainActivity extends Activity {
    final String TAG = "--yonga--";
    Button finish, startActivity;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        // 输出日志
        Log.d(TAG, "-------onCreate------");
        finish = (Button) findViewById(R.id.finish);
        startActivity = (Button) findViewById(R.id.startActivity);
        // 为startActivity按钮绑定事件监听器,启动对话框风格的 Activity
        startActivity.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View source) {
                Intent intent = new Intent(MainActivity.this
                        , SecondActivity.class);
                startActivity(intent);
            }
        });
        // 为finish按钮绑定事件监听器
        finish.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View source) {
                // 结束该Activity
                MainActivity.this.finish();
            }
        });
    }

    @Override
    public void onStart() {
        super.onStart();
        // 输出日志
        Log.d(TAG, "-------onStart------");
    }

    @Override
    public void onRestart() {
        super.onRestart();
        // 输出日志
        Log.d(TAG, "-------onRestart------");
    }

    @Override
    public void onResume() {
        super.onResume();
        // 输出日志
        Log.d(TAG, "-------onResume------");
    }

    @Override
    public void onPause() {
        super.onPause();
        // 输出日志
        Log.d(TAG, "-------onPause------");
    }

    @Override
    public void onStop() {
        super.onStop();
        // 输出日志
        Log.d(TAG, "-------onStop------");
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        // 输出日志
        Log.d(TAG, "-------onDestroy------");
    }
}

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="org.yonga.app" >

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <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>
        <activity
            android:name=".SecondActivity"
            android:label="对话框风格的Activity"
            android:theme="@android:style/Theme.Material.Dialog">
        activity>
    application>

manifest>

将该 Activity 设置成程序的入口 Activity,当程序启动时将会自动启动并执行该 Activity,此时将可以在 Android Studio 的 LogCat 窗口看到如图所示的输出。

【android_温故知新】深入理解 Activity_第1张图片

单击该程序界面上的“启动对话框风格的 Activity ”按钮,对话框风格的 Activity 进入前台,虽然 MainActivity 不能获得焦点,但依然“部分可见”,此时该 Activity 进入“暂停”状态。此时将可以在 Android Studio 的 LogCat 窗口看到如图所示的输出。

onPause

在当前状态下,按下模拟器右边的返回键,返回 Lifecycle Activity,该 Activity 再次进入“运行”状态,此时看到 LogCat 窗口有如图所示输出。

onResume

在当前程序运行状态下,返回系统桌面,当前该 Activity 将失去焦点且不可见。但该 Activity 并未被销毁,它进入“停止”状态,此时看到 LogCat 窗口有如图所示的输出。

onStop

在模拟器程序列表处再次找到该应用程序并启动它,将可以看到 LogCat 窗口有如图所示的输出。

【android_温故知新】深入理解 Activity_第2张图片

如果用户单击该程序界面上的“退出”按钮,该 Activity 将会结束自己,并且可以在 LogCat 窗口看到如图所示的输出。

【android_温故知新】深入理解 Activity_第3张图片

Activity 的 4 种加载模式

配置 Activity 时可指定 android:launchMode 属性,该属性用于配置该 Activity 的加载模式。该属性支持如下 4 个属性值。

  1. standard:标准模式,这是默认的加载模式。
  2. singleTop:Task 栈顶单例模式。
  3. singleTask:Task 内单例模式。
  4. singleInstance:全局单例模式。

android 采用 Task 来管理多个 Activity,当我们启动一个应用时,android 就会为之创建一个 Task,然后启动这个应用的入口 Activity(即< intent-filter…/ >中配置为 MAIN 和 LAUNCHER 的 Activity)。

因为 android 并没有为 Task 提供 API,因此开发者无法真正去访问 Task,只能调用 Activity 的 getTaskId()方法来获取它所在的 Task 的ID,事实上,我们可以把 Task 理解成 Activity 栈,Task 以栈的形式来管理 Activity:先启动的 Activity 被放在 Task 栈底,后启动的 Activity 被放在 Task 栈顶。

那么 Activity 的加载模式,就负责管理实例化、加载 Activity 的方式,并可以控制 Activity 与 Task 之间在加载关系。

1. standard 模式
每次通过这种模式来启动目标 Activity 时,android 总会为目标 Activity 创建一个新的实例,并将该 Activity 添加到当前 Task 栈中—-这种模式不会启动新的 Task,新的 Activity 将被添加到原有的 Task 中。

public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        LinearLayout layout = new LinearLayout(this);
        layout.setOrientation(LinearLayout.VERTICAL);
        this.setContentView(layout);
        // 创建一个TextView来显示该Activity和它所在Task ID
        TextView tv = new TextView(this);
        tv.setText("Activity为:" + this.toString()
                + "\n" + ",Task ID为:" + this.getTaskId());
        Button button = new Button(this);
        button.setText("启动MainActivity");
        // 添加TextView和Button
        layout.addView(tv);
        layout.addView(button);
        // 为button添加事件监听器,当单击该按钮时启动MainActivity
        button.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                // 创建启动MainActivity的Intent
                Intent intent = new Intent(MainActivity.this
                        , MainActivity.class);
                startActivity(intent);
            }
        });
    }
}

运行该程序,多次单击程序界面上的“启动 MainActivity”按钮,程序将会不断启动新的 MainActivity 实例(不同 Activity 实例的 hashCode 值有差异),但它们所在的 Task ID
总是相同的—-这表明这种加载模式不会使用全新的 Task。

当用户单击手机的“返回”键时,系统将会“逐一”从 Activity 栈顶删除 Activity 实例。

2. singleTop 模式

这种模式与 standard 模式基本相似,但是有一点不同:当将要启动的目标 Activity 已经位于 Task 栈顶时,系统不会重新创建目标 Activity 的实例,而是直接复用已有的 Activity 实例。

如果将上面实例中 MainActivity 的加载模式改为 singlerTop,那么无论用户单击多少次按钮,界面上的程序都不会有任何变化。

如果将要启动的目标 Activity 没有位于 Task 栈顶,此时系统会重新创建目标 Activity 的实例,并将它加载到 Task 栈顶—-此时与 standard 模式完全相同。

3. singleTask 模式

采用这种加载模式的 Activity 在同一个 Task 内只有一个实例,当系统采用 singleTask 模式启动目标 Activity 时,可分为如下三种情况。

  1. 如果将要启动的目标 Activity 不存在,系统将会创建目标 Activity 的实例,并将它加入 Task 栈顶。
  2. 如果将要启动的目标 Activity 已经位于 Task 栈顶,此时与 singleTop 模式的行为相同。
  3. 如果将要启动的目标 Activity 已经存在、但没有位于 Task 栈顶,系统将会把位于该 Activity 上面的所有 Activity 移出 Task 栈,从而使得目标 Activity 转入栈顶。

下面的实例示范了上面第三种情况。该实例包含两个 Activity,其中第一个 Activity 上显示文本框和按钮,按钮启动第二个 Activity;第二个 Activity 上显示文本框和按钮,按钮启动第一个 Activity。

第一个 Activity 代码如下。

public class SingleTaskTest extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        LinearLayout layout = new LinearLayout(this);
        layout.setOrientation(LinearLayout.VERTICAL);
        this.setContentView(layout);
        // 创建一个TextView来显示该Activity和它所在Task ID
        TextView tv = new TextView(this);
        tv.setText("Activity为:" + this.toString()
                + "\n" + ",Task ID为:" + this.getTaskId());
        Button button = new Button(this);
        button.setText("启动SecondActivity");
        layout.addView(tv);
        layout.addView(button);
        // 为button添加事件监听器,当单击该按钮时启动SecondActivity
        button.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(SingleTaskTest.this
                        , SecondActivity.class);
                startActivity(intent);
            }
        });
    }
}
public class SecondActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        LinearLayout layout = new LinearLayout(this);
        setContentView(layout);
        // 创建一个TextView来显示该Activity和它所在Task ID
        TextView tv = new TextView(this);
        tv.setText("Activity为:" + this.toString()
                + "\n" + ",Task ID为:" + this.getTaskId());
        Button button = new Button(this);
        button.setText("启动SingleTaskTest");
        // 添加TextView和Button
        layout.addView(button);
        layout.addView(tv);
        // 为button添加事件监听器,当单击该按钮时启动SingleTaskTest
        button.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(SecondActivity.this,
                        SingleTaskTest.class);
                startActivity(intent);
            }
        });
    }
}

【android_温故知新】深入理解 Activity_第4张图片
【android_温故知新】深入理解 Activity_第5张图片

  1. singleInstance 模式
    【android_温故知新】深入理解 Activity_第6张图片
public class SingleInstanceTest extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        LinearLayout layout = new LinearLayout(this);
        layout.setOrientation(LinearLayout.VERTICAL);
        this.setContentView(layout);
        // 创建一个TextView来显示该Activity和它所在Task ID
        TextView tv = new TextView(this);
        tv.setText("Activity为:" + this.toString()
                + "\n" + ",Task ID为:" + this.getTaskId());
        Button button = new Button(this);
        button.setText("启动SecondActivity");
        layout.addView(tv);
        layout.addView(button);
        // 为button添加事件监听器,当单击该按钮时启动SecondActivity
        button.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(SingleInstanceTest.this
                        , SecondActivity.class);
                startActivity(intent);
            }
        });
    }
}

此处输入图片的描述

配置该 Activity 的配置片段如下:


<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="org.yonga.app" >

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".SingleInstanceTest"
            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=".SecondActivity"
                  android:label="@string/second"
                  android:exported="true"
                  android:launchMode="singleInstance">
            <intent-filter>
                
                <action android:name="org.yonga.intent.action.YONGA_ACTION" />
                <category android:name="android.intent.category.DEFAULT" />
            intent-filter>
        activity>

    application>

manifest>

【android_温故知新】深入理解 Activity_第7张图片

public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        LinearLayout layout = new LinearLayout(this);
        layout.setOrientation(LinearLayout.VERTICAL);
        this.setContentView(layout);
        // 创建一个TextView来显示该Activity和它所在Task ID
        TextView tv = new TextView(this);
        tv.setText("Activity为:" + this.toString()
                + "\n" + ",Task ID为:" + this.getTaskId());
        Button button = new Button(this);
        button.setText("启动SecondActivity");
        // 添加TextView和Button
        layout.addView(tv);
        layout.addView(button);
        // 为button添加事件监听器,使用隐式Intent启动目标Activity
        button.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                // 使用隐式Intent启动Second Activity
                Intent intent = new Intent();
                intent.setAction("org.yonga.intent.action.YONGA_ACTION");
                startActivity(intent);
            }
        });
    }
}

【android_温故知新】深入理解 Activity_第8张图片

你可能感兴趣的:(Android,基础笔记)