Android一行命令上传你的项目到Jcenter/Maven

写在前面

最近挺多同学开始了最近开源之旅,在github上写了很多好的代码库但是发现上传到Jcenter的话比较的折腾,于是就写了个简单的脚步来一键上传你的项目到JCenter。

准备

先在https://bintray.com注册一个账号
然后在https://bintray.com/profile/edit下点击apikey,记录下自己的apikey和username,看下图。

注册获取信息

开始

到这里其实你已经完成了50%的工作量了是不是很简单,接下来就是运行脚本填写配置信息了。
在我的github(https://github.com/Yat3s/bintrayUpload)
下载uploadToBintray.sh,把它放在你的module目录下,例如CustomView/app/uploadToBintray.sh
然后打开你的终端或者android studio自带的终端,切换到你的app目录下 ,cd app
最后运行./uploadToBintray.sh,然后你按照提示填写就行了。

下载脚本

运行

上传成功

上传至Jcenter

大功告成了,你可能会遇到一些问题。

可能遇到的问题

  • 如果权限禁止的话,你需要运行chmod +x uploadToBintray.sh
  • 如果你要上传一个库的话,你的项目是依赖是apply plugin: ‘com.android.library’ 而不是apply plugin: ‘com.android.application’
  • 第一次运行需要下载几个依赖库可能会占用时间
  • 如果最后一步卡在97%请检查你是否开启了proxy,检查你的gradle.properties下是否有proxy的信息,将之删除
  • 上传maven成功后你需要打开你的bintray,然后将你的项目add to jcenter,一般在1小时候左右就会审核成功
  • 该脚本在Mac OS X运行正常,不确保在linux上运行无误。
  • 如果你想修改你的项目名(com.android.library.项目名),你需要修改你的module名后重新上传
  • 有其他问题欢迎关注我的Github

Android常用的Gradle配置和加速编译

Why Gradle

Gradle makes the impossible possible, the possible easy and the easy elegant.

在Android开发中经常会用Gradle来构建项目,Gradle能很方便的项目的版本集成和打包,虽Gradle官方已经给出很详细的文档了,但还是有必要抽离出一些常用的配置。
整个Android项目的编译依赖于Gradle的编译,虽然前几天发布了Android 2.0 stable,拥有了Instant Run强大的功能,但是有“改一行布局代码Run一次”习惯的同学有必要知道如何加速Gradle的编译。
主要介绍一下:

  • 如何配置Gradle
  • 如何加速Gradle的编译
  • 一些常用的项目构建知识

How Gradle

gradle.properties文件适合配置IDE的属性,当然也适合配置你在项目的关键/敏感参数,因为它将运行在Incubating parallel mode(
孵化并行模式,应该解释为运行时候嵌入在项目当中),也是属于默认的gitignore,这样你的敏感信息(key账号密码,appkey等)就不会被push到git了,你需要注意的是 属性中有中文的话,记得转成unicode来显示,不然可能引发一些莫名的错误

类似于这样,你可以把你的签名keystore的信息,服务端的endpoint,第三方服务的appkey,ide的配置信息放在这。

KEY_ALIAS=yat3s
KEYSTORE_PASSWORD=123456
KEY_PASSWORD=123456
umeng_appkey_product=adcdefghijk   
umeng_appkey_dev=adcdefghijk
deepshare_appid=adcdefghijk
bugly_appid_product=adcdefghijk  
bugly_appid_dev=adcdefghijk  
rong_appkey=adcdefghijk   
endpoint_product=http://api.yat3s.com
endpoint_dev=http://api.yat3s.com:9000
app_name=\u006f\u0070\u0065\u006e\u5f00\u8154

那么配置好这些信息如何在Gradle和Java中使用呢?
其实Groovy语言和Java很接近也很好用,往下看↓

buildTypes

buildTypes {
release {
    minifyEnabled true
    shrinkResources true
    signingConfig signingConfigs.release
    proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    buildConfigField "String", "ENDPOINT", "\"${endpoint_product}\""
    resValue "string", "umeng_appkey", "${umeng_appkey_product}"
    resValue "string", "deepshare_appid", "${deepshare_appid}"
    resValue "string", "bugly_appid", "${bugly_appid_product}"
    resValue "string", "rong_appkey", "${rong_appkey}"
    resValue "string", "channel", "product"
    resValue "string", "op_app_name", "${app_name}"
}
debug {
    signingConfig signingConfigs.release
    proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    buildConfigField "String", "ENDPOINT", "\"${endpoint_product}\""
    resValue "string", "umeng_appkey", "${umeng_appkey_dev}"
    resValue "string", "deepshare_appid", "${deepshare_appid}"
    resValue "string", "bugly_appid", "${bugly_appid_dev}"
    resValue "string", "rong_appkey", "${rong_appkey}"
    resValue "string", "channel", "dev"
    resValue "string", "op_app_name", "dev_${app_name}"
  }
}
  • 引用gradle.properties 只需要加 ${key}
  • 定义在 buildConfigField “String”, “ENDPOINT”,”\”${endpoint_product}\””,在Java享用时候只需要读取BuildConfig.ENDPOINT即可
  • 定义在 resValue “string”, “umeng_appkey”, “${umeng_appkey_product}” , 在xml中享用只需要R.string.umeng_appkey即可

记住几个点:

  • 在你确保你minifyEnabled(混淆)没问题的时候debug模式尽量别开minifyEnabled
    和shrinkResources,这样会大大降低编译速度
  • 如果真要开混淆,把生成mapping关掉
  • 尽量把一些第三方key分成staging和product,这样容错性高,避免你推送给你的用户一个”Test”
  • 定义buildConfigField时候注意双引号的问题 “\”${endpoint_product}\””
  • 如果不方便管理签名可debug和release同一个签名。 如果需要同时安装debug和release包只需要修改applicationId即可

defaultConfig

这里尽量新建一个Conifg.gradle文件来统一管理这些基本配置(在project下右键new file即可) ,

Config.gradle文件如下

ext {
android = [compileSdkVersion: 23,
           buildToolsVersion: "23.0.1",
           applicationId    : "com.yat3s.d3v",
           minSdkVersion    : 15,
           targetSdkVersion : 23,
           versionCode      : 36,
           versionName      : "1.3.1"]
}
dependencies = [
               design              : "com.android.support:design:23.3.0",
               nineoldandroids     : "com.nineoldandroids:library:2.4.0",
               retrofit            : "com.squareup.retrofit:retrofit:2.0.0",
               rxandroid           : "io.reactivex:rxandroid:1.0.0",
               okhttp-urlconnection: "com.squareup.okhttp:okhttp-urlconnection:2.0.0",
               okhttp              : "com.squareup.okhttp:okhttp:2.0.0",
               butterknife         : "com.jakewharton:butterknife:7.0.1"]

然后在项目的build.gradle文件下添加 apply from: “config.gradle”

apply from: "config.gradle"
  buildscript {
      repositories {
        jcenter()
      }
      dependencies {
        classpath 'com.android.tools.build:gradle:2.0.0'
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
      }
    }       

    allprojects {
      repositories {
        jcenter()
      }
    }       

然后你使用起来就简单多了

def config = rootProject.ext.android // 配置
def dep = rootProject.ext.dependencies // 依赖
useLibrary 'org.apache.http.legacy' // 如果你是api23的话,在用到apache的http库时候,记得加上这个,不然混淆时候因为urlConnection的升级导致异常

compileSdkVersion config.compileSdkVersion
buildToolsVersion config.buildToolsVersion

defaultConfig {
  applicationId config.applicationId
  minSdkVersion config.minSdkVersion
  targetSdkVersion config.targetSdkVersion
  versionCode config.versionCode
  versionName config.versionName
  manifestPlaceholders = [UMENG_CHANNEL_VALUE: "open"]
}

dependencies {
  compile fileTree(dir: 'libs', include: ['*.jar'])
  compile dep.design
  compile dep.retrofit
  compile dep.okhttp
}

那么这样用的好处是什么呢?

  • 如果你有多个Module都需要依赖design包的话,design包一升级导致了多个module需要改版本,你就可能同时引入了多个依赖导致apk增大
  • 统一管理versionCode和versionName
  • 看起来是不是更简洁

signingConfigs

def keystore = file('keystore/yat3s.jks')
signingConfigs {
    release {
        keyAlias KEY_ALIAS
        keyPassword KEY_PASSWORD
        storePassword KEYSTORE_PASSWORD
        storeFile keystore
    }
}    
  • 在非资源定义下引用gradle.properties可直接输入properties的key
  • 如果你要公开你的源码记得把你的keystore ignore掉,私有库无视

packagingOptions

packagingOptions {
  exclude 'META-INF/LICENSE'
  exclude('META-INF/LICENSE.txt')
  exclude('META-INF/NOTICE.txt')
  exclude 'META-INF/NOTICE'
  exclude 'META-INF/DEPENDENCIES'
  // umeng推送的jar包含有的okio库跟okhttp的okio库冲突
  exclude 'META-INF/maven/com.squareup.okio/okio/pom.xml'
  exclude 'META-INF/maven/com.squareup.okio/okio/pom.properties'
}
  • 主要处理第三方库在导入时候的一些声明文件冲突,Option this

compileOptions {

compileOptions {
  sourceCompatibility JavaVersion.VERSION_1_7
  targetCompatibility JavaVersion.VERSION_1_7
}
  • 个人觉得如果你不用lambda表达式的话,可以用java1.7,因为1.8还不是完美的支持Android

lintOptions

 lintOptions {
    disable "InvalidPackage"
    disable "MissingTranslation" // 禁用中英文string.xml的强制lint
    lintConfig file("lint.xml")
}

打包管理

def getDate() {
    return new Date().format("yyyy-MM-dd", TimeZone.getTimeZone("UTC")) // 注意时区
}     

// 获取当前git的Revision
def getRevision() {
    return ext.hash = 'git rev-parse --short HEAD'.execute().text.trim()
}
productFlavors {
   rc_open {}
   rc_360 {}
   rc_yingyongbao {}
   rc_baidu {}
   rc_91 {}
   rc_wandoujia {}
   rc_anzhuo {}
   rc_xiaomi {}
   rc_meizu {}
   rc_oppo {}
   rc_huawei {}
   rc_weibo {}
   rc_dev {}
   }
productFlavors.all { flavor ->
    // 这里只是方便友盟统计每个渠道的数据
    flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name]
 }

  // 修改打包后APK的文件名
  applicationVariants.all { variant ->
   variant.outputs.each { output ->
       def oldFile = output.outputFile
       if (variant.buildType.name.equals('release')) {
           // 输出apk名称为yat3s_v1.0_2016-04-12_yingyongbao_a23f2e1.apk
           def releaseApkName = 'yat3s-v' + defaultConfig.versionName + '_' + getDate() + '_' + variant.productFlavors[0].name + "_" + getRevision() + '.apk'
           output.outputFile = new File(oldFile.parent, releaseApkName)
       }
       if (variant.buildType.name.equals('debug')) {
          // Do nothing
       }
   }
 }
  • 多渠道打包用的是Gradle的productFlavors
  • git的Revision 方便你管理你的release和tag
  • 打包的时候可以用Jenkins来自动Build你的包

Speed Gradle

我们都知道编译项目时候是依赖gradle的,gradle的构建速度决定了你的工作效率,上面零散的提到几点,下面总结一下:

  • 第一个大招 升级Android studio 2.0 并且使用最新版的gradle 2.0 使用Instant Run
  • 在你确保你的混淆没问题时候在debug模式下关闭所有混淆minifyEnabled false, shrinkResources false
  • 尽可能的让你的所有module的依赖库版本一致。
  • 在允许的情况下,在android studio的配置中,开启offline模式,在构建项目时候在命令后面加上–daemon –parallel –offline即可
  • 在添加依赖的时候尽量明确版本号,省去gradle查找最新版的时间,不要compile ‘com.facebook.fresco:fresco:latest’ 或compile ‘com.facebook.fresco:fresco:1.+’,
  • 开启守护线程并行编译,在gradle.properties中添加
    org.gradle.parallel=true
    org.gradle.daemon=true
    org.gradle.jvmargs=-Xms256m -Xmx1024m
  • 在gradle中def方法的时候尽量在debug情况下减少耗时操作或者不操作,比如:
    def getRevision() {
      if (!System.getenv('CI_BUILD')) {
        return 0
      }
      return ext.hash = 'git rev-parse --short HEAD'.execute().text.trim()
    }
    

写在最后

如果那里写的不对或者有更好的方式,欢迎指出批评,感恩。

Android MVP模式架构分析

开始新的项目,当然前期工作需要做足,磨刀不误砍柴工。

公司已经把iOS版本开发出来,这几天都是忙着看需求然后架构android版本,刚好研究了一下android架构的MVP模式觉得不错,所以想记录分享。
MVC模式大家都比较了解,先看一张图看看MVC跟MVP的区别

刚初学的时候接触MVC模式,一直强行把activity来充当Controller,搞得activity代码上千行(因为xml布局太懒惰了),不方便后期维护,实际activity只是view层
实际在android开发中完整的用MVC模式是有点吃力的,你需要单独写controller层来处理逻辑,当然还有MVVM模式,model-view-viewmodel,这需要依赖数据绑定,没有细细研究。那我们来看看MVP模式吧


可以看到MVP模式中有个Presenter层

MVP有什么优势

MVP模式可以让数据跟界面完全分离,View跟Model完全的解耦,方便我们做单元测试,同时也给activity进行减压

Presenter

presenter层主要是处理一些数据逻辑,完成Model跟view层的交互,但是model跟view是无法单独交互的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class UserPresenter {

FeedActivity mView;
FeedService feedService;

public UserPresenter(FeedActivity mView, FeedService feedService) {
this.mView = mView;
this.feedService = feedService;
}

public void loadFeeds(){
User user = new User();
user.setId("561103121400006a6496b6c6");
user.setUsed_ids(new ArrayList<String>());
feedService.getApi().getFeeds(user, new Callback<BaseModel<List<Feed>>>() {
@Override
public void success(BaseModel<List<Feed>> listBaseModel, Response response) {
mView.display(listBaseModel.getData());
}

@Override
public void failure(RetrofitError error) {

}
});
}
}

View

这里主要是activity,fragment(如果对fragment的生命周期不太了解就慎用),dialog等界面元素
只是单纯的处理界面的交互

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class FeedActivity extends AppCompatActivity{
@Bind(R.id.recyclerView)
RecyclerView feedRv;
private FeedAdapter feedAdapter;
private List<Feed> feeds;
private UserPresenter userPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_feeds);
ButterKnife.bind(this);

initData();
getData();
}

private void initData() {
feeds = new ArrayList<>();
feedAdapter = new FeedAdapter(this,feeds);
feedRv.setLayoutManager(new StaggeredGridLayoutManager(this, null, 3, 5));
feedRv.setAdapter(feedAdapter);

userPresenter = new UserPresenter(this,FeedService.getInstance());
}


private void getData() {
userPresenter.loadFeeds();
}

public void display(List<Feed> feeds){
this.feeds.addAll(feeds);
feedAdapter.notifyDataSetChanged();
}
}

Model

这里主要是一些entity,bean等,这个是必须的所以就不细究了。

面试有感

想着很多路都需要自己去走,不应该驻足太久,背起行囊。

9月26日把原来的工作辞了,抱着屌丝逆序的梦想打算奔赴京城,投了十几个公司,最后面试的有4家, 360,猎豹,中文在线,还有个初创公司。
9.28 火车转飞机真够折腾了,一路上的天气逆转,路上好巧也遇到同行去北京的一对夫妇,在广州工作,问了一下广州的互联网情况,结果是我不太想继续聊下去了。
北上,思绪跟天气都渐渐变冷下来,晚上找到一家靠近360的小旅馆,满心期待又不安的度过了一晚上。
上午。下起了小雨,吃早餐的心情就全无了,街边买了把雨伞,径直的往360奔去。
等候了20分钟,一副满脸刻着程序员的辛酸的一个大叔喊了我名字,拉到一个封闭的小房间,然后也没有自我介绍,只是简单的问了我的大四情况(因为没课才出来找实习的),后来叫我写一个二分查找的代码,当时想了几分钟写出来了,后面给了我一个链表问题,敢情我是对数据结构空洞的很,勉强的hack了一下解决方法,面试对我的答案很不满意,然后又问了我对android第三方框架是如何实现的,当时也没有研究的比较深入,就是简单的回答一下,结果也不是很理想。
撑着雨伞走在冷冷的街,也不知道哪边反向是对的,一直在反思什么,或许大学确实有很多遗憾吧。
下午。1.50准时抵达猎豹所在大厦,上了电梯,有一个貌似保安一样的人员守在外面,问我是不是面试,然后叫我拨打墙上的面试接待人员的电话,还没拨号,就有一个甜美的女生过来问我是不是面试,后来把我带进到办公室里面,里面确实很大很多人,不过给我的感觉似乎并没有很好,每个人都用心的做着事情,少了点什么味道。
坐下来,旁边还有2个也是面试android的人,看起来比我大。拿到卷子,简单的翻阅了一下,确实很简单,二十多道题统统给解决了,当时自信心也上升了许多。
后来甜美女生带我到楼下跟面试官聊。来了一个25岁左右的男生,坐下来叫我自我介绍一下,当时确实有点紧张,还好大学这样的场景经历的多,硬是把声音压到平稳,一面问了一些android基础问题,也是比较简单,后来一面面试官给我的反馈就是java基础不怎么好,也是很无奈,好好恶补java基础吧。二面来了一个成熟一点的大叔,我很担心他会不会给我来个数据结构或者难的算法,还好只是跟我谈谈项目以及我的个人性格,问了很多我平时休息时候做什么,有什么爱好等等,终于心里平复了许多,最后他说leader不在北京,叫我等leader电话,我觉得希望还是挺大的,不想了,现在结果也不知道。
第二天。好像贪睡的我还是起来了,赶着地铁来到朝外soho,比较高大上的地方,都是写字楼,很巧在上电梯的时候就遇到了跟我约面试的Anita,是一个人很好的姐姐,也很漂亮哦。
北京的上班是10点,等到10点20左右,来了一个跟我室友长得很像的跟我聊,他说他是做IOS的,当时我心里就紧张全无了,后来拿着我的简历叫我介绍一下上面写的框架,我很详细的介绍了每一个框架的使用跟功能,因为都用的比较多,所以很容易的答出来了,后面给我一个二叉树问题,数据结构我比较头疼,不过在指引下我还是做出来了,后面叫我写一个归并排序的代码,也算是写出来吧(提醒的比较多),然后是公司的CTO跟我面试,一看就是个大牛,聊得很少跟android相关,感觉是2个程序员在闲聊,确实这样的面试确实很棒,没有一点的心理负担,整场面试3个半小时,后面还请我吃了中饭简直赞。
后来Anita约我到星巴克聊offer的事情,聊了十几分钟,包括待遇和正在做的项目,项目是一个比较文艺的众论app(很赞),聊到待遇的时候给我的答案就是实习生,确实很无奈,自己没有毕业证只能拿到实习生的待遇(实习生跟正式生的待遇差别很大),当时唯一的想法快点毕业吧。我说给我7天的时间考虑一下。
3天感受到很多,在这样一个大都市里,互联网发展程度确实是其他城市很难比及的,当然这也是我所想要的工作环境(虽然现在很冷。。).. 到饭店了,吃完饭再更吧。
10.2更新
一吃饭吃了1天….
昨晚很热情的Anita说老板想要见我,很惊讶(心想要干嘛XD),如约到了一家高大上的环境很赞的餐厅,乡下人毕竟不太适应这样的场合,等了几分钟一个爆炸头姐姐到了,也就是Anita的公司CEO,不知不觉就聊了3个多个小时,聊了很多很多,我当时的第一反应就是CEO不是想单纯的找一个程序员,更希望的是找一个综合素质高的人,我觉得这样的老板给人的感觉更像是在创业,瞬间对初创公司的好感度up很多,爆炸头老板人也超nice的,虽然疯疯癫癫的。
此时做出来自己的最后的选择,初创公司,我觉得我更像是是选择一个老板,我觉得一个好的公司不是说你的规模有多大逼格有多高,更应该是有一个好的工作气氛以及好的上司。希望未来的时光能够工作愉快。
背起背包选择了一条路,勇敢的走下去吧。

RecyclerView的使用技巧

RecyclerView(ListView)在一个App中出现的频率还是非常高的,它是官方出的一个用来替代ListView、GridView的控件,下面简单的介绍一下RecyclerView中一些简单的使用技巧。

官方

官方网址(需要翻墙)

https://developer.android.com/reference/android/support/v7/widget/RecyclerView.html

官方给出的一句话解释是:

A flexible view for providing a limited window into a large data set.

Gradle

compile ‘com.android.support:recyclerview-v7:+’

建议搭配cardView使用

compile ‘com.android.support:cardview-v7:+’

使用

RecyclerView使用起来很简单,但相比于ListView来说,使用的方法还是有一点改变的。

1
2
3
4
<android.support.v7.widget.RecyclerView 注意V7兼容包名 否则只能api 21才能用
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>

1
2
3
4
5
6
7
RecyclerView recyclerView = (RecyclerView)findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this));//线性显示(默认纵向显示) 类似于ListView
// recyclerView.setLayoutManager(new LinearLayoutManager(this,LinearLayoutManager.HORIZONTAL,false));//线性显示(横向显示)
// recyclerView.setLayoutManager(new GridLayoutManager(this, 2));//线性宫格显示 类似于GridView
// recyclerView.setLayoutManager(new StaggeredGridLayoutManager(2, OrientationHelper.VERTICAL));//瀑布流显示

recyclerView.setAdapter(new RecyclerViewAdapter(this,data));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> {
private Context context;
private List<Post> posts;

public RecyclerViewAdapter(Context context, List<Post> posts) {
this.context = context;
this.posts = posts;
}

@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_post, parent,false);
ViewHolder vh = new ViewHolder(v);
return vh;
}

@Override
public void onBindViewHolder(ViewHolder vh, int position) {
vh.contentTv.setText(posts.get(position).getContent());
}

@Override
public int getItemCount() {
return posts.size();
}

public class ViewHolder extends RecyclerView.ViewHolder {
private TextView contentTv;
public ViewHolder(View itemView) {
super(itemView);
contentTv = (TextView) itemView.findViewById(R.id.content_post_tv);
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//你的点击事件
}
});
}
}
}

做到这里就算完成了。

—————————–分割线—————————-

这些是简单的使用,不过我们可以写一个万能的Adapter来适配每一个RecyclerView,请往下看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class RVHolder  extends RecyclerView.ViewHolder {


private ViewHolder viewHolder;

public RVHolder(View itemView) {
super(itemView);
viewHolder=ViewHolder.getViewHolder(itemView);

}


public ViewHolder getViewHolder() {
return viewHolder;
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
public abstract class AutoRVAdapter extends RecyclerView.Adapter<RVHolder> {
public final static Integer VIEW_ITEM = 101;
public final static Integer VIEW_PROG = 100;
public List<?> list;

public Context context;

public AutoRVAdapter(Context context, List<?> list) {
this.list = list;
this.context = context;
}

@Override
public RVHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(onCreateViewLayoutID(viewType),parent,false);

return new RVHolder(view);
}

public abstract int onCreateViewLayoutID(int viewType);


@Override
public void onViewRecycled(final RVHolder holder) {
super.onViewRecycled(holder);
}

@Override
public void onBindViewHolder(final RVHolder holder, final int position) {

onBindViewHolder(holder.getViewHolder(), position);
if (onItemClickListener != null) {
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onItemClickListener.onItemClick(null, v, holder.getPosition(), holder.getItemId());
}
});
}

}

public abstract void onBindViewHolder(ViewHolder holder, int position);

@Override
public int getItemCount() {
return list.size();
}

private AdapterView.OnItemClickListener onItemClickListener;

public AdapterView.OnItemClickListener getOnItemClickListener() {
return onItemClickListener;
}

public void setOnItemClickListener(AdapterView.OnItemClickListener onItemClickListener) {
this.onItemClickListener = onItemClickListener;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class ViewHolder {
private SparseArray<View> viewHolder;
private View view;

public static ViewHolder getViewHolder(View view){
ViewHolder viewHolder = (ViewHolder) view.getTag();
if (viewHolder == null) {
viewHolder = new ViewHolder(view);
view.setTag(viewHolder);
}
return viewHolder;
}
private ViewHolder(View view) {
this.view = view;
viewHolder = new SparseArray<View>();
view.setTag(viewHolder);
}
public <T extends View> T get(int id) {
View childView = viewHolder.get(id);
if (childView == null) {
childView = view.findViewById(id);
viewHolder.put(id, childView);
}
return (T) childView;
}

public View getConvertView() {
return view;
}

public TextView getTextView(int id) {

return get(id);
}
public Button getButton(int id) {

return get(id);
}

public ImageView getImageView(int id) {
return get(id);
}

public void setTextView(int id,CharSequence charSequence){
getTextView(id).setText(charSequence);
}
}

以后我们只要集成AutoRVAdapter就好了,简单方便。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class RecyclerViewAdapter extends AutoRVAdapter{
public RecyclerViewAdapter(Context context, List<?> list) {
super(context, list);
}
@Override
public int onCreateViewLayoutID(int viewType) {
return R.layout.item_appointment;
}

@Override
public void onBindViewHolder(ViewHolder holder, int position) {
Post post = (Post) list.get(position);
holder.setTextView(R.id.text,post.getName());
}
}

推荐

https://github.com/cymcsg/UltimateRecyclerView

集成了很多RecyclerView的功能,包括上拉刷新,下拉加载,加载动画,视差动画等

注意的问题

  • RecyclerView嵌套在ScrollView里面是一行都不显示的(ListView显示一行)这里需要重写LayoutManager,具体可google
  • 在5.0以上的手机 RecyclerView嵌套在ScrollView 重写LayoutManager后 RecyclerView的滑动惯性会消失,此时可重写ScrollView即可
    -

异步图片加载Fresco v/s Universal Image Loader (UIL)

在开发中我们经常会接触到图片的加载,图片加载是一个比较头疼的问题,你可以要考虑到图片的异步加载,图片的缓存处理、
图片的压缩处理等等,所以今天总结对比一下自己用过的2个开源图片框架,一个是大名鼎鼎的facebook出的fresco,
另外一个是使用人最多的Universal Image Loader (以下简称UIL),当然还有几款比较有名的图片框架,比如square的Picasso,鄙人没用过就不做比较了。

官方 Official

Fresco

官方文档

http://fresco-cn.org/

Github

https://github.com/facebook/fresco

Gradle

compile ‘com.facebook.fresco:fresco:0.6.0+’

UIL

Github

https://github.com/nostra13/Android-Universal-Image-Loader

Gradle

UIL没有上传在maven里面 所以得导jar包,可以在github上下载jar包

使用

Fresco

使用起来非常简单

1
Fresco.initialize(context);//初始化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<!-- 在xml布局里使用 这里列举了很多可选项,可以根据需要来使用-->
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/my_image_view"
android:layout_width="20dp"
android:layout_height="20dp" 注意DraweeView不支持wrap_content
fresco:fadeDuration="300"
fresco:actualImageScaleType="focusCrop" 缩放形式
fresco:placeholderImage="@color/wait_color" 临时安放的图片
fresco:placeholderImageScaleType="fitCenter"
fresco:failureImage="@drawable/error" 加载失败的图片
fresco:failureImageScaleType="centerInside"
fresco:retryImage="@drawable/retrying" 重试的图片
fresco:retryImageScaleType="centerCrop"
fresco:progressBarImage="@drawable/progress_bar" 加载进度时候的图片
fresco:progressBarImageScaleType="centerInside"
fresco:progressBarAutoRotateInterval="1000"
fresco:backgroundImage="@color/blue"
fresco:overlayImage="@drawable/watermark" 叠加的图片
fresco:pressedStateOverlayImage="@color/red" 按下去时候的图片
fresco:roundAsCircle="false" 图片是否是圆形
fresco:roundedCornerRadius="1dp" 图片圆角角度
fresco:roundTopLeft="true"
fresco:roundTopRight="false"
fresco:roundBottomLeft="false"
fresco:roundBottomRight="true"
fresco:roundWithOverlayColor="@color/corner_color"
fresco:roundingBorderWidth="2dp" 圆角边框宽度
fresco:roundingBorderColor="@color/border_color" 圆角边框颜色
/>

1
2
SimpleDraweeView draweeView = (SimpleDraweeView)findViewById(R.id.my_image_view);
draweeView.setImageURI(Uri.parse("http://www.baidu.com/1.jpg"));//搞定!

剩下的,Fresco会替你完成:

  • 显示占位图直到加载完成;
  • 下载图片;
  • 缓存图片;
  • 图片不再显示时,从内存中移除; etc

Fresco含有的特点是:

  • focusCrop缩放属性,可以支持人脸识别(让你的脸居中显示)
  • 默认的渐进式加载效果(这个超级棒)
  • 圆角/圆形图
  • 加载进度条
  • 支持Gif图片
  • 修改图片(比如下载完加一个logo在上面,比如微博的图片)

UIL

导入jar包
用起来也非常简单

1
2
3
ImageView imageView = (ImageView)findViewById(R.id.imageView);
ImageLoader.getInstance().displayImage("http://www.baidu.com/1.jpg", imageView);//搞定
//如果需要修改默认配置参数,可以设置option 具体参考文档

对比
首先我们来看一张图

这种图对比了时下流行的4种框架的异同,从表层来说,Fresco 自己定义了自己的View来配合加载,UIL则允许用户使用系统的View来加载

问题总结

  • Fresco 可能会于百度地图发生冲突
  • Fresco 在IDE布局预览的时候可能会有问题
  • UIL 的在处理圆角边框缩放可能需要自己写View

Android Retrofit框架的使用和个人见解

在Android开发中经常会处理网络请求,接触这个框架也有一段时间了,它给了我很多的帮助,希望能分享给android新人。

简介

Retrofit官方给出的简介是这样的。

Type-safe HTTP client for Android and Java by Square, Inc.

简而言之就是帮我们处理好了很多Android网络请求相关的事情,比如异步加载数据、解析json、上传文件等
推荐Okhttp搭配使用,因为retrofit是优先使用okhttp。

特点

  • 对RxJava的支持
  • Gson解析json,快捷方便的处理网络数据
  • 配合Okhttp使用安全快捷的处理异步加载
  • 上传文件等处理便捷

官方

官方文档

http://square.github.io/retrofit/

Github地址

https://github.com/square/retrofit

Gradle

compile ‘com.squareup.retrofit:retrofit:2.0.0-beta1’

这是2.0的测试版本,可以去官方看看稳定的低版本

使用

一般的GET、POST、PUT、PATCH、DELETE方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

//创建Retrofit接口,这里用回调方式
public class API {
public interface MyService {
@GET("/{id}/api")
void getFoods(@path("id") int id ,@QueryMap Map<String,Object> param,Callback<<List<Food>> callback);

//这里要注意的是 post跟Patch方法都需要传Body(RESTful规范)
@POST("/your/api")
void createFood(@Body Food food,Callback<Response> callback);

//跟post方法类似
@PATCH("/your/api")
void modifyFood(@Body Food food ,Callback<<List<Food>> callback);

//删除
@DELETE("/{id}/api")
void deleteFood(@path("id") int id ,Callback<<List<Food>> callback);

}
public static MyService service = new RestAdapter.Builder()
//设置你的请求EndPoint
.setEndpoint(“http://123.0.0.0”)
//设置Log提示
.setLogLevel(BuildConfig.DEBUG ? RestAdapter.LogLevel.FULL : RestAdapter.LogLevel.NONE)
//设置请求其他参数 例如加header
.setRequestInterceptor(new RequestInterceptor() {
@Override
public void intercept(RequestFacade request) {
request.addHeader("token", "your token");
}
}).build().create(MyService.class);
}
//使用接口

API.service.getFoods(id,param, new Callback<List<Food>>() {
@Override
public void success(List<Food> foods, Response response) {
//请求成功 处理你的操作
// foods 为已经解析的数据
// response 含有服务器返回的所有信息
}

@Override
public void failure(RetrofitError error) {
//请求失败
//error含有所有错误信息
//例如 error.getKind() == RetrofitError.Kind.NETWORK 网络连接异常
//error.getResponse().getStatus(); 获取HTTP错误代码
}
});

文件上传

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//创建接口
@Multipart
@POST("/images")
void upload(@Part("file") TypedFile file, Callback<File> callback);

//使用
API.service.upload(new TypedFile("image/jpg",yourFile), new Callback<File>()
@Override
public void success(File file, Response response) {
//请求成功 处理你的操作
// foods 为已经解析的数据
// response 含有服务器返回的所有信息
}

@Override
public void failure(RetrofitError error) {
//请求失败
//error含有所有错误信息
}
});

可能遇到的问题

  • 如果需要单纯的获取服务器传来的json(也就是不解析)建议直接使用okhttp来获取网络。
  • 注意:post跟Patch方法都需要传Body对象,这也是RESTful的规范
  • 需要重写GSON解析可以参考官方文档

Powered by Hexo and Hexo-theme-hiker

Copyright © 2017 - 2017 HACKGROUND All Rights Reserved.

UV : | PV :