深层链接启动 Splash Activity 而不是 Main Activity

Posted

技术标签:

【中文标题】深层链接启动 Splash Activity 而不是 Main Activity【英文标题】:Deep link launches the Splash Activity and not the Main Activity 【发布时间】:2018-06-13 15:41:32 【问题描述】:

我跟随 this guide - Splash screens the right way 为我的 android 应用程序创建了一个启动画面,所以现在我有 2 个活动(MainActivity 和 SplashActivity)

问题在于 Deep Links 现在无法正常运行,因为他们没有启动 MainActivity,而是启动了 SplashActivity

我不希望 SplashActivity 出现,除非在应用启动时出现。

我能做什么?

SplashActivity:

public class SplashActivity extends AppCompatActivity 
    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        Intent intent = new Intent(this, MainActivity.class);
        startActivity(intent);
        finish();
    

MainActivity:

public class MainActivity extends SplashActivity implements OnImagePickerPermissionsCallback 

    private PermissionListener listener;

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

    @Override
    public void setPermissionListener(PermissionListener listener)
    
        this.listener = listener;
    

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)
    
        if (listener != null)
        
            listener.onRequestPermissionsResult(requestCode, permissions, grantResults);
        
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    


    @Override
    public View createSplashLayout() 
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) 
            Window window = getWindow();
            window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
            window.setStatusBarColor(getResources().getColor(R.color.navBarColor));
        
        LinearLayout view = new LinearLayout(this);
//        view.setBackgroundColor(getResources().getColor(R.color.catalyst_redbox_background));
        view.setBackgroundResource(R.drawable.launch_screen_radius);
        return view;
    

清单文件:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

<!-- For using react-native-fcm CLOUD MESSAGING-->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.VIBRATE" />


<!-- For using react-native-image-picker -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

<!-- For react-native-webview-file-upload-android -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>


<!-- For using NetInfo -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

<uses-sdk
    android:minSdkVersion="16"
    android:targetSdkVersion="24" />

<application
    android:name=".MainApplication"
    android:allowBackup="true"
    android:label="@string/app_name"
    android:icon="@mipmap/ic_launcher"
    android:theme="@style/AppTheme"
    >

    <!-- The 2 services below are for react-native-fcm cloud messaging -->
    <service android:name="com.evollu.react.fcm.MessagingService" android:enabled="true" android:exported="true">
        <intent-filter>
            <action android:name="com.google.firebase.MESSAGING_EVENT"/>
        </intent-filter>
    </service>
    <service android:name="com.evollu.react.fcm.InstanceIdService" android:exported="false">
        <intent-filter>
            <action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>
        </intent-filter>
    </service>

    <!-- The 2 receivers below are for react-native-fcm local notifications-->
    <receiver android:name="com.evollu.react.fcm.FIRLocalMessagingPublisher"/>
    <receiver android:enabled="true" android:exported="true"  android:name="com.evollu.react.fcm.FIRSystemBootEventReceiver">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED"/>
            <action android:name="android.intent.action.QUICKBOOT_POWERON"/>
            <action android:name="com.htc.intent.action.QUICKBOOT_POWERON"/>
            <category android:name="android.intent.category.DEFAULT" />
        </intent-filter>
    </receiver>

    <activity
        android:name=".SplashActivity"
        android:label="@string/app_name"
        android:theme="@style/SplashTheme">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

    <activity
        android:name=".MainActivity"
        android:label="@string/app_name"
        android:windowSoftInputMode="adjustResize"
        android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
        android:screenOrientation="portrait"
        android:launchMode="singleTop"
        >
        <!-- launchMode="singleTop" is for fcm cloud messaging -->
        <intent-filter>
            <action android:name="android.intent.action.DOWNLOAD_COMPLETE"/>
        </intent-filter>

        <!--
            The intent filter below are for react-native-fcm click_action
            https://github.com/evollu/react-native-fcm#config-for-notification-and-click_action-in-android
        -->
        <intent-filter>
            <action android:name="fcm.ACTION.HELLO" />
            <category android:name="android.intent.category.DEFAULT" />
        </intent-filter>

        <intent-filter>
            <!-- Sets the intent action to view the activity -->
            <action android:name="android.intent.action.VIEW" />

            <!-- Allows the deep link to be used without specifying the app name -->
            <category android:name="android.intent.category.DEFAULT" />

            <!-- Allows the link to be opened from a web browser -->
            <category android:name="android.intent.category.BROWSABLE" />

            <!-- Accepts URIs that begin with "https://www.myExampleDomain.com -->
            <data android:scheme="https" android:host="www.myExampleDomain.com" />

            <!-- Accepts URIs that begin with "myExampleDomain:// -->
            <data android:scheme="myExampleDomain"/>
        </intent-filter>
    </activity>
    <activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
</application>

编辑:有人指出我使用的是SplashActivity。没错,就是下面这个活动的代码。 它是我用于屏幕导航的库react-native-navigation 的一部分。不确定这是否有帮助,但这是代码:

SplashActivity:

public abstract class SplashActivity extends AppCompatActivity 
    public static boolean isResumed = false;

    public static void start(Activity activity) 
        Intent intent = activity.getPackageManager().getLaunchIntentForPackage(activity.getPackageName());
        if (intent == null) return;
        intent.setAction(Intent.ACTION_MAIN);
        intent.addCategory(Intent.CATEGORY_LAUNCHER);
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        activity.startActivity(intent);
    

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setSplashLayout();
        IntentDataHandler.saveIntentData(getIntent());
    

    @Override
    protected void onResume() 
        super.onResume();
        isResumed = true;

        if (NavigationApplication.instance.getReactGateway().hasStartedCreatingContext()) 
            if (CompatUtils.isSplashOpenedOverNavigationActivity(this, getIntent())) 
                finish();
                return;
            
            NavigationApplication.instance.getEventEmitter().sendAppLaunchedEvent();
            if (NavigationApplication.instance.clearHostOnActivityDestroy()) 
                overridePendingTransition(0, 0);
                finish();
            
            return;
        

        if (ReactDevPermission.shouldAskPermission()) 
            ReactDevPermission.askPermission(this);
            return;
        

        if (NavigationApplication.instance.isReactContextInitialized()) 
            NavigationApplication.instance.getEventEmitter().sendAppLaunchedEvent();
            return;
        

        // TODO I'm starting to think this entire flow is incorrect and should be done in Application
        NavigationApplication.instance.startReactContextOnceInBackgroundAndExecuteJS();
    

    @Override
    protected void onPause() 
        super.onPause();
        isResumed = false;
    

    private void setSplashLayout() 
        final int splashLayout = getSplashLayout();
        if (splashLayout > 0) 
            setContentView(splashLayout);
         else 
            setContentView(createSplashLayout());
        
    

    /**
     * @return xml layout res id
     */
    @LayoutRes
    public int getSplashLayout() 
        return 0;
    

    /**
     * @return the layout you would like to show while react's js context loads
     */
    public View createSplashLayout() 
        View view = new View(this);
        view.setBackgroundColor(Color.WHITE);
        return view;
    

MainApplication.java 公共类 MainApplication 扩展 NavigationApplication 实现 ReactInstanceHolder

    @Override
    public boolean clearHostOnActivityDestroy() 
        return false;
    

    @Override
    protected void attachBaseContext(Context base) 
        super.attachBaseContext(base);
        if (android.os.Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT_WATCH) 
            // only for KITKAT_WATCH and newer versions
            MultiDex.install(this);
        
    

    @Override
    public boolean isDebug() 
        // Make sure you are using BuildConfig from your own application
        return BuildConfig.DEBUG;
    

    @Override
    public String getJSMainModuleName() 
        return "index";
    


    @Override
    public void onConfigurationChanged(Configuration newConfig) 
        super.onConfigurationChanged(newConfig);
        // Log.v(TAG, "onConfigChange"+newConfig);
        Intent intent = new Intent("onConfigurationChanged");
        intent.putExtra("newConfig", newConfig);
        this.sendBroadcast(intent);
    


    // 2. Override the getJSBundleFile method in order to let
    // the CodePush runtime determine where to get the JS
    // bundle location from on each app start
    @Override
    public String getJSBundleFile() 
        return CodePush.getJSBundleFile();
    

    @NonNull
    @Override
    public List<ReactPackage> createAdditionalReactPackages() 
        return Arrays.<ReactPackage>asList(
            new LinearGradientPackage(),
            new OrientationPackage(),
            new VectorIconsPackage(),
            new KeychainPackage(),
            new BackgroundTimerPackage(),
            new RNI18nPackage(),
            BugsnagReactNative.getPackage(),
            new BlurViewPackage(),
            new PickerViewPackage(),
            new ImagePickerPackage(),
            new RNFetchBlobPackage(),
            new MapsPackage(),
            new FIRMessagingPackage(),
            new RNAmplitudeSDKPackage(MainApplication.this),
            new RNVersionCheckPackage(),
            new RNCardIOPackage(),
            new AndroidWebViewPackage(),
            new WheelPackage()
        );
    


    @Override
    public void onCreate() 
        super.onCreate();
        setActivityCallbacks(new ActivityCallbacks() 
            @Override
            public void onActivityCreated(Activity activity, Bundle savedInstanceState) 

            

            @Override
            public void onActivityStarted(Activity activity) 

            

            @Override
            public void onActivityResumed(Activity activity) 

            

            @Override
            public void onActivityPaused(Activity activity) 

            

            @Override
            public void onActivityStopped(Activity activity) 

            

            @Override
            public void onActivityResult(int requestCode, int resultCode, Intent data) 

            

            @Override
            public void onActivityDestroyed(Activity activity) 

            
//            @Override
//            public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) 
//                super.onRequestPermissionsResult(requestCode, permissions, grantResults);
//            

        );
    

    @Override
    public ReactInstanceManager getReactInstanceManager() 
        return getReactNativeHost().getReactInstanceManager();
    

任何提示将不胜感激, 谢谢!

【问题讨论】:

你知道MainActivity extends SplashActivity吗? @tynn 当然,那只是com.reactnativenavigation.controllers.SplashActivity; 那是因为我在使用一个 react-native 导航库,不要理会。​​span> 你可以通过不使用spash activity来防止导航问题,而只是使用窗口背景,一旦加载就将其切换为真实的。这不是您问题的直接答案,因为它是使用活动的不同方法。你可以在这里找到一个 tl;dr:github.com/bleeding182/samples/tree/master/SplashScreen 【参考方案1】:

例如:您的网址将类似于 https://example.com 并且您在 Android Manifest 中有如下意图过滤器:

<activity
    android:name="com.droid.MainActivity"
    android:label="@string/app_name" >

    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data
            android:host="example.com"
            android:scheme="https" />
    </intent-filter>
</activity>

使用 URL 映射 编辑器通过 Android 应用链接(Android Studio > 工具 > Android 应用链接)轻松地将 URL 意图过滤器添加到您的活动。

【讨论】:

感谢您抽出宝贵的时间来回答,但这如何回答这个问题?顺便说一句,深层链接确实可以正常工作,只是出现了错误的屏幕。 plus1 用于添加 url 链接 .. 我在手机中打开它并直接重定向到我的应用程序 ..wow 如果我们需要通过deeplink启动App类怎么办?有可能吗?【参考方案2】:

android 系统查看清单中的第一个标签:

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

将 MainActivity 设置为使用此标签而不是 SplashActivity。

始终首先启动 MainActivity,然后在您的 MainActivity onCreate 中对 Intent 执行任何检查。

    //replace getStringExtra with whatever you use to identify deeplink.
Boolean isDeepLink= getIntent().getStringExtra("").startsWith("mydeeplink://");

if(!isDeepLink)
    
        Intent splashIntent=new Intent(this, SplashActivity.class);
        startActivity(splashIntent);
    

【讨论】:

但是第二个被注释掉了! &lt;!-- --&gt; 对吗?我将编辑我的问题(删除注释掉的代码)以避免混淆。 @SudoPlz 请查看编辑后的答案。让我知道它是否适合您。【参考方案3】:

找到导致问题的原因。

其实就是MainApplication.java上的这行代码:

public boolean clearHostOnActivityDestroy() 
    return false;

当我将该值更改为 true 时,一切都开始按预期进行。

这是我的导航库引起的问题:https://github.com/wix/react-native-navigation

【讨论】:

以上是关于深层链接启动 Splash Activity 而不是 Main Activity的主要内容,如果未能解决你的问题,请参考以下文章

带你重新认识:Android Splash页秒开 Activity白屏 Activity黑屏

Unity 安卓启动图片(splash image)前黑屏解决方法

如何在一个 Activity 中管理 android 深层链接和 Applink?

制作启动动画

如何为程序添加splashScreen

Xamarin.Android splash页面瞬间响应_避免APP启动闪白屏