应用程序从错误的活动重新启动

And*_*dan 2 android

这是一个相当难的问题:

我打开我的应用程序.它启动一个活动,它充当splashscreen(ASplashscreen),在其中我JSON从本地存储(raw文件夹)加载一些数据并将其存储在singleton object(静态)内存中.完成此过程后,它会自动移动到主要活动(AMain)

我通过按下home button并运行其他应用程序,游戏等退出应用程序.当我重新打开我的应用程序时,应用程序在onCreate方法内崩溃,AMain因为它试图使用内部的一些数据,singleton object但数据是null.所以NullPointerException当它这样做时它会抛出一个.看来,它重新启动AMain,而不是ASplashscreensingleton没有机会重新初始化.

这种情况在多次尝试中随机发生......

我有两个假设......

  1. 我的第一个推测,也就是我对Android操作系统的了解,当我运行其他应用程序(尤其是游戏)时,其中一个需要大量内存,所以操作系统从内存中释放我的应用程序以腾出空间,所以singleton data是的garbage collected.

  2. 我还假设当gc从内存中删除我的单例时,操作系统仍保留一些与当前运行活动的"状态"相关的数据,因此它至少知道在AMain关闭应用程序之前它已经打开了活动.这可以解释为什么它重新开放AMain活动而不是ASplashscreen.

我对吗?或者是否有另一种解释为什么我得到这个例外?欢迎任何建议/澄清.

另外,处理这个问题的最佳方法是什么?我的方法是每当我尝试使用它时检查单例数据的存在,如果它是null,那么基本上只是重新启动应用程序.这使得它经历了ASplashscreen所以JSON初始化,一切都很好.

编辑根据要求,这是我的AndroidManifest

 <uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="com.android.vending.BILLING"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

<application
    android:name=".global.App"
    android:allowBackup="true"
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:largeHeap="true"
    android:theme="@style/AppTheme">

    <!--SPLASH SCREEN-->
    <activity
        android:name=".activities.ASplashscreen"
        android:label="@string/app_name"
        android:launchMode="singleTask"
        android:screenOrientation="portrait"
        android:theme="@style/AppTheme">
        <intent-filter>
            <action android:name="android.intent.action.MAIN"/>

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

    <!--MAIN-->
    <activity
        android:name=".activities.AMain"
        android:label="@string/app_name"
        android:launchMode="singleTask"
        android:screenOrientation="portrait"
        android:theme="@style/AppTheme"/>

    <!--MENU-->
    <activity
        android:name=".activities.AMenu"
        android:label="@string/app_name"
        android:launchMode="singleTask"
        android:screenOrientation="portrait"
        android:theme="@style/AppTheme"/>

    <!--HELP-->
    <activity
        android:name=".activities.AHelp"
        android:label="@string/app_name"
        android:launchMode="singleTask"
        android:screenOrientation="portrait"
        android:theme="@style/AppTheme"/>

    <!--ADMOB-->
    <activity
        android:name="com.google.android.gms.ads.AdActivity"
        android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"
        android:theme="@android:style/Theme.Translucent"/>

    <!--FACEBOOK LOGIN ACTIVITY (SDK)-->
    <activity
        android:name="com.facebook.LoginActivity"
        android:label="@string/app_name"
        android:screenOrientation="portrait"
        android:theme="@style/AppTheme"/>

    <!--This meta-data tag is required to use Google Play Services.-->
    <meta-data
        android:name="com.google.android.gms.version"
        android:value="@integer/google_play_services_version"/>

    <!--FACEBOOK STUFF-->
    <meta-data
        android:name="com.facebook.sdk.ApplicationId"
        android:value="@string/facebook_app_id"/>

    <!--GOOGLE PLUS-->
    <meta-data
        android:name="com.google.android.gms.version"
        android:value="@integer/google_play_services_version"/>

    <!--CRASHLYTICS-->
    <meta-data
        android:name="com.crashlytics.ApiKey"
        android:value="9249....."/>

</application>
Run Code Online (Sandbox Code Playgroud)

如果你们真的想要它,这里的内容就是 ASplashscreen

/**
 * @author MAB
 */
public class ASplashscreen extends ABase implements IIosLikeDialogListener {

    private final float SHEEP_WIDTH_FRAC = 0.8f;

    private final int SPLASHSCREEN_DELAY_MS = 500;

    //View references
    private View sheep_image;

    /** The timestamp recorded when this screen came into view. We'll used this to determine how much we'll need to keep the splash screen awake */
    private long mStartTimestamp;

    private IosLikeDialog mDialog;

    private IabHelper mIabHelper;

    // Listener that's called when we finish querying the items and subscriptions we own
    IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
        public void onQueryInventoryFinished(IabResult result, Inventory inventory) {

            // Have we been disposed of in the meantime? If so, quit.
            if (mIabHelper == null) {
                System.out.println("=== IAB INVENTORY PROBLEM :: WE'VE BEEN DISPOSED");
                displayAppStoreUnavailableDialog();
                return;
            }

            // Is it a failure?
            if (result.isFailure()) {
                displayAppStoreUnavailableDialog();
                System.out.println("=== IAB INVENTORY PROBLEM :: FAILED TO QUERY INVENTORY :: " + result);
                return;
            }

            //Sync our static stuff with the app store
            HSounds.instance().populate(ASplashscreen.this, inventory);
            HLights.instance().populate(ASplashscreen.this, inventory);

            //Store the stuff locally just to be sure
            HStorage.persistObjectToFile(ASplashscreen.this, HVersions.SOUNDS);
            HStorage.persistObjectToFile(ASplashscreen.this, HVersions.LIGHTS);

            System.out.println("=== SUCCESSFULLY SYNCED WITH STORE !");

            jumpToMainActivity();

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.a_splashscreen);

        init();

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        if (mIabHelper != null) {
            mIabHelper.dispose();
        }
        mIabHelper = null;
    }

    @Override
    public void onIosLikeDialogBtnsClick(int btnStringResID) {
        if (btnStringResID == IosLikeDialog.BTN_OK) {
            jumpToMainActivity();
        }
    }

    private void init() {
        //Get view references
        sheep_image = findViewById(R.id.splashscreen_sheep);

        mStartTimestamp = System.currentTimeMillis();

        VersionTracking.setVersions(this);

        //Set the width of the sheep
        RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) sheep_image.getLayoutParams();
        params.width = (int) ((float) UScreen.getScreenWidthInPortrait(this) * SHEEP_WIDTH_FRAC);
        sheep_image.setLayoutParams(params);

        mDialog = new IosLikeDialog()
                .with(findViewById(R.id.ios_like_dialog_main_container))
                .listen(this);

        new Thread(new Runnable() {
            @Override
            public void run() {

                parseJsons();

                //Get the filler bar values from shared prefs
                HBrightness.instance().retrieveFromPersist(ASplashscreen.this);
                HSensorAndTimer.instance().retrieveFromPersist(ASplashscreen.this);

                WsBuilder.build(ASplashscreen.this).getGift(new ResponseListener<EGift>() {
                    @Override
                    public void onSuccess(EGift gifts) {
                        long now = System.currentTimeMillis();
                        SimpleDateFormat fmt = new SimpleDateFormat(HJsonDataBase.GIFT_DATE_FORMAT);
                        Date start;
                        Date end;

                        //Handle the gifts
                        if (gifts != null && gifts.data != null && gifts.responseOK()) {
                            //Go through the SOUNDS and check if we need to set them as gifts, if not reset them
                            for (ESound sound : HSounds.instance().getValues().getSounds()) {
                                String sku = sound.getSku(ASplashscreen.this);
                                sound.giftStart = null;
                                sound.giftEnd = null;
                                for (String giftSku : gifts.data.inapps) {
                                    if (giftSku.equals(sku)) {
                                        sound.giftStart = gifts.data.start_date;
                                        sound.giftEnd = gifts.data.end_date;
                                        break;
                                    }
                                }
                                //Check if redeemed gift expired and if so, reset the dates
                                checkSoundGiftExpired(sound, fmt, now);
                            }
                            //Go through the LIGHTS and check if we need to set them as gifts, if not reset them
                            for (ELight light : HLights.instance().getValues().getLights()) {
                                String sku = light.getSku(ASplashscreen.this);
                                light.giftStart = null;
                                light.giftEnd = null;
                                for (String giftSku : gifts.data.inapps) {
                                    if (giftSku.equals(sku)) {
                                        light.giftStart = gifts.data.start_date;
                                        light.giftEnd = gifts.data.end_date;
                                        break;
                                    }
                                }
                                //Check if redeemed gift expired and if so, reset the dates
                                checkLightGiftExpired(light, fmt, now);
                            }
                            //Persist the data in the local storage
                            HStorage.persistObjectToFile(ASplashscreen.this, HVersions.SOUNDS);
                            HStorage.persistObjectToFile(ASplashscreen.this, HVersions.LIGHTS);
                        }

                        //Run the IAB helper now
                        runIabHelper();
                    }

                    @Override
                    public void onErrorResponse(VolleyError error) {
                        //This might mean we're in offline mode, so check if the gifts expired
                        checkAllLightsGiftExpired();
                        checkAllSoundsGiftExpired();

                        //Run the IAB helper now
                        runIabHelper();
                    }
                }, getPackageName());
            }
        });
    }

    /**
     * This is run on a non-UI thread !!
     */
    private void parseJsons() {

        /**
         * Versions
         */
        parseVersions();


        /**
         * BACKGROUND
         */
        parseBackgrounds();
        try {
            validateBackgrounds();
        } catch (NullPointerException e) {
            removeBackgroundsFile();
            parseBackgrounds();
        }

        /**
         * LIGHTS
         */
        parseLights();
        try {
            validateLights();
        } catch (NullPointerException e) {
            removeLightsFile();
            parseLights();
        }

        /**
         * SOUNDS
         */
        parseSounds();
        try {
            validateSounds();
        } catch (NullPointerException e) {
            removeSoundsFile();
            parseSounds();
        }

    }

    private void parseVersions() {
        InputStream in = getResources().openRawResource(R.raw.versions);
        EVersions versions = null;
        try {
            versions = UGson.jsonToObject(in, EVersions.class);
        } catch (Exception e) {
            System.out.println("==== PARSE ERROR :: VERSIONS :: " + e.getMessage());
            e.printStackTrace();
            return;
        }
        HVersions.instance().setValues(this, versions);
    }

    private void parseBackgrounds() {
        //Get the version of he JSONS at which we've last updated them from the "raw" folder
        int lastVersionBckgnds = UPersistent.getInt(ASplashscreen.this, HVersions.SHARED_PREF_LAST_JSONS_VERSION_BCKGNDS, 0);

        InputStream in;
        //If there are no files in local storage OR there's a new version of the JSON files that we need to retrieve
        if (!HStorage.fileExists(ASplashscreen.this, HStorage.FILE_JSON_BACKGROUNDS) ||
                HVersions.instance().shouldUpdateFromResources(HVersions.BACKGROUNDS, lastVersionBckgnds)) { //Update from raw folder
            in = getResources().openRawResource(R.raw.backgrounds);
        } else { //Update from local storage
            in = HStorage.getInputStreamForFile(ASplashscreen.this, HStorage.FILE_JSON_BACKGROUNDS);
        }
        EBackgrounds bckgnds = null;
        try {
            bckgnds = UGson.jsonToObject(in, EBackgrounds.class);
        } catch (Exception e) {
            System.out.println("==== PARSE ERROR :: BACKGROUNDS :: " + e.getMessage());
            e.printStackTrace();
        }
        HBackgrounds.instance().setValues(this, bckgnds);
    }

    private void parseLights() {
        //Get the version of he JSONS at which we've last updated them from the "raw" folder
        int lastVersionLights = UPersistent.getInt(ASplashscreen.this, HVersions.SHARED_PREF_LAST_JSONS_VERSION_LIGHTS, 0);

        InputStream in;
        //If there are no files in local storage OR there's a new version of the JSON files that we need to retrieve
        if (!HStorage.fileExists(ASplashscreen.this, HStorage.FILE_JSON_LIGHTS) ||
                HVersions.instance().shouldUpdateFromResources(HVersions.LIGHTS, lastVersionLights)) { //Update from raw folder
            in = getResources().openRawResource(R.raw.lights);
        } else { //Update from local storage
            in = HStorage.getInputStreamForFile(ASplashscreen.this, HStorage.FILE_JSON_LIGHTS);
        }
        ELights lights = null;
        try {
            lights = UGson.jsonToObject(in, ELights.class);
        } catch (Exception e) {
            System.out.println("==== PARSE ERROR :: LIGHTS :: " + e.getMessage());
            e.printStackTrace();
        }
        if (lights != null) {
            HLights.instance().setValues(this, lights);
        }
    }

    private void parseSounds() {
        int lastVersionSounds = UPersistent.getInt(ASplashscreen.this, HVersions.SHARED_PREF_LAST_JSONS_VERSION_SOUNDS, 0);

        InputStream in;
        //If there are no files in local storage OR there's a new version of the JSON files that we need to retrieve
        if (!HStorage.fileExists(ASplashscreen.this, HStorage.FILE_JSON_SOUNDS) ||
                HVersions.instance().shouldUpdateFromResources(HVersions.SOUNDS, lastVersionSounds)) { //Update from raw folder
            in = getResources().openRawResource(R.raw.sounds);
        } else { //Update from local storage
            in = HStorage.getInputStreamForFile(ASplashscreen.this, HStorage.FILE_JSON_SOUNDS);
        }
        ESounds sounds = null;
        try {
            sounds = UGson.jsonToObject(in, ESounds.class);
        } catch (Exception e) {
            System.out.println("==== PARSE ERROR :: SOUNDS" + e.getMessage());
        }
        if (sounds != null) {
            HSounds.instance().setValues(this, sounds);
        }
    }

    private void validateBackgrounds() throws NullPointerException {
        if (HBackgrounds.instance().getValues() == null) {
            throw new NullPointerException();
        }
        if (HBackgrounds.instance().getValues().getBackgrounds() == null) {
            throw new NullPointerException();
        }
    }

    private void validateLights() throws NullPointerException {
        if (HLights.instance().getValues() == null) {
            throw new NullPointerException();
        }
        if (HLights.instance().getValues().getLights() == null) {
            throw new NullPointerException();
        }
    }

    private void validateSounds() throws NullPointerException {
        if (HSounds.instance().getValues() == null) {
            throw new NullPointerException();
        }
        if (HSounds.instance().getValues().getSounds() == null) {
            throw new NullPointerException();
        }
    }

    private void removeBackgroundsFile() {
        HStorage.deleteFile(this, HStorage.FILE_JSON_BACKGROUNDS);
    }

    private void removeLightsFile() {
        HStorage.deleteFile(this, HStorage.FILE_JSON_LIGHTS);
    }

    private void removeSoundsFile() {
        HStorage.deleteFile(this, HStorage.FILE_JSON_SOUNDS);
    }

    private void runIabHelper() {

        //If there's no network connection, then ... sorry
        if (!UNetwork.isNetworkAvailable(this)) {
            displayAppStoreUnavailableDialog();
            System.out.println("=== IAB ERROR :: NO NETWORK");
            return;
        }

        try {
            mIabHelper = new IabHelper(ASplashscreen.this, CIab.IAB_PUBLIC_KEY);
            mIabHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
                @Override
                public void onIabSetupFinished(IabResult result) {
                    if (!result.isSuccess()) {
                        // Oh noes, there was a problem.
                        System.out.println("=== IAB ERROR :: CONNECTION :: " + result);
                        displayAppStoreUnavailableDialog();
                        return;
                    }

                    //Obtain and create the list of skus from both the LIGHTS and the SOUNDS handlers
                    List<String> skus = new ArrayList<String>();
                    skus.addAll(HSounds.instance().createSkuList(ASplashscreen.this, true));
                    skus.addAll(HLights.instance().createSkuList(ASplashscreen.this, true));

                    //Get the inventory
                    try {
                        mIabHelper.queryInventoryAsync(true, skus, mGotInventoryListener, new Thread.UncaughtExceptionHandler() {
                            @Override
                            public void uncaughtException(Thread thread, Throwable ex) {
                                //                            Crashlytics.logException(ex);
                                System.out.println("=== IAB ERROR :: query inventory crashed :: " + ex.getMessage());
                                displayAppStoreUnavailableDialog();
                            }
                        });
                    } catch (IllegalStateException e) {
                        displayAppStoreUnavailableDialog();
                    }
                }
            });
        } catch (NullPointerException e1) {
            //            Crashlytics.logException(e1);
            System.out.println("=== IAB ERROR :: query inventory crashed :: " + e1.getMessage());
            displayAppStoreUnavailableDialog();
        } catch (IllegalArgumentException e2) {
            //            Crashlytics.logException(e2);
            System.out.println("=== IAB ERROR :: query inventory crashed :: " + e2.getMessage());
            displayAppStoreUnavailableDialog();
        }
    }

    private void displayAppStoreUnavailableDialog() {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                if (mDialog == null) {
                    return;
                }
                mDialog.reset()
                        .header(R.string.inapp_store_unavailable_header)
                        .subheader(R.string.inapp_store_unavailable_subheader)
                        .btnOK()
                        .show();
            }
        });
    }

    private void jumpToMainActivity() {

        int timePassed = (int) (System.currentTimeMillis() - mStartTimestamp);

        int delay = (timePassed > SPLASHSCREEN_DELAY_MS) ? 0 : (SPLASHSCREEN_DELAY_MS - timePassed);

        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {

                //In case we need to display the tutorial, then do so
                if (AHelp.shouldDisplayTutorial(ASplashscreen.this)) {
                    CrashReport.log("ASplashscreen -> AHelp");
                    Intent i = new Intent(ASplashscreen.this, AHelp.class);
                    i.putExtra(AHelp.BUNDLE_SHOW_TUTORIAL, true);
                    startActivity(i);
                    finish();
                    overridePendingTransition(R.anim.anim_slide_in_from_bottom, R.anim.anim_stay_put);
                    return;
                } else { //Otherwise continue with normal flow
                    CrashReport.log("ASplashscreen -> AMain");
                    Intent i = new Intent(ASplashscreen.this, AMain.class);
                    i.putExtra(AMain.BUNDLE_DEBUGGING_CAME_FROM_SPLASHSCREEN, true);
                    startActivity(i);
                    finish();
                }

            }
        }, delay);
    }

    private void checkAllSoundsGiftExpired() {
        SimpleDateFormat fmt = new SimpleDateFormat(HJsonDataBase.GIFT_DATE_FORMAT);
        long now = System.currentTimeMillis();

        for (ESound sound : HSounds.instance().getValues().getSounds()) {
            if (sound != null) {
                checkSoundGiftExpired(sound, fmt, now);
            }
        }
    }

    private void checkAllLightsGiftExpired() {
        SimpleDateFormat fmt = new SimpleDateFormat(HJsonDataBase.GIFT_DATE_FORMAT);
        long now = System.currentTimeMillis();

        for (ELight light : HLights.instance().getValues().getLights()) {
            if (light != null) {
                checkLightGiftExpired(light, fmt, now);
            }
        }
    }

    private void checkSoundGiftExpired(ESound sound, SimpleDateFormat fmt, long now) {
        if (UString.stringsExist(sound.giftExpireStart, sound.giftExpireEnd)) {
            try {
                Date start = fmt.parse(sound.giftExpireStart);
                Date end = fmt.parse(sound.giftExpireEnd);
                if (now < start.getTime() || end.getTime() < now) {
                    sound.giftExpireStart = null;
                    sound.giftExpireEnd = null;
                }
            } catch (ParseException e) {
                //Do nothin'
            }
        }
    }

    private void checkLightGiftExpired
            (ELight light, SimpleDateFormat fmt, long now) {
        if (UString.stringsExist(light.giftExpireStart, light.giftExpireEnd)) {
            try {
                Date start = fmt.parse(light.giftExpireStart);
                Date end = fmt.parse(light.giftExpireEnd);
                if (now < start.getTime() || end.getTime() < now) {
                    light.giftExpireStart = null;
                    light.giftExpireEnd = null;
                }
            } catch (ParseException e) {
                //Do nothin'
            }
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

Irc*_*ver 7

使用单例时,应该有一些getInstane方法return instance,所以你可以把你的支票放在里面,如下所示:

public static SingletonClass getInstance() {
    if(instance == null) {
        instance = StaticMethodToLoadInstance();
    }
    return instance;
}
Run Code Online (Sandbox Code Playgroud)

我想你可以将所有数据加载代码放在静态内StaticMethodToLoadInstance().

更新

是的,加载数据可能会花费很多时间,因此可以使用其他方法.首先,创建自己的界面:

public static interface OnInstanceLoadedListener {
    public void onIntsanceLoaded(SingletonClass instance);
}
Run Code Online (Sandbox Code Playgroud)

然后getInstance按以下方式更改:

public static void getInstance(final OnInstanceLoadedListener listener, Activity context) {
    final ProgressDialog dialog = null;
    if(instance == null) {//if there should be loading
        dialog = StaticMethodToCreateProgressDialog(context);
        dialog.show();
    }
    new Thread(new Runnable() {
        @Override
        public void run() {
            if(instance == null) {
                instance = StaticMethodToLoadInstance();
            }
            context.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    listener.onIntsanceLoaded(instance);
                    if(dialog != null && dialog.isShowing())
                        dialog.dismiss();
                }
            });
        }
    }).start();
}
Run Code Online (Sandbox Code Playgroud)

而且你的getInstance用法会改变

SingletonClass object = SingletonClass.getInstance();
String data = object.getData();
Run Code Online (Sandbox Code Playgroud)

getInstance(new OnInstanceLoadedListener() {
    @Override
    public void onIntsanceLoaded(SingletonClass instance) {
        String data = instance.getData();
    }
}, YourActivityClass.this);
Run Code Online (Sandbox Code Playgroud)

这样,您的数据将异步加载.是的,它看起来更难,但它可以显示进度对话框 - 用户可以看到应用程序仍然有效.


Dav*_*ser 7

这是非常标准的Android行为.当您的应用程序在后台时,无论出于何种原因,它都可以随时被杀死.Android只会杀死托管您应用的操作系统进程.

当用户返回您的应用程序(或重新启动您的应用程序)时,Android意识到它之前已经杀死了您的应用程序,因此它创建了一个新的操作系统进程来托管您的应用程序,然后它Application为您的应用程序实例化实例,然后它实例化最顶层Activity在任务堆栈中(即:Activity当你的应用程序进入后台时屏幕上的那个),然后它调用onCreate()Activity以便Activity可以自我恢复.Android将最近保存的instaces状态Activity作为Bundle参数传递给onCreate().这样,Activity就有机会恢复自己.

Activity正在崩溃,因为它依赖于之前应该设置的数据.在Android杀死并重新创建应用程序的操作系统进程的情况下,此数据消失了.

有多种方法可以解决这个问题,其中一种方法已经使用过:

  • onCreate()所有活动中,检查是否已使用public static变量或单例执行"应用程序初始化" .如果初始化尚未完成,你知道你的应用程序的过程中被打死,重建,你需要或者将用户重定向到你的根Activity(即:从头再来的应用程序)或立即做初始化中onCreate()Activity.

  • 保存所需的数据onSaveInstanceState()并将其恢复到onCreate()和/ onRestoreInstanceState()或两者中.

  • 不要将此数据保留在内存中.将其保存在数据库或其他基于非内存的持久性结构中.

注意:一般情况下,您不应该使用launchMode="singleTask".在大多数情况下,这是不必要的,并且通常会导致比解决的问题更多的问题.这与您遇到的进程终止/重新创建问题无关,但您仍应避免使用特殊的启动模式singleTasksingleInstance.只有在创建HOME屏幕替换时才需要这些.