ARouter入門之從零開始——Kotlin版

緣起

隨着app項目的逐步迭代開發,單獨運行調試比較耗時,實行項目組件化拆分迫在眉睫,而跨組件通信是必須要解決的問題,而ARouter算是一個比較成熟的路由解決方案,所以寫下此篇文章,藉此來記錄。

一 、添加gradle基礎配置

apply plugin: 'kotlin-kapt'

android {
kapt {
    arguments {
        arg("AROUTER_MODULE_NAME", project.getName())
    }
    }
}
dependencies {
    implementation 'com.alibaba:arouter-api:1.5.0'
    kapt 'com.alibaba:arouter-compiler:1.2.2'
}

二、在application中初始化

if (BuildConfig.DEBUG) {           
// 這兩行必須寫在init之前,否則這些配置在init過程中將無效
    ARouter.openLog();     // 打印日誌
    ARouter.openDebug();   
    // 開啓調試模式(如果在InstantRun模式下運行,必須開啓調試模式!線上版本需要關閉,否則有安全風險)
}
ARouter.init(this);

三、ARouter跳轉

  • 抽取path常量
object PathConstant{
    const val SERVICE_PATH="/app/service_path"
    const val SECOND_ACTIVITY_PATH="/app/RouterSecondActivity"
    const val INNER_FRAGMENT_PATH="/app/InnerFragment"
    // secondModule
    const val SECOND_MODULE_ACTIVITY_PATH="/secondmodule/MainActivity"
}
  • 抽取Extra常量
object ExtraKeyConstant {
    const val KEY_STRING_EXTRA="key_string_extra"
    const val KEY_INT_EXTRA="key_int_extra"
    const val KEY_BOOLEAN_EXTRA="key_boolean_extra"
    const val KEY_LONG_EXTRA="key_long_extra"
    const val KEY_OBJECT_EXTRA="key_object_extra"
}
  • 跳轉
  // 帶基本類型參數
        btn_skip_two.setOnClickListener {
            ARouter.getInstance().build(PathConstant.APP_MODULE_ROUTER_SECOND_ACTIVITY)
                .withString(ExtraKeyConstant.KEY_STRING_EXTRA, "ARouter")
                .withInt(ExtraKeyConstant.KEY_INT_EXTRA, 10)
                .withLong(ExtraKeyConstant.KEY_LONG_EXTRA, 1)
                .withBoolean(ExtraKeyConstant.KEY_BOOLEAN_EXTRA, true)
                .navigation()
        }
        // 傳遞序列化對象
        btn_skip_with_obj.setOnClickListener {
            ARouter.getInstance().build(PathConstant.APP_MODULE_ROUTER_SECOND_ACTIVITY)
                .withSerializable(ExtraKeyConstant.KEY_OBJECT_EXTRA, PersonEntity("peter", 18))
                .navigation()
        }
        // 傳遞object
        btn_skip_with_list.setOnClickListener {
            val list = ArrayList<PersonEntity>()
            list.add(PersonEntity("peter", 18))
            list.add(PersonEntity("tommy", 20))
            ARouter.getInstance().build(PathConstant.APP_MODULE_ROUTER_SECOND_ACTIVITY)
                .withObject(ExtraKeyConstant.KEY_OBJECT_EXTRA, list)
                .navigation()
        }
        
        // 跨module跳轉
        btn_jump_second_module.setOnClickListener {
            ARouter.getInstance().build(PathConstant.SECOND_MODULE_ACTIVITY_PATH)
                .withTransition(R.anim.slide_in_bottom, R.anim.slide_out_bottom)
                .withString(ExtraKeyConstant.KEY_STRING_EXTRA, "helloWorld").navigation()
        }

注意跨module跳轉一定要在主module中添加其他module的引用,否則會找不到path

implementation project(':baselibrary')
implementation project(':secondmodule')

通過withObject()傳遞object,一定要新建一個類),然後實現 SerializationService,並使用@Route註解標註(方便用戶自行選擇序列化方式),否則會報空指針異常

  • 添加Gson依賴
 implementation 'com.google.code.gson:gson:2.8.6'
  • JsonServiceImpl類
@Route(path = "/service/json")
class JsonServiceImpl : SerializationService {
    private var mGson: Gson? = null
    override fun init(context: Context?) {
        mGson = Gson()
    }

    override fun <T> json2Object(text: String?, clazz: Class<T>?): T {
        checkJson()
        return mGson!!.fromJson(text, clazz)
    }

    override fun object2Json(instance: Any?): String {
        checkJson()
        return mGson!!.toJson(instance)
    }

    override fun <T> parseObject(input: String?, clazz: Type?): T {
        checkJson()
        return mGson!!.fromJson(input, clazz)
    }

    fun checkJson() {
        if (mGson == null) {
            mGson = Gson()
        }
    }
}
  • 接收參數
  • 注意要在接收跳轉的activity中添加Route註解,並添加 ARouter.getInstance().inject(this) 進行注入參數接收
@Route(path = PathConstant.APP_MODULE_ROUTER_SECOND_ACTIVITY)
class RouterSecondActivity : AppCompatActivity() {

    @Autowired(name = ExtraKeyConstant.KEY_STRING_EXTRA)
    @JvmField
    var text = ""

    @Autowired(name = ExtraKeyConstant.KEY_INT_EXTRA)
    @JvmField
    var intParam =0

    @Autowired(name = ExtraKeyConstant.KEY_BOOLEAN_EXTRA)
    @JvmField
    var booleanParam =false

    @Autowired(name = ExtraKeyConstant.KEY_LONG_EXTRA)
    @JvmField
    var longParam =0

    @Autowired(name = ExtraKeyConstant.KEY_SERIALIZABLE_EXTRA)
    @JvmField
    var mPerson: PersonEntity? = null

    @Autowired(name = ExtraKeyConstant.KEY_OBJECT_EXTRA)
    @JvmField
    var mList: List<PersonEntity>? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // 注意一定要注入才能接收到參數
        ARouter.getInstance().inject(this)
        setContentView(R.layout.activity_router_second)
        initData()
    }

    private fun initData() {
        btn_second_text.text = "String data is $text int data is $intParam  long data is $longParam boolean data is $booleanParam "
        // 接收object參數
        btn_second_obj_text.text = mPerson?.name + mPerson?.age
        // 接收list
        btn_second_list_text.text=if (mList.isNullOrEmpty()) "emptyList" else "list size is ${mList?.size}"
    }
}
  • 實現startActivityForResult效果獲取回傳數據

起始頁

     // 類似startActivityForResult
        btn_skip_with_response.setOnClickListener {
            ARouter.getInstance().build(PathConstant.APP_MODULE_ROUTER_SECOND_ACTIVITY).navigation(this,
                REQUEST_CODE)
        }
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode== REQUEST_CODE&&resultCode== RESULT_CODE){
            Log.d(TAG,data?.getStringExtra(ExtraKeyConstant.KEY_SECOND_ACTIVITY_EXTRA))
        }
    }


    companion object{
        const val REQUEST_CODE=0X01
        const val RESULT_CODE=0X02
    }

結果頁

  /**
     * 設置返回數據
     */
    private fun setResultData() {
        val intent = Intent()
        intent.putExtra(ExtraKeyConstant.KEY_SECOND_ACTIVITY_EXTRA, "secondActivityData")
        setResult(RouterMainActivity.RESULT_CODE, intent)
    }

四、跳轉過程監聽

    // 獲取跳轉過程監聽
        btn_skip_with_result.setOnClickListener {
            val list = ArrayList<PersonEntity>()
            list.add(PersonEntity("peter", 18))
            list.add(PersonEntity("tommy", 20))
            ARouter.getInstance().build(PathConstant.APP_MODULE_ROUTER_SECOND_ACTIVITY)
                .withObject(ExtraKeyConstant.KEY_OBJECT_EXTRA, list)
                .navigation(this, object : NavigationCallback {
                    override fun onLost(postcard: Postcard?) {
                        Log.d(TAG,"onLost")
                    }

                    override fun onFound(postcard: Postcard?) {
                        Log.d(TAG,"onFound")
                    }

                    override fun onInterrupt(postcard: Postcard?) {
                        Log.d(TAG,"onInterrupt")
                    }

                    override fun onArrival(postcard: Postcard?) {
                        Log.d(TAG,"onArrival")
                    }
                })
        }

五、添加攔截器,攔截路由跳轉參數,

AOP面向切面編程,做自己想做的事情

@Interceptor(priority = 8, name = "測試用攔截器")
 class RouterInterceptor : IInterceptor {
    override fun process(postcard: Postcard?, callback: InterceptorCallback?) {
        Log.d("peter","埋點")
        callback?.onContinue(postcard)
        Log.d("peter","process")
    }

    override fun init(context: Context?) {
        Log.d("peter","init")
    }

}

六、獲取Fragment實例

  • 創建Fragment並添加註解@Route
@Route(path = PathConstant.INNER_FRAGMENT_PATH)
class InnerFragment : Fragment() {


    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return LayoutInflater.from(context).inflate(R.layout.fragment_inner, container,false)
    }
}
  • 獲取fragment實例
 // 獲取fragment實例
        val fragment = ARouter.getInstance().build(PathConstant.INNER_FRAGMENT_PATH).navigation() as Fragment
        Log.d("peter", fragment.javaClass.canonicalName)      

七、暴露服務

類似接口回調實現

  • 定義接口實現IProvider
 interface IService : IProvider {
    override fun init(context: Context?) {
    }
    
    fun transferWords(words: String):String
}
  • 創建接口實現類
@Route(path = PathConstant.SERVICE_PATH)
class ServiceImpl :IService{
    override fun transferWords(words: String): String {
        Log.d("peter",words)
        return "words is helloWorld"
    }
    
}
  • 使用ARouter發現服務,並傳遞數據
 btn_jump_service.setOnClickListener {
           val serviceImpl= ARouter.getInstance().build(PathConstant.SERVICE_PATH).navigation() as IService
            serviceImpl.transferWords("你好")
        }

八、添加轉場動畫

  • 使用
 btn_jump_with_anim.setOnClickListener {
            ARouter.getInstance().build(PathConstant.SECOND_ACTIVITY_PATH)
                .withTransition(R.anim.slide_in_bottom, R.anim.slide_out_bottom)
                .withString(ExtraKeyConstant.KEY_STRING_EXTRA, "helloWorld").navigation()
        }
  • anim文件

slide_in_bottom

<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="300"
android:fromYDelta="0%"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:toYDelta="100%"/>

slide_out_bottom

<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="300"
    android:fromYDelta="100%"
    android:interpolator="@android:anim/accelerate_decelerate_interpolator"
    android:toYDelta="0%"/>

九、代碼混淆規則

-keep public class com.alibaba.android.arouter.routes.**{*;}
-keep public class com.alibaba.android.arouter.facade.**{*;}
-keep class * implements com.alibaba.android.arouter.facade.template.ISyringe{*;}

# 如果使用了 byType 的方式獲取 Service,需添加下面規則,保護接口
-keep interface * implements com.alibaba.android.arouter.facade.template.IProvider

# 如果使用了 單類注入,即不定義接口實現 IProvider,需添加下面規則,保護實現
# -keep class * implements com.alibaba.android.arouter.facade.template.IProvider

十、參考:

ARouter中文文檔README_CN

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章