WMRouter使用记录

官方的说明还是不够简单直白,特别对于没有接触过路由的人来说

基础,也是所以外部URI路由跳转的根本所在

首先说下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>

配置WMRouter

库地址: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算是可以满足我的需求了

你可能感兴趣的:(WMRouter,Android路由,android)