如何在应用程序首次启动时只使用特定活动来启动应用程序?

3
我想在我的应用程序仅首次启动时启动一个活动,此后每次启动应用程序时都应启动另一个(启动器)活动。 因此,我实现了基于这个SO答案的解决方案。
解决方案围绕一个布尔偏好项(以下代码中具有startedBeforePreferenceKey作为键的偏好项)展开。 在启动器活动的onCreate()中,我尝试检索具有键startedBeforePreferenceKey的偏好项,并将其存储在变量startedBefore中。 如果不存在该喜好,则将为startedBefore分配false。
然后,我检查startedBefore是否为false,如果是,我创建所提到的偏好项,给它一个值true并将其存储在SharedPreferences中,并启动此活动,该活动应仅在第一次启动应用程序时启动。 这样,当执行下一次此检查时,将执行onCreate(),startedBefore将被分配为true,因此此仅启动一次的活动将不会启动。 问题是,当应用程序首次启动时,正常的启动器会瞬间显示,然后启动仅在首次启动应用程序时启动应用程序的活动。 当应用程序首次启动时,正常的启动器根本不应该显示。 直接显示我想要在应用程序首次启动时显示的特殊活动。 我该怎么办?
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
boolean startedBefore = sharedPreferences.getBoolean(getString(R.string.startedBeforePreferenceKey), false);

if (!startedBefore) {
    SharedPreferences.Editor sharedPreferencesEditor = sharedPreferences.edit();
    sharedPreferencesEditor.putBoolean(getString(R.string.startedBeforePreferenceKey), true);
    sharedPreferencesEditor.commit();
    startActivity(new Intent(this, MainActivity.class)); 
} 

编辑:@HammadTariqSahi

首先,这是来自LogCat的摘录:

03-16 08:42:25.629: E/AndroidRuntime(1837): FATAL EXCEPTION: main
03-16 08:42:25.629: E/AndroidRuntime(1837): Process: tests.globalactivitytest, PID: 1837
03-16 08:42:25.629: E/AndroidRuntime(1837): java.lang.RuntimeException: Unable to instantiate application tests.globalactivitytest.activity.GlobalActivity: java.lang.ClassNotFoundException: Didn't find class "tests.globalactivitytest.activity.GlobalActivity" on path: DexPathList[[zip file "/data/app/tests.globalactivitytest-1/base.apk"],nativeLibraryDirectories=[/data/app/tests.globalactivitytest-1/lib/x86, /vendor/lib, /system/lib]]
03-16 08:42:25.629: E/AndroidRuntime(1837):     at android.app.LoadedApk.makeApplication(LoadedApk.java:578)
03-16 08:42:25.629: E/AndroidRuntime(1837):     at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4680)
03-16 08:42:25.629: E/AndroidRuntime(1837):     at android.app.ActivityThread.-wrap1(ActivityThread.java)
03-16 08:42:25.629: E/AndroidRuntime(1837):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1405)
03-16 08:42:25.629: E/AndroidRuntime(1837):     at android.os.Handler.dispatchMessage(Handler.java:102)
03-16 08:42:25.629: E/AndroidRuntime(1837):     at android.os.Looper.loop(Looper.java:148)
03-16 08:42:25.629: E/AndroidRuntime(1837):     at android.app.ActivityThread.main(ActivityThread.java:5417)
03-16 08:42:25.629: E/AndroidRuntime(1837):     at java.lang.reflect.Method.invoke(Native Method)
03-16 08:42:25.629: E/AndroidRuntime(1837):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
03-16 08:42:25.629: E/AndroidRuntime(1837):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
03-16 08:42:25.629: E/AndroidRuntime(1837): Caused by: java.lang.ClassNotFoundException: Didn't find class "tests.globalactivitytest.activity.GlobalActivity" on path: DexPathList[[zip file "/data/app/tests.globalactivitytest-1/base.apk"],nativeLibraryDirectories=[/data/app/tests.globalactivitytest-1/lib/x86, /vendor/lib, /system/lib]]
03-16 08:42:25.629: E/AndroidRuntime(1837):     at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
03-16 08:42:25.629: E/AndroidRuntime(1837):     at java.lang.ClassLoader.loadClass(ClassLoader.java:511)
03-16 08:42:25.629: E/AndroidRuntime(1837):     at java.lang.ClassLoader.loadClass(ClassLoader.java:469)
03-16 08:42:25.629: E/AndroidRuntime(1837):     at android.app.Instrumentation.newApplication(Instrumentation.java:981)
03-16 08:42:25.629: E/AndroidRuntime(1837):     at android.app.LoadedApk.makeApplication(LoadedApk.java:573)
03-16 08:42:25.629: E/AndroidRuntime(1837):     ... 9 more
03-16 08:42:25.629: E/AndroidRuntime(1837):     Suppressed: java.lang.ClassNotFoundException: tests.globalactivitytest.activity.GlobalActivity
03-16 08:42:25.629: E/AndroidRuntime(1837):         at java.lang.Class.classForName(Native Method)
03-16 08:42:25.629: E/AndroidRuntime(1837):         at java.lang.BootClassLoader.findClass(ClassLoader.java:781)
03-16 08:42:25.629: E/AndroidRuntime(1837):         at java.lang.BootClassLoader.loadClass(ClassLoader.java:841)
03-16 08:42:25.629: E/AndroidRuntime(1837):         at java.lang.ClassLoader.loadClass(ClassLoader.java:504)
03-16 08:42:25.629: E/AndroidRuntime(1837):         ... 12 more
03-16 08:42:25.629: E/AndroidRuntime(1837):     Caused by: java.lang.NoClassDefFoundError: Class not found using the boot class loader; no stack trace available

GlobalActivity.java:

package tests.globalactivitytest;

import android.app.Application;
import android.content.Intent;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;

public class GlobalActivity extends Application {
    @Override
    public void onCreate() {
        super.onCreate();

        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
        //SharedPreferences.Editor editor = sharedPreferences.edit(); 
        boolean launchedBefore = sharedPreferences.getBoolean("launchedBefore", false);
        if (launchedBefore) {
            Intent intent = new Intent(this, MainActivity.class);
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            startActivity(intent);
        } else {
            Intent intent = new Intent(this, LaunchOnceActivity.class);
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            startActivity(intent);
        }
    }
}

MainActivity.java:

package tests.globalactivitytest;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

LaunchOnceActivity.java:

package tests.globalactivitytest;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;

public class LaunchOnceActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_launch_once);
    }
}

清单文件:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="tests.globalactivitytest"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="23" />

    <application
        android:name=".activity.GlobalActivity"
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".LaunchOnceActivity"
            android:label="@string/title_activity_launch_once" >
        </activity>
    </application>

</manifest>

3
如果没有一些“闪光”效果,你就无法真正地处理这个问题。我的建议是将启动屏幕视图设置为你的启动活动,并从那里处理重定向,这样无论加载哪个活动,你都可以获得一致的体验。 - zgc7009
1
你可以使用某种形式的 SplashScreen(如@zgc7009所述),以及应用程序版本等信息(共享首选项是存储数据的好地方)。官方的 Android 并没有提供你想要实现的功能,这是一个缺点。 - joninx
1
是的,绝对没问题。这并不是任何黑客行为,实际上它是 Android 开发中相当常见的一部分,我的许多应用程序都有启动画面。它们甚至在官方文档中有记录。Smith 先生在下面给出了一个可行的答案。 - zgc7009
由于您的GlobalActivity类在globalactivitytest包中,但是您将其引用为globalactivitytest.activity.GlobalActivity,因此您会收到ClassNotFound异常。我已经检查了您的代码,并且在您的项目中没有activity包。我已经向您发送了更新的apk和代码。 - Developine
更新了答案中的代码,请检查。 - Developine
显示剩余2条评论
3个回答

5

以上的答案也可以,但如果您需要在不使用启动界面的情况下完成操作,则可以使用以下方法:

步骤1:创建一个新类并从Application扩展,添加以下代码(不要忘记相应更改您的活动名称)。其最重要的部分是在启动任何活动之前设置适当的意图标志。

这个GlobalActivity将在您的启动器Activity之前被调用

public class GlobalActivity extends Application {


@Override
public void onCreate() {
    super.onCreate();
  SharedPreferences userInfo = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
  SharedPreferences.Editor  editor = userInfo.edit();
  boolean logedIn =  userInfo.getBoolean("loggedIn", false);
    if (logedIn)
    {
        Intent intent = new Intent(this,MainActivity.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
        startActivity(intent);
    }
    else {
        Intent intent = new Intent(this,LaunchOnceActivity.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
        startActivity(intent);
    }
}
}

步骤2: 您的清单文件应如下所示。
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
 package="com.app.appid" >


<application
android:name=".GlobalActivity"
android:allowBackup="true"
android:icon="@drawable/launch"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
    android:name=".MainActivity"
    android:label="@string/app_name" >
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>
<activity
    android:name=".LaunchOnceActivity"
    android:label="Launch Once" >
</activity>
</application>
</manifest>

然后根据需要在任何活动中相应地更改loggedIn变量,以供将来使用。


onCreate()的第二个语句中,使用getApplicationContext()而不是this有什么原因吗?因为你已经在所有需要ContextstartActivity()语句中使用了this - Solace
我应该在哪里添加launcher意图过滤器? - Solace
1
启动器意图过滤器将与往常一样,无需更改。只需在您想要首先启动的活动中添加,在全局活动的onCreate方法之前调用Launcher活动的onCreate方法。 - Developine
1
如果你想在GlobalActivity中决定启动器,可以像上面那样做。只需将任何活动设置为启动器即可。 - Developine
1
getApplicationContext()和this在这里是相同的,因为您是从应用程序扩展的。 - Developine
显示剩余2条评论

3
你可以创建一个闪屏活动,在它的onCreate方法中检查接下来要运行哪个活动,等待几秒钟,启动活动,然后立即完成闪屏。
如果你不想创建一个闪屏,那么创建一个没有GUI内容和半透明主题的虚假闪屏活动,并在onCreate中进行路由,然后立即完成。这将非常快速,用户不会注意到。

我喜欢这个回答,它提供了一个选项,不强制显示闪屏,但仍然隐藏了闪烁。如果您有时间发布一个示例,那将是很棒的,但这仍然是一个好建议。 - zgc7009
你看过Instagram吗?Instagram、Quora和WhatsApp在首次启动应用时都要求用户注册或登录。难道它们会使用类似的hack吗?我认为他们不会采用hacky解决方案,因为它们都是科技巨头。:s - Solace
@Solace 这个处理那种情况。 - zgc7009
1
@Solace 如果你找到了那些公司是如何做到的,请在这里发布,但我担心它不是开源代码 :) 仍然对他们的方法很好奇。 - Mister Smith
1
@Solace 请注意,Instagram、WhatsApp、Facebook和其他应用程序可能没有最好或最用户友好的做法。以Facebook为例。该应用程序使用的资源与整个Android操作系统的20%相当(在Nexus 5X上进行了测试)。我不会试图跟随他们的脚步。 - SoroushA
我猜测了一些它们的工作方式。如果我们仔细观察,当我们第一次安装Instagram或Quora时,会有一个白色的空白屏幕(闪屏?),然后跳转到登录页面。Quora甚至在白屏上有一个不确定的进度轮。但是困惑的是,对于未来的启动,那个空白的白屏不会显示出来。 - Solace

1
你可以将活动和正常的启动器包装在片段中。然后,在其onCreate中创建一个带有fragmentmanager的另一个活动,并始终将其作为启动器。在该活动中放置正确的片段之前,请检查偏好设置。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接