4. 车辆属性读取

任务说明

车载系统对应用生命周期有更严格的管控:车辆熄火、屏幕关闭或切换到其他车载服务时,系统可能随时回收后台资源。本案例实现一个最小可运行的Activity与Fragment生命周期管理示例,重点验证:Activity在配置变更(如车机屏幕旋转)时状态保留、Fragment在Activity重建后正确恢复,以及关键生命周期回调中资源的正确释放与重建。

环境依赖

  • Android SDK API 26+(覆盖车载系统常见版本)
  • AndroidX Fragment库(androidx.fragment:fragment:1.6.0+)
  • 编译工具:Android Studio 或 Gradle命令行

核心实现

以下代码包含一个Activity和一个Fragment。Activity通过onSaveInstanceState保存计数器状态,在重建后恢复;Fragment在Activity重建时通过FragmentManager自动恢复,并在其生命周期回调中打印日志,便于在Logcat中观察调用顺序。

// MainActivity.java
package com.example.carlifecycle;

import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "CarLifecycle";
    private static final String KEY_COUNT = "count";
    private int count = 0;
    private TextView tvCount;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tvCount = findViewById(R.id.tv_count);

        if (savedInstanceState != null) {
            count = savedInstanceState.getInt(KEY_COUNT);
            Log.d(TAG, "Activity onCreate - 恢复状态, count=" + count);
        } else {
            Log.d(TAG, "Activity onCreate - 首次创建");
        }
        tvCount.setText("计数: " + count);

        // 添加Fragment(仅在首次创建时添加,避免重复)
        if (savedInstanceState == null) {
            FragmentManager fm = getSupportFragmentManager();
            FragmentTransaction ft = fm.beginTransaction();
            ft.add(R.id.fragment_container, new CarFragment(), "car_fragment");
            ft.commit();
        }
    }

    @Override
    protected void onStart() {
        super.onStart();
        Log.d(TAG, "Activity onStart");
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.d(TAG, "Activity onResume");
    }

    @Override
    protected void onPause() {
        super.onPause();
        Log.d(TAG, "Activity onPause");
    }

    @Override
    protected void onStop() {
        super.onStop();
        Log.d(TAG, "Activity onStop");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "Activity onDestroy");
    }

    @Override
    protected void onSaveInstanceState(@NonNull Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putInt(KEY_COUNT, count);
        Log.d(TAG, "Activity onSaveInstanceState - 保存 count=" + count);
    }

    // 模拟用户操作:点击按钮增加计数
    public void incrementCount() {
        count++;
        if (tvCount != null) {
            tvCount.setText("计数: " + count);
        }
        Log.d(TAG, "计数增加至: " + count);
    }
}
// CarFragment.java
package com.example.carlifecycle;

import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;

public class CarFragment extends Fragment {
    private static final String TAG = "CarLifecycle";

    @Override
    public void onAttach(@NonNull android.content.Context context) {
        super.onAttach(context);
        Log.d(TAG, "Fragment onAttach");
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, "Fragment onCreate");
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater,
                             @Nullable ViewGroup container,
                             @Nullable Bundle savedInstanceState) {
        Log.d(TAG, "Fragment onCreateView");
        View view = inflater.inflate(R.layout.fragment_car, container, false);
        Button btnIncrement = view.findViewById(R.id.btn_increment);
        btnIncrement.setOnClickListener(v -> {
            if (getActivity() instanceof MainActivity) {
                ((MainActivity) getActivity()).incrementCount();
            }
        });
        return view;
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        Log.d(TAG, "Fragment onViewCreated");
    }

    @Override
    public void onStart() {
        super.onStart();
        Log.d(TAG, "Fragment onStart");
    }

    @Override
    public void onResume() {
        super.onResume();
        Log.d(TAG, "Fragment onResume");
    }

    @Override
    public void onPause() {
        super.onPause();
        Log.d(TAG, "Fragment onPause");
    }

    @Override
    public void onStop() {
        super.onStop();
        Log.d(TAG, "Fragment onStop");
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        Log.d(TAG, "Fragment onDestroyView");
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "Fragment onDestroy");
    }

    @Override
    public void onDetach() {
        super.onDetach();
        Log.d(TAG, "Fragment onDetach");
    }
}
<!-- res/layout/activity_main.xml -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp">

    <TextView
        android:id="@+id/tv_count"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="24sp"
        android:text="计数: 0" />

    <FrameLayout
        android:id="@+id/fragment_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>
<!-- res/layout/fragment_car.xml -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:padding="16dp">

    <Button
        android:id="@+id/btn_increment"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="增加计数" />
</LinearLayout>

运行与验证

  1. 创建Android工程,包名设为com.example.carlifecycle,将上述Java代码放入app/src/main/java/com/example/carlifecycle/,XML布局放入app/src/main/res/layout/。
  2. 在app/build.gradle中确认依赖包含androidx.appcompat:appcompat:1.6.0和androidx.fragment:fragment:1.6.0。
  3. 连接车载模拟器或真机(API 26+),点击Run运行应用。
  4. 打开Logcat,过滤标签CarLifecycle,观察生命周期日志。

通过条件

  • 首次启动时,日志顺序为:Activity onCreate → onStart → onResume,Fragment onAttach → onCreate → onCreateView → onViewCreated → onStart → onResume。
  • 点击“增加计数”按钮,界面计数递增,Logcat输出对应日志。
  • 旋转屏幕(或模拟配置变更),Activity和Fragment均经历销毁与重建。Activity的onSaveInstanceState被调用,重建后onCreate中恢复count值,界面显示旋转前的计数。
  • Fragment在Activity重建后自动恢复,不会重复添加,且其视图正常显示。
  • 按Home键或关闭屏幕(模拟车载熄屏),Activity依次回调onPause → onStop;重新回到应用时回调onRestart → onStart → onResume。
车载环境Activity与Fragment生命周期时序 Activity Fragment 车载系统事件 首次启动 onCreate onAttach onCreate onCreateView onViewCreated onStart onStart onResume onResume 屏幕旋转/配置变更 onSaveInstanceState onDestroyView onDestroy onDestroy Activity回调 Fragment回调 系统事件

常见问题

  • Fragment重复添加:Activity重建后若在onCreate中无条件添加Fragment,会导致重复。本案例通过savedInstanceState == null判断避免。
  • 状态丢失:车载系统可能因内存不足直接杀死后台Activity,此时onSaveInstanceState仍会被调用,但恢复时机在用户重新导航回该Activity时。本案例的计数恢复逻辑覆盖此场景。
  • 车载特殊场景:车辆熄火时系统可能直接调用onStop而不经过onPause(取决于OEM实现),因此资源释放应放在onStop而非onPause中,本案例日志可帮助观察实际回调顺序。