官方的说明还是不够简单直白,特别对于没有接触过路由的人来说
首先说下android系统路由, android:exported="true"允许外部调用,再配置intent-filter,就可以在任意浏览器实现点击进入LoginActivity
<activity android:name=".wmrouter.LoginActivity" android:exported="true" >
<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:scheme="testwmrouter"
android:host="wmrouter"
android:path="/login"/>
intent-filter>
activity>
库地址:https://github.com/meituan/WMRouter
首先,根据官方文档一通配置接入
然后,在Application中初始化
//App.kt
import android.annotation.SuppressLint
import android.content.Context
import android.os.AsyncTask
import com.sankuai.waimai.router.Router
import com.sankuai.waimai.router.annotation.RouterProvider
import com.sankuai.waimai.router.annotation.RouterService
import com.sankuai.waimai.router.common.DefaultRootUriHandler
import com.sankuai.waimai.router.components.DefaultLogger
import com.sankuai.waimai.router.components.DefaultOnCompleteListener
import com.sankuai.waimai.router.core.Debugger.*
@RouterService(interfaces = [Context::class], key = ["/application"], singleton = true)
class App : Application() {
companion object {
lateinit var instance: App
private set
@RouterProvider
fun provideApplication(): App {
return instance
}
fun getContext(): Context {
return instance
}
}
override fun onCreate() {
instance = this
super.onCreate()
initRouter(this)
}
@SuppressLint("StaticFieldLeak")
private fun initRouter(context: Context) {
// 自定义Logger
val logger = object : DefaultLogger() {
override fun handleError(t: Throwable) {
super.handleError(t)
// 此处上报Fatal级别的异常
t.printStackTrace()
}
}
// 设置Logger
setLogger(logger)
// Log开关,建议测试环境下开启,方便排查问题。
setEnableLog(true)
// 调试开关,建议测试环境下开启。调试模式下,严重问题直接抛异常,及时暴漏出来。
setEnableDebug(true)
// 创建RootHandler,可以在这里设置默认scheme和host
val rootHandler = DefaultRootUriHandler(context, "testwmrouter", "wmrouter")
// 设置全局跳转完成监听器,可用于跳转失败时统一弹Toast提示,做埋点统计等。
rootHandler.globalOnCompleteListener = DefaultOnCompleteListener.INSTANCE
// 初始化
Router.init(rootHandler)
// 懒加载后台初始化(可选)
object : AsyncTask<Void, Void, Void>() {
override fun doInBackground(vararg voids: Void): Void? {
Router.lazyInit()
return null
}
}.execute()
}
}
接下来,需要一个中转页面UriProxyActivity
//UriProxyActivity.kt
import com.sankuai.waimai.router.core.UriRequest
import com.sankuai.waimai.router.core.OnCompleteListener
import com.sankuai.waimai.router.common.DefaultUriRequest
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity;
class UriProxyActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
DefaultUriRequest.startFromProxyActivity(this, object : OnCompleteListener {
override fun onSuccess(request: UriRequest) {
finish()
}
override fun onError(request: UriRequest, resultCode: Int) {
finish()
}
})
}
}
<activity android:name=".UriProxyActivity" android:exported="true">
<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:scheme="testwmrouter"/>
intent-filter>
activity>
再接下来,也是我使用路由的原因,拦截器(以登录为例)
//LoginInterceptor.kt
import android.widget.Toast;
import androidx.annotation.NonNull;
import com.sankuai.waimai.router.core.UriCallback;
import com.sankuai.waimai.router.core.UriInterceptor;
import com.sankuai.waimai.router.core.UriRequest;
import com.sankuai.waimai.router.core.UriResult;
/**
* Created by jzj on 2018/3/20.
*/
public class LoginInterceptor implements UriInterceptor {
@Override
public void intercept(@NonNull UriRequest request, @NonNull final UriCallback callback) {
//注意:这里使用FakeAccountService会导致uriproxyactivity无法正常回调,不知道什么原因,暂不深究,官方文档给出的就是使用这个,下面也是官方的demo
final IAccountService accountService = DemoServiceManager.getAccountService();
if (accountService.isLogin()) {
callback.onNext();
} else {
Toast.makeText(request.getContext(), "请先登录~", Toast.LENGTH_SHORT).show();
accountService.registerObserver(new IAccountService.Observer() {
@Override
public void onLoginSuccess() {
accountService.unregisterObserver(this);
callback.onNext();
}
@Override
public void onLoginCancel() {
accountService.unregisterObserver(this);
//-100为取消登录码,可自定义
callback.onComplete(-100);
}
@Override
public void onLoginFailure() {
accountService.unregisterObserver(this);
//-101为登录失败码,可自定义
callback.onComplete(-101);
}
@Override
public void onLogout() {
accountService.unregisterObserver(this);
callback.onComplete(UriResult.CODE_ERROR);
}
});
accountService.startLogin(request.getContext());
}
}
}
拦截器相关代码
//IAccountService.kt
import android.content.Context;
/**
* Created by jzj on 2018/4/19.
*/
public interface IAccountService {
boolean isLogin();
void startLogin(Context context);
void registerObserver(Observer observer);
void unregisterObserver(Observer observer);
void notifyLoginSuccess();
void notifyLoginCancel();
void notifyLoginFailure();
void notifyLogout();
interface Observer {
void onLoginSuccess();
void onLoginCancel();
void onLoginFailure();
void onLogout();
}
}
//FakeAccountService.kt
import android.content.Context;
import androidx.annotation.NonNull;
import com.example.testwmrouter.Constants;
import com.sankuai.waimai.router.Router;
import com.sankuai.waimai.router.annotation.RouterProvider;
import com.sankuai.waimai.router.annotation.RouterService;
import java.util.ArrayList;
import java.util.List;
/**
* Created by jzj on 2018/3/26.
*/
@RouterService(interfaces = IAccountService.class, key = "/singleton", singleton = true)
public class FakeAccountService implements IAccountService {
@RouterProvider
public static FakeAccountService getInstance() {
return new FakeAccountService(Router.getService(Context.class, "/application"));
}
private boolean mIsLogin = false;
private final List<Observer> mObservers = new ArrayList<>();
private FakeAccountService(Context context) {
// ...
}
@Override
public void startLogin(Context context) {
Router.startUri(context, "testwmrouter://wmrouter/login");
}
@Override
public boolean isLogin() {
return mIsLogin;
}
@Override
public void registerObserver(Observer observer) {
if (observer != null && !mObservers.contains(observer)) {
mObservers.add(observer);
}
}
@Override
public void unregisterObserver(Observer observer) {
if (observer != null) {
mObservers.remove(observer);
}
}
@Override
public void notifyLoginSuccess() {
mIsLogin = true;
Observer[] observers = getObservers();
for (int i = observers.length - 1; i >= 0; --i) {
observers[i].onLoginSuccess();
}
}
@Override
public void notifyLoginCancel() {
Observer[] observers = getObservers();
for (int i = observers.length - 1; i >= 0; --i) {
observers[i].onLoginCancel();
}
}
@Override
public void notifyLoginFailure() {
Observer[] observers = getObservers();
for (int i = observers.length - 1; i >= 0; --i) {
observers[i].onLoginFailure();
}
}
@Override
public void notifyLogout() {
mIsLogin = false;
Observer[] observers = getObservers();
for (int i = observers.length - 1; i >= 0; --i) {
observers[i].onLogout();
}
}
@NonNull
private Observer[] getObservers() {
return mObservers.toArray(new Observer[mObservers.size()]);
}
}
//DemoServiceManager.kt
import com.sankuai.waimai.router.Router;
/**
* Created by jzj on 2018/4/19.
*/
public class DemoServiceManager {
public static IAccountService getAccountService() {
return Router.getService(IAccountService.class, "/singleton");
}
}
下面是登录界面
//LoginActivity.kt
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import androidx.annotation.Nullable;
import com.sankuai.waimai.router.annotation.RouterUri;
import androidx.appcompat.app.AppCompatActivity;
/**
* 登录页
*
* Created by jzj on 2018/3/19.
*/
@RouterUri(path = "/login",exported = true)
public class LoginActivity extends AppCompatActivity{
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
Button button = findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.show(this,"登录成功",Toast.LENGTH_SHORT).show();
DemoServiceManager.getAccountService().notifyLoginSuccess();
finish();
}
});
}
@Override
public void onBackPressed() {
Toast.show(this,"登录取消",Toast.LENGTH_SHORT).show();
DemoServiceManager.getAccountService().notifyLoginCancel();
super.onBackPressed();
}
}
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:ignore="HardcodedText">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="text"
android:text="UserName"/>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="text"
android:text="Password"/>
<Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="登录"/>
LinearLayout>
目标页面
@RouterUri(path = ["main"], exported = true, interceptors = [LoginInterceptor::class])
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
androidx.coordinatorlayout.widget.CoordinatorLayout>
然后,你需要一个html
<html><head><title>测试WMRoutertitle><head><body><br><br><h1><a href='testwmrouter://wmrouter/main'>测试路由testwmrouter://wmrouter/maina>h1><br><br><br><h1 color='red' id='test'>测试js路由testwmrouter://wmrouter/mainh1><script>document.getElementById('test').onclick = function(){window.location='testwmrouter://wmrouter/main'};script>body>html>
如果觉得创建一个html,然后在手机上访问不太方便,可以使用webview,下面是一个简单的WebView实现
//WebActivity.kt
import android.annotation.SuppressLint
import android.os.Build
import android.os.Bundle
import android.webkit.*
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity
import com.sankuai.waimai.router.Router
import com.viz.tools.Toast
import kotlinx.android.synthetic.main.activity_web.*
class WebActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_web)
initWebSettings()
initWebClient()
initWebChromeClient()
val url = Constants.schema + "://" + Constants.host + "/user/member/join"
textInputEditText_url.setText(url)
materialButton_go.setOnClickListener {
val url = textInputEditText_url.text.toString()
if (url.isNotEmpty()) {
// webView.loadUrl(url)//直接加载会无法拦截
//下面两种方式实现,原理都是一样的,按需选择吧
// webView.loadUrl("javascript:window.location='$url';")
webView.loadData("测试WMRouter
测试路由$url
测试js路由$url
","text/html","utf-8")
} else {
Toast.show(this, "请输入网址")
}
}
}
private fun initWebChromeClient() {
webView.webChromeClient = object : WebChromeClient() {
override fun onProgressChanged(view: WebView, newProgress: Int) {
super.onProgressChanged(view, newProgress)
//newProgress 当前的加载进度
}
override fun onReceivedTitle(view: WebView, title: String) {
super.onReceivedTitle(view, title)
//获取网页中的标题
}
override fun onJsAlert(
view: WebView,
url: String,
message: String,
result: JsResult
): Boolean {
return super.onJsAlert(view, url, message, result)
//支持js中的警告框,自己定义弹出框 返回true
}
override fun onJsConfirm(
view: WebView,
url: String,
message: String,
result: JsResult
): Boolean {
return super.onJsConfirm(view, url, message, result)
//支持js中的确认框,自己定义弹出框 返回true
}
override fun onJsPrompt(
view: WebView,
url: String,
message: String,
defaultValue: String,
result: JsPromptResult
): Boolean {
return super.onJsPrompt(view, url, message, defaultValue, result)
//支持js中的输入框,自己定义弹出框 返回true
}
}
}
private fun initWebClient() {
webView.webViewClient = object : WebViewClient() {
override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
if (url != null) {
if(url.startsWith("http://") && !url.startsWith("https://")) {
Router.startUri(this@WebActivity, url)
return true
}
}
return super.shouldOverrideUrlLoading(view, url)
}
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
override fun shouldOverrideUrlLoading(
view: WebView?,
request: WebResourceRequest?
): Boolean {
if (request != null) {
//这里设置除http/https请求外使用路由,也可以指定特定的scheme
if (request.url.scheme != "http" && request.url.scheme != "https") {
//这里路由可以分为两种
//第一种由WMRouter控制
Router.startUri(this@WebActivity, request.url.toString())
//第二种由系统控制
// val intent = Intent()
// intent.data = request.url
// startActivity(intent)
return true
}
}
return super.shouldOverrideUrlLoading(view, request)
}
}
}
@SuppressLint("SetJavaScriptEnabled")
private fun initWebSettings() {
//声明WebSettings子类
val webSettings = webView.settings
//如果访问的页面中要与Javascript交互,则webview必须设置支持Javascript
webSettings.javaScriptEnabled = true
// 若加载的 html 里有JS 在执行动画等操作,会造成资源浪费(CPU、电量)
// 在 onStop 和 onResume 里分别把 setJavaScriptEnabled() 给设置成 false 和 true 即可
//支持插件
// webSettings.setPluginsEnabled(true)
//设置自适应屏幕,两者合用
webSettings.useWideViewPort = true //将图片调整到适合webview的大小
webSettings.loadWithOverviewMode = true // 缩放至屏幕的大小
//缩放操作
webSettings.setSupportZoom(true) //支持缩放,默认为true。是下面那个的前提。
webSettings.builtInZoomControls = true //设置内置的缩放控件。若为false,则该WebView不可缩放
webSettings.displayZoomControls = false //隐藏原生的缩放控件
//其他细节操作
webSettings.cacheMode = WebSettings.LOAD_CACHE_ELSE_NETWORK
//缓存模式如下:
//LOAD_CACHE_ONLY: 不使用网络,只读取本地缓存数据
//LOAD_DEFAULT: (默认)根据cache-control决定是否从网络上取数据。
//LOAD_NO_CACHE: 不使用缓存,只从网络获取数据.
//LOAD_CACHE_ELSE_NETWORK,只要本地有,无论是否过期,或者no-cache,都使用缓存中的数据。
//关闭webview中缓存
webSettings.allowFileAccess = true //设置可以访问文件
webSettings.javaScriptCanOpenWindowsAutomatically = true //支持通过JS打开新窗口
webSettings.loadsImagesAutomatically = true //支持自动加载图片
webSettings.defaultTextEncodingName = "utf-8"//设置编码格式
//5.1以上默认http和https不混合处理
//开启
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
webView.settings.mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW
}
}
}
到这里WMRouter算是可以满足我的需求了