文章目录

  • Ch4 程序活动单元Activity
  • 4.1 Activity的生命周期
  • 4.1.1 生命周期状态
  • 4.1.2 生命周期方法
  • 4.2 Activity的创建、配置、开启和关闭
  • 4.2.1 创建Activity
  • 4.2.2 配置Activity
  • 4.2.3 开启和关闭Activity
  • 4.3 Intent与IntentFilter
  • 4.3.1 Intent(意图)
  • 4.3.2 IntentFilter(过滤器)
  • 4.4 Activity之间的跳转
  • 4.4.1 在Activity之间数据传递
  • 4.5 Activity的任务栈和启动模式
  • 4.5.1 Android中的任务栈
  • 4.5.2 Activity的启动模式
  • 4.6 使用Fragment(碎片)
  • 4.6.1 Fragment简介
  • 4.6.2 Fragment的生命周期
  • 4.6.3 创建Fragment
  • 4.6.4 在Activity中添加Fragment


Ch4 程序活动单元Activity

Android四大组件:Activity,Service,ContentProvider,BroadcastReceiver
Activity负责与用户交互

4.1 Activity的生命周期

4.1.1 生命周期状态

Activity的生命周期指Activity从创建到销毁的整个过程。

  1. 启动状态
    一般情况下,当Activity启动后便会进入运行状态
  2. 运行状态
    Activity在此状态时处于界面最前端,它是可见、有焦点的,可以与用户进行交互。
    当Activity处于运行状态时,Android会尽可能地保持这种状态。如果出现内存不足的情况,Android也会先销毁栈底的Activity来确保当前Activity正常运行。
  3. 暂停状态
    Activity对用户来说依然可见,但无法获取焦点,用户对它操作没有响应。(例如Activity上覆盖了一个透明或非全屏的界面)
  4. 停止状态
    当Activity完全不可见时处于停止状态。
  5. 销毁状态
    当Activity处于销毁状态时,将被清理出内存

Activity生命周期的启动状态和销毁状态是过渡状态,Activity不会在这两种状态停留

4.1.2 生命周期方法

  1. onCreat()
    Activity创建时调用
  2. onStart()
    Activity即将可见时调用
  3. onResume()
    Activity获取焦点时调用
  4. onPause()
    Activity被其他Activity覆盖或屏幕锁屏时调用
  5. onStop()
    Activity对用户不可见时调用
  6. onDestroy()
    Activity销毁时调用
  7. onRestart()
    Activity从停止状态到再次启动时调用
    如果程序中只有一个Activity,则程序无法进行从停止状态到再次启动状态的操作。

当手机横竖屏切换时,会根据AndroidManifest.xml文件中Activity的configChanges属性值的不同而调用相应的生命周期方法。
在进行横竖屏切换时,首先会调用onDestory()方法销毁Activity,之后调用onCreate()方法重建Activity。如果不希望在横竖屏切换时Activity被销毁重建,可以通过configChanges属性进行设置

<activity android:name".ManActivity"
          android:configChanges="orientation|KeyboardHidden">

如果希望某一个界面一直处于竖屏或者横屏状态,可以在清单文件中通过设置Activity的screenOrientation属性完成。

竖屏: android:screenOrientation="portrait"
横屏:android:screenOrientation="landscape"

4.2 Activity的创建、配置、开启和关闭

4.2.1 创建Activity

[右击包名]->[new]->[Activity]->[Empty Activity]->创建ActivityExample

4.2.2 配置Activity

创建一个SecondAcitvity类继承Activity,当在ActivityExample的onCreat()方法中启动ScondActivity时,会抛出异常信息

package com.milk.learnproject_activity;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //启动SecondActivity
        Intent intent= new Intent(this,SecondActivity.class);
        startActivity(intent);
    }
}

每一个创建的Activity都必须在清单文件AndroidManifest.xml中配置才能生效。
配置SecondAcivity示例代码:

<activity 
    android:name="com.milk.learnproject_activity.SecondActivity"/>

如果Activity所在的包与AndroidManifest.xml文件的标签中通过package属性指定的包名一致,则android:name属性的值可以直接设置为".Activity名称"

<activity 
    android:name=".SecondActivity"
    android:exported="true"
</activity>

android:exported=“true”
这句代码的意思就是该activity允许外部应用调用。

编译器有新建Activity时自动修改AndroidManifest.xml配置的功能

4.2.3 开启和关闭Activity

  1. 启动Activity
    可以通过startActivity()方法开启创建的Activity
public void startActivity(Intend intent)

参数Intent为Android应用中各组件之间通信的桥梁,一个Activity通过Intent表达自己的“意图”。
在MainActivity的onCreate()方法中启动SecondActivity实例代码

Intent intent = new Intent(MainActivity.this,SecondActivity.class);
startActivity(intent);
  1. 关闭Activity
    调用Activity的finish()方法关闭当前的Activity
public void finish()

finish()方法既没有参数,也没有返回值,只需要在Activity的相应事件中调用该方法即可。

4.3 Intent与IntentFilter

如果用户需要从一个Activity切换到另一个Activity,则必须使用Intent来进行切换。Intent用于相同或者不同应用程序组件间的绑定。

4.3.1 Intent(意图)

Intent是程序中各组件间进行交互的一种重要方式,它不仅可以指定当前组件要执行的动作,还可以在不同组件之间进行数据传递。

  1. 显示Intent
    显示Intent指直接指定目标组件
    使用Intent显示指定要跳转的目标Activity:
Intent intent = new Intent(this,SecondActivity.class);
startActivity(intent);

第一个参数this表示当前的Activity,第二个参数SecondActivity.class表示要跳转到的目标Activity

  1. 隐式Intent
    隐式Intent不会明确指出需要激活的目标组件
  • action:表示Intent对象要完成的动作
  • data:表示Intent对象中传递的数据
  • category:表示为action添加的额外信息
    使用App1打开App2的SecondActivity:
    SecondActivity的配置代码
<activity
            android:name=".SecondActivity"
            android:exported="true" >
        <intent-filter>
            <action android:name="com.milk.learnproject_activity_secondapp.SE_APP_SE_ACT" />

            <category android:name="android.intent.category.DEFAULT" />
        </intent-filter>
        </activity>

App1打开SecondActivity的Button点击事件:

button1 = (Button) findViewById(R.id.Button1);
        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent=new Intent();
                intent.setAction("com.milk.learnproject_activity_secondapp.SE_APP_SE_ACT");
                //可以写成Intent intent=new Intent().setAction("com.milk.learnproject_activity_secondapp.SE_APP_SE_ACT");
                startActivity(intent);
            }
        });

打开bilibili应用

package com.milk.learnproject_activity;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
    Button button1;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        button1 = (Button) findViewById(R.id.Button1);
        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent=getPackageManager().getLaunchIntentForPackage("tv.danmaku.bili");//获得bilibili app的启动包名
                startActivity(intent);
            }
        });
    }
}

在使用隐式Intent开启Activity时,系统会默认为该Intent添加"Android.intent.category.DEFAULT"的category,因此为了被开启的Activity能够接收隐式Intent,必须在AndroidManifest.xml文件的Activity标签下的<intent-filter>中为被开启的Activity指定catrgory为"android:intent.category.DEFAULT"

4.3.2 IntentFilter(过滤器)

当发送一个隐式Intent后,Android系统会将它与程序中每一个组件的过滤器进行匹配,匹配属性有action、data、category,需要三个属性都匹配成功才能唤起相应组件。

  1. action属性匹配规则
    action属性用来指定Intent对象的动作
<intent-filter>
     <action android:name="android.intent.action.EDIT"/>
     ······
 <intent-filter>

标签中间可以罗列多个action属性,但是当使用隐式Intent激活组件时,只要Intent携带的action与其中一个标签中action的声明相同,action属性就匹配成功。

在清单文件中为Activity添加标签时,必须添加action属性,否则隐式Intent无法开启该Activity。

  1. data属性匹配规则
    data属性用来指定数据的URI或者数据MIME类型,它的值通常与Intent的action属性有关联。
<intent-filter>
    <data android:mimeType="Video/mpeg" android:scheme="http..."/>
    ···
</intent-filter>

<intent-filter>标签中间可以罗列多个data属性,每个data属性可以指定数据的MIME类型和URI。其中MIME类型可以表示image/ipeg、video/*等媒体类型。
隐式Intent携带的data与其中一个标签中data的声明相同,data属性就匹配成功。

  1. category属性匹配规则
    category属性用于为action添加额外信息,一个interFilter可以不声明category属性,也可以声明多个category属性。
<intent-filter>
    <category android:name="android.intent.category.DEFAULT"/>
    ······
</intent-filter>

隐式Intent中声明的category必须全部能够与某一个IntentFilter中的category匹配才算匹配成功。IntentFilter中罗列的category属性数量必须大于或者等于隐式Intent携带的category属性数量时,category属性才能匹配成功。如果一个隐式Intent没有设置category属性,那么它可以通过任何一个IntentFilter的category匹配。

4.4 Activity之间的跳转

4.4.1 在Activity之间数据传递

  1. 使用Intent的putExtra()方法传递数据
    通过putExtra()方法将传递的数据存储在Intent对象后,如果想获取该数据,可以通过getXxxExtra()方法来实现。
  2. 使用Bundle类传递数据
    Bundle类与Map接口比较类似,都是通过键值对的形式来保存数据。
Intent intent = new Intent();
intent.setClass(this,SecondActivity.class);
Bundle bundle = new Bundle();//创建Bundle对象
bundle.putString("account","Admin");//封装用户名信息
bundle.putString("password","123456");//封装密码信息
intent.putExtras(bundle);//将Bundle对象封装到Intent对象
startActivity(intent);

SecondActivity获取传递数据:

Bundle bundle = getIntent().getExtras();//获取Bundle对象
String account = bundle.getString("account");
String password = bundle.getString("Password");

4.4.2 Activity之间的数据回转

  1. startActivityForResult()方法
startActivityForResult(Intent intent, int requestCode)

startActivityForResult()方法用于开启一个Activity,当开启的Activity销毁时,希望从中返回数据。
requestCode表示请求码,用于标识请求来源。

  1. setResult()方法
setResult(int resultCode, Intent intent)

resultCode表示返回码,用于标识返回的数据来自哪个Activity。
intent用于携带数据并回传到上个界面。

  1. onActivityResult()
onActivityResult(int requestCode, int resultCode, Intent data)

onActivityResult()方法用于接收回传的数据,并根据传递的参数requestCode、resultCode来识别数据来源

在MainActivity中点击button1控件跳转到SecondActivity示例代码:

button1.setOnClickListener(new View.OnClickListener(){
    @Override
    public void onClick(View view){
    Intent intent = new Intent(MainActivity.this,SecondActivity.class);
    startActivityForResult(intent,1);
}
});

在SecondActivity中点击button2控件返回数据到MainActivity的示例代码:

button2.setOnClickListener(new View.OnClickListener){
    @Override
    public void onClick(View view){
        Intent intent = new Intent();
        intent.putExtra("data","Hello MainActivity");
        setResult(2,intent);
        finish();
    }
});

setResult()方法只负责返回数据,没有跳转功能,需要调用finish()方法关闭SecondActivity。

在MainActivity中调用startActivityForResult()方法启动SecondActivity,在SecondActivity被销毁后程序会回调MainActivity中的OnActivityResult()方法来接收回传的数据,因此需要在MainActivity中重写onActivityResult()方法。

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data){
    super.onActivityResult(requestCode,resultCode,data);
    if(requestCode == 1 && resultCode == 2){
        String acquiredData = data.getStringExtra("data");
        Toast.makeText(MainActivity.this,acquiredData,Toast.LENGTH_SHORT).show();
    }
}

在SecondActivity的EditView输入文字修改MainActivity的TextView:
MainActivity:

package com.milk.learnproject_activity;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.graphics.Typeface;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
    TextView tv1;
    Button button1;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tv1 = (TextView) findViewById(R.id.Main_TextView1);
        button1=(Button)findViewById(R.id.Main_Button1);
        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent=new Intent(MainActivity.this,SecondActivity.class);
                startActivityForResult(intent,1);
            }
        });
    }
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data){
        super.onActivityResult(requestCode,resultCode,data);
        if(requestCode == 1 && resultCode == 2){
            String acquiredData = data.getStringExtra("data");
            Toast.makeText(MainActivity.this,acquiredData,Toast.LENGTH_SHORT).show();
            tv1.setText(acquiredData);
        }
    }
}

SecondActivity:

package com.milk.learnproject_activity;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Typeface;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class SecondActivity extends Activity {
    EditText edittext1;
    Button button1;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        edittext1=(EditText)findViewById(R.id.Second_EditView1);
        button1=(Button)findViewById(R.id.Second_Button1);
        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String text=edittext1.getText().toString();
                Intent intent = new Intent();
                intent.putExtra("data",text);
                setResult(2,intent);
                finish();
            }
        });
    }
}

1.在Fragment中使用startActivityForResult的时候,不要使用getActivity().startActivityForResult,而是应该直接使startActivityForResult()。
2.如果activity中重写了onActivityResult,那么activity中的onActivityResult一定要加上super.onActivityResult(requestCode, resultCode, data)。
如果违反了上面两种情况,那么onActivityResult只能够传递到activity中的,无法传递到Fragment中的。
没有违反上面两种情况的前提下,可以直接在Fragment中使用startActivityForResult和onActivityResult,和在activity中使用的一样。

4.5 Activity的任务栈和启动模式

4.5.1 Android中的任务栈

Android的任务栈是一种用来存放Activity实例的容器。任务栈最大的特点就是先进后出。主要有压栈和出栈两个操作。
用户操作的Activity永远都是栈顶的Activity。

4.5.2 Activity的启动模式

  1. standard模式
    standard是Activity的默认启动方式。当android:launchMode没有被指定属性时默认为standard。这种方式的特点是每启动一个Activity就会在栈顶创建一个新的实例(闹钟程序)。
    当Activity已经位于栈顶时,再次启动该Activity时还需要创建一个新的实例压入任务栈,不能直接复用。
  2. singleTop模式
    会判断要启动的Activity实例是否位于栈顶,如果位于栈顶则直接复用,否则创建新的实例(浏览器书签)。
    如果Activity并未处于栈顶位置,则在栈中还会压入多个不相连的Activity实例
  3. singleTask模式
    每次启动Activity时系统首先检查栈中是否存在当前Activity实例,如果存在则直接使用,并把当前Activity上面的所有实例全部弹出栈(浏览器主页面)。
  4. singleInstance模式
    Activity会启动一个新的任务栈来管理Activity实例,无论从哪个任务栈中启动该Activity,该实例在整个系统中只有一个(Android桌面)。
  1. 要启动的Activity实例在栈中不存在,则系统先创建一个新任务栈再压入Activity实例
  2. 要启动的Activity已经存在,系统会把Activity所在任务栈转移到前台,从而显示Activity(来电界面)。

4.6 使用Fragment(碎片)

为了能够同时兼顾到手机和平板电脑的开发,自Android3.0版本开始提供了Fragment

4.6.1 Fragment简介

Fragment是一种嵌入在Activity中的UI片段,它可以用来描述Activity中的一部分布局。
一个Activity可以包含多个Fragment,一个Fragment也可以在多个Activity中使用。

4.6.2 Fragment的生命周期

当在Activity中创建Fragment时,Fragment处于启动状态,
当Activity被暂停时,其中的所有Fragment也被暂停,
当Activity被销毁时,其中的所有Fragment也被销毁,
当一个Activity处于运行状态时,可以单独地对每一个Fragment进行操作:添加时Fragment处于启动状态,删除时Fragment处于销毁状态。
Fragment生命周期相比Activity额外方法:

  • onAttach():Fragment和Activity建立关联时调用
  • onCreateView():Fragment创建视图(加载布局)时创建
  • onActivityCreate():Fragment相关联的Activity已经创建时调用
  • onDestroyView(): Fragment关联的视图被移除时调用
  • onDetach(): Fragment和Activity解除关联时调用

4.6.3 创建Fragment

创建Fragment时必须创建一个类继承自Fragement。

public class NewListFragment extends Fragment{
    @Override
    public View on CreareView(LayoutInflater, inflater, ViewGroup container, Bundle savedInstanceState){
        View v = inflater.inflate(R.layout.fragment,container, false);
        return v;
    }
}

4.6.4 在Activity中添加Fragment

Fragment创建完成后并不能单独使用,还需要将Fragment添加到Activity中。

  1. 在布局文件中添加Fragment
    需要在Activity引用的布局文件中使用标签,必须指定android:name属性,属性值为Fragment的全路径名称。
<fragment
     android:name="cn.itcast.NewsListFragment"
     android:id="@+id/newslist"
     android:layout_width="match_parent"
     android:layout_height="match_parent"/>
  1. 在Activity中动态加载Fragment
  1. 创建一个Fragment实例
  2. 获取FragmentManager的实例
  3. 开启FragmentTransaction
  4. 向Activity的布局容器中添加Fragment
  5. 通过commit()提交事务
public class MainActivity extends Activity{
    @SuppressLint("NewApi")
    @Override
    protected void onCreate(Bundle savedInstanceState){
        super.onCreate(saveInstanceState);
        setContentView(R.layout.activity_main);
        NewsListFragment fragment = new NewsListFragment();
        FragmentManage fm = getFragementManager();
        FragmentTransaction beginTransaction = fm.beginTransaction();
        beginTransaction.replace(R.id.ll,fragment);
        beginTransaction.commit();
    }
}

“getFragmentManager和android.app.Fragment在API 28中已弃用。
现在,我们应该分别使用支持库中的Fragment和getSupportFragmentManager。
但是,如果您担心这些弃用,则还应注意,API 28也弃用了PreferenceFragments。
相反,您应该使用PreferenceFragmentCompat”

应将getFragementManager替换为getSupportFragmentManager()

//1.在Fragment中使用startActivityForResult的时候,不要使用getActivity().startActivityForResult,而是应该直接使startActivityForResult()。
//2.如果activity中重写了onActivityResult,那么activity中的onActivityResult一定要加上super.onActivityResult(requestCode, resultCode, data)。
//如果违反了上面两种情况,那么onActivityResult只能够传递到activity中的,无法传递到Fragment中的。
//没有违反上面两种情况的前提下,可以直接在Fragment中使用startActivityForResult和onActivityResult,和在activity中使用的一样。