android组件化学习
网上关于组件化的文章有很多了,大部分都知道实现的思路,不过还是要自己去实践一下才会发现问题。文章的项目地址,项目用到CleanArchitecture框架,本文会介绍CleanArchitecture框架和dagger2在组件化的使用。
项目关系图
sdk: 一些公用库,各种辅助类,和第三方view
basic: (依赖sdk) 网络访问初始化,本地缓存和第三方包等。
commonbusiness: (依赖basic) 这里为什么我会多出这一层,因为有很多公共的业务,好比公司的app是强登录的,我会把登录模块写在这里,里面也包含了一些baseActivity和BaseApplication和各个组件的一些公共方法还有组件各种的服务接口的定义。
module_archives和module_knowledge: 就是两个组件,可以单独运行。
application和library切换
想必大家都知道了,定义一个isBuildModule=false,在组件的build.gradle中加入
|
|
maven-release-kline-aar.gradle是一个打包aar的文件,在切换的时候也会使用不同的AndroidManifest.xml,因为在组件是debug的时候它是单独单独运行的,还有就是代码可以在建立一个debug包,可以在单独运行的时候做些初始化app的,打包的时候回剔除这部分代码。
library依赖和资源问题
我把所以的library都依赖在basic,每个组件都会依赖这个包,这样就不会存在library的版本问题,资源的问题就是
|
|
组件之间的通信
你可以选择阿里的ARouter库,但是我的项目没有那么复杂,组件也就4个左右,所以没有使用阿里的库,我采用的是以下就是核心代码
|
|
但是采用Class.forName会有个问题那就是在混淆的时候,className是指定的,所以要在类上加@Keep 。
CleanArchitecture框架和dagger在组件化的使用
CleanArchitecture框架的github地址,这里再介绍分享一篇文章小鄧子的Easy Clean architecture on Android,我把data和domain会写在各自的业务模块中,自己的模块只要定义自己的就可以了,有个ApplicationModule会定义一些每个模块都需要的
|
|
然后在module_archives模块中会有ArchivesDataRepositoryModule和ArchivesActivityModule
其中KnowledgeDataRepositoryModule用于提供如下:
|
|
ArchivesActivityModule的代码如下:
|
|
这样在主app的AppComponent类中加入:
|
|
在module_archives为debug模式下也会有个AppComponent
|
|
各种模块初始化api和Repository,你要是嫌弃每个模块都要引入两个.class文件,你可以使用一个然后采用include的方式好比dagger中的AndroidSupportInjectionModule类方式
|
|
这样就会很清楚的知道自己模块需要初始化什么和使用什么,也不用考虑其它模块的初始化的数据,之后只需要在主app加入就行,也是比较方便的。
打包
各种模块当为library是要打包成aar的,maven-release-kline-aar.gradle文件代码如下:
|
|
我只是打包到本地,你可以自己建立一个本地maven库。
如要改进的地方
1:每次建立一个模块还是要做很多初始化的工作
2:maven-release-kline-aar.gradle每个模块都会有,希望后期能改进。
3: debug模式下各个模块登录之后跳转问题
4:等等。。。。。
参考
Kotlin学习之hot_kotlin
自从google IO大会之后,kotlin的热度一直在上升,kotlin官方文档,这里也有个中文的中文文档,作为android开发也可以看Kotlin for Android Developers ,接下来就说说在android方面的使用,记录一下自己的学习,大部分是通过Kotlin for Android Developers 学习到的。
Delegated Properties的使用(委托属性)
(1)这里简单的说明一下:用var修饰的可变属性和由val修饰的只读属性。由val修饰的只读属性使用的委托需要实现ReadOnlyProperty,而var修饰的可变属性则需要实现ReadWriteProperty。在调用被委托的属性的getter和setter时,对应操作会被委托给getValue()以及setValue()。
因为我在项目中使用了dagger2所以在app上要初始化ApplicationComponent提供给其它的component作为依赖,
|
|
graph属性只会被初始化一次,当你还没有初始化就去使用和多次初始化会报IllegalStateException异常。其中最主要的是getValue和setValue方法。
thisRef —— 必须与 属性所有者 类型(对于扩展属性——指被扩展的类型)相同或者是它的超类型,
property —— 必须是类型 KProperty<*> 或其超类型,
这个函数必须返回与属性相同的类型(或其子类型),
new value —— 必须和属性同类型或者是它的超类型。
(2)Lazy(延迟属性)
大部分app都会使用到toolbar,这时候你可以使用个接口对其统一的处理
|
|
使用的时候
|
|
因为当你不使用lazy关键字的时候,因为view都还没有创建所以一定找不到的。
(3)Observable(可观察属性)
Delegates.observable()
接受两个参数:初始值和修改时处理程序(handler), 每当我们给属性赋值时会调用该处理程序(在赋值后执行),它有三个参数:被赋值的属性、旧值和新值。在android使用就是adapter的使用
|
|
就是在adapter的populars属性变化的时候调用notifyDataSetChanged() 方法,后期为了为了学习DiffUtil加上了,把之前的代码去掉了,因为我的viewmodel是data类,会自动推断产生以下函数,equals()/hashCode() pair,toString(),componentN(),copy()这样就会方便使用。
Extensions(扩展函数)
用户经常会短时间多次点击按钮,所以我们可以对View进行扩展,
|
|
这样View就有singleClick()函数了,其实你对代码进行Bytecode会发现
|
|
这不就是java的静态方法,这样你就可以根据自己的项目要求去愉快的扩展了,Extensions are resolved statically,扩展函数是静态解析的。
inline function(内联函数)
以下代码来自anko,
|
|
这段代码包含的知识点很多,这里说一下reified修饰符“具体化”, 几乎就像是一个普通的类一样,正常的操作符如 !is 和 as 现在都能用了,在调用的时候要是具体的类型而不能是范型。
项目介绍:hot项目之前使用java的时候写过链接,这里就不再过多的介绍了,kotlin_hot地址,项目采用第三方框架如下:
- Rxjava
- MVP
- Dagger2
- Retrofit
- OkHttp3
- Glide
- Kotlin
通过此次的学习自己也加深了对kotlin的理解,虽然还处于小白阶段,希望能帮助到你,要是有写的不对的地方望指出,一起学习进步。
databinding和CleanArchitecture框架的使用
最近在做一个公司内部的app,框架方面使用了CleanArchitecture,因为本身app的业务逻辑不是很复杂,所以在使用的时候我没有写presenter这一层,只有当页面逻辑比较复杂的时候还会去写presenter层,主要还是懒得去写那么多接口。
databinding的使用
textview
|
|
使用多个的的时候在strings里面定义的记得要写上%1和%2要不然会报错的。
|
|
view的VISIBLE和GONE
|
|
这部分很好的说明了点击事件的使用和view的显示和隐藏,也可以根据状态来选择不同的src。
onLongClick事件
|
|
记得在定义方法的时候记得要有个boolean类型的返回,当时没有注意到导致了databinding生成不了,后来看了官网才发现,怪自己啊。。。
|
|
RecyclerView方面的使用
使用了databindingAdapter,代码非常的简单,使用起来也非常的方便好比
|
|
这是我RecyclerView多个布局的定义,在xml中使用
|
|
就是数据的name为item和事件的name为presenter。更多的使用可以自己去看github上的介绍。
图片访问方面
因为这个app图片的使用上还是蛮多的,所以选择了Fresco(也看中它能在xml中写一些配置),不太好的地方就是包的体积大了些,但是还是能接受的。
|
|
这里的MyDraweeView是继承SimpleDraweeView因为fresco是不支持自适应的,所以在这里你可以自己设置view的LayoutParams来达到目的。
CleanArchitecture框架的使用
api和repository
api和repository,我把各个模块的api都分开定义,这样在分模块开发的时候,可以各自定义api
module设计
dagger,在定义module的时候我采用的是new的方式,而不是使用在构造函数上加@Inject,因为采用new的方式能达到局部单例的效果,
|
|
|
|
这里的doublecheck就是起到这里这个效果,dagger中的单例也是如此,而不是你加上@Singleton就是单例了,是要在Application中才能达到单例的效果。这里就不展开了。
component设计
component也是采用的分模块定义,在fragment和activity使用上我也是采用和github上一样的方法在activity上提供component,activity实现HasComponent接口,在fragment里面
就能获取到activity的component了。
数据模型的转换
CleanArchitecture中的各种DataMapper,之前不是很理解这个是干什么用的,github上在data层和presentation两层同时使用了。后来在实践的时候发现presentation这层的转化还是非常有必要的。当后台接口返回的数据格式不是你想要的时候,(后台还懒得去帮你改)你可以定义一层转换,还是就是viewmodel中还包含了一些接口返回没有的字段,好比一个view是否显示当后台返回的数据类型不是你想要的好比boolean类型返回的是string的‘0’和‘1’,还有就是null的判断,尽量的让view获取到的数据不是空对象。
缓存
缓存方面,本身app就没有多大的缓存,只是记录一下搜索记录,没有引入什么第三方的数据库之类的,只是很简单的把搜索记录存入一个文件中
|
|
在每次搜索的时候保存搜索记录
|
|
RecyclerView嵌套webview的问题
记录一次RecyclerView嵌套webview的问题,一开始我直接在layout布局里面是这样写的,发现页面显示不全(原因未找到。。。要是有人知道原因欢迎告知)。
|
|
解决方法就是自定义一个view继承LinearLayout,采用动态生成webview这样就能显示全了。
|
|
总结
使用databinding的时候确实让我感受到了比平时写的要快一些,再也不是写id了也不用设置值了,写起来还是比较爽的,期间也遇到了一些问题,就是使用databinding和dagger一起使用的时候报错,有时候定位不是很准确,但是只要认真看看还是能找得到错误之处的,可能是因为我还是能力还不够的问题。通过这次的实践,让我更加深刻的理解了CleanArchitecture这个框架,也体会到了一些好的框架带来的好处。
FragmentTabHost使用遇到的问题
本文记录一下在使用fragmentTabHost的时候所遇到的问题。
fragment的重新建立的原因
原生的fragmenttabhost在切换tab的时候,会导致fragment的重新建立
|
|
很清楚的看到在切换的时候上个fragment调用detach,新的fragment调用的是attach,所以会导致重新初始化。
解决办法
就是把源码复杂一份,修改这个部分代码就可以解决了。
|
|
要求是当点击其中一个tab的时候不导致fragment的切换,而是做一些其他的操作
解决办法:
|
|
app当再次点击当前tab的时候会去刷新操作
|
|
给每个的tabwidget的子view设置监听事件,再onTouch的处理
|
|
在这里你就可以获取当前的fragment再处理逻辑了。
原生项目(hot)集成react-native
最近react-native(以下简称rn)的技术越来越活,国内的使用情况也越来越多,国内的氛围也越来越好,其实我是从去年就接触了一下rn,当时我学习了一个多星期,然后刚好处于要换工作就没有学习了,刚好最近有点空闲的时间,就再次去学习,只能说rn的更新速度太快了,很多之前的写法都变了,只能从头开始,但是一整个项目都采用rn来构建还不是很现实,所以国内很多的也是集成rn,所以我也来凑凑热闹。
添加依赖
在项目的build.gradle下添加
|
|
app的实现ReactApplication接口
|
|
AndroidManifest.xml
|
|
加入以下的activity可以在摇一摇出现rn的配置页面。
activity来继承ReactActivity
|
|
在这里介绍一篇大帅写的React Native 0.31 Bundle 预加载优化主要解决从原生页面到rn页面会有短暂的白屏。使用方法
|
|
把之前继承ReactActivity变成继承MrReactActivity
|
|
在调用rn页面调用
|
|
这样就能很好的解决上述的问题。
react-native端的代码
在rn中我引入了redux,以下就贴出关键行代码,在最外层
|
|
同时个actions的文件夹里面放着各种的操作,以下是我的newsList的代码
|
|
还有就是ruducers文件夹下的和action所对应的
|
|
还有就是一个转化的
|
|
这样在使用的时候就是调用action的方法
|
|
接受到数据
|
|
这样以后要是添加新的页面就很清楚了,在action里面添加动作,在reducers添加action的处理器,这样就很清晰了。
我添加了一个Services,来获取数据
|
|
总结
通过这次的学习,让我了解了react-native,因为react-native的周边产品还是很多的,也学习了redux是的reacr-native也有一个小小的框架实现,使得代码结构更加的清晰,本人rn的水平有限,要是有不对的地方,还请指出和谅解。
Hot项目进行部分修改(加入Dagger2)
最近在看Dagger2,网上资料也很多,并且在自己私下的项目中Hot运用(一个关于微信热门头条的分享)。这里就不介绍dagger的一些使用方法啊,这里有个很好的介绍dagger介绍
DataManager管理
|
|
AppModule的构造的对象和提供的依赖
|
|
接下来的Component的注入的代码
ActivityComponent的代码
|
|
3.FragmentComponent的代码
之前在View 和P的关联在通过在p构造方法中传入,现在是使用调用p的attachView()方法。
现在BasePresenter设计
|
|
BaseActivity中的p之前需要自己new出来,现在加入了dagger2,通过注入的方式
/**
Created by wukewei on 16/5/26.
*/
public abstract class BaseActivityextends AppCompatActivity implements IView { @Inject
protected T mPresenter;
protected Activity mContext;@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState); setContentView(getLayout()); ButterKnife.bind(this); mContext = this; setupActivityComponent(App.getAppComponent(),new ActivityModule(this)); mPresenter.attachView(this); initEventAndData();
}
protected void setCommonBackToolBack(Toolbar toolbar, String title) {
toolbar.setTitle(title); setSupportActionBar(toolbar); getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayShowHomeEnabled(true); toolbar.setNavigationOnClickListener(v -> onBackPressed());
}
@Override
protected void onDestroy() {super.onDestroy(); ButterKnife.unbind(this); if (mPresenter != null) mPresenter.detachView();
}
/**
- 依赖注入的入口
@param appComponent appComponent
*/
protected abstract void setupActivityComponent(AppComponent appComponent, ActivityModule activityModule);protected abstract int getLayout();
protected abstract void initEventAndData();
}12多了setupActivityComponent()方法这就是依赖注入的入口好比。
@Override
protected void setupActivityComponent(AppComponent appComponent, ActivityModule activityModule) {DaggerActivityComponent.builder() .appComponent(appComponent) .activityModule(activityModule) .build() .inject(this);
}
````
总结
这个项目是我私下自己在学习新的技术,并且运用在一起的项目,也是像对待产品一个对待这个app开发,后续会不断的学习和更新项目。本人也是个android小菜鸟,从最弱最弱的做起。
Rxjava实现三级缓存的两种方式
本文正如标题所说的用rxjava实现数据的三级缓存分别为内存,磁盘,网络,刚好最近在看Android源码设计模式解析与实战(受里面的ImageLoader的设计启发),我把代码放到了我的hot项目中。github地址
使用concat()和first()的操作符
用concat()和first()的操作符来实现,这是我在看Android源码设计模式解析与实战,作者在第一章的时候就介绍ImageLoader的设计。在内存中存储的方式LruCache来实现的,磁盘存储的方式就是序列化存储。
定义一个接口
|
|
2.内存存储的实现
|
|
磁盘存储的实现
|
|
isCacheDataFailure()方式中就是判断当前的数据是否失效,我是根据当前的网络状况来分wifi状况和非wifi状况,wifi状态下数据过期时间比较短,其他状态过期时间比较长。
CacheLoader的设计
|
|
网络获取的NetworkCache
|
|
6.接下来看怎么使用
一定要给个key,我是根据key来获取数据的,还要就是给个类型。但是这个我设计的这个缓存还是不是很理想,接来下想要实现的就是在传入的时候类的class都不用给明,要是有好的实现的方式,欢迎告诉我。
使用BehaviorSubject
BehaviorSubject的实现方法,废话不多说直接上代码
|
|
其中最主要的是subscriptionData()这个方法,就是先判断 cache是否存在要是存在的话就返回内存中数据,再去判断磁盘数据是否存在,如果存在就返回,要是前面两种都不存在的时候,再去网络中获取数据。还有最重要的是当你从网络获取数据的时候要记得保存在内存中和保存在磁盘中,在磁盘获取数据的时候把它赋值给内存。
MVP+Rxjava+Retrofit构建项目
第一次写文章,不好之处还请谅解。2015最值得android程序猿去学习的就是rxjava了,关于rxjava就不再多描述了。本文是介绍怎么使用mvp+rxjava+retrofit来构建一个新的项目,项目采用mvp的方式,参考了google的官方mvp项目。Hot是关于微信头条分享的app,项目地址
项目介绍
BaseActivity的设计:
|
|
最主要的设置了presenter的泛型,并且提供了初始化的函数 protected abstract T getPresenter();。
BasePresenter的设计
|
|
也采用了范型来绑定view,使用了CompositeSubscription来进行避免内存的泄漏。
Api的设计
在实际情况中每个公司的api的设计都是不一样的,我采用如下的设计形式。
|
|
你们可以根据自己公司的实际情况修改返回的成功码。
Rxjava的一些设计:
大家都知道rxjava就是能灵活的在线程之间进行切换,在使用的时候我使用了Transformer操作符。
|
|
在数据处理的时候,
|
|
在这里你可以根据自己的实际情况来添加,大部分app都是有token一说,你可以在这里判断当token过期的时候可以跳到登录界面。
在项目中的时候就是
|
|
是不是觉得使用起来非常的爽啊,还有一个就是在实际情况中很多一部分请求要设置一些数据好比来自android的还是ios的,或者添加一些token在请求中,本项目没有使用token,但是有个apikey代码如下:
|
|
你可以在获取本地token的时候,当有的时候加上即可。
7月13日更新:DataManager
网上有很多在mvp的时候关于数据层的设计,我这边是添加了DataManager来管理app数据,在p层是不关心数据的来源,无论是网络的还是本地缓存的数据,这个设计参考了别的大神的实现。
|
|
记得在app的时候初始化
|
|
这样在p中的使用如下
|
|
结束
通过这次的实践加深了我对mvp的认知,本人对rxjava认识不够深入,还在不断的学习中,要是有什么错误之处还望指正。
Hello World
Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.
Quick Start
Create a new post
|
|
More info: Writing
Run server
|
|
More info: Server
Generate static files
|
|
More info: Generating
Deploy to remote sites
|
|
More info: Deployment