如何处理 android 应用程序的多个深层链接
How to handle multiple deeplinks for android app
我创建了一个 Android 应用程序。每当有人通过应用程序打开网站时,它就会转到确切的 link。但是如果用户打开另一个link,应用程序仍然在之前打开的link。您可以在此处查看应用程序:https://play.google.com/store/apps/details?id=app.freeairdrop.io
通过 Telegram 或 Whatsapp 将这 2 link 发送给自己:
现在,当系统提示选择应用程序时,打开我的应用程序中的第一个 link。切换回 Telegram/whatsapp,然后单击第二个 link。我的应用程序将打开,但它仍在该页面上(第一个 link)。没有任何反应,该应用程序无法加载第二个 link,除非应用程序已关闭。
MainActivity.java代码:
package app.freeairdrop.io;
import...
public class MainActivity extends Activity{
private ProgressBar progressBar;
private WebView webView;
private SwipeRefreshLayout mySwipeRefreshLayout;
private boolean mShouldPause;
@SuppressLint("SetJavaScriptEnabled")
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
progressBar = (ProgressBar) findViewById(R.id.progressBar);
progressBar.setMax(100);
webView = (WebView) findViewById(R.id.webView);
webView.setWebViewClient(new WebViewClientDemo());
webView.setWebChromeClient(new WebChromeClientDemo());
mySwipeRefreshLayout = (SwipeRefreshLayout) this.findViewById(R.id.swipeContainer);
mySwipeRefreshLayout.setOnRefreshListener(
new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
webView.reload();
mySwipeRefreshLayout.setRefreshing(false);
}
}
);
// ATTENTION: This was auto-generated to handle app links.
Intent appLinkIntent = getIntent();
String appLinkAction = appLinkIntent.getAction();
Uri appLinkData = appLinkIntent.getData();
if (getIntent().getExtras() != null) {
if (appLinkData == null){
webView.loadUrl("https://freeairdrop.io/");
}else
webView.loadUrl(String.valueOf(appLinkData));
} else if (getIntent().getExtras() == null){
webView.loadUrl("https://freeairdrop.io/");
}webView.reload();
}
private class WebViewClientDemo extends WebViewClient {
@Override
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
super.onReceivedError(view, errorCode, description, failingUrl);
Toasty.error(getApplicationContext(), "No Internet, pull down to refresh when you're connected to internet", Toast.LENGTH_LONG, true).show();
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
Uri uri = Uri.parse(url);
if (uri.getHost() != null && (url.startsWith("https://freeairdrop.io/") || url.startsWith("https://www.freeairdrop.io/"))) {
return false;
}
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
view.getContext().startActivity(intent);
return true;
}
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
progressBar.setVisibility(View.GONE);
progressBar.setProgress(100);
}
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
progressBar.setVisibility(View.VISIBLE);
progressBar.setProgress(0);
}
}
private class WebChromeClientDemo extends WebChromeClient {
public void onProgressChanged(WebView view, int progress) {
progressBar.setProgress(progress);
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_BACK) && webView.canGoBack()) {
webView.goBack();
return true;
}
else {
}
return super.onKeyDown(keyCode, event);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
return true;
}
@Override
// This method is used to detect back button
public void onBackPressed() {
if (this.webView.canGoBack()) {
this.webView.goBack();
return;
}
else {
// Let the system handle the back button
super.onBackPressed();
}
}
}
Android舱单代码:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="app.freeairdrop.io">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<application
android:appCategory="productivity"
android:hardwareAccelerated="true"
android:allowBackup="true"
android:fullBackupContent="true"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:label="@string/app_name"
android:theme="@style/AppTheme"
android:name=".ApplicationClass"
tools:ignore="GoogleAppIndexingWarning">
<activity
android:name="app.freeairdrop.io.MainActivity"
android:label="@string/app_name"
android:configChanges="orientation|screenSize|screenLayout"
android:resizeableActivity="false"
android:supportsPictureInPicture="false"
android:launchMode="singleInstance"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="https"
android:host="freeairdrop.io" />
</intent-filter>
</activity>
</application>
</manifest>
将 android:launchMode="singleInstance"
设置为 activity 只会导致 activity 变为 运行 的一个实例。这意味着当您的 activity 通过 deeplink 打开一次时,该实例是 运行ning。当您打开第二个 link 时,您希望 UI 得到更新,但由于该实例仍在 运行 中,activity 的 onCreate
不会被调用,这就是为什么您会看到之前 link 的视图。(另一点是当设置了 singleInstance 时,它不允许堆栈中的任何其他 activity。)
您可以在 official docs 中获得更多信息。
现在,即使从未调用 onCreate
,但在设置 singleInstance 时会调用 onResume
和 onNewIntent
方法。虽然通常提到 onNewIntent
与 singleTop
一起工作,但根据我的经验,当设置三个单一标志中的任何一个时,它确实会被调用。
因此您需要重写两者中的任何一个并编写代码来更新您在其中的 UI。还要注意 onResume
在 onCreate
之后第一次创建 activity 时确实会被调用,所以如果那是你放置代码的地方,你可能需要优化以避免重新加载相同的东西两次并且每次从后台返回后都会调用 onResume 。在这种情况下 onNewIntent()
似乎是一个更好的选择。
希望对您有所帮助。
更新:根据要求,您可以在下面找到编辑后的代码
package app.freeairdrop.io;
import...
public class MainActivity extends Activity{
private ProgressBar progressBar;
private WebView webView;
private SwipeRefreshLayout mySwipeRefreshLayout;
private boolean mShouldPause;
@SuppressLint("SetJavaScriptEnabled")
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
progressBar = (ProgressBar) findViewById(R.id.progressBar);
progressBar.setMax(100);
webView = (WebView) findViewById(R.id.webView);
webView.setWebViewClient(new WebViewClientDemo());
webView.setWebChromeClient(new WebChromeClientDemo());
mySwipeRefreshLayout = (SwipeRefreshLayout) this.findViewById(R.id.swipeContainer);
mySwipeRefreshLayout.setOnRefreshListener(
new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
webView.reload();
mySwipeRefreshLayout.setRefreshing(false);
}
}
);
// call your method here to manage the intent
manageIntent(getIntent());
}
/* Simply moved your code which handles the intent to a common method
* Call this method from onCreate so that when first instance of activity gets created it handles it
* Similarly call it from onNewIntent to manage the new link you get
* You just need to pass the respective intents from the methods
*/
public void manageIntent(Intent intent) {
// ATTENTION: This was auto-generated to handle app links.
Intent appLinkIntent = intent;
String appLinkAction = appLinkIntent.getAction();
Uri appLinkData = appLinkIntent.getData();
if (getIntent().getExtras() != null) {
if (appLinkData == null){
webView.loadUrl("https://freeairdrop.io/");
}else
webView.loadUrl(String.valueOf(appLinkData));
} else if (getIntent().getExtras() == null){
webView.loadUrl("https://freeairdrop.io/");
}
}
// override to get the new intent when this activity has an instance already running
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
// again call the same method here with the new intent received
manageIntent(intent);
}
private class WebViewClientDemo extends WebViewClient {
@Override
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
super.onReceivedError(view, errorCode, description, failingUrl);
Toasty.error(getApplicationContext(), "No Internet, pull down to refresh when you're connected to internet", Toast.LENGTH_LONG, true).show();
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
Uri uri = Uri.parse(url);
if (uri.getHost() != null && (url.startsWith("https://freeairdrop.io/") || url.startsWith("https://www.freeairdrop.io/"))) {
return false;
}
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
view.getContext().startActivity(intent);
return true;
}
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
progressBar.setVisibility(View.GONE);
progressBar.setProgress(100);
}
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
progressBar.setVisibility(View.VISIBLE);
progressBar.setProgress(0);
}
}
private class WebChromeClientDemo extends WebChromeClient {
public void onProgressChanged(WebView view, int progress) {
progressBar.setProgress(progress);
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_BACK) && webView.canGoBack()) {
webView.goBack();
return true;
}
else {
}
return super.onKeyDown(keyCode, event);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
return true;
}
@Override
// This method is used to detect back button
public void onBackPressed() {
if (this.webView.canGoBack()) {
this.webView.goBack();
return;
}
else {
// Let the system handle the back button
super.onBackPressed();
}
}
}
看看这是否适合你,然后告诉我。
我创建了一个 Android 应用程序。每当有人通过应用程序打开网站时,它就会转到确切的 link。但是如果用户打开另一个link,应用程序仍然在之前打开的link。您可以在此处查看应用程序:https://play.google.com/store/apps/details?id=app.freeairdrop.io
通过 Telegram 或 Whatsapp 将这 2 link 发送给自己:
现在,当系统提示选择应用程序时,打开我的应用程序中的第一个 link。切换回 Telegram/whatsapp,然后单击第二个 link。我的应用程序将打开,但它仍在该页面上(第一个 link)。没有任何反应,该应用程序无法加载第二个 link,除非应用程序已关闭。
MainActivity.java代码:
package app.freeairdrop.io;
import...
public class MainActivity extends Activity{
private ProgressBar progressBar;
private WebView webView;
private SwipeRefreshLayout mySwipeRefreshLayout;
private boolean mShouldPause;
@SuppressLint("SetJavaScriptEnabled")
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
progressBar = (ProgressBar) findViewById(R.id.progressBar);
progressBar.setMax(100);
webView = (WebView) findViewById(R.id.webView);
webView.setWebViewClient(new WebViewClientDemo());
webView.setWebChromeClient(new WebChromeClientDemo());
mySwipeRefreshLayout = (SwipeRefreshLayout) this.findViewById(R.id.swipeContainer);
mySwipeRefreshLayout.setOnRefreshListener(
new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
webView.reload();
mySwipeRefreshLayout.setRefreshing(false);
}
}
);
// ATTENTION: This was auto-generated to handle app links.
Intent appLinkIntent = getIntent();
String appLinkAction = appLinkIntent.getAction();
Uri appLinkData = appLinkIntent.getData();
if (getIntent().getExtras() != null) {
if (appLinkData == null){
webView.loadUrl("https://freeairdrop.io/");
}else
webView.loadUrl(String.valueOf(appLinkData));
} else if (getIntent().getExtras() == null){
webView.loadUrl("https://freeairdrop.io/");
}webView.reload();
}
private class WebViewClientDemo extends WebViewClient {
@Override
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
super.onReceivedError(view, errorCode, description, failingUrl);
Toasty.error(getApplicationContext(), "No Internet, pull down to refresh when you're connected to internet", Toast.LENGTH_LONG, true).show();
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
Uri uri = Uri.parse(url);
if (uri.getHost() != null && (url.startsWith("https://freeairdrop.io/") || url.startsWith("https://www.freeairdrop.io/"))) {
return false;
}
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
view.getContext().startActivity(intent);
return true;
}
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
progressBar.setVisibility(View.GONE);
progressBar.setProgress(100);
}
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
progressBar.setVisibility(View.VISIBLE);
progressBar.setProgress(0);
}
}
private class WebChromeClientDemo extends WebChromeClient {
public void onProgressChanged(WebView view, int progress) {
progressBar.setProgress(progress);
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_BACK) && webView.canGoBack()) {
webView.goBack();
return true;
}
else {
}
return super.onKeyDown(keyCode, event);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
return true;
}
@Override
// This method is used to detect back button
public void onBackPressed() {
if (this.webView.canGoBack()) {
this.webView.goBack();
return;
}
else {
// Let the system handle the back button
super.onBackPressed();
}
}
}
Android舱单代码:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="app.freeairdrop.io">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<application
android:appCategory="productivity"
android:hardwareAccelerated="true"
android:allowBackup="true"
android:fullBackupContent="true"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:label="@string/app_name"
android:theme="@style/AppTheme"
android:name=".ApplicationClass"
tools:ignore="GoogleAppIndexingWarning">
<activity
android:name="app.freeairdrop.io.MainActivity"
android:label="@string/app_name"
android:configChanges="orientation|screenSize|screenLayout"
android:resizeableActivity="false"
android:supportsPictureInPicture="false"
android:launchMode="singleInstance"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="https"
android:host="freeairdrop.io" />
</intent-filter>
</activity>
</application>
</manifest>
将 android:launchMode="singleInstance"
设置为 activity 只会导致 activity 变为 运行 的一个实例。这意味着当您的 activity 通过 deeplink 打开一次时,该实例是 运行ning。当您打开第二个 link 时,您希望 UI 得到更新,但由于该实例仍在 运行 中,activity 的 onCreate
不会被调用,这就是为什么您会看到之前 link 的视图。(另一点是当设置了 singleInstance 时,它不允许堆栈中的任何其他 activity。)
您可以在 official docs 中获得更多信息。
现在,即使从未调用 onCreate
,但在设置 singleInstance 时会调用 onResume
和 onNewIntent
方法。虽然通常提到 onNewIntent
与 singleTop
一起工作,但根据我的经验,当设置三个单一标志中的任何一个时,它确实会被调用。
因此您需要重写两者中的任何一个并编写代码来更新您在其中的 UI。还要注意 onResume
在 onCreate
之后第一次创建 activity 时确实会被调用,所以如果那是你放置代码的地方,你可能需要优化以避免重新加载相同的东西两次并且每次从后台返回后都会调用 onResume 。在这种情况下 onNewIntent()
似乎是一个更好的选择。
希望对您有所帮助。
更新:根据要求,您可以在下面找到编辑后的代码
package app.freeairdrop.io;
import...
public class MainActivity extends Activity{
private ProgressBar progressBar;
private WebView webView;
private SwipeRefreshLayout mySwipeRefreshLayout;
private boolean mShouldPause;
@SuppressLint("SetJavaScriptEnabled")
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
progressBar = (ProgressBar) findViewById(R.id.progressBar);
progressBar.setMax(100);
webView = (WebView) findViewById(R.id.webView);
webView.setWebViewClient(new WebViewClientDemo());
webView.setWebChromeClient(new WebChromeClientDemo());
mySwipeRefreshLayout = (SwipeRefreshLayout) this.findViewById(R.id.swipeContainer);
mySwipeRefreshLayout.setOnRefreshListener(
new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
webView.reload();
mySwipeRefreshLayout.setRefreshing(false);
}
}
);
// call your method here to manage the intent
manageIntent(getIntent());
}
/* Simply moved your code which handles the intent to a common method
* Call this method from onCreate so that when first instance of activity gets created it handles it
* Similarly call it from onNewIntent to manage the new link you get
* You just need to pass the respective intents from the methods
*/
public void manageIntent(Intent intent) {
// ATTENTION: This was auto-generated to handle app links.
Intent appLinkIntent = intent;
String appLinkAction = appLinkIntent.getAction();
Uri appLinkData = appLinkIntent.getData();
if (getIntent().getExtras() != null) {
if (appLinkData == null){
webView.loadUrl("https://freeairdrop.io/");
}else
webView.loadUrl(String.valueOf(appLinkData));
} else if (getIntent().getExtras() == null){
webView.loadUrl("https://freeairdrop.io/");
}
}
// override to get the new intent when this activity has an instance already running
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
// again call the same method here with the new intent received
manageIntent(intent);
}
private class WebViewClientDemo extends WebViewClient {
@Override
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
super.onReceivedError(view, errorCode, description, failingUrl);
Toasty.error(getApplicationContext(), "No Internet, pull down to refresh when you're connected to internet", Toast.LENGTH_LONG, true).show();
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
Uri uri = Uri.parse(url);
if (uri.getHost() != null && (url.startsWith("https://freeairdrop.io/") || url.startsWith("https://www.freeairdrop.io/"))) {
return false;
}
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
view.getContext().startActivity(intent);
return true;
}
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
progressBar.setVisibility(View.GONE);
progressBar.setProgress(100);
}
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
progressBar.setVisibility(View.VISIBLE);
progressBar.setProgress(0);
}
}
private class WebChromeClientDemo extends WebChromeClient {
public void onProgressChanged(WebView view, int progress) {
progressBar.setProgress(progress);
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_BACK) && webView.canGoBack()) {
webView.goBack();
return true;
}
else {
}
return super.onKeyDown(keyCode, event);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
return true;
}
@Override
// This method is used to detect back button
public void onBackPressed() {
if (this.webView.canGoBack()) {
this.webView.goBack();
return;
}
else {
// Let the system handle the back button
super.onBackPressed();
}
}
}
看看这是否适合你,然后告诉我。