安卓高效框架
Rx系列
RxAndroid
本文使用的是RxAndroid 2.0.1,Rx2.xx和Rx1.xx有较大的区别,此处只讨论2.xx
Rx牛逼的异步响应式框架,这里只是个人的学习记录,可能有些地方有误,仅供参考。
如果想深入了解请移步到官网,或者是中文翻译的官网
学习地址:
https://gold.xitu.io/entry/5884374e570c350062c1ac3b
https://gank.io/post/560e15be2dca930e00da1083
Rx实现原理个人以为可简单理解为一个事件发出者和一个事件接收者,两者之间通过订阅建立联系,事件发出和事件接收处理都可以在不同的线程,框架默认是在主线程中做所有的事,所以如果有耗时任务,请务必使用相应的线程切换方法进行切换。
配置Rx2.0:
compile ‘io.reactivex.rxjava2:rxandroid:2.0.1’
如果要使用retrofit网络加载框架,则需添加以下依赖:
compile ‘com.squareup.retrofit2:retrofit:2.1.0’
compile ‘com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0’
compile ‘com.squareup.retrofit2:converter-gson:2.0.1’
使用RxAndroid:
Observable:
Observable的创建方法很多,列举如下:
- Create — 常规创建方式,事件的产生需要自己实现(调用onNext)
- Defer — 不会立即创建一个Observable,只有调用subscribe时才会创建,而且会为每一个订阅者创建一个新的Observable(事件可以被多个订阅者订阅)
- Empty/Never/Throw —创建精确和有限行为的观测值,不知道搞什么的~
- fromIterable — 从一个可以迭代的集合中产生事件
- fromArray — 从一个数组中产生
- Interval — 按一定的时间间隔发送相同事件
- Just — 传递一个数值集合,可以是数组和集合或者多个单一元素
- Range — 传递一个数值范围,然后框架会产生范围之间的所有数值
- Repeat — 重复发送某个相同的事件
- Start — 通过一个函数返回值床架Observable对象
- Timer — 定时产生Observable对象
常规使用:
//创建一个Observable
Observable.create(new ObservableOnSubscribe<Integer>()
{
//在该方法中产生事件,并调用Emitter发射事件
@Override
public void subscribe(ObservableEmitter<Integer> e) throws Exception
{
//一般使用onNext产生事件交给下游处理
e.onNext(1);
SystemClock.sleep(5000);
e.onNext(2);
SystemClock.sleep(5000);
e.onNext(3);
//此处的onComplete表示事件已经发送完成,下游不需要再处理了,应该在事件产生完成时调用一下onComplete
e.onComplete();
}
})
//订阅在io线程,意思是上面的subscribe的事件产生在哪个线程,subscribeOn一般只会调用一次
//newThread() 总是在新的线程中执行
//computation 主要用于CPU密集型任务
//io 主要用于网络访问,文件处理
//immediate() 在当前线程立即开始执行任务
.subscribeOn(Schedulers.io())
//观察在哪个线程,也不知道是不是这个意思
//observeOn可以随便切换,表示接下来的操作在observeOn设定的线程执行,切换线程的灵魂所在
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Integer>()
{
@Override
public void onSubscribe(Disposable d)
{
Toast.makeText(mContext, "开始", Toast.LENGTH_SHORT).show();
}
@Override
public void onNext(Integer value)
{
tv.setText("" + value);
}
@Override
public void onError(Throwable e)
{
}
@Override
public void onComplete()
{
Toast.makeText(mContext, "完成", Toast.LENGTH_SHORT).show();
}
});
//subscribeOn会产生Disposable,这个Disposable是用于取消订阅事件,防止内存泄漏,
//使用该框架请注意在OnDestroy方法或者其他合适的方法中调用Disposable对象的
if(dis != null && (!dis.isDisposed()))
{
dis.dispose();
}
使用Flowable
Flowable(处理背压问题,observable不具备了,所谓背压就是当生产速度大于消费速度时的一种处理策略,比如快速点击事件)
Flowable<String> flow = Flowable.create(new FlowableOnSubscribe<String>()
{
@Override
public void subscribe(FlowableEmitter<String> e) throws Exception
{
e.onNext("hello rx");
e.onComplete();
}
}, BackpressureStrategy.BUFFER);
Subscriber<String> sub = new Subscriber<String>()
{
@Override
public void onSubscribe(Subscription s)
{
Log.e("s", "订阅开始...");
s.request(Long.MAX_VALUE);
}
@Override
public void onNext(String s)
{
Toast.makeText(RxActivity.this, s, Toast.LENGTH_SHORT).show();
}
@Override
public void onError(Throwable t)
{
Toast.makeText(RxActivity.this, "error", Toast.LENGTH_SHORT).show();
}
@Override
public void onComplete()
{
Toast.makeText(RxActivity.this, "完成", Toast.LENGTH_SHORT).show();
}
};
flow.subscribe(sub);
使用Subject
Subject既可以发事件又可以处理事件
Subject subject = PublishSubject.create();
subject.observeOn(EventThread.getScheduler(thread))
.subscribe(o -> {
try {
if (valid) {
handleEvent(o);
}
} catch (InvocationTargetException e) {
throwRuntimeException("Could not dispatch event: " +
o.getClass() + " to subscriber " + SubscriberEvent.this, e);
}
});
RxBus
github:https://github.com/AndroidKnife/RxBus
注意该项目是使用的RxJava1.xx,如果项目中使用RxJava2.xx会出现Rx版本冲突而无法通过编译,个人解决方法是复制了RxBus的代码到项目中,再稍作修改。
RxBus作用跟EventBus一样,也是通过订阅发布的方式来达到组件间通信。
RxBus也好EventBus也好,过度使用会导致代码结构混乱,逻辑难以追踪,维护困难,所以也要适度使用。
使用方法
首先注册
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
//注册
RxBus.get().register(this);
}
@Override
protected void onDestroy()
{
super.onDestroy();
//取消注册,防止内存泄漏
RxBus.get().unregister(this);
}
//在响应activity或fragment中添加某个方法,如:
@Subscribe
public void eat(String food) {
// purpose
}
//或者这样的
//thread指定响应线程,如果是ui操作,需要选择MAIN_THREAD,或者默认不写,
//tags是标签,用于事件之间进行区分
//此处是个数组,可以同时响应多种事件,如果要单独处理,需要分开写。
@Subscribe(thread = EventThread.IO,tags = {@Tag(BusAction.EAT_MORE)})
public void doSomething(List<String> foods) {
// purpose
}
//订阅以后在其他的activity或者fragment中使用post
//带tags,只有订阅时tags里面有"dd"才会响应
RxBus.get().post("dd", "走你");
//默认
RxBus.get().post("走你");
Retrofit
square出品,Rx风格的网络请求框架,注解式的请求方式,底层使用OkHttp,稳定性不用怀疑,必是良品。在访问一个链接得到一个结果,然后由结果去访问另外一个链接的这种情形下尤其有用。
使用Retrofit需要以下依赖:
compile ‘com.squareup.retrofit2:retrofit:2.1.0’
compile ‘com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0’
compile ‘com.squareup.retrofit2:converter-gson:2.0.1’
使用方法如下,由于没有对Retrofit太多的研究,如果要深入了解可移步官网或者
http://www.jianshu.com/p/308f3c54abdd
Retrofit retrofit = new Retrofit.Builder()
//Retrofit建议此处放app后台的公共地址,并带斜杠
.baseUrl("http://192.254.1.5:8080/HotMusic/")
//此处就是和RxJava连接的关键
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
//此处是Retrofit解析json需要使用的转换器
.addConverterFactory(GsonConverterFactory.create())
.build();
ApiService ser = retrofit.create(ApiService.class);
Call<User> call = ser.getUser();
call.enqueue(new Callback<User>()
{
@Override
public void onResponse(Call<User> call, Response<User> response)
{
Log.e("s", response.body().name);
}
@Override
public void onFailure(Call<User> call, Throwable t)
{
}
});
}
//
public interface ApiService
{
@GET("hot/getUser/{id}") //这里的{id} 表示是一个变量
Call<User> getUser(/** 这里的id表示的是上面的{id} */@Path("id") int id);
}
Green Dao
greenDao是一个将对象映射到SQLite数据库中的轻量且快速的ORM解决方案。关于greenDAO的概念可以看官网
学习地址:http://www.jianshu.com/p/dac3bd9bad72
配置:
Project中的build.gradle中添加以下内容:
repositories {
jcenter()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.2.3'
//lambda的配置
classpath 'me.tatarka:gradle-retrolambda:3.2.5'
//green dao的配置
classpath 'org.greenrobot:greendao-gradle-plugin:3.2.1'
}
app中的build.gradle中添加以下内容:
//Green Dao的配置
使用插件
apply plugin: ‘org.greenrobot.greendao’
//添加依赖
compile ‘org.greenrobot:greendao:3.2.0’
//数据库配置,会自动在gen包下生成响应的类
greendao {
schemaVersion 1
daoPackage 'com.white.hot.hotmusic.gen'
targetGenDir 'src/main/java'
}
- schemaVersion 对应当前数据库版本
- daoPackage 由GreenDao自动生成代码所在的包名,默认的是在项目包下面新建一个gen。
- targetGenDir 设置自动生成代码的目录。
写好bean类,注意id必须为long型,然后点一下build就会自动生成DaoMaster,DaoSession,xxDao等类
- @Property 标识该属性在表中对应的列名称,默认会都会存储到数据库,可以不写这个注解,使用@Property(nameInDb = “fnam”)自定义存储时的字段名,外键不能使用该属性
- @Transient 标识该属性将不会映射到表中
- @Entity:告诉GreenDao该对象为实体,只有被@Entity注释的Bean类才能被dao类操作,表名默认是实体类的名称
- @Id:对象的Id,使用Long类型作为EntityId,否则会报错。(autoincrement = true)表示主键会自增,如果false就会使用旧值
- @NotNull:属性不能为空
- @Unique:该属性值必须在数据库中是唯一值
- @Generated:编译后自动生成的构造函数、方法等的注释,提示构造函数、方法等不能被修改
使用本地数据库:
注意:由于green dao的内部机制,所有表的id都是 _id ,所有字段名都是大写,所以如果要使用raw中的数据库,则表的设计请遵循该原则。
常规用法
创建一个管理类
public class GreenDaoManager
{
private static GreenDaoManager instance;
private DaoMaster mDaoMaster;
private DaoSession mDaoSession;
private static WeakReference<Context> wrContext;
private String dbName;
private GreenDaoManager(Context context, String dbName)
{
wrContext = new WeakReference<Context>(context);
this.dbName = dbName;
init();
}
public static GreenDaoManager getInstance(Context context, String dbName)
{
if(instance == null)
{
synchronized (GreenDaoManager.class)
{
if(instance == null)
{
instance = new GreenDaoManager(context,dbName);
}
}
}
return instance;
}
private void init()
{
//字符串是数据库的名字
CustomDevOpenHelper helper = new CustomDevOpenHelper(wrContext.get(), dbName);
mDaoMaster = new DaoMaster(helper.getWritableDatabase());
mDaoSession = mDaoMaster.newSession();
}
public DaoMaster getDaoMaster()
{
return mDaoMaster;
}
public DaoSession getDaoSession()
{
return mDaoSession;
}
public DaoSession getNewSession()
{
mDaoSession = mDaoMaster.newSession();
return mDaoSession;
}
}
在其他地方使用
GreenDaoManager.getInstance(this, "dbName").getDaoSession().getUserDao().save(user);
详细增删该查自行百度~
使用本地数据库的管理类:
以下关于greendao读取raw中数据库的内容参考了:http://www.cnblogs.com/libertycode/p/6256000.html
public class LocalGDManager
{
private static LocalGDManager instance;
private DaoMaster mDaoMaster;
private DaoSession mDaoSession;
private String dbName;
private static WeakReference<Context> wrContext;
private LocalGDManager(Context context,String dbName)
{
wrContext = new WeakReference<Context>(context);
this.dbName = dbName;
init();
}
public static LocalGDManager getInstance(Context context, String dbName)
{
if(instance == null)
{
synchronized (LocalGDManager.class)
{
if(instance == null)
{
instance = new LocalGDManager(context, dbName);
}
}
}
return instance;
}
private void init()
{
//字符串是数据库的名字
//此处是自定义的Helper,因为自动生成的helper会强制生成所有表,对于我们从本地复制过去的表再对它进行创建肯定会出错
CustomDevOpenHelper devOpenHelper = new CustomDevOpenHelper(new GreenDaoContextWrapper(wrContext.get()),dbName, null);
mDaoMaster = new DaoMaster(devOpenHelper.getWritableDatabase());
mDaoSession = mDaoMaster.newSession();
}
public DaoMaster getDaoMaster()
{
return mDaoMaster;
}
public DaoSession getDaoSession()
{
return mDaoSession;
}
public DaoSession getNewSession()
{
mDaoSession = mDaoMaster.newSession();
return mDaoSession;
}
}
自定的helper,就是为了修改greendao的默认行为
public class CustomDevOpenHelper extends DaoMaster.DevOpenHelper
{
@Override
public void onCreate(Database db)
{
DaoMaster.createAllTables(db, true);
}
@Override
public void onUpgrade(Database db, int oldVersion, int newVersion) {
Log.i("greenDAO", "Upgrading schema from version " + oldVersion + " to " + newVersion + " by dropping all tables");
DaoMaster.dropAllTables(db, false);
onCreate(db);
}
public CustomDevOpenHelper(Context context, String name)
{
super(context, name);
}
public CustomDevOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory) {
super(context, name, factory);
}
}
需要自定义一个context wrapper用于复制本地数据库;
public class GreenDaoContextWrapper extends ContextWrapper
{
private Context mContext;
private int dbRes;
public GreenDaoContextWrapper(Context base, int localDbRes)
{
super(base);
this.mContext = base;
dbRes = localDbRes;
}
@Override
public File getDatabasePath(String name)
{
String filePath = mContext.getDatabasePath(name).getAbsolutePath();
File file = new File(filePath);
if (!file.exists())
{
if(!file.getParentFile().exists())
{
file.getParentFile().mkdir();
}
buildDatabase(filePath);
}
return file;
}
/**
* 创建数据库文件,其实就是将raw文件夹下的数据库文件复制到应用的database文件夹下:
* /data/data/com.xxxx/databases/
*
* @param filePath
*/
private void buildDatabase(String filePath)
{
Log.d("GreenDao", "buildDatabase");
InputStream inputStream = mContext.getResources().openRawResource(dbRes);
FileOutputStream fos = null;
try
{
fos = new FileOutputStream(filePath);
byte[] buffer = new byte[1024];
int length;
while ((length = inputStream.read(buffer)) > 0)
{
fos.write(buffer, 0, length);
}
fos.flush();
fos.close();
inputStream.close();
} catch (Exception e)
{
e.printStackTrace();
}
}
@Override
public SQLiteDatabase openOrCreateDatabase(String name, int mode, SQLiteDatabase.CursorFactory factory)
{
Log.d("GreenDao", "openOrCreateDatabase");
SQLiteDatabase result = SQLiteDatabase.openOrCreateDatabase(getDatabasePath(name), factory);
return result;
}
@Override
public SQLiteDatabase openOrCreateDatabase(String name, int mode,
SQLiteDatabase.CursorFactory factory, DatabaseErrorHandler errorHandler)
{
Log.d("GreenDao", "openOrCreateDatabase");
SQLiteDatabase result = SQLiteDatabase.openOrCreateDatabase(getDatabasePath(name), factory);
return result;
}
}
图片加载Glide
github:https://github.com/bumptech/glide
添加依赖:
compile ‘com.github.bumptech.glide:glide:3.7.0’
使用方法:
@Override public void onCreate(Bundle savedInstanceState) {
...
ImageView imageView = (ImageView) findViewById(R.id.my_image_view);
Glide.with(this).load("http://goo.gl/gEgYUd").into(imageView);
}
// For a simple image list:
@Override public View getView(int position, View recycled, ViewGroup container) {
final ImageView myImageView;
if (recycled == null) {
myImageView = (ImageView) inflater.inflate(R.layout.my_image_view, container, false);
} else {
myImageView = (ImageView) recycled;
}
String url = myUrls.get(position);
Glide
.with(myFragment)
.load(url)
.centerCrop()
.placeholder(R.drawable.loading_spinner)
.crossFade()
.into(myImageView);
return myImageView;
}
加载gif
Glide.with(this)
.load("http://wx2.sinaimg.cn/mw690/685d4d6cgy1fckkeajaovg20b407gb2b.gif")
.asGif()
.diskCacheStrategy(DiskCacheStrategy.SOURCE)
.into(vf);
View注解ButterKnife
@Bind(R.id.btnf)
Button btnf;
@OnClick(R.id.btnf)
public void onClick(View v)
{
Toast.makeText(getContext(), "click fragment", Toast.LENGTH_SHORT).show();
}
frament绑定
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)
{
View v = inflater.inflate(R.layout.frag, container);
ButterKnife.bind(this, v);
return v;
}
@Override
public void onDestroyView()
{
super.onDestroyView();
ButterKnife.unbind(this);
}
activity绑定:
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
}
@OnClick(R.id.btn)
public void onClick(View v)
{
v.getId();
Toast.makeText(this, "click activity", Toast.LENGTH_SHORT).show();
}
OkHttp3
square出品的网络加载框架,使用广泛,安卓系统内部已经集成了该框架。
关于使用,这里不重复了,网上资料相当多,这里我列出自己写的一个okhttp帮助类,由于资源模块暂时不能用,只能贴代码了:
参考了鸿洋大神的博客:http://blog.csdn.net/lmj623565791/article/details/47911083
使用这个类需要添加Gson的依赖:compile ‘com.google.code.gson:gson:2.2.4’
public class OkHttpManager
{
private static OkHttpManager mInstance;
private Handler mHandler;
private Gson mGson;
//默认超时时间20秒
private static int timeout = 20;
private OkHttpClient clientDefault;
private static Request simpleGetReq;
private static Request simplePostReq;
private static final String NET_FAILED_MSG = "未知异常!";
private static final String NET_FAILED_JSON_MSG = "Json 转换异常";
private static final String NET_FAILED_LINK_UNAVIlABLE_MSG = "无法连接";
private static final String NET_FAILED_TIMEOUT_MSG = "连接超时";
private static final String NET_FAILED_FILE_MSG = "文件不存在";
private OkHttpManager()
{
mGson = new Gson();
clientDefault = new OkHttpClient.Builder()
.connectTimeout(timeout, TimeUnit.SECONDS)
.readTimeout(timeout, TimeUnit.SECONDS)
.retryOnConnectionFailure(true)
.build();
mHandler = new Handler(Looper.getMainLooper());
}
public static OkHttpManager getInstance()
{
if (mInstance == null)
{
synchronized (OkHttpManager.class)
{
if (mInstance == null)
{
mInstance = new OkHttpManager();
}
}
}
return mInstance;
}
/***
* <b>发送一个get请求</b>, 回调要传入类型
*
* @param url 链接
* @param callback 回调函数
*/
public void doGet(String url, final ResultCallback<? extends Object> callback)
{
if (callback == null)
{
throw new RuntimeException("回调不能为空");
}
if (simpleGetReq == null)
{
simpleGetReq = new Request.Builder().get().url(url).build();
} else
{
simpleGetReq = simpleGetReq.newBuilder().get().url(url).build();
}
deliveryRequest(simpleGetReq, callback);
}
/***
* <b>发送一个post请求</b>, 回调要传入类型
*
* @param url 链接
* @param callback 回调函数
* @param reqBodyParam 请求体参数
* @param reqHeader 请求头参数
*/
public void doPost(String url, final ResultCallback<? extends Object> callback,
Map<String, String> reqBodyParam, Map<String, String> reqHeader)
{
if (callback == null)
{
throw new RuntimeException("回调不能为空");
}
if (simplePostReq == null)
{
if(reqHeader == null)
{
simplePostReq = new Request.Builder().post(reqBodyParam == null ?
buildEmptyBody() :buildParams(reqBodyParam).build()).url(url).build();
}else
{
//构造请求头
Request.Builder builder = new Request.Builder();
Set<String> set = reqHeader.keySet();
for(Iterator<String> it = set.iterator();it.hasNext();)
{
String key = it.next();
String value = reqHeader.get(key);
builder.addHeader(key, value);
}
simplePostReq = builder.post(reqBodyParam == null ? buildEmptyBody() :
buildParams(reqBodyParam).build()).url(url).build();
}
} else
{
if(reqHeader == null)
{
simplePostReq = simplePostReq.newBuilder().post(reqBodyParam == null ?
buildEmptyBody() :buildParams(reqBodyParam).build()).url(url).build();
}else
{
//构造请求头
Request.Builder builder = simplePostReq.newBuilder();
Set<String> set = reqHeader.keySet();
for(Iterator<String> it = set.iterator();it.hasNext();)
{
String key = it.next();
String value = reqHeader.get(key);
builder.addHeader(key, value);
}
simplePostReq = builder.post(reqBodyParam == null ? buildEmptyBody() :
buildParams(reqBodyParam).build()).url(url).build();
}
}
deliveryRequest(simplePostReq, callback);
}
/***
* <b>发送一个post请求</b>, 回调要传入类型
*
* @param url 链接
* @param callback 回调函数
* @param reqBodyParam 请求体参数
*/
public void doPost(String url, final ResultCallback<? extends Object> callback, Map<String, String> reqBodyParam)
{
doPost(url, callback, reqBodyParam, null);
}
// public void doPost(String url, final ResultCallback<? extends Object> callback, Object obj)
// {
// Map<String, String> param = new HashMap<>();
// }
/***
* <b>构建参数</b>
*
* @param map 参数
* @return
*/
private FormBody.Builder buildParams(Map<String, String> map)
{
FormBody.Builder builder = new FormBody.Builder();
if (map != null && map.size() > 0)
{
Set<String> set = map.keySet();
for (Iterator<String> it = set.iterator(); it.hasNext(); )
{
String key = it.next();
String value = map.get(key);
builder.add(key, value);
}
}
return builder;
}
private RequestBody buildEmptyBody()
{
return RequestBody.create(null, new byte[0]);
}
/***
* <b>传递请求</b>
*
* @param request
* @param callback
*/
private void deliveryRequest(Request request, final ResultCallback callback)
{
clientDefault.newCall(request).enqueue(new Callback()
{
@Override
public void onFailure(Call call, final IOException e)
{
failedResponse(e, callback);
}
@Override
public void onResponse(Call call, Response response) throws IOException
{
try
{
final String string = response.body().string();
if(string.startsWith("<html>") || string.startsWith("<HTML>"))
{
int errStartIndex = string.indexOf("<body>")+6;
int errEndIndex = string.indexOf("</body>");
String errStr = string.substring(errStartIndex, errEndIndex);
errStartIndex = errStr.indexOf("<h1>")+4;
errEndIndex = errStr.indexOf("</h1>");
errStr = errStr.substring(errStartIndex, errEndIndex);
failedResponse(new IllegalArgumentException(errStr), callback);
return;
}
if (callback.mType == String.class)
{
successResponse(string, callback);
} else
{
Object o = mGson.fromJson(string, callback.mType);
successResponse(o, callback);
}
} catch (com.google.gson.JsonParseException e)//Json解析的错误
{
failedResponse(e, callback);
} catch (IOException e)
{
failedResponse(e, callback);
}
}
});
}
/***
* <b>下载, 如果要拿到进度,需要复写callback的<b>onProgress()</b>方法</b>, 回调方法要传入类型
*
* @param url 下载链接<br/>
* @param fileAbsolutePath 文件绝对路径<br/>
* @param callback 回调<br/>
*/
public void download(final String url, final String fileAbsolutePath, final ResultCallback callback)
{
final Request request = new Request.Builder()
.url(url)
.build();
final DownloadProgressListener progressListener = new DownloadProgressListener()
{
@Override
public void update(long bytesRead, long contentLength, boolean done)
{
onDownloadProgress(bytesRead, contentLength, done, callback);
}
};
clientDefault = clientDefault.newBuilder().addInterceptor(new Interceptor()
{
@Override
public Response intercept(Chain chain) throws IOException
{
Response originalResponse = chain.proceed(chain.request());
return originalResponse.newBuilder().body(
new ProgressResponseBody(originalResponse.body(), progressListener))
.build();
}
}).build();
final Call call = clientDefault.newCall(request);
call.enqueue(new Callback()
{
@Override
public void onFailure(Call call, final IOException e)
{
failedResponse(e, callback);
}
@Override
public void onResponse(Call call, Response response) throws IOException
{
InputStream is = null;
byte[] buf = new byte[2048];
int len = 0;
FileOutputStream fos = null;
try
{
is = response.body().byteStream();
File file = new File(fileAbsolutePath);
fos = new FileOutputStream(file);
while ((len = is.read(buf)) != -1)
{
fos.write(buf, 0, len);
}
fos.flush();
successResponse(file, callback);
} catch (IOException e)
{
e.printStackTrace();
failedResponse(e, callback);
} finally
{
try
{
if (is != null) is.close();
} catch (IOException e)
{
}
try
{
if (fos != null) fos.close();
} catch (IOException e)
{
}
}
}
});
}
/***
* 文件上传
* @param url url
* @param fileAbsolutePath 文件绝对路径
* @param fileParamName 文件上传的参数名,相当于post的param
* @param callback 回调函数
*/
public void upload(final String url, final String fileAbsolutePath, final String fileParamName,
final ResultCallback callback)
{
if(callback == null)
{
throw new RuntimeException("回调不能为空");
}
File file = new File(fileAbsolutePath);
if(!file.exists())
{
failedResponse(new FileNotFoundException(), callback);
return;
}
final UploadProgressListener listener = new UploadProgressListener()
{
@Override
public void onRequestProgress(long bytesWrite, long contentLength, boolean done)
{
onUploadProgress(bytesWrite, contentLength, done, callback);
}
};
String fileName = getFileName(fileAbsolutePath);
RequestBody reqBody = RequestBody.create(MediaType.parse("application/octet-stream"), file);
MultipartBody mBody = new MultipartBody.Builder("--------------------------").setType(MultipartBody.FORM)
.addFormDataPart(fileParamName , fileName , new ProgressRequestBody(reqBody, listener))
.build();
clientDefault = clientDefault.newBuilder().build();
if (simplePostReq == null)
{
simplePostReq = new Request.Builder().post(mBody).url(url).build();
}else
{
simplePostReq = simplePostReq.newBuilder().url(url).post(mBody).build();
}
Call call = clientDefault.newCall(simplePostReq);
call.enqueue(new Callback()
{
@Override
public void onFailure(Call call, IOException e)
{
failedResponse(e, callback);
}
@Override
public void onResponse(Call call, Response response) throws IOException
{
try
{
final String string = response.body().string();
if (callback.mType == String.class)
{
successResponse(string, callback);
} else
{
Object o = mGson.fromJson(string, callback.mType);
successResponse(o, callback);
}
} catch (com.google.gson.JsonParseException e)//Json解析的错误
{
failedResponse(e, callback);
} catch (IOException e)
{
failedResponse(e, callback);
}
}
});
}
public String getFileName(String filePath)
{
if (TextUtils.isEmpty(filePath))
return "";
return filePath.substring(filePath.lastIndexOf(File.separator) + 1);
}
/***
* 文件下载的响应体
*/
private static class ProgressResponseBody extends ResponseBody
{
private final ResponseBody responseBody;
private final DownloadProgressListener progressListener;
private BufferedSource bufferedSource;
public ProgressResponseBody(ResponseBody responseBody, DownloadProgressListener progressListener)
{
this.responseBody = responseBody;
this.progressListener = progressListener;
}
@Override
public MediaType contentType()
{
return responseBody.contentType();
}
@Override
public long contentLength()
{
return responseBody.contentLength();
}
@Override
public BufferedSource source()
{
if (bufferedSource == null)
{
bufferedSource = Okio.buffer(source(responseBody.source()));
}
return bufferedSource;
}
private Source source(Source source)
{
return new ForwardingSource(source)
{
long totalBytesRead = 0L;
@Override
public long read(Buffer sink, long byteCount) throws IOException
{
long bytesRead = super.read(sink, byteCount);
totalBytesRead += bytesRead != -1 ? bytesRead : 0;
progressListener.update(totalBytesRead, responseBody.contentLength(), bytesRead == -1);
return bytesRead;
}
};
}
}
interface DownloadProgressListener
{
/**
* @param bytesRead 已下载字节数
* @param contentLength 总字节数
* @param done 是否下载完成
*/
void update(long bytesRead, long contentLength, boolean done);
}
/***
* 文件上传的请求体
*/
public class ProgressRequestBody extends RequestBody
{
//实际的待包装请求体
private final RequestBody requestBody;
//进度回调接口
private final UploadProgressListener progressListener;
//包装完成的BufferedSink
private BufferedSink bufferedSink;
/**
* 构造函数,赋值
* @param requestBody 待包装的请求体
* @param progressListener 回调接口
*/
public ProgressRequestBody(RequestBody requestBody, UploadProgressListener progressListener) {
this.requestBody = requestBody;
this.progressListener = progressListener;
}
/**
* 重写调用实际的响应体的contentType
* @return MediaType
*/
@Override
public MediaType contentType() {
return requestBody.contentType();
}
/**
* 重写调用实际的响应体的contentLength
* @return contentLength
* @throws IOException 异常
*/
@Override
public long contentLength() throws IOException {
return requestBody.contentLength();
}
/**
* 重写进行写入
* @param sink BufferedSink
* @throws IOException 异常
*/
@Override
public void writeTo(BufferedSink sink) throws IOException {
if (bufferedSink == null) {
//包装
bufferedSink = Okio.buffer(sink(sink));
}
//写入
requestBody.writeTo(bufferedSink);
//必须调用flush,否则最后一部分数据可能不会被写入
bufferedSink.flush();
}
/**
* 写入,回调进度接口
* @param sink Sink
* @return Sink
*/
private Sink sink(Sink sink) {
return new ForwardingSink(sink) {
//当前写入字节数
long bytesWritten = 0L;
//总字节长度,避免多次调用contentLength()方法
long contentLength = 0L;
@Override
public void write(Buffer source, long byteCount) throws IOException {
super.write(source, byteCount);
if (contentLength == 0) {
//获得contentLength的值,后续不再调用
contentLength = contentLength();
}
//增加当前写入的字节数
bytesWritten += byteCount;
//回调
progressListener.onRequestProgress(bytesWritten, contentLength, bytesWritten == contentLength);
}
};
}
}
interface UploadProgressListener
{
/**
* @param bytesWrite 已上传字节数
* @param contentLength 总字节数
* @param done 是否上传完成
*/
void onRequestProgress(long bytesWrite, long contentLength, boolean done);
}
private void failedResponse(final Exception e, final ResultCallback callback)
{
mHandler.post(new Runnable()
{
@Override
public void run()
{
if (callback != null)
{
if (e instanceof com.google.gson.JsonParseException)
{
callback.onError(NET_FAILED_JSON_MSG, e);
} else if (e instanceof ConnectException)
{
callback.onError(NET_FAILED_LINK_UNAVIlABLE_MSG, e);
} else if (e instanceof SocketTimeoutException)
{
callback.onError(NET_FAILED_TIMEOUT_MSG, e);
} else if (e instanceof FileNotFoundException)
{
callback.onError(NET_FAILED_FILE_MSG, e);
} else if (e instanceof IllegalArgumentException)
{
callback.onError("参数异常-"+e.getMessage(), e);
} else
{
callback.onError(NET_FAILED_MSG, e);
}
}
}
});
}
private void onDownloadProgress(final long progress, final long allLength,
final boolean done, final ResultCallback callback)
{
mHandler.post(new Runnable()
{
@Override
public void run()
{
callback.onDownloadProgress(progress, allLength, done);
}
});
}
private void onUploadProgress(final long progress, final long allLength,
final boolean done, final ResultCallback callback)
{
mHandler.post(new Runnable()
{
@Override
public void run()
{
callback.onUploadProgress(progress, allLength, done);
}
});
}
private void successResponse(final Object object, final ResultCallback callback)
{
mHandler.post(new Runnable()
{
@Override
public void run()
{
if (callback != null)
{
callback.onSuccess(object);
}
}
});
}
/***
* 回调类,用于返回结果到前台
*
* @param <T> 传入一个泛型,会自动根据类型产生对象,在post中用处较多
*/
public static abstract class ResultCallback<T extends Object>
{
Type mType;
public ResultCallback()
{
mType = getSuperclassTypeParameter(getClass());
}
public static Type getSuperclassTypeParameter(Class<?> subclass)
{
Type superclass = subclass.getGenericSuperclass();
if (superclass instanceof Class)
{
throw new RuntimeException("ResultCallback泛型缺少对象类型");
}
ParameterizedType parameterized = (ParameterizedType) superclass;
return $Gson$Types.canonicalize(parameterized.getActualTypeArguments()[0]);
}
public abstract void onError(String simpleMsg, Exception e);
public void onDownloadProgress(long progress, long allLength, boolean done){}
public void onUploadProgress(long progress, long allLength, boolean done){}
public abstract void onSuccess(T response);
}
/***
* 此方法用于设置https访问时证书,仅用于单向验证,inputstream为证书文件的输入流,可以将证书放到main/assets目录下,
* 也可以使用rfc命令得到字符串用 new Buffer().writeUtf(str).inputStream()得到输入流,
* 需要包含-----BEGIN CERTIFICATE-----和-----END CERTIFICATE-----
* @param certificate
*/
public void setCertificate(String alias, String password, InputStream certificate)
{
try
{
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null);
int index = 0;
// for (InputStream certificate : certificates)
// {
// String certificateAlias = Integer.toString(index++);
keyStore.setCertificateEntry(alias, certificateFactory.generateCertificate(certificate));
try
{
if (certificate != null)
certificate.close();
} catch (IOException e)
{
}
// }
SSLContext sslContext = SSLContext.getInstance("TLS");
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(
KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, password.toCharArray());
TrustManagerFactory trustManagerFactory =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
throw new IllegalStateException("Unexpected default trust managers:"
+ Arrays.toString(trustManagers));
}else
{
sslContext.init(null,new TrustManager[]{trustManagers[0]},new SecureRandom());
clientDefault = clientDefault.newBuilder()
.sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) trustManagers[0])
.build();
}
} catch (Exception e)
{
e.printStackTrace();
}
}
}
使用:
manager = OkHttpManager.getInstance();
manager.doPost(Constant.WEB_BASE + "/jpush/notifyOne", new OkHttpManager.ResultCallback<BaseResp>()
{
@Override
public void onError(String simpleMsg, Exception e)
{
Toast.makeText(PushActivity.this, simpleMsg, Toast.LENGTH_SHORT).show();
}
@Override
public void onSuccess(BaseResp response)
{
Toast.makeText(PushActivity.this, response.getInfo(), Toast.LENGTH_SHORT).show();
}
}, null);
其他使用方法自行研究~