Android笔记—Activity


  • 资料来源如下
  • 第一行代码(第二版)

Activity基础

Activity定义

  • Activity 是Android四大组件之一,用户可与其提供的屏幕进行交互,以执行操作。 每个 Activity 都会获得一个用于绘制其用户界面的窗口。窗口可以充满屏幕,也可浮动。应用通常由多个彼此联系的 Activity 组成。应用中的某个 Activity 为“主”Activity,即首次启动应用时的Activity。

创建Activity

  • 新建FirstAtivity继承自AppCompatActivity

    1
    2
    3
    4
    5
    public class FirstActivity extends AppCompatActivity{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    } }
  • 创建加载布局
    1.jpg

  • 切换到first-layout

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
      <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <Button
    android:id="@+id/button_1"
    android:text="button_1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />
    </LinearLayout>
  • 在FirestActivity中加载layout
    在onCreate中加入

    1
    setContentView(R.layout.first_layout);
  • 在AndroidMainfest文件中注册。

    1
    2
    3
    4
    5
    6
    7
    8
    <activity
    android:name=".FirstActivity"
    android:label="This is FirstActivity">
    <intent-filter>
    <action android:name="android.intent.action.MAIN"/>
    <category android:name="android.intent.category.LAUNCHER"/>
    </intent-filter>
    </activity>
  • 在模拟器中启动apk
    5ea429.jpg

使用Toast

Toast google官方说明

  • 推送一个短小信息推送给用户
    如图(摘自android developer)
    toast.png

  • 使用方法

    1
    2
    Toast.makeText(context, text, duration).show();

    举例

    1
    2
    Toast.makeText(FirstActivity.this, "you clicked button 1", Toast.LENGTH_SHORT).show();
    /* activity.this 消息内容 显示时间设置 */

使用Menu

  • 创建菜单

    • 在res下新疆menu文件夹,右击menu文件夹—new—Menu resource file,创建main的菜单文件。
    • main.xml中添加如下代码
      1
      2
      3
      4
      5
      6
      7
      8
      9
       <?xml version="1.0" encoding="utf-8"?>
      <menu xmlns:android="http://schemas.android.com/apk/res/android">
      <item
      android:id="@+id/add_item"
      android:title="Add"/>
      <item
      android:id="@+id/remove_item"
      android:title="Remove"/>
      </menu>
      这里创建了两个菜单项 Add和Remove
    • 在FirestActivity中重写 onCreateOptionsMenu()方法(快捷键 Ctrl+O)
      1
      2
      3
      4
      5
        @Override
      public boolean onCreateOptionsMenu(Menu menu) {
      getMenuInflater().inflate(R.menu.main, menu);
      return true;
      }
      getMenuInflater()可以得到MenuInflater对象,再调用.inflate就可以创建菜单。.inflat接受俩个参数,一是资源文件名,二是菜单项添加至那个对象中。onCreateOptionsMenu方法中返回true表示创建菜单并显示。
    • 效果如下。
      Screenshot_1481113497.png
  • 创建菜单点击响应事件

    • 重写onOptionsItemSelected方法
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      @Override
      public boolean onOptionsItemSelected(MenuItem item) {
      switch (item.getItemId()) {
      case R.id.add_item:
      Toast.makeText(this, "Add", Toast.LENGTH_SHORT).show();
      break;
      case R.id.remove_item:
      Toast.makeText(this, "Remove", Toast.LENGTH_SHORT).show();
      break;
      default:
      }
      return true;
      }
      通过item.getItemId()判断点击选项,弹出不同的Toast

向一个activity 并传递字符串

  • 构建一个Intent
    Intent intent = new Intent(this, DisplayMessageActivity.class);
    构造方法有两个参数:
    Context 是第一个参数,这里使用了this是因为Activity是Context的子类。
    Class 类是系统将要分发的APP组件,在这里,这个Activity将会被启动。
  • ```java
    public void sendMessage(View view) {
      // 创建一个新的intent对象,绑定DisplayMessageActivity
      Intent intent = new Intent(this, DisplayMessageActivity.class);
      //创建一个editText对象,绑定xml中editText
      EditText editText = (EditText) findViewById(R.id.edit_message);
      //获取editText中输入文字,转成字符串
      String message = editText.getText().toString();
      //一个Intent对象可以携带被称为extras的键值对。
      // putExtra()方法将键放在第一个参数中,将值放在第二个参数中。
      intent.putExtra(EXTRA_MESSAGE, message);
      //启动intent对应Activity
      startActivity(intent);
      }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22

    * ```java
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_display_message);

    //创建本地intent
    Intent intent = getIntent();
    //取出String类型数据,如果是Int类型就是getIntExtra,Boolean类型就是getBooleanExtra
    String message = intent.getStringExtra(MainActivity.EXTRA_MESSAGE);
    //显示String
    //创建新的TextView
    TextView textView = new TextView(this);
    //设置文本大小
    textView.setTextSize(40);
    //设置显示内容
    textView.setText(message);
    //绑定xml
    ViewGroup layout = (ViewGroup) findViewById(R.id.activity_display_message);
    //ViewGroup中添加TextView
    layout.addView(textView);
    }

返回数据给上一个Activity

  • 构建Intent使用startActivityForResult()方法传入请求码,启动下一个Activity
    在下一个Activity中构建Intent,intent.putExtra存入键值-key,调用setResult()方法,传入finish()结束掉Activity
    重写第一个Activity中的onActivityResult()方法,调用.getStringExtra取出key对应键值。

  • startActivityForResult(Intent, int Bundle) Intent与单纯启动Activity的Intent相同,第二个是请求码,下一级 回调提供相同的请求码,以便您应用可以正确识别结果。

    1
    2
    Intent intent = new Intent(FirestActivity.this,ScendActivity.class);
    startActivityForResult(intent,1);
  • setResult()方法,第一个参数向上级方法处理结果,一般使用RESULT_OK或RESULT_CANCELED,第二个参数 对应Intent

    1
    2
    3
    4
    5
    6
    7
    //新建显示Intent
    Intent intent = new Intent();
    //存入key-键值
    intent.putExtra("data_return","Hello Firest");
    setResult(RESULT_OK,intent);
    //结束Activity
    finish();
  • onActivityResult()三个参数.第一个startActivityForResult() 传递的请求代码。第二个 Activity 指定的结果代码。成功是 RESULT_OK;失败,则是 RESULT_CANCELED。第三个是传送结果数据的 Intent。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     @Override
    protected void onActivityResult(int requestCode,int resultCode,Intent data){
    //选择不同请求码对应处理逻辑
    switch(requestCode){
    case 1:
    //处理结果时候ok
    if(resultCode == RESULT_OK){
    //取出数据
    String returnedData = data.getStringExtra("data_return");
    Log.d("FirstActivity", returnedData);
    }
    }
    }

Activity生命周期

basic-lifecycle.png

  • Activity 3种状态
    • Resumed/继续
      Activity 处于前台,且用户可以与其交互
    • Paused/暂停
      Activity 被在前台中处于另一个 Activity—部分阻挡。 暂停的 Activity 不会接收用户输入并且无法执行任何代码。
    • Stopped/停止
      Activity完全隐藏,对用户完全不可见.当停止时,activity的所有状态信息比如成员变量都会被保留,但是不能再执行任何代码

启动一个Activity

  • onCreate方法
  • 主Activity:
    • 用户点击app图标,启动主Activity
    • 在AndroidManifest.xml中声明
      1
      2
      3
      4
      <intent-filter>
      <action android:name="android.intent.action.MAIN" />
      <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
  • 通过调用onCreate方法创建一个Activity实例,onCreate方法在Activity的整个生命周期中只执行一次。
  • 之后系统会很快的调用onStart和onResume方法,Activity进入Resumed/继续模式。直到被其他activity覆盖/屏幕关闭。

    销毁Activity

  • onDestory方法
    大多数的APP不需要实现这个方法,因为本地类引用会随着Activity一起总结,不过Activity的清理工作应该放在onPause下或者onStop。
  • Note:
    在所有的情况下系统调用onDestory方法之后已经调用过onPause方法与onStop方法,不过有一个例外情况:你在onCreate方法中调用了finish方法。在一些例子中,当你的Activity临时决定要启动另一个Activity,你可能要在onCreate方法内调用finish方法来销毁这个Activity,在这种情况下,系统会立即调用onDestory方法,而不会调用其它任何生命周期方法。

暂停Activity

  • onPause()方法
  • Activity被其他Activity覆盖/失去用户焦点,系统调用onPause()方法,Activity 进入暂停状态。
    • note:android7.0及以上版本加入了多窗口模式,当Activity失去用户焦点时,可能处于多窗口模式。
  • onPause() 常用回调:
    • 检查 Activity 是否可见。不可见则停止可能消耗 CPU 的操作
    • 提交未保存的更改,仅保存用户离开时希望永久性保存此类更改(比如电子邮件草稿)。
    • 释放系统资源,GPS/Camer等
    • 示例 (释放Camer)
      1
      2
      3
      4
      5
      6
      7
      8
      9
      public void onPause() {
      super.onPause(); // Always call the superclass method first

      // Release the Camera because we don't need it when paused
      // and other activities might need to use it.
      if (mCamera != null) {
      mCamera.release();
      mCamera = null;
      }
    • 注意事项:
      • 在onPause()一般不执行永久性存储用户更改,不执行 CPU 密集型工作,这些工作一般放在onStop() 。

继续 Activity

  • onResume() 方法
  • 暂停状态回到继续状态,Activity第一次启动时也会调用这个方法。
  • onResume() 以初始化在 onPause() 期间释放的组件。
  • 示例(重新获取Camera)
    1
    2
    3
    4
    5
    6
    7
    public void onResume() {
    super.onResume(); // Always call the superclass method first

    // Get the Camera instance as the activity achieves full user focus
    if (mCamera == null) {
    initializeCamera(); // Local method to handle camera init
    }

停止 Activity

  • note:
    大多数相对简单的 Activity 而言,系统在 Activity 停止时会将Activity 实例保留在系统内存中,无需实现 onStop() 和 onRestart() 或甚至onStart() 方法。可能只需使用 onPause() 暂停正在进行的操作,并从系统资源断开连接。
  • onStop() 方法

  • 场景:

    • 用户在最近应用切换到另一个应用
    • 应用中执行开始新 Activity 的操作
    • Activity使用时,接打电话
  • 调用时,Activity不再可见,释放几乎所有用户不使用时不需要的资源。如果系统内存紧张,则可能销毁内存中的Acitivity实例。

  • onStop() 方法调用后,Activity不再可见,极端情况下,系统可能会仅终止应用进程,而不调用 onDestroy() ,因此需要使用 onStop() 释放几乎所有用户不使用时不需要的资源。

  • 尽管 onPause() 方法在 onStop()之前调用,在onStop() 执行更大、占用更多 CPU 的关闭操作,比如向数据库写入信息

  • 示例(草稿笔记内容保存在永久存储)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
      protected void onStop() {
    super.onStop(); // Always call the superclass method first

    // Save the note's current draft, because the activity is stopping
    // and we want to be sure the current note progress isn't lost.
    ContentValues values = new ContentValues();
    values.put(NotePad.Notes.COLUMN_NAME_NOTE, getCurrentNoteText());
    values.put(NotePad.Notes.COLUMN_NAME_TITLE, getCurrentNoteTitle());

    getContentResolver().update(
    mUri, // The URI for the note to update.
    values, // The map of column names and new values to apply to them.
    null, // No SELECT criteria are used.
    null // No WHERE columns are used.
    );
    }

启动/重启 Activity

  • onStart() 方法
  • Activity 停止转换为继续状态时,系统回调onRestart() 方法+ onStart() 方法.onStop() 方法清理了所有 Activity 的资源,重启 Activity 需要重新实例化它们。同时 Activity 初次创建时重新实例化它们。 出于此,经常使用 onStart() 方法作为 onStop() 方法的对应
  • 示例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
     @Override
    protected void onStart() {
    super.onStart(); // Always call the superclass method first

    // The activity is either being restarted or started for the first time
    // so this is where we should make sure that GPS is enabled
    LocationManager locationManager =
    (LocationManager) getSystemService(Context.LOCATION_SERVICE);
    boolean gpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);

    if (!gpsEnabled) {
    // Create a dialog here that requests the user to enable GPS, and use an intent
    // with the android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS action
    // to take the user to the Settings screen to enable GPS when they click "OK"
    }
    }

    @Override
    protected void onRestart() {
    super.onRestart(); // Always call the superclass method first

    // Activity being restarted from stopped state
    }

    保存 Activity 状态

  • onSaveInstanceState()方法
  • 默认情况下,Activity 实例被销毁时系统会使用 Bundle 实例状态保存 Activity 布局中有关每个 View 对象的信息。在Activity 重建时,布局状态便自动恢复先前的状态。
  • 默认实现保存有关 Activity 视图层次的状态信息,例如 EditText 小部件中的文本或ListView 的滚动位置
  • 要恢复的更多信息,需要重写 onSaveInstanceState()方法,将键值对添加至 Bundle 对象
  • basic-lifecycle-savestate.png
  • note:
    旋转屏幕时,Activity 将被销毁并重新创建。原因:方向更改时可能需要时加载备用资源(比如布局)
  • 示例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    static final String STATE_SCORE = "playerScore";
    static final String STATE_LEVEL = "playerLevel";
    ...

    @Override
    public void onSaveInstanceState(Bundle savedInstanceState) {
    // Save the user's current game state
    savedInstanceState.putInt(STATE_SCORE, mCurrentScore);
    savedInstanceState.putInt(STATE_LEVEL, mCurrentLevel);

    // Always call the superclass so it can save the view hierarchy state
    super.onSaveInstanceState(savedInstanceState);
    }

恢复 Activity

  • onCreate() 和 onRestoreInstanceState() 回调方法均接收包含实例状态信息的相同 Bundle
  • onCreate() 方法
  • 调用onCreate() 方法需要区分是创建 Activity 的新实例还是恢复先前的实例,判断 Bundle 是否为 null
  • 示例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState); // Always call the superclass first

    // Check whether we're recreating a previously destroyed instance
    if (savedInstanceState != null) {
    // Restore value of members from saved state
    mCurrentScore = savedInstanceState.getInt(STATE_SCORE);
    mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL);
    } else {
    // Probably initialize members with default values for a new instance
    }
    ...
    }
  • onRestoreInstanceState()方法
  • 只需要恢复的已保存的状态
  • 示例
    1
    2
    3
    4
    5
    6
    7
    8
     public void onRestoreInstanceState(Bundle savedInstanceState) {
    // Always call the superclass so it can restore the view hierarchy
    super.onRestoreInstanceState(savedInstanceState);

    // Restore state members from saved instance
    mCurrentScore = savedInstanceState.getInt(STATE_SCORE);
    mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL);
    }

重启Activity其他选择

  • 官方说明
  • 重启应用并恢复大量数据不仅成本高昂,而且会留下糟糕的使用体验,有两个其他选择

    在配置变更期间保留对象

  • Activity 因配置变更而重启,则可通过保留 Fragment 来减轻重新初始化 Activity 的负担
  • 当 Android 系统因配置变更而关闭 Activity 时,不会销毁已标记为要保留的 Activity 的片段。 您可以将此类片段添加到 Activity 以保留有状态的对象。

Activity最佳实践

知晓当前运行的活动

  • 自定义BaseActivity继承自AppCompatActivity
    重写onCreate方法,打印当前运行的Activity名
    app类所有Activity改为继承BaseActivity

  • 代码如下

    1
    2
    3
    4
    5
    6
    7
    public class BaseActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    //打印当前类名
    Log.d("BaseActivity", getClass().getSimpleName());
    }
  • 效果如下
    ScreenShot_20161208172740.png

随时退出程序

  • 新建ActivityCollector类作为活动管理器,添加/删除Activity登记,在BaseActivity中添加对应代码。
  • 代码如下
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
     public class ActivityCollector {
    //新建Activity的列表对象
    public static List<Activity> activities = new ArrayList<>();
    //添加Activity进入列表
    public static void addActivity(Activity activity) {
    activities.add(activity);
    }
    //在列表中移除Activity
    public static void removeActivity(Activity activity) {
    activities.remove(activity);
    }
    //for循环列表 结束所有Activity
    public static void finishAll() {
    for (Activity activity : activities) {
    if (!activity.isFinishing()) {
    activity.finish();
    }
    }
    }
    }
  • BaseActivity
    在onCreate()方法中添加
    1
    2
    //Activity管理器中添加新的活动
    ActivityCollector.addActivity(this);
    重写onDestroy()方法
    1
    2
    3
    4
    5
    6
    @Override
    protected void onDestroy(){
    super.onDestroy();
    //Activity管理器中移除活动
    ActivityCollector.removeActivity(this);
    }
  • 在任何地方调用finishAll()方法即可。

启动活动的最近写法

  • 启动下一个Activity并传递数据模块化
  • 在SecondActivity内增加actionStart()方法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     //调用的Activity,数据1,数据2
    public static void actionStart(Context context, String data1, String data2){
    //新建Intent,绑定SecondActivity
    Intent intent = new Intent(context,ScendActivity.class);
    //存入data1,data2
    intent.putExtra("param1",data1);
    intent.putExtra("param2",data2);
    //启动Activity
    context.startActivity(intent);
    }
  • 启动SecondActivity方式
    ScendActivity.actionStart(FirestActivity.this,"data1","data2");