当前位置: 首页 > news >正文

php部署网站数据分析师课程

php部署网站,数据分析师课程,标准网站建设,中文域名注册收费标准阅读前请点击右上角“关注”#xff0c;每天免费获取Android知识解析及面试解答。Android架构解析#xff0c;只做职场干货#xff0c;完全免费分享#xff01;之前一直听说过组件化开发#xff0c;而且面试也有这方面的提问#xff0c;但都未曾有涉及具体的项目。所以就…阅读前请点击右上角“关注”每天免费获取Android知识解析及面试解答。Android架构解析只做职场干货完全免费分享之前一直听说过组件化开发而且面试也有这方面的提问但都未曾有涉及具体的项目。所以就萌生了基于Github的开放Api并使用组件化的方式来从零搭建一个Github客户端起名为AwesomeGithub。在这里对组件化开发进行一个总结同时也希望能够帮助别人更好的理解组件化开发。先来看下项目的整体效果下面是项目的结构为何要使用组件化对于传统的开发模式一个app下面是包含项目的全部页面模块与逻辑。这样项目一旦迭代时间过长业务模块逐渐增加相应的业务逻辑复杂度也成指数增加。模块间的互相调用频繁这样必定会导致模块间的耦合增加业务逻辑嵌套程度加深。一旦修改其中一个模块可能就牵一发动全身了。传统的开发模式不利于团队的集体开发合作因为每个开发者都是在同一个app模块下开发。这样导致的问题是不能预期每个开发者所会修改到的具体代码部分即所能够修改的代码区域。因为模块耦合在一起涉及的区域不可预期导致不同开发者会修改同一个文件或者同一段代码逻辑从而导致异常冲突。传统开发模式不利于测试每次迭代都要将项目整体测试一遍。因为在同一个app下面代码是缺乏约束的你不能保证只修改了迭代过程中所涉及的需求逻辑。以上问题随着项目的迭代周期的增大会表现的越来越明显。那么使用组件化又能够解决什么问题了组件化能够解决的问题组件化开发是将各个相关功能进行分离分别独立成一个单独可运行的app并且组件之间不能相互直接引用。这样就减少了代码耦合达到业务逻辑分层效果。组件化可以提高团队协作能力不同的人员可以开发不同的组件保证不同开发人员互不影响。组件化将app分成多个可单独运行的子项目可以用自己独立的版本可以独立编译打包、测试与部署。这样不仅可以提高单个模块的编译速度同时也可以提高测试的效率。组件化可以提高项目的灵活性app可以按需加载所要有的组件提高app的灵活性可以快速生成可定制化的产品。现在我们已经了解了组件化的作用但要实现组件化达到其作用必须解决实现组件化过程中所遇到的问题。组件化需要解决的问题组件单独运行组件间数据传递主项目使用组件中的Fragment组件间界面的跳转组件解耦以上是实现组件化时所遇到的问题下面我会结合AwesomeGithub来具体说明解决方案。组件单独运行组件的创建可以直接使用library的方式进行创建。只不过在创建完之后要让组件达到可以单独运行调试的地步还需要进行相关配置。运行方式动态配置首先当创建完library时在build.gradle中可以找到这么一行代码apply plugin: com.android.library这是gradle插件所支持的一种构建类型代表可以将其依赖到主项目中构建后输出aar包。这种方式对于我们将组件依赖到主项目中完全吻合的。而gradle插件的另一种构建方式可以在主项目的build.gradle中看到这么一行代码apply plugin: com.android.application这代表在项目构建后会输出apk安装包是一个独立可运行的项目。明白了gradle的这两种构建方式之后我们接下需要做的事也非常明了需要将这两种方式进行动态配置保证组件在主项目中以library方式存在而自己单独的时候则以application的方式存在。下面我以AwesomeGithub中的login组件为例。首先我们在根项目的gradle.properties中添加addLogin变量addLogin  true然后在login中的build.gradle通过addLogin变量来控制构建方式if (addLogin.toBoolean()) { apply plugin: com.android.library} else { apply plugin: com.android.application}这样就实现了对login的构建控制可单独运行也可依赖于app项目。ApplicationId与AndroidManifest除了修改gradle的构建方式还需要动态配置ApplicationId与AndroidManifest文件。有了上面的基础实现方式也很简单。可以在defaultConfig中增加对applicationId的动态配置 defaultConfig { if (!addLogin.toBoolean()) { applicationId com.idisfkj.awesome.login } minSdkVersion Versions.min_sdk targetSdkVersion Versions.target_sdk versionCode Versions.version_code versionName Versions.version_name testInstrumentationRunner androidx.test.runner.AndroidJUnitRunner }而AndroidManifest文件可以通过sourceSets来配置 sourceSets { main { if (addLogin.toBoolean()) { manifest.srcFile src/main/AndroidManifest.xml } else { manifest.srcFile src/main/manifest/AndroidManifest.xml } } }同时addLogin也可以作用于app让login组件可配置依赖这样login组件就可以独立于app进行单独构建、打包、调试与运行。组件间的数据传递由于组件与组件、项目间是不能直接使用类的相互引用来进行数据的传递所以为了解决这个问题这里通过一个公共库来做它们之间调用的桥梁它们不直接拿到具体的引用对象而是通过接口的方式来获取所需要的数据。在AwesomeGithub中我将其命名为componentbridge各个组件都依赖于该公共桥梁通过该公共桥梁各个组件间可以轻松的实现数据传递。上图圈起来的部分都是componentbridge的重点也是公共桥梁实现的基础。下面来分别详细说明。BridgeInterface这是公共桥梁的底层接口每一个组件要向外实现自己的桥梁都要实现这个接口。interface BridgeInterface { fun onClear() {}}内部很简单只有一个方式onClear(), 用来进行数据的释放。BridgeStore用来做数据存储对桥梁针对不同的key进行缓存。避免桥梁内部的实例多次创建。具体实现方式如下class BridgeStore { private val mMap HashMap() fun put(key: String, bridge: BridgeInterface) { mMap.put(key, bridge)?.onClear() } fun get(key: String): BridgeInterface? mMap[key] fun clear() { for (item in mMap.values) { item.onClear() } mMap.clear() }}Factory桥梁的实例构建工厂默认提供通过反射的方式来实例化不同的类。Factory接口只提供一个create方法实现方式由子类自行解决interface Factory { fun create(bridgeClazz: Class): T}在AwesomeGithub中提供了通过反射方式来实例化不同类的具体实现NewInstanceFactoryclass NewInstanceFactory : Factory { companion object { val instance: NewInstanceFactory by lazy { NewInstanceFactory() } } override fun create(bridgeClazz: Class): T try { bridgeClazz.newInstance() } catch (e: InstantiationException) { throw RuntimeException(Cannot create an instance of $bridgeClazz, e) } catch (e: IllegalAccessException) { throw RuntimeException(Cannot create an instance of $bridgeClazz, e) } }Factory的作用是通过抽象的方式来获取所需要类的实例至于该类如何实例化将通过create方法自行实现。ProviderProvider是提供桥梁的注册与获取各个组件暴露的接口实现。通过register来统一各个组件向外暴露的桥梁类最后再通过getBridge来获取具体的桥梁类然后调用所需的相关方法最终达到组件间的数据传递。来看下BridgeProviders的具体实现class BridgeProviders { private val mProvidersMap HashMap, BridgeProvider() private val mBridgeMap HashMap, Class() private val mDefaultBridgeProvider BridgeProvider(NewInstanceFactory.instance) companion object { val instance: BridgeProviders by lazy { BridgeProviders() } } fun register( clazz: Class, factory: Factory? null, replace: Boolean false ) apply { if (clazz.interfaces.isEmpty() || !clazz.interfaces[0].interfaces.contains(BridgeInterface::class.java)) { throw RuntimeException($clazz must implement BridgeInterface) } // 1. get contract interface as key, and save implement class to map value. // 2. get contract interface as key, and save bridgeProvider of implement class instance // to map value. clazz.interfaces[0].let { if (mProvidersMap[it] null || replace) { mBridgeMap[it] clazz mProvidersMap[it] if (factory null) { mDefaultBridgeProvider } else { BridgeProvider(factory) } } } } fun getBridge(clazz: Class): T { mProvidersMap[clazz]?.let { Suppress(UNCHECKED_CAST) return it.get(mBridgeMap[clazz] as Class) } throw RuntimeException($clazz subClass is not register) } fun clear() { mProvidersMap.clear() mBridgeMap.clear() mDefaultBridgeProvider.bridgeStore.clear() }}每次register之后都会保存一个BridgeProvider实例如果没有实现自定义的Factory将会使用默认是mDefaultBridgeProvider它内部使用的就是默认的NewInstanceFactoryclass BridgeProvider(private val factory: Factory) { val bridgeStore BridgeStore() companion object { private const val DEFAULT_KEY com.idisfkj.awesome.componentbridge } fun get(key: String, bridgeClass: Class): T { var componentBridge bridgeStore.get(key) if (bridgeClass.isInstance(componentBridge)) { Suppress(UNCHECKED_CAST) return componentBridge as T } componentBridge factory.create(bridgeClass) bridgeStore.put(key, componentBridge) return componentBridge } fun get(bridgeClass: Class): T get(DEFAULT_KEY bridgeClass.canonicalName, bridgeClass)}注册完之后就可以在任意的组件中通过调用桥梁的getBridge来获取组件向外暴露的方法从而达到数据的传递。我们来看下具体的使用示例。AwesomeGithub项目使用的是Github Open Api用到的接口基本都要AuthorizationBasic或者是AccessToken而为了让每一个组件在调用接口时都能够正常获取到AuthorizationBasic或者AccessToken所以提供了一个AppBridge与AppBridgeInterface来向外暴露这些数据实现如下interface AppBridgeInterface: BridgeInterface { /** * 获取用户的Authorization Basic */ fun getAuthorizationBasic(): String? fun setAuthorizationBasic(authorization: String?) /** * 获取用户的AccessToken */ fun getAccessToken(): String? fun setAccessToken(accessToken: String?)}class AppBridge : AppBridgeInterface { override fun getAuthorizationBasic(): String? App.AUTHORIZATION_BASIC override fun setAuthorizationBasic(authorization: String?) { App.AUTHORIZATION_BASIC authorization } override fun getAccessToken(): String? App.ACCESS_TOKEN override fun setAccessToken(accessToken: String?) { App.ACCESS_TOKEN accessToken } }有了上面的桥梁接口接下来需要做的是先在App主项目中进行注册 private fun registerBridge() { BridgeProviders.instance.register(AppBridge::class.java, object : Factory { override fun create(bridgeClazz: Class): T { Suppress(UNCHECKED_CAST) return AppBridge() as T } }) .register(HomeBridge::class.java) .register(UserBridge::class.java) .register(ReposBridge::class.java) .register(FollowersBridge::class.java) .register(FollowingBridge::class.java) .register(NotificationBridge::class.java) .register(SearchBridge::class.java) .register(WebViewBridge::class.java) }在注册AppBridge时使用的是自定义的Factory这里只是为了简单展示自定义的Factory的使用其实没有特殊需求可以与后面的bridge一样直接调用regiser进行注册。注册完了之后就可以直接在需要的地方进行调用。首先在登录组件中将获取到的AuthorizationBasic或者AccessToken进行保存以便被之后的组件进行调用。以AccessToken为例在login组件中的核心调用代码如下 fun getAccessTokenFromCode(code: String) { showLoading.value true repository.getAccessToken(code, object : RequestCallback { override fun onSuccess(result: ResponseSuccess) { try { appBridge.setAccessToken( result.data?.body()?.string()?.split()?.get(1)?.split()?.get( 0 ) ) getUser() } catch (e: IOException) { e.printStackTrace() } } override fun onError(error: ResponseError) { showLoading.value false } }) }如上所示只需调用appBridge.setAccessToken将数据进行保存而appBridge可以通过如下获取appBridge BridgeProviders.instance.getBridge(AppBridgeInterface::class.java)现在已经有了AccessToken数据为了避免每次调用接口都手动加入AccessToken可以使用okhttp的Interceptor即在network组件中进行统一加入。class GithubApiInterceptor : Interceptor { override fun intercept(chain: Interceptor.Chain): Response { val request chain.request() val appBridge BridgeProviders.instance.getBridge(AppBridgeInterface::class.java) Timber.d(intercept url %s %s %s, request.url(), appBridge.getAuthorizationBasic(), appBridge.getAccessToken()) val builder request.newBuilder() val authorization if (!TextUtils.isEmpty(appBridge.getAuthorizationBasic())) Basic appBridge.getAuthorizationBasic() else token appBridge.getAccessToken() builder.addHeader(Authorization, authorization) val response chain.proceed(builder.build()) Timber.d(intercept url %s, response %s ,code %d, request.url(), response.body().toString(), response.code()) return response }}这样就完成了将AccessToken从login组件到network组件间的传递。单个组件中调用以上是主项目中集成了login组件login组件会提供AuthorizationBasic或者AccessToken。那么对于单个组件(组件可以单独运行)为了让组件单独运行时也能调通相关的接口在调用的时候加入正确的AuthorizationBasic或者AccessToken。需要提供默认的AppBridgeInterface实现类。我这里命名为DefaultAppBridgeclass DefaultAppBridge : AppBridgeInterface { override fun getAuthorizationBasic(): String? BuildConfig.AUTHORIZATION_BASIC override fun setAuthorizationBasic(authorization: String?) { } override fun getAccessToken(): String? BuildConfig.ACCESS_TOKEN override fun setAccessToken(accessToken: String?) { }}里面具体的AuthorizationBasic与AccessToken值可以通过BuildConfig获取而值的定义可以在local.properities中进行设置AuthorizationBasicxxxxAccessTokenxxx因为每个组件都会依赖与桥梁componentbridge所以将值配置到componentbridge的build中具体如下android { compileSdkVersion Versions.target_sdk buildToolsVersion Versions.build_tools defaultConfig { minSdkVersion Versions.min_sdk targetSdkVersion Versions.target_sdk versionCode Versions.version_code versionName Versions.version_name buildConfigField String, AUTHORIZATION_BASIC, getProperties(AuthorizationBasic) buildConfigField String, ACCESS_TOKEN, getProperties(AccessToken) testInstrumentationRunner androidx.test.runner.AndroidJUnitRunner } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile(proguard-android-optimize.txt), proguard-rules.pro } } }有了默认的组件桥梁实现现在只需在对应的组件Application中进行注册即可。例如项目中的followers组件单独运行时使用DefaultAppBridge来达到接口的正常调用。class FollowersApp : Application() { override fun onCreate() { super.onCreate() SPUtils.init(this) initTimber() initRouter() // register bridges BridgeProviders.instance.register(DefaultAppBridge::class.java) .register(DefaultWebViewBridge::class.java) } private fun initTimber() { if (BuildConfig.DEBUG) { Timber.plant(Timber.DebugTree()) } } private fun initRouter() { if (BuildConfig.DEBUG) { ARouter.openLog() ARouter.openDebug() } ARouter.init(this) }}在组件单独运行时的Application中注册之后单独运行时调用的就是local.properities中设置的值。即保证了组件正常单独运行。以上是组件间数据传递的全部内容即解决了组件间的数据传递也解决了组件单独运行时的默认数据调用问题。如需了解全部代码可以查看AwesomeGithub项目。主项目使用组件中的FragmentAwesomeGithub主页有三个tab分别是三个组件。这个三个组件是主页viewpager中的三个fragment。前面已经说了在主项目中不能直接调用各个组件那么组件中的fragment又该如何加入到主项目中呢其实也很简单可以将获取fragment的实例当作为组件间的数据传递的一种特殊形式。那么有了上面的组件间数据传递的基础实现在主项目中调用组件的fragment也瞬间简单了许多。借助的还是桥梁componentbridge。下面以主页的search为例SearchBridgeInterface首先在componentbridge中创建SearchBridgeInterface接口并且实现默认的桥梁的BridgeInterface接口。interface SearchBridgeInterface : BridgeInterface { fun getSearchFragment(): Fragment}其中就一个方法用来向外提供SearchFragment的获取接下来在search组件中实现SearchBridgeInterface的具体实现类class SearchBridge : SearchBridgeInterface { override fun getSearchFragment(): Fragment SearchFragment.getInstance() }然后回到主项目的Application中进行注册BridgeProviders.instance.register(SearchBridge::class.java)注册完之后就可以在主项目的ViewPagerAdapter中进行获取SearchFragment实例class MainViewPagerAdapter(fm: FragmentManager?) : FragmentPagerAdapter(fm) { override fun getItem(position: Int): Fragment when (position) { 0 - BridgeProviders.instance.getBridge(SearchBridgeInterface::class.java).getSearchFragment() 1 - BridgeProviders.instance.getBridge(NotificationBridgeInterface::class.java) .getNotificationFragment() else - BridgeProviders.instance.getBridge(UserBridgeInterface::class.java).getUserFragment() } override fun getCount(): Int 3}主项目中调用组件中的Fragment就是这么简单基本上与之前的数据传递是一致的。组件间界面的跳转有了上面的基础可能会联想到使用处理Fragment方式来进行组件间页面的跳转。的确这也是一种解决方式不过接下来要介绍的是另一种更加方便与高效的跳转方式。项目中使用的是ARouter它是一个帮助App进行组件化改造的框架支持模块间的路由、通信与解藕。下面简单的介绍下它的使用方式。首先需要去官网找到版本依赖并进行导入。这里不多说然后需要在你所有用到的模块中的build.gradle中添加以下配置kapt { arguments { arg(AROUTER_MODULE_NAME, project.getName()) }}记住只要该模块需要调用ARouter就需要添加以上代码。配置完之后就可以开始使用。下面我以项目中的webview组件为例跳转到组件中的WebViewActivity上面已经将相关依赖配置好了首先需要在Application中进行ARouter初始化 private fun initRouter() { if (BuildConfig.DEBUG) { ARouter.openLog() ARouter.openDebug() } ARouter.init(this) }再为WebViewActivity进行path定义object ARouterPaths { const val PATH_WEBVIEW_WEBVIEW /webview/webview}因为每一个ARouter进行路由的时候都需要配置一个包含两级的路径然后将定义的路径配置到WebViewActivity中Route(path ARouterPaths.PATH_WEBVIEW_WEBVIEW)class WebViewActivity : BaseActivity() { Autowired lateinit var url: String Autowired lateinit var requestUrl: String override fun getVariableId(): Int BR.vm override fun getLayoutId(): Int R.layout.webview_activity_webview override fun getViewModelInstance(): WebViewVM WebViewVM() override fun getViewModelClass(): Class WebViewVM::class.java override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ARouter.getInstance().inject(this) viewModel.url.value url viewModel.request(requestUrl) } override fun addObserver() { super.addObserver() viewModel.backClick.observe(this, Observer { finish() }) } override fun onBackPressed() { if (viewDataBinding.webView.canGoBack()) { viewDataBinding.webView.goBack() return } super.onBackPressed() } }如上所示在进行配置时只需在类上添加Route注解然后再将定义的路径配置到path上。其中的Autowired注解代表WebViewActivity在使用ARouter进行跳转时接收两个参数分别为url与requestUrl。ARouter本质是解析注解然后定位到参数再通过原始的Intent中获取到传递过来的参数值。有了上面的准备过程最后剩下的就是调用ARouter进行页面跳转。这里为了统一调用方式将其调加到桥梁中。class WebViewBridge : WebViewBridgeInterface { override fun toWebViewActivity(context: Context, url: String, requestUrl: String) { ARouter.getInstance().build(ARouterPaths.PATH_WEBVIEW_WEBVIEW).with( bundleOf(url to url, requestUrl to requestUrl) ).navigation(context) } }前面是定义的跳转路径后面紧接的是页面传递的参数值。剩下的就是在别的组件中调用该桥梁例如followers组件中的contentClick点击:class FollowersVHVM(private val context: Context) : BaseRecyclerVM() { var data: FollowersModel? null override fun onBind(model: FollowersModel?) { data model } fun contentClick() { BridgeProviders.instance.getBridge(WebViewBridgeInterface::class.java) .toWebViewActivity(context, data?.html_url ?: , ) }}更多ARouter的使用方式读者可以自行查阅官方文档在AwesomeGithub项目中组件化过程中的主要难点与解决方案已经分析的差不多了。最后我们来聊聊组件间的解藕优化。组件解耦组件化本身就是对项目进行解藕所以如果要进一步进行优化主要是对组件间的依赖或者资源等方面进行解藕。而对于组件间的依赖尝试过在依赖的时候使用runtimeOnly。因为runtimeOnly可以避免依赖的组件在运行之前进行引用调用它只会在项目运行时才能够正常的引用这样就可以防止主项目中进行开发时直接引用依赖的组件。但是在实践的过程中如果项目中使用了DataBinding此时使用runtimeOnly进行依赖组件通过该方式依赖的组件在运行的过程中会出现错误。这是由于DataBinding需要在编译时生成对应资源文件。使用runtimeOnly会导致其缺失最终在程序进行运行时找不到对应资源导致程序异常。当然如果没有使用DataBinding就不会有这种问题。这是组件依赖方面下面再来说说资源相关的。由于不同组件模块下可以引入相同命名的资源文件为了防止开发过程中不同组件下相同名称的资源文件引用错乱这里可以通过在不同组件模块中的build.gradle中添加资源前缀。例如login组件中resourcePrefix代表login组件中的所有资源文件命名都必须以login_为前缀命名。如果没有编译器将会标红并提示你正确的使用方式。这种方式可以一定程度上避免资源文件的乱用与错乱。以上是AwesomeGithub组件化过程中的整个探索经历。如果你想更深入的了解其实现过程强烈建议你直接查看项目的源码毕竟语言上的描述是有限的程序员就应该直接看代码才能更快更准的理解。原文作者午后一小憩推荐阅读阿里P7进阶学习之路你还没掌握的几个关键技术年薪60W不是梦
http://www.zqtcl.cn/news/738875/

相关文章:

  • 本地郑州网站建设东莞网站优化中易
  • 动态域名可以建网站德州公司做网站
  • 深圳建设银行官方网站wordpress 添加qq
  • 甘肃第九建设集团公司网站网站对企业的好处
  • 论坛网站建设规划书公司网站建设与设计制作
  • 做棋牌游戏网站犯法吗如何进行搜索引擎的优化
  • 常见的网站首页布局有哪几种陈光锋网站运营推广新动向
  • 手机网站活动策划方案开一个设计公司
  • 宝塔建设网站教程visual studio 2010 网站开发教程
  • 做网站购买服务器做谷歌网站使用什么统计代码吗
  • 网站系统与网站源码的关系emlog轻松转wordpress
  • 网站的简介怎么在后台炒做吉林省住房城乡建设厅网站首页
  • 泉州易尔通网站建设国际酒店网站建设不好
  • 网页下载网站福田企业网站推广公司
  • 北京网站建设开发公司哪家好网站添加在线留言
  • 新建的网站怎么做seo优化平面广告创意设计
  • yy陪玩网站怎么做软件项目管理计划
  • 西安建网站价格低百度推广区域代理
  • 中英网站模板 照明公司注册在自贸区的利弊
  • 全球十大网站排名wordpress标题连接符
  • 网站开发可能遇到的问题四川建筑人才招聘网
  • 镇江网站托管怎么做淘宝网站赚钱吗
  • 交互式网站是什么知名vi设计企业
  • 上海个人做网站网站建设销售好做嘛
  • 邵阳建设网站哪家好手机网站栏目结构图
  • 做动车哪个网站查网站环境配置
  • 那些网站可以做h5国内新闻最新消息今天简短
  • asp网站开发实例河南省建设招投标网站
  • 营销型网站搭建公司有没有专做推广小说的网站
  • 汕头网站搭建wordpress文章列表摘要