Skip to content

模块 04:Activity 与 Fragment

目标:理解 Android 的页面模型、Activity 和 Fragment 的生命周期,以及页面间的跳转和通信。

前端对照:页面模型差异

前端 (SPA)Android说明
路由页面 /homeActivity / Fragment一个页面
history.pushState()startActivity(Intent)导航到新页面
history.back()finish() / 返回键返回上一页
URL 参数 ?id=123Intent Extras页面间传参
组件嵌套Fragment 嵌套在 Activity 中页面内模块化
useEffect(() => cleanup, [])onDestroy()组件销毁时清理
React Router <Outlet />Fragment 容器动态内容区域

1. Activity 基础

1.1 创建 Activity

java
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 绑定布局文件(类似 React 的 render() 返回 JSX)
        setContentView(R.layout.activity_main);

        // 初始化视图
        TextView titleText = findViewById(R.id.tv_title);
        Button submitBtn = findViewById(R.id.btn_submit);

        // 设置点击事件
        submitBtn.setOnClickListener(v -> {
            // 处理点击
            titleText.setText("按钮被点击了!");
        });
    }
}

AppCompatActivity vs Activity:始终继承 AppCompatActivity,它提供了向后兼容的 Material Design 支持。

1.2 Activity 生命周期

这是 Android 开发最核心的概念之一。类比前端的组件生命周期:

前端组件生命周期          Android Activity 生命周期
─────────────            ─────────────────────
constructor()            onCreate()          ← 创建,初始化,绑定布局
componentDidMount()      onStart()           ← 可见
useEffect 中订阅          onResume()          ← 获得焦点,可交互
                         onPause()           ← 失去焦点(弹出对话框/切换应用)
                         onStop()            ← 不可见(导航到其他 Activity)
componentWillUnmount()   onDestroy()         ← 销毁,清理资源

完整生命周期图:

                    ┌──────────┐
                    │ onCreate │  初始化:setContentView, findViewById
                    └────┬─────┘

                    ┌────▼─────┐
                    │ onStart  │  可见但可能未获焦
                    └────┬─────┘

              ┌──────────▼──────────┐
              │      onResume       │  获取焦点,用户可交互 ← 运行状态
              └──────────┬──────────┘

                   ┌─────▼─────┐
                   │  onPause   │  失去焦点(如弹出对话框)
                   └─────┬─────┘

                   ┌─────▼─────┐
                   │  onStop    │  完全不可见
                   └─────┬─────┘

                  ┌──────▼──────┐
                  │ onDestroy   │  即将销毁
                  └─────────────┘
java
public class MyActivity extends AppCompatActivity {

    private static final String TAG = "MyActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, "onCreate");
        setContentView(R.layout.activity_my);
        // savedInstanceState: 从被销毁状态恢复时传入之前保存的数据
        if (savedInstanceState != null) {
            String savedText = savedInstanceState.getString("key_text");
        }
    }

    @Override
    protected void onStart() {
        super.onStart();
        Log.d(TAG, "onStart");
        // 适合:注册广播接收器、开始动画
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.d(TAG, "onResume");
        // 适合:恢复相机、重新开始轮询
    }

    @Override
    protected void onPause() {
        super.onPause();
        Log.d(TAG, "onPause");
        // 适合:暂停动画、保存临时数据
        // ⚠️ 不要做耗时操作!
    }

    @Override
    protected void onStop() {
        super.onStop();
        Log.d(TAG, "onStop");
        // 适合:释放重资源、取消网络请求
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy");
        // 适合:释放所有引用、注销监听器
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        // 保存 UI 状态(如旋转屏幕时)
        outState.putString("key_text", "保存的文本");
    }
}

1.3 关键生命周期场景

场景回调顺序
启动 ActivityonCreate → onStart → onResume
按 Home 键onPause → onStop
从后台返回onRestart → onStart → onResume
按返回键onPause → onStop → onDestroy
旋转屏幕onPause → onStop → onDestroy → onCreate → onStart → onResume
A 跳到 BA:onPause → B:onCreate → onStart → onResume → A:onStop
B 返回 AB:onPause → A:onRestart → onStart → onResume → B:onStop → onDestroy

⚠️ 屏幕旋转会销毁并重建 Activity:这是很多 Bug 的来源。解决方案:使用 ViewModel 保存数据、在 AndroidManifest.xml 中配置 android:configChanges="orientation|screenSize"(不推荐)、或在 onSaveInstanceState 中保存状态。

2. Intent:页面间导航

Intent 是 Android 中的"消息"对象,用于组件间通信。

2.1 显式 Intent(明确目标)

java
// 从 MainActivity 跳转到 DetailActivity
// 类似前端:router.push('/detail?id=123&name=张三')
Intent intent = new Intent(MainActivity.this, DetailActivity.class);

// 传递参数(类似 URL 查询参数)
intent.putExtra("id", 123);
intent.putExtra("name", "张三");
intent.putExtra("is_vip", true);

// 启动 Activity
startActivity(intent);

// 如果需要返回结果(类似 await router.push())
// 新方式:ActivityResultLauncher(推荐)
ActivityResultLauncher<Intent> launcher = registerForActivityResult(
    new ActivityResultContracts.StartActivityForResult(),
    result -> {
        if (result.getResultCode() == RESULT_OK) {
            String data = result.getData().getStringExtra("result_key");
        }
    }
);
launcher.launch(intent);

老项目写法:startActivityForResult(已过时但老项目大量存在)

java
// ⚠️ 老项目中大量使用这种方式,新代码推荐用上面的 ActivityResultLauncher
// 发起请求(requestCode = 0)
Intent intent = new Intent(this, ActResponseActivity.class);
Bundle bundle = new Bundle();                          // 创建包裹(类似 JS 的打包对象)
bundle.putString("request_time", DateUtil.getNowTime());   // 往包裹存入数据
bundle.putString("request_content", "你吃饭了吗?");
intent.putExtras(bundle);                             // 把包裹塞给意图
startActivityForResult(intent, 0);                     // 第二个参数为请求代码

// 接收返回数据(类似 await router.push() 的返回值)
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
    super.onActivityResult(requestCode, resultCode, intent);
    if (intent != null && requestCode == 0 && resultCode == Activity.RESULT_OK) {
        Bundle bundle = intent.getExtras();              // 从返回的意图中获取包裹
        String responseTime = bundle.getString("response_time");
        String responseContent = bundle.getString("response_content");
    }
}

Bundle 类似前端的序列化对象,支持 putString/putInt/putBoolean/putSerializable 等方法。在 Activity、Fragment、Service 之间传递数据时经常使用。

2.2 在目标 Activity 中接收参数

java
public class DetailActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_detail);

        // 获取传入的参数
        Intent intent = getIntent();
        int id = intent.getIntExtra("id", 0);           // 第二个参数是默认值
        String name = intent.getStringExtra("name");
        boolean isVip = intent.getBooleanExtra("is_vip", false);

        // 使用参数
        TextView tv = findViewById(R.id.tv_info);
        tv.setText("ID: " + id + ", Name: " + name);
    }
}

2.3 隐式 Intent(通过动作匹配)

java
// 打开网页(类似 window.open)
Intent webIntent = new Intent(Intent.ACTION_VIEW,
    Uri.parse("https://www.example.com"));
startActivity(webIntent);

// 拨打电话
Intent callIntent = new Intent(Intent.ACTION_DIAL,
    Uri.parse("tel:10086"));
startActivity(callIntent);

// 分享内容
Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.setType("text/plain");
shareIntent.putExtra(Intent.EXTRA_TEXT, "来看看这个应用!");
startActivity(Intent.createChooser(shareIntent, "分享到"));

// 发送邮件
Intent emailIntent = new Intent(Intent.ACTION_SENDTO,
    Uri.parse("mailto:someone@example.com"));
emailIntent.putExtra(Intent.EXTRA_SUBJECT, "主题");
emailIntent.putExtra(Intent.EXTRA_TEXT, "正文内容");
startActivity(emailIntent);

2.4 常见 Intent 操作

java
// 返回上一页
finish();

// 返回并传递结果
Intent result = new Intent();
result.putExtra("result_key", "操作成功");
setResult(RESULT_OK, result);
finish();

// 清空栈中所有 Activity 并跳转到新页面
// 类似:router.replace('/login') 然后清空历史
Intent loginIntent = new Intent(this, LoginActivity.class);
loginIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(loginIntent);

// 跳转到某个页面,但保持当前页面在栈中
// 类似:router.push('/settings')
startActivity(new Intent(this, SettingsActivity.class));

2.5 Activity 启动模式(launchMode)

在 AndroidManifest.xml 中配置:

xml
<activity
    android:name=".MainActivity"
    android:launchMode="standard" />   <!-- 默认:每次都创建新实例 -->

<activity
    android:name=".HomeActivity"
    android:launchMode="singleTop" />  <!-- 如果已在栈顶,不创建新实例 -->

<activity
    android:name=".LoginActivity"
    android:launchMode="singleTask" /> <!-- 全栈唯一实例 -->

<activity
    android:name=".SplashActivity"
    android:launchMode="singleInstance" /> <!-- 独占一个任务栈 -->

老项目中singleTask 常见于首页和登录页,避免重复创建。

3. Fragment

Fragment 是 Activity 内的"子页面",可以复用和组合。类似前端中的路由嵌套或组件嵌套。

3.1 创建 Fragment

java
public class HomeFragment extends Fragment {

    // 绑定布局
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // 类似 Activity 的 setContentView
        return inflater.inflate(R.layout.fragment_home, container, false);
    }

    // 视图已创建,可以 findViewById
    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        TextView title = view.findViewById(R.id.tv_title);
        Button btn = view.findViewById(R.id.btn_action);

        btn.setOnClickListener(v -> {
            title.setText("Fragment 中的按钮被点击了");
        });
    }
}

3.2 Fragment 生命周期

onAttach()       ← Fragment 绑定到 Activity
onCreate()       ← 初始化(无 UI)
onCreateView()   ← 创建 UI(inflate 布局)
onViewCreated()  ← UI 已创建,可以操作视图 ★ 推荐在这里初始化
onStart()        ← 可见
onResume()       ← 可交互
onPause()        ← 失去焦点
onStop()         ← 不可见
onDestroyView()  ← UI 被销毁(Fragment 可能仍在回退栈中)
onDestroy()      ← Fragment 被销毁
onDetach()       ← 从 Activity 解绑

关键差异:Fragment 有 onDestroyView() 但 Activity 没有。当 Fragment 进入回退栈时,View 会被销毁但 Fragment 实例还在。下次回来时会重新调用 onCreateView()

3.3 Fragment 管理

java
// 添加 Fragment 到 Activity
getSupportFragmentManager()
    .beginTransaction()
    .replace(R.id.fragment_container, new HomeFragment())
    .commit();

// 添加到回退栈(类似 router.push)
getSupportFragmentManager()
    .beginTransaction()
    .replace(R.id.fragment_container, new DetailFragment())
    .addToBackStack("detail")     // 加入回退栈
    .commit();

// 传递参数给 Fragment
DetailFragment fragment = new DetailFragment();
Bundle args = new Bundle();
args.putInt("id", 123);
args.putString("name", "张三");
fragment.setArguments(args);

// 在 Fragment 中获取参数
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
    Bundle args = getArguments();
    if (args != null) {
        int id = args.getInt("id");
        String name = args.getString("name");
    }
}

// 回退到上一个 Fragment(类似 router.back())
getSupportFragmentManager().popBackStack();

// 清空回退栈
getSupportFragmentManager().popBackStack(null,
    FragmentManager.POP_BACK_STACK_INCLUSIVE);

3.4 Fragment 与 Activity 通信

java
// 方式 1:通过接口回调(老项目常见)
public class HomeFragment extends Fragment {
    public interface OnItemSelectedListener {
        void onItemSelected(String item);
    }

    private OnItemSelectedListener listener;

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        if (context instanceof OnItemSelectedListener) {
            listener = (OnItemSelectedListener) context;
        }
    }

    // 触发回调
    private void onItemClick(String item) {
        if (listener != null) {
            listener.onItemSelected(item);
        }
    }
}

// Activity 实现接口
public class MainActivity extends AppCompatActivity
    implements HomeFragment.OnItemSelectedListener {

    @Override
    public void onItemSelected(String item) {
        // 处理选择事件
    }
}
java
// 方式 2:通过 ViewModel 共享(推荐,Jetpack 方式)
// 在 Activity 和 Fragment 中使用同一个 ViewModel 实例
public class SharedViewModel extends ViewModel {
    private final MutableLiveData<String> selectedItem = new MutableLiveData<>();

    public void select(String item) {
        selectedItem.setValue(item);
    }

    public LiveData<String> getSelectedItem() {
        return selectedItem;
    }
}

// 在 Fragment 中
SharedViewModel model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
model.getSelectedItem().observe(getViewLifecycleOwner(), item -> {
    // 响应选择
});

// 在 Activity 中
SharedViewModel model = new ViewModelProvider(this).get(SharedViewModel.class);
model.select("某个选项");

4. 四大组件速览

除了 Activity,Android 还有三个重要组件:

4.1 Service(后台服务)

java
// 类似前端的 Web Worker / Service Worker
public class DownloadService extends Service {

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        String url = intent.getStringExtra("download_url");
        // 在后台执行长时间运行的任务
        new Thread(() -> {
            // 下载逻辑
            downloadFile(url);
            stopSelf(); // 完成后停止服务
        }).start();
        return START_STICKY;
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

// 启动服务
Intent serviceIntent = new Intent(this, DownloadService.class);
serviceIntent.putExtra("download_url", "https://example.com/file.zip");
startService(serviceIntent);

4.2 BroadcastReceiver(广播接收器)

java
// 类似 window.addEventListener('online', handler)
public class NetworkReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) {
            boolean isConnected = checkNetwork(context);
            // 处理网络变化
        }
    }
}

// 动态注册(在 Activity/Fragment 中)
IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
NetworkReceiver receiver = new NetworkReceiver();
registerReceiver(receiver, filter);

// 记得在 onDestroy 中注销
unregisterReceiver(receiver);

4.3 ContentProvider(内容提供者)

java
// 用于跨应用共享数据,老项目中使用较少
// 常见场景:读取联系人、相册等系统数据
Cursor cursor = getContentResolver().query(
    ContactsContract.Contacts.CONTENT_URI,
    null, null, null, null
);

5. Context 概念

Context 是 Android 中最重要但也最令人困惑的概念。你可以把它理解为应用环境的引用,几乎所有操作都需要它。

java
// Activity 本身就是 Context
Activity activity = this;  // this 就是 Context

// Fragment 中获取 Context
Context context = requireContext();     // 通用 Context
Activity activity = requireActivity();  // 宿主 Activity

// Application Context(全局生命周期)
Context appContext = getApplicationContext();

// 常见需要 Context 的场景
Toast.makeText(this, "提示消息", Toast.LENGTH_SHORT).show();
Intent intent = new Intent(this, OtherActivity.class);
SharedPreferences prefs = this.getSharedPreferences("prefs", MODE_PRIVATE);
Resources res = this.getResources();

何时使用哪种 Context

场景使用
UI 操作(Toast、Dialog)Activity Context (this)
加载资源Activity Context
长期持有引用(单例、缓存)Application Context
Service 中Service Context (this)
Fragment 中requireContext()

常见 Bug:在非 UI 线程或非 Activity 环境中使用 Activity Context 创建 Dialog,会导致崩溃。

6. Handler 延迟任务与页面跳转

Handler 是 Android 中处理延迟和线程间通信的核心机制,老项目中大量使用。

java
// 场景:启动页 3 秒后自动跳转到主页
// 前端类比:setTimeout(() => router.push('/home'), 3000)
public class SplashActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_splash);

        TextView tv = findViewById(R.id.tv_hint);
        tv.setText("3秒后进入主页...");

        // 延迟 3 秒后启动跳转任务
        new Handler(Looper.myLooper()).postDelayed(mGoMain, 3000);
    }

    private Runnable mGoMain = new Runnable() {
        @Override
        public void run() {
            // 从 SplashActivity 跳到 MainActivity
            startActivity(new Intent(SplashActivity.this, MainActivity.class));
            finish(); // 关闭启动页
        }
    };
}
java
// 场景:生命周期日志记录(调试常用)
// 参考自实际教程项目 ActLifeActivity.java
public class LifeCycleActivity extends AppCompatActivity implements View.OnClickListener {
    private static final String TAG = "LifeCycleActivity";
    private TextView tvLife; // 声明文本视图对象
    private String mStr = ""; // 日志拼接字符串

    private void refreshLife(String desc) {
        Log.d(TAG, desc); // 打印到 Logcat
        mStr = String.format("%s%s %s\n", mStr, DateUtil.getNowTimeDetail(), desc);
        tvLife.setText(mStr); // 显示在屏幕上
    }

    @Override protected void onCreate(Bundle s) { super.onCreate(s); refreshLife("onCreate"); }
    @Override protected void onStart()    { super.onStart();    refreshLife("onStart"); }
    @Override protected void onResume()   { super.onResume();   refreshLife("onResume"); }
    @Override protected void onPause()    { super.onPause();    refreshLife("onPause"); }
    @Override protected void onStop()     { super.onStop();     refreshLife("onStop"); }
    @Override protected void onRestart()  { super.onRestart();  refreshLife("onRestart"); }
    @Override protected void onDestroy()  { super.onDestroy();  refreshLife("onDestroy"); }
}

implements View.OnClickListener 是老项目中非常常见的模式:Activity 自身实现点击接口,通过 v.getId() 区分不同按钮。前端类比:class MyPage extends React.Component implements ClickHandler

7. 线程模型

Android 有严格的线程规则:

java
// ⚠️ 主线程(UI 线程)规则:
// 1. 所有 UI 操作必须在主线程
// 2. 耗时操作(网络、数据库、文件)不能在主线程
// 3. 违反规则会导致 ANR(Application Not Responding)或崩溃

// 在后台线程执行耗时操作
new Thread(() -> {
    // 网络请求、数据库操作等
    String result = fetchDataFromNetwork();

    // ⚠️ 不能在后台线程更新 UI!
    // textView.setText(result);  // ❌ 崩溃!

    // 切回主线程更新 UI
    runOnUiThread(() -> {
        textView.setText(result);  // ✅ 正确
    });
}).start();

// 使用 Handler(老项目常见)
Handler mainHandler = new Handler(Looper.getMainLooper());
mainHandler.post(() -> {
    textView.setText(result);
});

// 使用 AsyncTask(已废弃,但老项目中大量存在)
// ⚠️ 了解即可,新代码不要用
class MyAsyncTask extends AsyncTask<String, Void, String> {
    @Override
    protected String doInBackground(String... params) {
        return fetchData(params[0]);  // 后台线程
    }

    @Override
    protected void onPostExecute(String result) {
        textView.setText(result);     // 主线程
    }
}

8. 状态保存与恢复

java
public class FormActivity extends AppCompatActivity {
    private EditText editText;
    private boolean isChecked;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_form);
        editText = findViewById(R.id.et_input);

        // 从保存的状态恢复
        if (savedInstanceState != null) {
            editText.setText(savedInstanceState.getString("input_text"));
            isChecked = savedInstanceState.getBoolean("checkbox_state");
        }
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putString("input_text", editText.getText().toString());
        outState.putBoolean("checkbox_state", isChecked);
    }
}

ViewModel 可以替代 onSaveInstanceState:ViewModel 在配置变更(如旋转屏幕)时不会被销毁,是更现代的状态管理方式(见模块 08)。

前端开发者备忘

前端概念Android 对应备注
React 组件Activity / Fragment页面/模块
useEffect(() => {}, [])onCreate()初始化
useEffect(cleanup, [])onDestroy()清理
组件可见时执行onResume()每次可见
组件不可见时暂停onPause()每次不可见
router.push(path, params)startActivity(intent)导航
router.back()finish() / popBackStack()返回
URL params / queryIntent.putExtra()页面传参
useParams()getIntent().getXxxExtra()接收参数
Context (React)Context (Android)概念不同但名称相同
Web WorkerService后台任务
addEventListenerBroadcastReceiver事件监听
window.location.hashActivity 回退栈导航历史

下一步

理解了 Activity 和 Fragment 后,接下来学习 模块 05:UI 布局与控件,了解如何用 XML 构建界面。

面向前端开发者的 Java Android 开发教程