<|start_filename|>saf-core/src/main/java/com/safframework/saf/async/RxAsyncTask.java<|end_filename|>
package com.safframework.saf.async;

import android.app.Dialog;

import io.reactivex.BackpressureStrategy;
import io.reactivex.Flowable;
import io.reactivex.FlowableEmitter;
import io.reactivex.FlowableOnSubscribe;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.annotations.NonNull;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.functions.Action;
import io.reactivex.functions.Consumer;
import io.reactivex.schedulers.Schedulers;

/**
 * Created by <NAME> on 2016/11/9.
 */

public abstract class RxAsyncTask<T> {

    private Dialog mDialog;
    private SuccessHandler successHandler;
    private FailedHandler failedHandler;
    private int retryCount = 3;

    private CompositeDisposable composite = new CompositeDisposable();

    public RxAsyncTask() {
        this(null);
    }

    public RxAsyncTask(Dialog dialog) {
        this.mDialog = dialog;
    }

    private void onPreExecute() {
        if (mDialog != null) {
            mDialog.show();
        }
    }

    private void execute() {
        onPreExecute();
        Flowable<T> flowable = createFlowable();

        if (retryCount > 0) {
            composite.add(flowable.retryWhen(new RetryWithDelay(retryCount, 1000))
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(new Consumer<T>() {

                        @Override
                        public void accept(@NonNull T t) throws Exception {

                            if (successHandler != null) {
                                successHandler.onSuccess(t);
                            }
                        }
                    }, new Consumer<Throwable>() {
                        @Override
                        public void accept(@NonNull Throwable throwable) throws Exception {

                            if (throwable != null && failedHandler != null) {
                                failedHandler.onFail(throwable);
                            }
                        }
                    }, new Action() {
                        @Override
                        public void run() throws Exception {
                            if (mDialog != null) {
                                mDialog.dismiss();
                                mDialog = null;
                            }
                        }
                    }));
        } else {
            composite.add(flowable
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(new Consumer<T>() {
                        @Override
                        public void accept(@NonNull T t) throws Exception {

                            if (successHandler != null) {
                                successHandler.onSuccess(t);
                            }
                        }

                    }, new Consumer<Throwable>() {
                        @Override
                        public void accept(@NonNull Throwable throwable) throws Exception {

                            if (throwable != null && failedHandler != null) {
                                failedHandler.onFail(throwable);
                            }
                        }
                    }, new Action() {
                        @Override
                        public void run() throws Exception {
                            if (mDialog != null) {
                                mDialog.dismiss();
                                mDialog = null;
                            }
                        }
                    }));
        }

    }

    /**
     * 所有的task都必须执行这个方法，否则无法运行
     */
    public void start() {
        execute();
    }

    /**
     * 取消task的执行
     */
    public void cancel() {
        composite.clear();
    }

    private Flowable<T> createFlowable() {
        return Flowable.create(new FlowableOnSubscribe<T>() {
            @Override
            public void subscribe(@NonNull FlowableEmitter<T> e) throws Exception {
                e.onNext(onExecute());
                e.onComplete();
            }
        }, BackpressureStrategy.BUFFER);
    }

    public RxAsyncTask success(SuccessHandler successHandler) {
        this.successHandler = successHandler;
        return this;
    }

    public RxAsyncTask failed(FailedHandler failedHandler) {
        this.failedHandler = failedHandler;
        return this;
    }

    /**
     * 重试次数，默认是3次，可以根据实际情况修改
     * @param retryCount
     * @return
     */
    public RxAsyncTask retry(int retryCount) {
        this.retryCount = retryCount;
        return this;
    }

    /**
     * 可以在Activity/Fragment注销时使用，防止内存泄露
     */
    public void destory() {

        if (!composite.isDisposed()) {
            composite.dispose();
        }
    }

    /**
     * 执行任务的结果
     *
     * @return
     */
    public abstract T onExecute();

    public interface SuccessHandler<T> {

        void onSuccess(T t);
    }

    public interface FailedHandler {

        void onFail(Throwable e);
    }
}


<|start_filename|>app/src/main/java/com/test/saf/activity/SplashActivity.java<|end_filename|>
package com.test.saf.activity;

import android.content.Intent;
import android.os.Bundle;
import android.util.DisplayMetrics;

import com.safframework.log.L;
import com.test.saf.R;
import com.test.saf.app.BaseActivity;
import com.test.saf.config.Config;

import java.util.concurrent.TimeUnit;

import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.annotations.NonNull;
import io.reactivex.functions.Consumer;


/**
 * Created by <NAME> on 2016/12/5.
 */

public class SplashActivity extends BaseActivity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_splash);
        initData();
    }

    private void initData() {
        DisplayMetrics metric = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(metric);
        int width = metric.widthPixels; // 屏幕宽度（像素）
        int height = metric.heightPixels; // 屏幕高度（像素）
        float density = metric.density; // 屏幕密度（0.75 / 1.0 / 1.5 /2.0）
        L.i("device screen: " + width + "*" + height + " desity: " + density);
        Config.height = height;
        Config.width = width;
        Config.density = density;

        loadingNext();
    }

    /**
     * 跳转到主页面
     */
    private void loadingNext() {

        // 延迟2秒跳到首页
        Observable.timer(2000, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread())
                .subscribe(new Consumer<Long>() {
                    @Override
                    public void accept(@NonNull Long aLong) throws Exception {
                        Intent i = new Intent(mContext, MainActivity.class);
                        startActivity(i);
                        finish();
                    }
                });
    }
}

<|start_filename|>saf-core/src/main/java/com/safframework/saf/rest/ConnectionFactory.java<|end_filename|>
/**
 * 
 */
package com.safframework.saf.rest;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.Proxy;
import java.net.URL;

/**
 * @author <NAME>
 *
 */
public interface ConnectionFactory {

    HttpURLConnection create(URL url) throws IOException;

    HttpURLConnection create(URL url, Proxy proxy) throws IOException;
}


<|start_filename|>saf-rxlifecycle/src/main/java/com/safframework/lifecycle/LifecycleTransformer.java<|end_filename|>
package com.safframework.lifecycle;

import org.reactivestreams.Publisher;

import io.reactivex.Completable;
import io.reactivex.CompletableSource;
import io.reactivex.CompletableTransformer;
import io.reactivex.Flowable;
import io.reactivex.FlowableTransformer;
import io.reactivex.Maybe;
import io.reactivex.MaybeSource;
import io.reactivex.MaybeTransformer;
import io.reactivex.Observable;
import io.reactivex.ObservableSource;
import io.reactivex.ObservableTransformer;
import io.reactivex.Single;
import io.reactivex.SingleSource;
import io.reactivex.SingleTransformer;
import io.reactivex.annotations.NonNull;
import io.reactivex.functions.Function;
import io.reactivex.functions.Predicate;
import io.reactivex.processors.BehaviorProcessor;

/**
 * Created by <NAME> on 2017/5/25.
 */

public class LifecycleTransformer<T> implements ObservableTransformer<T, T>,
        FlowableTransformer<T, T>,
        SingleTransformer<T, T>,
        MaybeTransformer<T, T>,
        CompletableTransformer {

    private final BehaviorProcessor<Integer> lifecycleBehavior;

    private LifecycleTransformer() throws IllegalAccessException {
        throw new IllegalAccessException();
    }

    public LifecycleTransformer(@NonNull BehaviorProcessor<Integer> lifecycleBehavior) {
        this.lifecycleBehavior = lifecycleBehavior;
    }

    @Override
    public CompletableSource apply(Completable upstream) {
        return upstream.ambWith(
                lifecycleBehavior.filter(new Predicate<Integer>() {
                    @Override
                    public boolean test(@LifecyclePublisher.Event Integer event) throws Exception {
                        return event == LifecyclePublisher.ON_DESTROY_VIEW ||
                                event == LifecyclePublisher.ON_DESTROY ||
                                event == LifecyclePublisher.ON_DETACH;
                    }
                }).take(1).flatMapCompletable(new Function<Integer, Completable>() {
                    @Override
                    public Completable apply(Integer flowable) throws Exception {
                        return Completable.complete();
                    }
                })
        );
    }

    @Override
    public Publisher<T> apply(final Flowable<T> upstream) {
        return upstream.takeUntil(
                lifecycleBehavior.skipWhile(new Predicate<Integer>() {
                    @Override
                    public boolean test(@LifecyclePublisher.Event Integer event) throws Exception {
                        return event != LifecyclePublisher.ON_DESTROY_VIEW &&
                                event != LifecyclePublisher.ON_DESTROY &&
                                event != LifecyclePublisher.ON_DETACH;
                    }
                })
        );
    }

    @Override
    public MaybeSource<T> apply(Maybe<T> upstream) {
        return upstream.takeUntil(
                lifecycleBehavior.skipWhile(new Predicate<Integer>() {
                    @Override
                    public boolean test(@LifecyclePublisher.Event Integer event) throws Exception {
                        return event != LifecyclePublisher.ON_DESTROY_VIEW &&
                                event != LifecyclePublisher.ON_DESTROY &&
                                event != LifecyclePublisher.ON_DETACH;
                    }
                })
        );
    }

    @Override
    public ObservableSource<T> apply(Observable<T> upstream) {
        return upstream.takeUntil(
                lifecycleBehavior.skipWhile(new Predicate<Integer>() {
                    @Override
                    public boolean test(@LifecyclePublisher.Event Integer event) throws Exception {
                        return event != LifecyclePublisher.ON_DESTROY_VIEW &&
                                event != LifecyclePublisher.ON_DESTROY &&
                                event != LifecyclePublisher.ON_DETACH;
                    }
                }).toObservable()
        );
    }

    @Override
    public SingleSource<T> apply(Single<T> upstream) {
        return upstream.takeUntil(
                lifecycleBehavior.skipWhile(new Predicate<Integer>() {
                    @Override
                    public boolean test(@LifecyclePublisher.Event Integer event) throws Exception {
                        return event != LifecyclePublisher.ON_DESTROY_VIEW &&
                                event != LifecyclePublisher.ON_DESTROY &&
                                event != LifecyclePublisher.ON_DETACH;
                    }
                })
        );
    }
}


<|start_filename|>app/src/main/java/com/test/saf/fragment/CacheFragment.java<|end_filename|>
package com.test.saf.fragment;

import com.test.saf.app.BaseFragment;

/**
 * Created by <NAME> on 2016/11/22.
 */

public class CacheFragment extends BaseFragment {
}


<|start_filename|>saf-core/src/main/java/com/safframework/saf/orm/TableInfo.java<|end_filename|>
/**
 * 
 */
package com.safframework.saf.orm;

import com.safframework.saf.orm.annotation.Column;
import com.safframework.saf.orm.annotation.Table;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author <NAME>
 *
 */
public final class TableInfo {

	private Class<? extends DBDomain> mType;
	private String mTableName;

	private Map<Field, String> mColumnNames = new HashMap<Field, String>();

	public TableInfo(Class<? extends DBDomain> type) {
		mType = type;

		final Table tableAnnotation = type.getAnnotation(Table.class);
		if (tableAnnotation != null) {
			mTableName = tableAnnotation.name();
		}
		else {
			mTableName = type.getSimpleName();
		}

		List<Field> fields = new ArrayList<Field>(Arrays.asList(type.getDeclaredFields()));
		fields.add(getIdField(type));

		for (Field field : fields) {
			if (field.isAnnotationPresent(Column.class)) {
				final Column columnAnnotation = field.getAnnotation(Column.class);
				mColumnNames.put(field, columnAnnotation.name());
			}
		}
	}

	public Class<? extends DBDomain> getType() {
		return mType;
	}

	public String getTableName() {
		return mTableName;
	}

	public Collection<Field> getFields() {
		return mColumnNames.keySet();
	}

	public String getColumnName(Field field) {
		return mColumnNames.get(field);
	}

	private Field getIdField(Class<?> type) {
		if (type.equals(DBDomain.class)) {
			try {
				return type.getDeclaredField("mId");
			}
			catch (NoSuchFieldException e) {
			}
		}
		else if (type.getSuperclass() != null) {
			return getIdField(type.getSuperclass());
		}

		return null;
	}

}


<|start_filename|>saf-core/src/main/java/com/safframework/saf/app/SAFAppCompatActivity.java<|end_filename|>
package com.safframework.saf.app;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;

import com.safframework.log.L;
import com.safframework.saf.utils.SAFUtils;
import com.safframework.saf.utils.ToastUtils;


/**
 * Created by <NAME> on 2016/11/17.
 */

public class SAFAppCompatActivity extends AppCompatActivity {

    public SAFApp app;
    public String TAG;
    protected Context mContext;
    protected Handler mHandler = new SafeHandler(this);

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        app = (SAFApp) this.getApplication();

        mContext = this;

        TAG = SAFUtils.makeLogTag(this.getClass());
        L.init(TAG);
        addActivityToManager(this);
    }

    protected  void addActivityToManager(Activity act) {
        Log.i(TAG, "addActivityToManager");
        if (!app.activityManager.contains(act)) {
            Log.i(TAG , "addActivityToManager, packagename = " + act.getClass().getName()) ;
            app.activityManager.add(act);
        }
    }

    protected void closeAllActivities() {
        Log.i(TAG, "closeAllActivities");
        for (final Activity act : app.activityManager) {
            if (act != null) {
                act.finish();
            }
        }
    }

    protected  void delActivityFromManager(Activity act) {
        Log.i(TAG,"delActivityFromManager") ;
        if (app.activityManager.contains(act)) {
            app.activityManager.remove(act);
        }
    }

    /**
     * 返回当前运行activity的名称
     * @return
     */
    protected String getCurrentActivityName() {
        int size = app.activityManager.size();
        if (size > 0) {
            return app.activityManager.get(size-1).getClass().getName();
        }
        return null;
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        delActivityFromManager(this);
    }

    /**
     * @param message toast的内容
     */
    protected void toast(String message) {
        ToastUtils.showShort(this, message);
    }

    /**
     * @param resId toast的内容来自String.xml
     */
    protected void toast(int resId) {
        ToastUtils.showShort(this, resId);
    }
}


<|start_filename|>saf-core/src/main/java/com/safframework/saf/utils/ReflectionUtils.java<|end_filename|>
/**
 * 
 */
package com.safframework.saf.utils;

import android.content.Context;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

import dalvik.system.DexFile;

/**
 * 反射的帮助类
 * @author <NAME>
 *
 */
public class ReflectionUtils {
	
	/**
	 * 获取某一个包名下的所有类名
	 * @param context
	 * @param packageName
	 * @return
	 * @throws IOException
	 */
    public static List<String> getPackageAllClassName(Context context,String packageName) throws IOException{ 
		String sourcePath = context.getApplicationInfo().sourceDir;
		List<String> paths = new ArrayList<String>();
		
		if (sourcePath != null) {
			DexFile dexfile = new DexFile(sourcePath);
			Enumeration<String> entries = dexfile.entries();

			while (entries.hasMoreElements()) {
				String element = entries.nextElement();
				if (element.contains(packageName)) {
					paths.add(element);
				}
			}
		}
		
		return paths;
    }
}


<|start_filename|>saf-core/src/main/java/com/safframework/saf/rest/HttpResponseRetryHandler.java<|end_filename|>
/**
 * 
 */
package com.safframework.saf.rest;


/**
 * 带http重试机制的handler
 * @author <NAME>
 *
 */
public abstract class HttpResponseRetryHandler implements HttpResponseHandler,HttpRetryStrategy {
	
	private int retryNum = RestConstant.DEFAULT_RETRY_NUM;
	
	public HttpResponseRetryHandler(){
	}
	
	public HttpResponseRetryHandler(int retryNum){
		this.retryNum = retryNum;
	}
	
	public int getRetryNum() {
		return retryNum;
	}

	public void setRetryNum(int retryNum) {
		this.retryNum = retryNum;
	}
}


<|start_filename|>saf-core/src/main/java/com/safframework/saf/utils/StringUtils.java<|end_filename|>
/**
 * 
 */
package com.safframework.saf.utils;

import com.safframework.tony.common.utils.Preconditions;

import java.security.MessageDigest;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 字符串的帮助类
 * @author <NAME>
 *
 */
public class StringUtils {

    private static final char CHAR_CHINESE_SPACE = '\u3000';//中文（全角）空格
	
	/**
	 * 判断字符串是否为手机号码</br>
	 * 只能判断是否为大陆的手机号码
	 * @param str
	 * @return
	 */
	public static boolean checkMobile(String str) {
		Pattern p = Pattern.compile("1[34578][0-9]{9}");
		Matcher m = p.matcher(str);
		return m.matches();
	}
	
	/**
	 * 验证email的合法性
	 * 
	 * @param emailStr
	 * @return
	 */
	public static boolean checkEmail(String emailStr) {
		String check = "^([a-z0-9A-Z]+[-|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$";
		Pattern regex = Pattern.compile(check);
		Matcher matcher = regex.matcher(emailStr.trim());

		return matcher.matches();
	}
	
	/**
	 * 对字符串进行MD5加密</br>
	 * 如果返回为空，则表示加密失败
	 * @param s
	 * @return
	 */
	public static String md5(String s) {
		char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
				'a', 'b', 'c', 'd', 'e', 'f' };
		try {
			byte[] strTemp = s.getBytes("UTF-8");
			// 使用MD5创建MessageDigest对象
			MessageDigest mdTemp = MessageDigest.getInstance("MD5");
			mdTemp.update(strTemp);
			byte[] md = mdTemp.digest();
			int j = md.length;
			char str[] = new char[j * 2];
			int k = 0;
			for (int i = 0; i < j; i++) {
				byte b = md[i];
				// 将每个数(int)b进行双字节加密
				str[k++] = hexDigits[b >> 4 & 0xf];
				str[k++] = hexDigits[b & 0xf];
			}
			return new String(str);
		} catch (Exception e) {
			return null;
		}
	}

	/**
	 * 去掉字符串的空格
	 * @param input
	 * @return
	 */
	public static String trim(String input){
		if(input==null) return "";
		StringBuilder sb = new StringBuilder();
		for(int i=0;i<input.length();i++){
			if(input.charAt(i)==' '){
				continue;
			}else{
				sb.append(input.charAt(i));
			}
		}
		return sb.toString();
	}
	
	/**
	 * 从字符串s中截取某一段字符串
	 * @param s
	 * @param startToken 开始标记
	 * @param endToken 结束标记
	 * @return
	 */
    public static String mid(String s, String startToken, String endToken) {
        return mid(s, startToken, endToken, 0);
    }

    public static String mid(String s, String startToken, String endToken, int fromStart) {
        if (startToken==null || endToken==null)
            return null;
        int start = s.indexOf(startToken, fromStart);
        if (start==(-1))
            return null;
        int end = s.indexOf(endToken, start + startToken.length());
        if (end==(-1))
            return null;
        String sub = s.substring(start + startToken.length(), end);
        return sub.trim();
    }
    
    /**
     * 简化字符串，通过删除空格键、tab键、换行键等实现
     * @param s
     * @return
     */
    public static String compact(String s) {
        char[] cs = new char[s.length()];
        int len = 0;
        for(int n=0; n<cs.length; n++) {
            char c = s.charAt(n);
            if(c==' ' || c=='\t' || c=='\r' || c=='\n' || c==CHAR_CHINESE_SPACE)
                continue;
            cs[len] = c;
            len++;
        }
        return new String(cs, 0, len);
    }
    
    /**
     * 生成uuid
     * @return
     */
	public static String getUUID() {
		return UUID.randomUUID().toString().replace("-", "").toLowerCase();
	}
	
	/**
	 * 求两个字符串数组的并集，利用set的元素唯一性  
	 * @param arr1
	 * @param arr2
	 * @return
	 */
    public static String[] union(String[] arr1, String[] arr2) {  
        Set<String> set = new HashSet<String>();  
        for (String str : arr1) {  
            set.add(str);  
        }  
        for (String str : arr2) {  
            set.add(str);  
        }
        String[] result = {};  
        return set.toArray(result);  
    }
    
    /**
     * 求两个字符串数组的交集 
     * @param arr1
     * @param arr2
     * @return
     */
    public static String[] intersect(String[] arr1, String[] arr2) {  
        Map<String, Boolean> map = new HashMap<String, Boolean>();  
        LinkedList<String> list = new LinkedList<String>();  
        for (String str : arr1) {  
            if (!map.containsKey(str)) {  
                map.put(str, Boolean.FALSE);  
            }  
        }
        
        for (String str : arr2) {  
            if (map.containsKey(str)) {  
                map.put(str, Boolean.TRUE);  
            }  
        }  
  
        for (Entry<String, Boolean> e : map.entrySet()) {  
            if (e.getValue().equals(Boolean.TRUE)) {  
                list.add(e.getKey());  
            }  
        }  
  
        String[] result = {};  
        return list.toArray(result);  
    }
    
    /**
     * 求两个字符串数组的差集  
     * @param arr1
     * @param arr2
     * @return
     */
    public static String[] minus(String[] arr1, String[] arr2) {  
        LinkedList<String> list = new LinkedList<String>();  
        LinkedList<String> history = new LinkedList<String>();  
        String[] longerArr = arr1;  
        String[] shorterArr = arr2;  
        //找出较长的数组来减较短的数组  
        if (arr1.length > arr2.length) {  
            longerArr = arr2;  
            shorterArr = arr1;  
        }  
        for (String str : longerArr) {  
            if (!list.contains(str)) {  
                list.add(str);  
            }  
        }  
        for (String str : shorterArr) {  
            if (list.contains(str)) {  
                history.add(str);  
                list.remove(str);  
            } else {  
                if (!history.contains(str)) {  
                    list.add(str);  
                }  
            }  
        }  
  
        String[] result = {};  
        return list.toArray(result);  
    }
    
    /**
     * 字符串反转
     * @param str
     * @return
     */
    public static String reverse(String str){
    	return new StringBuffer(str).reverse().toString();  
    }
    
	/**
	 * 字符串数组反转
	 * @param strs
	 * @return
	 */
	public static String[] reverse(String[] strs) {
		for (int i = 0; i < strs.length; i++) {
			String top = strs[0];
			for (int j = 1; j < strs.length - i; j++) {
				strs[j - 1] = strs[j];
			}
			strs[strs.length - i - 1] = top;
		}
		return strs;
	}

    /**
     * html的转义字符转换成正常的字符串
     * 
     * @param html
     * @return
     */
    public static String htmlEscapeCharsToString(String html) {
        if (Preconditions.isBlank(html)) {
            return html;
        }

        return html.replaceAll("&lt;", "<").replaceAll("&gt;", ">").replaceAll("&amp;", "&").replaceAll("&quot;","\"");
    }
}


<|start_filename|>saf-core/src/main/java/com/safframework/saf/rest/RestUtil.java<|end_filename|>
/**
 * 
 */
package com.safframework.saf.rest;

import com.alibaba.fastjson.JSON;

import java.io.IOException;
import java.util.ArrayList;

/**
 * @author <NAME>
 *
 */
public class RestUtil {
	
	/**
	 * 验证charset
	 * @param charset
	 * @return
	 */
	public static String getValidCharset(String charset) {
		if (charset != null && charset.length() > 0)
			return charset;
		else
			return RestConstant.CHARSET_UTF8;
	}
	
	/**
	 * 根据返回的http body内容转换成json对象
	 * @param cls
	 * @param body
	 * @return
	 * @throws IOException
	 */
    public static <T> T parseAs(Class<T> cls,String body) throws IOException {
    	return JSON.parseObject(body, cls);
    }
    
	/**
	 * 根据返回的http body内容转换成json对象数组
	 * @param cls
	 * @param body
	 * @return
	 * @throws IOException
	 */
    public static <T> ArrayList<T> parseArray(Class<T> cls,String body) throws IOException {
    	return (ArrayList<T>) JSON.parseArray(body, cls);
    }
}


<|start_filename|>saf-core/src/main/java/com/safframework/saf/utils/ViewUtils.java<|end_filename|>
/**
 * 
 */
package com.safframework.saf.utils;

import android.graphics.Rect;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.DisplayMetrics;
import android.view.TouchDelegate;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

import com.safframework.tony.common.utils.Preconditions;

/**
 * @author <NAME>
 * 
 */
public class ViewUtils {

	/**
	 * 增加view的点击区域，当view是一张小图或者不方便点击时可采用此方法
	 *
	 * @param amount
	 * @param delegate
	 */
	public static void increaseHitRectBy(final int amount, final View delegate) {
		increaseHitRectBy(amount, amount, amount, amount, delegate);
	}

	/**
	 * 
	 * @param top
	 * @param left
	 * @param bottom
	 * @param right
	 * @param delegate
	 */
	public static void increaseHitRectBy(final int top, final int left,
			final int bottom, final int right, final View delegate) {
		final View parent = (View) delegate.getParent();
		if (parent != null && delegate.getContext() != null) {
			parent.post(new Runnable() {
				// Post in the parent's message queue to make sure the parent
				// lays out its children before we call getHitRect()
				public void run() {
					final float densityDpi = delegate.getContext()
							.getResources().getDisplayMetrics().densityDpi;
					final Rect r = new Rect();
					delegate.getHitRect(r);
					r.top -= transformToDensityPixel(top, densityDpi);
					r.left -= transformToDensityPixel(left, densityDpi);
					r.bottom += transformToDensityPixel(bottom, densityDpi);
					r.right += transformToDensityPixel(right, densityDpi);
					parent.setTouchDelegate(new TouchDelegate(r, delegate));
				}
			});
		}
	}

	public static int transformToDensityPixel(int regularPixel,
			DisplayMetrics displayMetrics) {
		return transformToDensityPixel(regularPixel, displayMetrics.densityDpi);
	}

	public static int transformToDensityPixel(int regularPixel, float densityDpi) {
		return (int) (regularPixel * densityDpi);
	}
	
	public static boolean checkBtnEnable(EditText... editTexts) {
		boolean enable = true;
		for (EditText each : editTexts) {
			if (Preconditions.isBlank(each.getText())) {
				enable = false;
				break;
			}
		}
		return enable;
	}

	public static void checkNotBlank(final Button button,
			final EditText... editTexts) {
		TextWatcher notBlank = new TextWatcher() {
			@Override
			public void beforeTextChanged(CharSequence charSequence, int i,
					int i2, int i3) {

			}

			@Override
			public void onTextChanged(CharSequence charSequence, int i, int i2,
					int i3) {
				if (checkBtnEnable(editTexts)) {
					button.setEnabled(true);
				} else {
					button.setEnabled(false);
				}
			}

			@Override
			public void afterTextChanged(Editable editable) {

			}
		};
		
		for (EditText each : editTexts) {
			each.addTextChangedListener(notBlank);
		}
	}

}


<|start_filename|>app/src/main/java/com/test/saf/app/BaseFragment.java<|end_filename|>
package com.test.saf.app;

import android.os.Bundle;
import android.support.v4.app.FragmentManager;

import com.safframework.cache.Cache;
import com.safframework.saf.app.SAFFragment;


/**
 * Created by <NAME> on 15/11/19.
 */
public class BaseFragment extends SAFFragment {

    protected DemoApp app;
    protected FragmentManager fmgr;
    protected Cache mCache;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        app = DemoApp.getInstance();
        fmgr = getFragmentManager();

        mCache = Cache.get(mContext);
    }
}


<|start_filename|>saf-rxlifecycle/src/main/java/com/safframework/lifecycle/BindingV4Fragment.java<|end_filename|>
package com.safframework.lifecycle;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

/**
 * Created by <NAME> on 2017/5/25.
 */

public class BindingV4Fragment extends Fragment {

    private final LifecyclePublisher lifecyclePublisher = new LifecyclePublisher();

    public BindingV4Fragment() {
    }

    public LifecyclePublisher getLifecyclePublisher() {
        return lifecyclePublisher;
    }

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        lifecyclePublisher.onAttach();
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        lifecyclePublisher.onAttach();
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        lifecyclePublisher.onCreate();
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        lifecyclePublisher.onCreateView();
        return null;
    }

    @Override
    public void onStart() {
        super.onStart();
        lifecyclePublisher.onStart();
    }

    @Override
    public void onResume() {
        super.onResume();
        lifecyclePublisher.onResume();
    }

    @Override
    public void onPause() {
        super.onPause();
        lifecyclePublisher.onPause();
    }

    @Override
    public void onStop() {
        super.onStop();
        lifecyclePublisher.onStop();
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        lifecyclePublisher.onDestroyView();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        lifecyclePublisher.onDestroy();
    }

    @Override
    public void onDetach() {
        super.onDetach();
        lifecyclePublisher.onDetach();
    }
}


<|start_filename|>app/src/main/java/com/test/saf/app/DemoApp.java<|end_filename|>
package com.test.saf.app;

import com.safframework.queue.Queue;
import com.safframework.router.RouterManager;
import com.safframework.saf.app.SAFApp;

/**
 * Created by <NAME> on 15/11/19.
 */
public class DemoApp extends SAFApp {

    private static DemoApp mInstance = null;

    public Queue queue;

    public static DemoApp getInstance() {
        return mInstance;
    }

    public void onCreate() {

        super.onCreate();
        mInstance = this;

        initData();
    }

    private void initData() {

        RouterManager.init(this);// 这一步是必须的，用于初始化Router

        // 初始化队列
        queue = new Queue("saf_queue");
        queue.start();
    }
}


<|start_filename|>saf-core/src/main/java/com/safframework/saf/eventbus/EventBus.java<|end_filename|>
/**
 * 
 */
package com.safframework.saf.eventbus;

import com.safframework.saf.concurrent.BackgroundExecutor;

import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;

/**
 * 
 * @author <NAME>
 * 
 */
public class EventBus {
    static ExecutorService executorService = new BackgroundExecutor();
    static ScheduledThreadPoolExecutor scheduledExecutorService = new ScheduledThreadPoolExecutor(1);
    
	public static final String DEFAULT_IDENTIFIER = "saf_bus";

	//所有注册的event handler
	private final ConcurrentMap<Class<?>, Set<EventHandler>> handlersByType = new ConcurrentHashMap<Class<?>, Set<EventHandler>>();

	//所有注册的event product
	private final ConcurrentMap<Class<?>, EventProducer> producersByType = new ConcurrentHashMap<Class<?>, EventProducer>();

	private final String identifier; // eventbus的名称

	// 当前线程分发的事件队列
	private final ThreadLocal<ConcurrentLinkedQueue<EventWithHandler>> eventsToDispatch = new ThreadLocal<ConcurrentLinkedQueue<EventWithHandler>>() {
		@Override
		protected ConcurrentLinkedQueue<EventWithHandler> initialValue() {
			return new ConcurrentLinkedQueue<EventWithHandler>();
		}
	};

	// 当前线程正在分发的事件，分发完成后设置为false
	private final ThreadLocal<Boolean> isDispatching = new ThreadLocal<Boolean>() {
		@Override
		protected Boolean initialValue() {
			return false;
		}
	};

    private final BackgroundPoster backgroundPoster;
    private final ScheduledPoster scheduledPoster;
	private final Map<Class<?>, Set<Class<?>>> flattenHierarchyCache = new HashMap<Class<?>, Set<Class<?>>>();

	@Override
	public String toString() {
		return "[\"" + identifier + "\"]";
	}

	public EventBus() {
		this(DEFAULT_IDENTIFIER);
	}

	public EventBus(String identifier) {
		this.identifier = identifier;
		backgroundPoster = new BackgroundPoster(this);
		scheduledPoster = new ScheduledPoster(this);
	}

	/**
	 * 注册到eventbus，支持activity、fragment
	 * @param object
	 */
	public void register(Object object) {

		Map<Class<?>, EventProducer> foundProducers = EventBusUtils
				.findAllProducers(object);
		for (Class<?> type : foundProducers.keySet()) {

			final EventProducer producer = foundProducers.get(type);
			EventProducer previousProducer = producersByType.putIfAbsent(type,
					producer);
			// checking if the previous producer existed
			if (previousProducer != null) {
				throw new IllegalArgumentException("Producer method for type "
						+ type + " found on type " + producer.target.getClass()
						+ ", but already registered by type "
						+ previousProducer.target.getClass() + ".");
			}
			Set<EventHandler> handlers = handlersByType.get(type);
			if (handlers != null && !handlers.isEmpty()) {
				for (EventHandler handler : handlers) {
					dispatchProducerResultToHandler(handler, producer);
				}
			}
		}

		Map<Class<?>, Set<EventHandler>> foundHandlersMap = EventBusUtils
				.findAllSubscribers(object);
		for (Class<?> type : foundHandlersMap.keySet()) {
			Set<EventHandler> handlers = handlersByType.get(type);
			if (handlers == null) {
				Set<EventHandler> handlersCreation = new CopyOnWriteArraySet<EventHandler>();
				handlers = handlersByType.putIfAbsent(type, handlersCreation);
				if (handlers == null) {
					handlers = handlersCreation;
				}
			}
			final Set<EventHandler> foundHandlers = foundHandlersMap.get(type);
			handlers.addAll(foundHandlers);
		}

		for (Map.Entry<Class<?>, Set<EventHandler>> entry : foundHandlersMap
				.entrySet()) {
			Class<?> type = entry.getKey();
			EventProducer producer = producersByType.get(type);
			if (producer != null && producer.isValid()) {
				Set<EventHandler> foundHandlers = entry.getValue();
				for (EventHandler foundHandler : foundHandlers) {
					if (!producer.isValid()) {
						break;
					}
					if (foundHandler.isValid()) {
						dispatchProducerResultToHandler(foundHandler, producer);
					}
				}
			}
		}
	}

	private void dispatchProducerResultToHandler(EventHandler handler,
			EventProducer producer) {
		Object event = null;
		try {
			event = producer.produceEvent();
		} catch (InvocationTargetException e) {
			throw new EventBusException("Producer " + producer
					+ " threw an exception.",e);
		}
		if (event == null) {
			return;
		}
		dispatch(event, handler);
	}

	/**
	 * 从eventbus移除
	 * @param object
	 */
	public void unregister(Object object) {

		Map<Class<?>, EventProducer> producersInListener = EventBusUtils
				.findAllProducers(object);
		for (Map.Entry<Class<?>, EventProducer> entry : producersInListener
				.entrySet()) {
			final Class<?> key = entry.getKey();
			EventProducer producer = getProducerForEventType(key);
			EventProducer value = entry.getValue();

			if (value == null || !value.equals(producer)) {
				throw new IllegalArgumentException(
						"Missing event producer for an annotated method. Is "
								+ object.getClass() + " registered?");
			}
			producersByType.remove(key).invalidate();
		}

		Map<Class<?>, Set<EventHandler>> handlersInListener = EventBusUtils
				.findAllSubscribers(object);
		for (Map.Entry<Class<?>, Set<EventHandler>> entry : handlersInListener
				.entrySet()) {
			Set<EventHandler> currentHandlers = getHandlersForEventType(entry
					.getKey());
			Collection<EventHandler> eventMethodsInListener = entry.getValue();

			if (currentHandlers == null
					|| !currentHandlers.containsAll(eventMethodsInListener)) {
				throw new IllegalArgumentException(
						"Missing event handler for an annotated method. Is "
								+ object.getClass() + " registered?");
			}

			for (EventHandler handler : currentHandlers) {
				if (eventMethodsInListener.contains(handler)) {
					handler.invalidate();
				}
			}
			currentHandlers.removeAll(eventMethodsInListener);
		}
	}

	/**
	 * 发送event，event发送成功后，订阅者可以在回调中处理
	 *
	 * @param event
	 */
	public void post(Object event) {

		Set<Class<?>> dispatchTypes = flattenHierarchy(event.getClass());

		boolean dispatched = false;
		for (Class<?> eventType : dispatchTypes) {
			Set<EventHandler> wrappers = getHandlersForEventType(eventType);

			if (wrappers != null && !wrappers.isEmpty()) {
				dispatched = true;
				for (EventHandler wrapper : wrappers) {
					enqueueEvent(event, wrapper);
				}
			}
		}

		if (!dispatched && !(event instanceof DeadEvent)) {
			post(new DeadEvent(this, event));
		}

		dispatchQueuedEvents();
	}

	/**
	 * 事件进入队列
	 * @param event
	 * @param handler
	 */
	protected void enqueueEvent(Object event, EventHandler handler) {
		eventsToDispatch.get().offer(new EventWithHandler(event, handler));
	}

	/**
	 * 分发队列事件
	 */
	protected void dispatchQueuedEvents() {
		// 如果当前的事件已经在处理了，则不分发该事件
		if (isDispatching.get()) {
			return;
		}

		isDispatching.set(true);
		try {
			while (true) {
				EventWithHandler eventWithHandler = eventsToDispatch.get()
						.poll();
				if (eventWithHandler == null) {
					break;
				}

				if (eventWithHandler.handler.isValid()) {
					dispatch(eventWithHandler.event, eventWithHandler.handler);
				}
			}
		} finally {
			isDispatching.set(false);
		}
	}

	/**
	 * 根据订阅者的threadMode类型，分别处理event
	 * @param event
	 * @param wrapper
	 */
	protected void dispatch(Object event, EventHandler wrapper) {
		switch(wrapper.subscriberMethod.threadMode) {
		case PostThread:
			invokeSubscriber(event,wrapper);
			break;
		case BackgroundThread:
			backgroundPoster.enqueue(event,wrapper);
			break;
		case Async:
			break;
		case ScheduleBackgroundThread:
			if (event instanceof ScheduledEvent) {
				scheduledPoster.enqueue(event, wrapper);
			}

			break;
        default:
            throw new IllegalStateException("Unknown thread mode: " + wrapper.subscriberMethod.threadMode);
		}
	}

	/**
	 * 调用订阅者
	 * @param event
	 * @param wrapper
	 */
	void invokeSubscriber(Object event, EventHandler wrapper) {
		try {
			wrapper.handleEvent(event);
		} catch (InvocationTargetException e) {
			throw new EventBusException("Could not dispatch event: " + event.getClass()
							+ " to handler " + wrapper, e);
		}
	}

	EventProducer getProducerForEventType(Class<?> type) {
		return producersByType.get(type);
	}

	Set<EventHandler> getHandlersForEventType(Class<?> type) {
		return handlersByType.get(type);
	}

	Set<Class<?>> flattenHierarchy(Class<?> concreteClass) {
		Set<Class<?>> classes = flattenHierarchyCache.get(concreteClass);
		if (classes == null) {
			classes = getClassesFor(concreteClass);
			flattenHierarchyCache.put(concreteClass, classes);
		}

		return classes;
	}

	private Set<Class<?>> getClassesFor(Class<?> concreteClass) {
		List<Class<?>> parents = new LinkedList<Class<?>>();
		Set<Class<?>> classes = new HashSet<Class<?>>();

		parents.add(concreteClass);

		while (!parents.isEmpty()) {
			Class<?> clazz = parents.remove(0);
			classes.add(clazz);

			Class<?> parent = clazz.getSuperclass();
			if (parent != null) {
				parents.add(parent);
			}
		}
		return classes;
	}

	static class EventWithHandler {
		final Object event;
		final EventHandler handler;

		public EventWithHandler(Object event, EventHandler handler) {
			this.event = event;
			this.handler = handler;
		}
	}
}


<|start_filename|>saf-core/src/main/java/com/safframework/saf/app/SafeHandler.java<|end_filename|>
package com.safframework.saf.app;

import android.app.Activity;
import android.os.Handler;
import android.os.Message;

import java.lang.ref.WeakReference;

/**
 * Created by <NAME> on 16/7/5.
 */
public class SafeHandler extends Handler {

    private final WeakReference<Activity> mActivity;

    public SafeHandler(Activity activity) {
        mActivity = new WeakReference<Activity>(activity);
    }

    @Override
    public void handleMessage(Message msg) {
        if(mActivity.get() == null) {
            return;
        }
    }
}


<|start_filename|>saf-core/src/main/java/com/safframework/saf/eventbus/ThreadMode.java<|end_filename|>
/*
 */
package com.safframework.saf.eventbus;

/**
 * 线程模式的枚举
 * @author Tony
 */
public enum ThreadMode {

    PostThread,

    BackgroundThread,

    Async,
    
    ScheduleBackgroundThread
}

<|start_filename|>saf-core/src/main/java/com/safframework/saf/parcel/AbstractParcelable.java<|end_filename|>
/**
 * 
 */
package com.safframework.saf.parcel;

import android.os.Parcel;
import android.os.Parcelable;

import com.safframework.log.L;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;

/**
 * 抽象的基类Parcelable，可以直接继承该类
 * @author <NAME>
 *
 */
public class AbstractParcelable implements Parcelable {

	public AbstractParcelable() {
	}

	public AbstractParcelable(Parcel in) {
		String className = in.readString();
		L.i("Constructor: "
				+ this.getClass().getSimpleName() + "; In parcel: " + className);
		try {
			read(this, in);
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
	}

	public static final Creator<AbstractParcelable> CREATOR = new Creator<AbstractParcelable>() {
		public AbstractParcelable createFromParcel(Parcel in) {
			// get class from first parcelled item
			Class<?> parceledClass;
			try {
				parceledClass = Class.forName(in.readString());
				L.i("Creator: " + parceledClass.getSimpleName());
				// create instance of that class
				AbstractParcelable model = (AbstractParcelable) parceledClass
						.newInstance();
				read(model, in);
				return model;
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			} catch (InstantiationException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
			return null;
		}

		public AbstractParcelable[] newArray(int size) {
			return new AbstractParcelable[size];
		}
	};

	@Override
	public void writeToParcel(Parcel dest, int flags) {
		dest.writeString(this.getClass().getName());
		try {
			write(this, dest);
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
	}

	@Override
	public int describeContents() {
		return 0;
	}

	protected static void write(AbstractParcelable model, Parcel out)
			throws IllegalArgumentException, IllegalAccessException {
		L.i("dehydrating... "
				+ model.getClass().toString());
		Field[] fields = model.getClass().getDeclaredFields();
		Arrays.sort(fields, compareMemberByName);
		for (Field field : fields) {
			field.setAccessible(true);
			if (field.getType().equals(int.class)) {
				out.writeInt(field.getInt(model));
			} else if (field.getType().equals(double.class)) {
				out.writeDouble(field.getDouble(model));
			} else if (field.getType().equals(float.class)) {
				out.writeFloat(field.getFloat(model));
			} else if (field.getType().equals(long.class)) {
				out.writeLong(field.getLong(model));
			} else if (field.getType().equals(String.class)) {
				out.writeString((String) field.get(model));
			} else if (field.getType().equals(boolean.class)) {
				out.writeByte(field.getBoolean(model) ? (byte) 1 : (byte) 0);
			} else if (field.getType().equals(Date.class)) {
				Date date = (Date) field.get(model);
				if (date != null) {
					out.writeLong(date.getTime());
				} else {
					out.writeLong(0);
				}
			} else if (AbstractParcelable.class.isAssignableFrom(field.getType())) {
				L.e("AbstractParcelable" + " ("
						+ field.getType().toString() + ")");
				out.writeParcelable((AbstractParcelable) field.get(model), 0);
			} else {
				L.e("Could not write field to parcel: "
						+ " (" + field.getType().toString() + ")");
			}
		}
	}

	protected static void read(AbstractParcelable model, Parcel in)
			throws IllegalArgumentException, IllegalAccessException {
		L.i("rehydrating... "
				+ model.getClass().toString());
		Field[] fields = model.getClass().getDeclaredFields();
		Arrays.sort(fields, compareMemberByName);

		for (Field field : fields) {
			field.setAccessible(true);
			if (field.getType().equals(int.class)) {
				field.set(model, in.readInt());
			} else if (field.getType().equals(double.class)) {
				field.set(model, in.readDouble());
			} else if (field.getType().equals(float.class)) {
				field.set(model, in.readFloat());
			} else if (field.getType().equals(long.class)) {
				field.set(model, in.readLong());
			} else if (field.getType().equals(String.class)) {
				field.set(model, in.readString());
			} else if (field.getType().equals(boolean.class)) {
				field.set(model, in.readByte() == 1);
			} else if (field.getType().equals(Date.class)) {
				Date date = new Date(in.readLong());
				field.set(model, date);
			} else if (AbstractParcelable.class.isAssignableFrom(field.getType())) {
				L.e("read AbstractParcelable: " + " ("
						+ field.getType().toString() + ")");
				field.set(model,
						in.readParcelable(field.getType().getClassLoader()));
			} else {
				L.e("Could not read field from parcel: "
						+ field.getName() + " (" + field.getType().toString()
						+ ")");
			}
		}
	}

	/**
	 * Comparator object for Members, Fields, and Methods
	 */
	private static Comparator<Field> compareMemberByName = new CompareMemberByName();

	private static class CompareMemberByName implements Comparator {
		public int compare(Object o1, Object o2) {
			String s1 = ((Member) o1).getName();
			String s2 = ((Member) o2).getName();

			if (o1 instanceof Method) {
				s1 += getSignature((Method) o1);
				s2 += getSignature((Method) o2);
			} else if (o1 instanceof Constructor) {
				s1 += getSignature((Constructor) o1);
				s2 += getSignature((Constructor) o2);
			}
			return s1.compareTo(s2);
		}
	}

	/**
	 * 计算该类的jvm签名
	 */
	private static String getSignature(Class clazz) {
		String type = null;
		if (clazz.isArray()) {
			Class cl = clazz;
			int dimensions = 0;
			while (cl.isArray()) {
				dimensions++;
				cl = cl.getComponentType();
			}
			StringBuilder sb = new StringBuilder();
			for (int i = 0; i < dimensions; i++) {
				sb.append("[");
			}
			sb.append(getSignature(cl));
			type = sb.toString();
		} else if (clazz.isPrimitive()) {
			if (clazz == Integer.TYPE) {
				type = "I";
			} else if (clazz == Byte.TYPE) {
				type = "B";
			} else if (clazz == Long.TYPE) {
				type = "J";
			} else if (clazz == Float.TYPE) {
				type = "F";
			} else if (clazz == Double.TYPE) {
				type = "D";
			} else if (clazz == Short.TYPE) {
				type = "S";
			} else if (clazz == Character.TYPE) {
				type = "C";
			} else if (clazz == Boolean.TYPE) {
				type = "Z";
			} else if (clazz == Void.TYPE) {
				type = "V";
			}
		} else {
			type = "L" + clazz.getName().replace('.', '/') + ";";
		}
		return type;
	}

	private static String getSignature(Method method) {
		StringBuilder sb = new StringBuilder();

		sb.append("(");

		Class[] params = method.getParameterTypes(); // avoid clone
		for (int j = 0; j < params.length; j++) {
			sb.append(getSignature(params[j]));
		}
		sb.append(")");
		sb.append(getSignature(method.getReturnType()));
		return sb.toString();
	}

	private static String getSignature(Constructor cons) {
		StringBuilder sb = new StringBuilder();

		sb.append("(");

		Class[] params = cons.getParameterTypes(); // avoid clone
		for (int j = 0; j < params.length; j++) {
			sb.append(getSignature(params[j]));
		}
		sb.append(")V");
		return sb.toString();
	}
}


<|start_filename|>app/src/main/java/com/test/saf/adapter/AnnotationAdapter.java<|end_filename|>
package com.test.saf.adapter;

import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.test.saf.R;

import java.util.List;

/**
 * Created by <NAME> on 2016/11/22.
 */

public class AnnotationAdapter extends RecyclerView.Adapter<NormalTextViewHolder> {

    private List<String> mList;

    public AnnotationAdapter(List<String> data) {
        mList = data;
    }

    @Override
    public NormalTextViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.cell_general_annotation, parent, false);
        return new NormalTextViewHolder(view);
    }

    @Override
    public void onBindViewHolder(NormalTextViewHolder holder, int position) {
        holder.name.setText((String)mList.get(position));
    }

    @Override
    public int getItemCount() {
        return mList!=null?mList.size():0;
    }

}


<|start_filename|>saf-core/src/main/java/com/safframework/saf/rest/HttpRetryStrategy.java<|end_filename|>
/**
 * 
 */
package com.safframework.saf.rest;

/**
 * http重试机制的策略
 * @author <NAME>
 *
 */
public interface HttpRetryStrategy {

	/**
	 * 执行重试的逻辑,这些逻辑往往是比较特别的
	 */
	public void retry();
}


<|start_filename|>saf-core/src/main/java/com/safframework/saf/config/package-info.java<|end_filename|>
/**
 * SAF框架中常量信息
 */
package com.safframework.saf.config;

<|start_filename|>app/src/main/java/com/test/saf/domain/MMPicsResponse.java<|end_filename|>
package com.test.saf.domain;

import java.io.Serializable;
import java.util.List;

/**
 * Created by <NAME> on 2016/12/1.
 */

public class MMPicsResponse extends HttpResponse {

    public List<Pic> tngou;

    public static class Pic implements Serializable {

        private static final long serialVersionUID = 3376457660406783270L;

        public String img;
        public String title;

    }
}


<|start_filename|>saf-rxlifecycle/src/main/java/com/safframework/lifecycle/RxLifecycle.java<|end_filename|>
package com.safframework.lifecycle;

import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.RequiresApi;
import android.support.v7.app.AppCompatActivity;

import io.reactivex.Flowable;
import io.reactivex.Observable;

/**
 * Created by <NAME> on 2017/5/25.
 */

public class RxLifecycle {

    private static final String FRAGMENT_TAG = "_BINDING_FRAGMENT_";
    private final LifecyclePublisher lifecyclePublisher;

    @RequiresApi(api = Build.VERSION_CODES.HONEYCOMB)
    public static RxLifecycle bind(@NonNull AppCompatActivity targetActivity) {
        return bind(targetActivity.getSupportFragmentManager());
    }


    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)
    public static RxLifecycle bind(@NonNull Fragment targetFragment) {
        return bind(targetFragment.getChildFragmentManager());
    }

    @RequiresApi(api = Build.VERSION_CODES.HONEYCOMB)
    public static RxLifecycle bind(@NonNull FragmentManager fragmentManager) {
        BindingFragment fragment = (BindingFragment) fragmentManager.findFragmentByTag(FRAGMENT_TAG);
        if (fragment == null) {
            fragment = new BindingFragment();

            final FragmentTransaction transaction = fragmentManager.beginTransaction();
            transaction.add(fragment, FRAGMENT_TAG);
            transaction.commit();

        } else if (Build.VERSION.SDK_INT >= 13 && fragment.isDetached()) {
            final FragmentTransaction transaction = fragmentManager.beginTransaction();
            transaction.attach(fragment);
            transaction.commit();
        }

        return bind(fragment.getLifecyclePublisher());
    }

    public static RxLifecycle bind(@NonNull android.support.v4.app.Fragment targetFragment) {
        return bind(targetFragment.getChildFragmentManager());
    }

    public static RxLifecycle bind(@NonNull android.support.v4.app.FragmentManager fragmentManager) {
        BindingV4Fragment fragment = (BindingV4Fragment) fragmentManager.findFragmentByTag(FRAGMENT_TAG);
        if (fragment == null) {
            fragment = new BindingV4Fragment();

            final android.support.v4.app.FragmentTransaction transaction = fragmentManager.beginTransaction();
            transaction.add(fragment, FRAGMENT_TAG);
            transaction.commit();
        } else if (fragment.isDetached()) {

            final android.support.v4.app.FragmentTransaction transaction = fragmentManager.beginTransaction();
            transaction.attach(fragment);
            transaction.commit();
        }

        return bind(fragment.getLifecyclePublisher());
    }

    public static RxLifecycle bind(@NonNull LifecyclePublisher lifecyclePublisher) {
        return new RxLifecycle(lifecyclePublisher);
    }

    private RxLifecycle() throws IllegalAccessException {
        throw new IllegalAccessException();
    }

    private RxLifecycle(@NonNull LifecyclePublisher lifecyclePublisher) {
        this.lifecyclePublisher = lifecyclePublisher;
    }

    public Flowable<Integer> asFlowable() {
        return lifecyclePublisher.getBehavior();
    }

    public Observable<Integer> asObservable() {
        return lifecyclePublisher.getBehavior().toObservable();
    }

    public <T> LifecycleTransformer<T> toLifecycleTransformer() {
        return new LifecycleTransformer<T>(lifecyclePublisher.getBehavior());
    }
}


<|start_filename|>saf-cache/src/main/java/com/safframework/cache/CacheException.java<|end_filename|>
package com.safframework.cache;

/**
 * Created by <NAME> on 2017/7/16.
 */

public class CacheException extends RuntimeException {

    private static final long serialVersionUID = 6330641953592523314L;

    public CacheException(String message) {
        super(message);
    }
}


<|start_filename|>saf-core/src/main/java/com/safframework/saf/rest/UrlBuilder.java<|end_filename|>
/**
 * 
 */
package com.safframework.saf.rest;

import com.safframework.tony.common.utils.Preconditions;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;

/**
 * 构造url的builder，用法:
 * <pre>
 * <code>
 * UrlBuilder builder = new UrlBuilder("http://localhost:8080/test");
 * builder.parameter("one", "one");
 * builder.parameter("two", "two");
 * String url = builder.buildUrl();
 * </code>
 * </pre>
 * <pre>
 * <code>
 * UrlBuilder builder = new UrlBuilder("http://localhost:8080/test.json?apiVersion=1.0");
 * builder.parameter("one", "one");
 * builder.parameter("two", "two");
 * String url = builder.buildUrl();
 * </code>
 * </pre>
 * <pre>
 * <code>
 * UrlBuilder builder = new UrlBuilder("http://localhost:8080/test.json?apiVersion=1.0&");
 * builder.parameter("one", "one");
 * builder.parameter("two", "two");
 * String url = builder.buildUrl();
 * </code>
 * </pre>
 * @author <NAME>
 *
 */
public class UrlBuilder {

    private final String urlFormat;
    private Map<String, String> parametersMap = new HashMap<String, String>();
    private boolean firstParameter = true;
    
    public UrlBuilder(String urlFormat) {
		this.urlFormat = urlFormat;
		int index = urlFormat.indexOf("?");
		if(index!=-1) {
			String parameter = urlFormat.substring(index+1);
			String[] params = parameter.split("&");
			if (Preconditions.isNotBlank(params)) {
				for (int i = 0; i < params.length; i++) {
					String[] p = params[i].split("=");
					if (p.length == 2) {
						firstParameter = false;
						break;
					}
				}
			}
		}
	}
    
	/**
	 * 增加一个url参数
	 * 
	 * @param name Name.
	 * @param value Value.
	 * @return Current instance for builder pattern.
	 */
    public UrlBuilder parameter(String name, String value) {
    	return this.parameter(name, value, true);
	}

	/**
	 * 增加一个url参数
	 * 
	 * @param name Name.
	 * @param value Value.
	 * @param escape Whether the value needs to be escaped.
	 * @return Current instance for builder pattern.
	 */
    public UrlBuilder parameter(String name, String value, boolean escape) {
    	if (escape) {
    		this.parametersMap.put(name, UrlBuilder.encodeUrl(value));
    	} else {
    		this.parametersMap.put(name, value);
    	}
    	
    	return this;
    }

	/**
	 * 增加一个url参数
	 * 
	 * @param name Name.
	 * @param value Value.
	 * @return Current instance for builder pattern.
	 */
    public UrlBuilder parameter(String name, int value) {
		return this.parameter(name, Integer.toString(value));
	}

	/**
	 * 增加一个url参数
	 * 
	 * @param name Name.
	 * @param value Value.
	 * @return Current instance for builder pattern.
	 */
    public UrlBuilder parameter(String name, boolean value) {
		return this.parameter(name, Boolean.toString(value));
	}
    
    public UrlBuilder parameter(Map<String, String> map) {
    	parametersMap.putAll(map);
    	return this;
    }
    
	/**
	 * 判断url中是否存在这个Parameter
	 * 
	 * @param name Name of key.
	 * @return True if the key exists in the parameter set.
	 */
    protected boolean hasParameter(String name) {
		return this.parametersMap.containsKey(name);
	}

	/**
	 * 构造url
	 * 
	 * @return String representation of the URL.
	 */
    public String buildUrl() {
		StringBuilder urlBuilder = new StringBuilder();
		urlBuilder.append(this.urlFormat);
		if (urlFormat.endsWith("&")) {
			urlBuilder.deleteCharAt(urlBuilder.length()-1);
		}
		
		if (this.parametersMap.size() > 0) {
			for (String parameterName : this.parametersMap.keySet()) {
				if (firstParameter) {
					firstParameter = false;
					urlBuilder.append("?");
				} else {
					urlBuilder.append("&");
				}

				urlBuilder.append(parameterName);
				urlBuilder.append("=");
				urlBuilder.append(this.parametersMap.get(parameterName));
			}
		}
		return urlBuilder.toString();
	}

    /**
	 * Encode URL内容
	 * 
	 * @param content Content to encode.
	 * @return Encoded string.
	 */
    protected static String encodeUrl(String content) {
    	try {
			return URLEncoder.encode(content, RestConstant.CHARSET_UTF8);
		} catch (UnsupportedEncodingException e) {
			return content;
		}
    }
}


<|start_filename|>saf-core/src/main/java/com/safframework/saf/utils/NoEmptyHashMap.java<|end_filename|>
package com.safframework.saf.utils;

import com.safframework.tony.common.utils.Preconditions;

import java.util.HashMap;
import java.util.Map;

/**
 * 此类存在的目的:hashmap可以存key为null或者value为null的
 * 但是这个map不能,保证所有的value都不为空
 * Created by <NAME> on 16/4/22.
 */
public class NoEmptyHashMap<K,V> extends HashMap<K, V> {

    public NoEmptyHashMap() {
        super();
    }

    public NoEmptyHashMap(final Map<K,V> map) {
        super();
        putAll(map);
    }

    @Override
    public V put(final K key, final V value) {
        if (Preconditions.isBlank(key) || Preconditions.isBlank(value)) {
            return null;
        }
        return super.put(key, value);
    }

    @Override
    public void putAll(final Map<? extends K, ? extends V> map) {
        for (Entry<? extends K, ? extends V> entry : map.entrySet()) {
            put(entry.getKey(), entry.getValue());
        }
    }
}


<|start_filename|>saf-core/src/main/java/com/safframework/saf/rest/RestConstant.java<|end_filename|>
/**
 * 
 */
package com.safframework.saf.rest;

/**
 * @author <NAME>
 * 
 */
public class RestConstant {

	/**
	 * 'UTF-8' charset name
	 */
	public static final String CHARSET_UTF8 = "UTF-8";

	/**
	 * 'application/x-www-form-urlencoded' content type header value
	 */
	public static final String CONTENT_TYPE_FORM = "application/x-www-form-urlencoded";

	/**
	 * 'application/json' content type header value
	 */
	public static final String CONTENT_TYPE_JSON = "application/json";

	/**
	 * 'gzip' encoding header value
	 */
	public static final String ENCODING_GZIP = "gzip";

	/**
	 * 'Accept' header name
	 */
	public static final String HEADER_ACCEPT = "Accept";

	/**
	 * 'Accept-Charset' header name
	 */
	public static final String HEADER_ACCEPT_CHARSET = "Accept-Charset";

	/**
	 * 'Accept-Encoding' header name
	 */
	public static final String HEADER_ACCEPT_ENCODING = "Accept-Encoding";

	/**
	 * 'Authorization' header name
	 */
	public static final String HEADER_AUTHORIZATION = "Authorization";

	/**
	 * 'Cache-Control' header name
	 */
	public static final String HEADER_CACHE_CONTROL = "Cache-Control";

	/**
	 * 'Content-Encoding' header name
	 */
	public static final String HEADER_CONTENT_ENCODING = "Content-Encoding";

	/**
	 * 'Content-Length' header name
	 */
	public static final String HEADER_CONTENT_LENGTH = "Content-Length";

	/**
	 * 'Content-Type' header name
	 */
	public static final String HEADER_CONTENT_TYPE = "Content-Type";

	/**
	 * 'Date' header name
	 */
	public static final String HEADER_DATE = "Date";

	/**
	 * 'ETag' header name
	 */
	public static final String HEADER_ETAG = "ETag";

	/**
	 * 'Expires' header name
	 */
	public static final String HEADER_EXPIRES = "Expires";

	/**
	 * 'If-None-Match' header name
	 */
	public static final String HEADER_IF_NONE_MATCH = "If-None-Match";

	/**
	 * 'Last-Modified' header name
	 */
	public static final String HEADER_LAST_MODIFIED = "Last-Modified";

	/**
	 * 'Location' header name
	 */
	public static final String HEADER_LOCATION = "Location";

	/**
	 * 'Server' header name
	 */
	public static final String HEADER_SERVER = "Server";

	/**
	 * 'User-Agent' header name
	 */
	public static final String HEADER_USER_AGENT = "User-Agent";

	/**
	 * 'DELETE' request method
	 */
	public static final String METHOD_DELETE = "DELETE";

	/**
	 * 'GET' request method
	 */
	public static final String METHOD_GET = "GET";

	/**
	 * 'HEAD' request method
	 */
	public static final String METHOD_HEAD = "HEAD";

	/**
	 * 'OPTIONS' options method
	 */
	public static final String METHOD_OPTIONS = "OPTIONS";

	/**
	 * 'POST' request method
	 */
	public static final String METHOD_POST = "POST";

	/**
	 * 'PUT' request method
	 */
	public static final String METHOD_PUT = "PUT";

	/**
	 * 'TRACE' request method
	 */
	public static final String METHOD_TRACE = "TRACE";

	/**
	 * 'charset' header value parameter
	 */
	public static final String PARAM_CHARSET = "charset";

	public static final String BOUNDARY = "00content0boundary00";

	public static final String CONTENT_TYPE_MULTIPART = "multipart/form-data; boundary="
			+ BOUNDARY;

	public static final String CRLF = "\r\n";
	
	public static int DEFAULT_READ_TIMEOUT = 30000;
	
	public static int DEFAULT_CONNECTION_TIMEOUT = 30000;
	
	public static int DEFAULT_RETRY_NUM = 3;
	
	public static final int SUCCESS = 200;
	
	public static final String COOKIE = "Cookie";
	
	public static final String SET_COOKIE = "Set-Cookie";
}


<|start_filename|>app/src/main/java/com/test/saf/activity/User.java<|end_filename|>
package com.test.saf.activity;

import java.io.Serializable;

/**
 * Created by <NAME> on 16/2/5.
 */
public class User implements Serializable{

    public String userName;
    public String password;
}


<|start_filename|>saf-core/src/main/java/com/safframework/saf/rest/HttpResponseHandler.java<|end_filename|>
/**
 * 
 */
package com.safframework.saf.rest;

import java.util.List;
import java.util.Map;

/**
 * @author <NAME>
 *
 */
public interface HttpResponseHandler {
	
	/**
	 * http请求成功后，response转换成content
	 * @param content
	 * @param heads
	 */
	public void onSuccess(String content, Map<String, List<String>> heads);
	
	/**
	 * http请求失败后，response转换成jsonString
	 * 
	 * @param e
	 */
	public void onFail(RestException e);
	
}


<|start_filename|>saf-core/src/main/java/com/safframework/saf/utils/DeviceUtils.java<|end_filename|>
package com.safframework.saf.utils;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Point;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.provider.Settings;
import android.support.annotation.NonNull;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.WindowManager;

import com.safframework.tony.common.utils.IOUtils;
import com.safframework.tony.common.utils.Preconditions;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Locale;

/**
 * Created by <NAME> on 2016/11/9.
 */

public class DeviceUtils {

    public final static String CHINA_CARRIER_UNKNOWN = "0";
    public final static String CHINA_MOBILE = "1";
    public final static String CHINA_UNICOM = "2";
    public final static String CHINA_TELECOM = "3";
    public final static String CHINA_TIETONG = "4";

    public final static String NETWORK_WIFI = "0";
    public final static String NETWORK_2G = "1";
    public final static String NETWORK_3G = "2";
    public final static String NETWORK_4G = "3";
    public final static String NO_NETWORK = "-1";

    public static String getFingerPrinter(@NonNull Context context) {
        StringBuilder fa = new StringBuilder();
        fa.append(getLocal());
        fa.append(getDevice());
        fa.append(getBranding());
        if (!TextUtils.isEmpty(getMacAddress(context))) {
            fa.append(getMacAddress(context));
        } else {
            fa.append(getDeviceId(context));
        }
        fa.append(getManufacturer());
        fa.append(getScreenSize(context));
        fa.append(getCpuInfo());
        fa.append(String.valueOf((Calendar.getInstance().get(Calendar.ZONE_OFFSET) + Calendar.getInstance().get(Calendar.DST_OFFSET)) / 60000));
        String fp = String.valueOf(Math.abs(StringUtils.md5(fa.toString()).hashCode()));
        return fp;
    }

    private static String getCpuInfo() {
        String str1 = "/proc/cpuinfo";
        String str2 = "";
        String[] cpuInfo = {"", ""};
        String[] arrayOfString;
        BufferedReader localBufferedReader = null;
        try {
            File file = new File(str1);
            if(!file.exists()){
                return "";
            }
            FileReader fr = new FileReader(file);
            //防止fr为空
            if (Preconditions.isBlank(fr)) {
                return "";
            }
            localBufferedReader = new BufferedReader(fr, 8192);
            str2 = localBufferedReader.readLine();
            //防止str2为空
            if (Preconditions.isBlank(str2)) {
                return "";
            }
            arrayOfString = str2.split("\\s+");
            for (int i = 2; i < arrayOfString.length; i++) {
                cpuInfo[0] = cpuInfo[0] + arrayOfString[i] + " ";
            }
            str2 = localBufferedReader.readLine();
            arrayOfString = str2.split("\\s+");
            cpuInfo[1] += arrayOfString[2];
            localBufferedReader.close();
        } catch (IOException ignored) {
        } finally {
            IOUtils.closeQuietly(localBufferedReader);
        }
        return Arrays.toString(cpuInfo);
    }

    public static String getNetGeneration(@NonNull Context context) {
        if (!SAFUtils.checkPermissions(context, "android.permission.ACCESS_NETWORK_STATE")) {
            return "";
        }
        String result = NO_NETWORK;
        try {
            ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService
                    (Context.CONNECTIVITY_SERVICE);
            NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();

            if (connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI).getState() ==
                    NetworkInfo.State.CONNECTED) {
                result = NETWORK_WIFI;
            }
            if (networkInfo != null && networkInfo.isAvailable()) {
                TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(
                        Context.TELEPHONY_SERVICE);
                switch (telephonyManager.getNetworkType()) {
                    case TelephonyManager.NETWORK_TYPE_GPRS:
                    case TelephonyManager.NETWORK_TYPE_EDGE:
                    case TelephonyManager.NETWORK_TYPE_CDMA:
                    case TelephonyManager.NETWORK_TYPE_1xRTT:
                    case TelephonyManager.NETWORK_TYPE_IDEN:
                        result = NETWORK_2G;
                        break;
                    case TelephonyManager.NETWORK_TYPE_UMTS:
                    case TelephonyManager.NETWORK_TYPE_EVDO_0:
                    case TelephonyManager.NETWORK_TYPE_EVDO_A:
                    case TelephonyManager.NETWORK_TYPE_HSDPA:
                    case TelephonyManager.NETWORK_TYPE_HSUPA:
                    case TelephonyManager.NETWORK_TYPE_HSPA:
                    case TelephonyManager.NETWORK_TYPE_EVDO_B:
                    case TelephonyManager.NETWORK_TYPE_EHRPD:
                    case TelephonyManager.NETWORK_TYPE_HSPAP:
                        result = NETWORK_3G;
                        break;
                    case TelephonyManager.NETWORK_TYPE_LTE:
                        result = NETWORK_4G;
                        break;
                    case TelephonyManager.NETWORK_TYPE_UNKNOWN:
                        result = NETWORK_4G;
                        break;
                    default:
                        result = NETWORK_2G;
                        break;
                }
            }
        } catch (Exception ignored) {

        }
        return result;
    }

    /**
     * 获得Android设备唯一标识：Device_id
     * 尽量获取一次，以后把数值保存起来，减少漂移
     *
     * @param context context
     * @return device id
     */
    @SuppressLint("HardwareIds")
    public static String getDeviceId(@NonNull Context context) {

        if (context == null) {
            return "";
        }
        String result = "";
        try {
            if (SAFUtils.checkPermissions(context, "android.permission.READ_PHONE_STATE")) {
                TelephonyManager tm = (TelephonyManager) context.getSystemService(Context
                        .TELEPHONY_SERVICE);
//                获得设备IMEI标识
                String deviceId = tm.getDeviceId();
                String backId = "";
                if (deviceId != null) {
                    backId = deviceId;
                    backId = backId.replace("0", "");
                }

                if (((TextUtils.isEmpty(deviceId)) || TextUtils.isEmpty(backId)) && (Build.VERSION
                        .SDK_INT >= 9)) {
                    try {
                        Class c = Class.forName("android.os.SystemProperties");
                        Method get = c.getMethod("get", new Class[]{String.class, String.class});
                        deviceId = (String) get.invoke(c, new Object[]{"ro.serialno", "unknown"});
                    } catch (Exception t) {
                        deviceId = null;
                    }
                }

                if (Preconditions.isNotBlank(deviceId)) {
                    result = deviceId;
                } else {
                    result = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
                }
            } else {
                result = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
            }
        } catch (Exception ignored) {

        }
        return result;
    }

    /**
     * 获得系统版本
     *
     * @return os version
     */
    public static String getOSVersion() {
        return Build.VERSION.RELEASE;
    }

    /**
     * 获取设备品牌
     *
     * @return branding
     */
    public static String getBranding() {
        return Build.BRAND;
    }

    /**
     * 获得制造商
     *
     * @return manufacturer
     */
    public static String getManufacturer() {
        return Build.MANUFACTURER;
    }

    /**
     * 设备的名字
     *
     * @return device model
     */
    public static String getDevice() {
        return Build.MODEL;
    }

    //这个可获取到类似1080*1920
    public static String getScreenSize(@NonNull Context context) {

        String result = "";
        try {
            //first method
            if (Build.VERSION.SDK_INT >= 13 && Build.VERSION.SDK_INT < 17) {
                WindowManager windowManager = (WindowManager) context.getSystemService(Context
                        .WINDOW_SERVICE);
                Display display = windowManager.getDefaultDisplay();
                Point size = new Point();
                display.getSize(size);

                int screenWidth = size.x;
                int screenHeight = size.y;
                if (context.getResources().getConfiguration().orientation == Configuration
                        .ORIENTATION_PORTRAIT) {
                    result = screenWidth + "x" + screenHeight;
                } else {
                    result = screenHeight + "x" + screenWidth;
                }
            } else if (Build.VERSION.SDK_INT >= 17) {
                WindowManager windowManager = (WindowManager) context.getSystemService(Context
                        .WINDOW_SERVICE);
                Display display = windowManager.getDefaultDisplay();
                Point size = new Point();

                display.getRealSize(size);

                int screenWidth = size.x;
                int screenHeight = size.y;
                if (context.getResources().getConfiguration().orientation == Configuration
                        .ORIENTATION_PORTRAIT) {
                    result = screenWidth + "x" + screenHeight;
                } else {
                    result = screenHeight + "x" + screenWidth;
                }
            } else {
                DisplayMetrics dm2 = context.getResources().getDisplayMetrics();
                // 竖屏
                if (context.getResources().getConfiguration().orientation == Configuration
                        .ORIENTATION_PORTRAIT) {
                    result = dm2.widthPixels + "x" + dm2.heightPixels;
                } else {// 横屏
                    result = dm2.heightPixels + "x" + dm2.widthPixels;
                }
            }
        } catch (Exception ignored) {

        }
        return result;
    }

    /**
     * 获得应用的包名
     *
     * @param context context
     * @return package name
     */
    public static String getPackageName(@NonNull Context context) {
        return context.getPackageName();
    }


    /**
     * 获得应用的名称
     *
     * @param context context
     * @return package name
     */
    public static String getAppName(@NonNull Context context) {
        String appName = "";
        try {
            appName = (String) context.getPackageManager().getApplicationLabel(context.getApplicationInfo());
        } catch (Exception ignored) {
        }
        return appName;
    }


    /**
     * 获得当前应用的版本号
     *
     * @param context context
     * @return App Version
     */
    public static synchronized String getAppVersion(@NonNull Context context) {
        String result = "1.0";
        try {
            result = context.getPackageManager().getPackageInfo(context.getPackageName(), 0)
                    .versionName;
        } catch (PackageManager.NameNotFoundException ignored) {
        }

        return result;
    }

    /**
     * 获得注册运营商的名字
     *
     * @param context context
     * @return Carrier
     */
    public static String getCarrier(@NonNull Context context) {
        String result = "";
        try {
            TelephonyManager manager = (TelephonyManager) context.getSystemService(Context
                    .TELEPHONY_SERVICE);
            if (!SAFUtils.checkPermissions(context, "android.permission.READ_PHONE_STATE")) {
                result = carryByOperator(manager);
            } else {
                @SuppressLint("HardwareIds")
                String imsi = manager.getSubscriberId();

                if (TextUtils.isEmpty(imsi)) {
                    result = carryByOperator(manager);
                }

                if (imsi.startsWith("46000") || imsi.startsWith("46002") || imsi.startsWith("46007")) {
                    //中国移动
                    result = CHINA_MOBILE;
                } else if (imsi.startsWith("46001") || imsi.startsWith("46006")) {
                    //中国联通
                    result = CHINA_UNICOM;
                } else if (imsi.startsWith("46003") || imsi.startsWith("46005")) {
                    //中国电信
                    result = CHINA_TELECOM;
                } else if (imsi.startsWith("46020")) {
                    result = CHINA_TIETONG;
                } else {
                    result = carryByOperator(manager);
                }
            }
        } catch (Exception ignored) {

        }
        return result;

    }

    private static String carryByOperator(TelephonyManager manager) {
        String operatorString = manager.getSimOperator();

        if ("46000".equals(operatorString) || "46002".equals(operatorString) || "46007".equals
                (operatorString)) {
            //中国移动
            return CHINA_MOBILE;
        } else if ("46001".equals(operatorString) || "46006".equals(operatorString)) {
            //中国联通
            return CHINA_UNICOM;
        } else if ("46003".equals(operatorString) || "46005".equals(operatorString)) {
            //中国电信
            return CHINA_TELECOM;
        } else if ("46020".equals(operatorString)) {
            return CHINA_TIETONG;
        } else {
            return CHINA_CARRIER_UNKNOWN;
        }
    }

    /**
     * 获得本地语言和国家
     *
     * @return Language +county
     */
    public static String getLocal() {
        Locale locale = Locale.getDefault();
        return locale.getLanguage() + "_" + locale.getCountry();
    }

    /**
     * 获得网管硬件地址
     *
     * @param context context
     * @return Mac Address
     */
    @SuppressLint("HardwareIds")
    public static String getMacAddress(@NonNull Context context) {

        if (!SAFUtils.checkPermissions(context, "android.permission.ACCESS_WIFI_STATE")) {
            return "";
        }

        String result = "";

        try {
            WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
            if (wifiManager != null) {
                WifiInfo wifiInfo = wifiManager.getConnectionInfo();
                if (wifiInfo != null) {
                    result = wifiInfo.getMacAddress();
                }
            }
        } catch (Exception ignored) {

        }

        return result;
    }

    /**
     * 判断当前设备是手机还是平板
     *
     * @param context
     * @return 平板返回 True，手机返回 False
     */
    public static boolean isTablet(Context context) {
        return (context.getResources().getConfiguration().screenLayout & Configuration
                .SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE;
    }
}


<|start_filename|>saf-core/src/main/java/com/safframework/saf/orm/DBVersionPrefs.java<|end_filename|>
/**
 * 
 */
package com.safframework.saf.orm;

import android.content.Context;

import com.safframework.prefs.BasePrefs;

/**
 * @author <NAME>
 *
 */
public class DBVersionPrefs extends BasePrefs {

	private static final String PREFS_NAME = "DBVersionPrefs";
	public static final String DB_VERSION = "db_version";
	
	private DBVersionPrefs(Context context) {
		super(context, PREFS_NAME);
	}

	public static DBVersionPrefs get(Context context) {
		return new DBVersionPrefs(context);
	}
	
	public int getDBVersion() {
		return getInt(DB_VERSION, -1);
	}
	
	public void setDBVersion(int v) {
		putInt(DB_VERSION, v);
	}
}


<|start_filename|>app/src/main/java/com/test/saf/fragment/HomeFragment.java<|end_filename|>
package com.test.saf.fragment;

import android.app.ProgressDialog;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;

import com.safframework.injectview.Injector;
import com.safframework.injectview.annotations.InjectView;
import com.test.saf.R;
import com.test.saf.app.BaseFragment;



/**
 * Created by tony on 2016/11/20.
 */

public class HomeFragment extends BaseFragment {

    @InjectView(R.id.webview)
    WebView webview;

    private ProgressDialog progDailog;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.fragment_home, container, false);
        Injector.injectInto(this, v);

//        progDailog = ProgressDialog.show(mContext, "Loading","Please wait...", true);
//        progDailog.setCancelable(false);
//
//        initViews();

        return v;
    }

    private void initViews() {
        webview.loadUrl("https://github.com/fengzhizi715/SAF");
        //覆盖webView默认通过系统浏览器打开网页的方式
        webview.setWebViewClient(new WebViewClient(){

            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                progDailog.show();
                view.loadUrl(url);

                return true;
            }
            @Override
            public void onPageFinished(WebView view, final String url) {
                progDailog.dismiss();
            }
        });
        //获取WebView类设置对象
        WebSettings settings = webview.getSettings();
        //使webView支持js
        settings.setJavaScriptEnabled(true);
        //设置webView缓存模式
        webview.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);//(优先使用缓存)
        //webView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);//(不使用缓存)
        webview.setWebChromeClient(new WebChromeClient() {});

        webview.getSettings().setAllowFileAccess(true);
        //如果访问的页面中有Javascript，则webview必须设置支持Javascript
        webview.getSettings().setJavaScriptEnabled(true);
//        webView.getSettings().setUserAgentString(MyApplication.getUserAgent());
        webview.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
        webview.getSettings().setAllowFileAccess(true);
        webview.getSettings().setAppCacheEnabled(true);
        webview.getSettings().setDomStorageEnabled(true);
        webview.getSettings().setDatabaseEnabled(true);
    }
}


<|start_filename|>saf-cache/src/main/java/com/safframework/prefs/AppPrefs.java<|end_filename|>
package com.safframework.prefs;

import android.content.Context;

/**
 * 配合使用@Prefs
 * Created by <NAME> on 16/3/28.
 */
public class AppPrefs extends BasePrefs {

    private static final String PREFS_NAME = "AppPrefs";

    private AppPrefs(Context context) {
        super(context, PREFS_NAME);
    }

    public static AppPrefs get(Context context) {
        return new AppPrefs(context);
    }
}


<|start_filename|>app/src/main/java/com/test/saf/adapter/NormalTextViewHolder.java<|end_filename|>
package com.test.saf.adapter;

import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.TextView;

import com.safframework.injectview.Injector;
import com.safframework.injectview.annotations.InjectView;
import com.test.saf.R;


/**
 * Created by <NAME> on 2016/12/24.
 */

public class NormalTextViewHolder extends RecyclerView.ViewHolder {

    @InjectView(R.id.name)
    TextView name;

    public NormalTextViewHolder(View itemView) {
        super(itemView);
        Injector.injectInto(this,itemView);
    }
}


<|start_filename|>saf-core/src/main/java/com/safframework/saf/app/SAFFragment.java<|end_filename|>
/**
 * 
 */
package com.safframework.saf.app;

import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.Fragment;

import com.safframework.log.L;
import com.safframework.saf.utils.SAFUtils;
import com.safframework.saf.utils.ToastUtils;

/**
 * @author <NAME>
 *
 */
public class SAFFragment extends Fragment {

	/**
     * Fragment 所在的 FragmentActivity
     */
	public Activity mContext;
    
    public String TAG;

	/**
	 * Deprecated on API 23
	 * @param activity
     */
	@Override
    public void onAttach(Activity activity) {
		super.onAttach(activity);
		if (!SAFUtils.isMOrHigher()) {
			this.mContext = activity;
		}
    }

	@TargetApi(23)
	public void onAttach(Context context) {
		super.onAttach(context);

		if (context instanceof Activity){
			this.mContext = (Activity) context;
		}
	}

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
        
        TAG = SAFUtils.makeLogTag(this.getClass());
		L.init(TAG);
	}
    
	/**
	 * @param message toast的内容
	 */
	protected void toast(String message) {
		ToastUtils.showShort(mContext, message);
	}

	/**
	 * @param resId toast的内容来自String.xml
	 */
	protected void toast(int resId) {
		ToastUtils.showShort(mContext, resId);
	}
}


<|start_filename|>saf-core/src/main/java/com/safframework/saf/eventbus/DeadEvent.java<|end_filename|>
/**
 * 
 */
package com.safframework.saf.eventbus;

/**
 * EventBus不处理DeadEvent
 * @author <NAME>
 *
 */
public class DeadEvent {

	public final Object source;
	public final Object event;

	public DeadEvent(Object source, Object event) {
		this.source = source;
		this.event = event;
	}
}


<|start_filename|>saf-core/src/main/java/com/safframework/saf/orm/DBManager.java<|end_filename|>
/**
 * 
 */
package com.safframework.saf.orm;

import android.app.Application;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.support.v4.util.LruCache;
import android.util.Log;

import java.util.Collection;

/**
 * 如果要使用sqlite orm，必须在application中先使用DBManager.initialize(this);<br>
 * 如果结合SAFApp的功能，可以先写一个application继承SAFApp，然后将DBManager.initialize(this);
 * @author <NAME>
 *
 */
public final class DBManager {
	
	public static final int DEFAULT_CACHE_SIZE = 1024;
	public static final String TAG = "DBManager";
	
	private static boolean isInitialized = false;
	private static Context mContext;
	private static DomainInfo mDomainInfo;
	private static DatabaseHelper mDatabaseHelper;
	private static LruCache<String, DBDomain> mEntities;
	
	private DBManager() {
	}
	
	public static void initialize(Application app) {
		initialize(app,DEFAULT_CACHE_SIZE);
	}

	public static void initialize(Application app, int cacheSize) {
		if (isInitialized) {
			Log.i(TAG,"DBManager already initialized.");
			return;
		}
		
		mContext = app;
		mDomainInfo = new DomainInfo(app);
		mDatabaseHelper = new DatabaseHelper(app);
		mEntities = new LruCache<String,DBDomain>(cacheSize);
		openDatabase();
		isInitialized = true;

		Log.i(TAG,"DBManager initialized successfully.");
	}
	
	public static synchronized void close() {
		if (mDatabaseHelper!=null) {
			mDatabaseHelper.close();
		}
		mDatabaseHelper = null;
		
		if (mEntities!=null) {
			mEntities.evictAll();
		}
		mEntities = null;
		
		mDomainInfo = null;
		isInitialized = false;

		Log.i(TAG,"DBManager closed.");
	}

	public static Context getContext() {
		return mContext;
	}

	public static synchronized SQLiteDatabase openDatabase() {
		return mDatabaseHelper.getWritableDatabase();
	}

	public static synchronized Collection<TableInfo> getTableInfos() {
		return mDomainInfo.getTableInfos();
	}
	
	public static synchronized TableInfo getTableInfo(Class<? extends DBDomain> type) {
		return mDomainInfo.getTableInfo(type);
	}
	
	public static synchronized String getTableName(Class<? extends DBDomain> type) {
		return mDomainInfo.getTableInfo(type).getTableName();
	}
	
	public static String getId(Class<? extends DBDomain> type, Long id) {
		return getTableName(type) + "@" + id;
	}

	public static String getId(DBDomain entity) {
		return getId(entity.getClass(), entity.getId());
	}

	public static synchronized void addEntity(DBDomain entity) {
		mEntities.put(getId(entity), entity);
	}
	
	public static synchronized DBDomain getEntity(Class<? extends DBDomain> type, long id) {
		return mEntities.get(getId(type, id));
	}
}


<|start_filename|>app/src/main/java/com/test/saf/activity/MainActivity.java<|end_filename|>
package com.test.saf.activity;

import android.os.Bundle;
import android.support.design.widget.NavigationView;
import android.support.v4.app.Fragment;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBar;
import android.support.v7.widget.Toolbar;
import android.view.KeyEvent;
import android.view.MenuItem;
import android.view.View;

import com.safframework.injectview.annotations.InjectView;
import com.safframework.tony.common.utils.Preconditions;
import com.test.saf.R;
import com.test.saf.app.BaseActivity;
import com.test.saf.fragment.HomeFragment;
import com.test.saf.menu.MenuManager;
import com.test.saf.utils.DoubleClickExitUtils;

public class MainActivity extends BaseActivity {

	@InjectView(R.id.drawer_layout)
	DrawerLayout drawerLayout;

	@InjectView(R.id.navigation_view)
	NavigationView navigationView;

	@InjectView(R.id.toolbar)
	Toolbar toolbar;

	private MenuManager menuManager;
	private Fragment mContent;
	private DoubleClickExitUtils doubleClickExitHelper;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		initViews();
		initData();
	}

	private void initViews() {
		initToolbar();
		navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
			@Override
			public boolean onNavigationItemSelected(MenuItem menuItem) {

				if (Preconditions.isNotBlank(menuItem.getTitle())) {
					toolbar.setTitle(menuItem.getTitle());
				}
				showMenu(menuItem);
				menuItem.setChecked(true);

				if (drawerLayout.isDrawerOpen(GravityCompat.START)) {
					drawerLayout.closeDrawer(GravityCompat.START);
				}
				return true;
			}
		});
	}

	private void initData() {

		doubleClickExitHelper = new DoubleClickExitUtils(this);

		if (mContent == null) {
			menuManager = MenuManager.getInstance(getSupportFragmentManager());
			mContent = new HomeFragment();
		}

		getSupportFragmentManager().beginTransaction().add(R.id.content_frame,mContent, MenuManager.MenuType.HOME.getTitle()).commit();
	}

	private void initToolbar() {
		setSupportActionBar(toolbar);
		final ActionBar actionBar = getSupportActionBar();

		if (actionBar != null) {
			actionBar.setHomeAsUpIndicator(R.drawable.ic_menu_black_24dp);
			actionBar.setDisplayHomeAsUpEnabled(true);
		}

		toolbar.setNavigationOnClickListener(new View.OnClickListener() {

			@Override
			public void onClick(View v) {
				drawerLayout.openDrawer(GravityCompat.START);
			}
		});
	}

	private void showMenu(MenuItem menuItem) {

		switch (menuItem.getItemId()) {
			case R.id.drawer_saf:
				menuManager.show(MenuManager.MenuType.HOME);
				break;
			case R.id.drawer_anno:
				menuManager.show(MenuManager.MenuType.ANNOTATION);
				break;
			case R.id.drawer_eventbus:
				menuManager.show(MenuManager.MenuType.EVENTBUS);
				break;
			case R.id.drawer_sqlite:
				menuManager.show(MenuManager.MenuType.SQLITE);
				break;
			case R.id.drawer_router:
				menuManager.show(MenuManager.MenuType.ROUTER);
				break;
			case R.id.drawer_cache:
				menuManager.show(MenuManager.MenuType.CACHE);
				break;
			case R.id.drawer_log:
				menuManager.show(MenuManager.MenuType.LOG);
				break;
			case R.id.drawer_permission:
				menuManager.show(MenuManager.MenuType.PERMISSION);
				break;
			default:
				break;
		}
	}

	//重写物理按键的返回逻辑(实现返回键跳转到上一页)
	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		//用户触摸返回键
		if(keyCode == KeyEvent.KEYCODE_BACK){
			doubleClickExitHelper.onKeyDown(keyCode, event);
			return true;
		}
		return super.onKeyDown(keyCode, event);
	}
}


<|start_filename|>saf-core/src/main/java/com/safframework/saf/orm/DBDomain.java<|end_filename|>
/**
 * 
 */
package com.safframework.saf.orm;

import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;

import com.safframework.saf.orm.annotation.Column;

import java.lang.reflect.Field;
import java.util.List;

/**
 * 
 * @author <NAME>
 *
 */
public class DBDomain {

	@Column(name = "Id")
	private Long mId = null;
	
	private TableInfo mTableInfo;
	
	public Long getId() {
		return mId;
	}
	
	public DBDomain() {
		mTableInfo = DBManager.getTableInfo(getClass());
	}

	public int count() {
		SQLiteDatabase db = DBManager.openDatabase();
        Cursor cursor  = db.rawQuery("select count(*) from "+mTableInfo.getTableName(),null);
        int count = 0;
        if (cursor.moveToNext()) {
            count = cursor.getInt(0);
        }
        return count;
	}

	public void save() {
		final SQLiteDatabase db = DBManager.openDatabase();
		final ContentValues values = new ContentValues();

		for (Field field : mTableInfo.getFields()) {
			final String fieldName = mTableInfo.getColumnName(field);
			Class<?> fieldType = field.getType();

			field.setAccessible(true);

			try {
				Object value = field.get(this);

				if (value == null) {
					values.putNull(fieldName);
				}
				else if (fieldType.equals(Byte.class) || fieldType.equals(byte.class)) {
					values.put(fieldName, (Byte) value);
				}
				else if (fieldType.equals(Short.class) || fieldType.equals(short.class)) {
					values.put(fieldName, (Short) value);
				}
				else if (fieldType.equals(Integer.class) || fieldType.equals(int.class)) {
					values.put(fieldName, (Integer) value);
				}
				else if (fieldType.equals(Long.class) || fieldType.equals(long.class)) {
					values.put(fieldName, (Long) value);
				}
				else if (fieldType.equals(Float.class) || fieldType.equals(float.class)) {
					values.put(fieldName, (Float) value);
				}
				else if (fieldType.equals(Double.class) || fieldType.equals(double.class)) {
					values.put(fieldName, (Double) value);
				}
				else if (fieldType.equals(Boolean.class) || fieldType.equals(boolean.class)) {
					values.put(fieldName, (Boolean) value);
				}
				else if (fieldType.equals(Character.class) || fieldType.equals(char.class)) {
					values.put(fieldName, value.toString());
				}
				else if (fieldType.equals(String.class)) {
					values.put(fieldName, value.toString());
				}
				else if (fieldType.equals(Byte[].class) || fieldType.equals(byte[].class)) {
					values.put(fieldName, (byte[]) value);
				}
			}
			catch (IllegalArgumentException e) {
			}
			catch (IllegalAccessException e) {
			}
		}

		if (mId == null) {
			mId = db.insert(mTableInfo.getTableName(), null, values);
		} else {
			db.update(mTableInfo.getTableName(), values, "Id=" + mId, null);
		}
	}

	public void delete() {
		SQLiteDatabase db = DBManager.openDatabase();
		if (mId!=null) {
			db.delete(mTableInfo.getTableName(), "Id=" + mId, null);
		}
	}

	public void delete(int id) {
		SQLiteDatabase db = DBManager.openDatabase();
		db.delete(mTableInfo.getTableName(), "Id=" + id, null);
	}

	public <T extends DBDomain>T get(int id) {
		return SQLiteUtils.rawQuerySingle(getClass(),"select * from "+mTableInfo.getTableName()+" where Id="+id,null);
	}

	public <T extends DBDomain> List<T> getAll() {
		return executeQuery("select * from "+mTableInfo.getTableName());
	}

	public <T extends DBDomain> List<T> executeQuery(String query) {
		return SQLiteUtils.rawQuery(getClass() , query ,null);
	}

	/**
	 * 支持类似于new Autocomplete().executeQuery("select * from autocomplete where KEY_WORDS = ? and Id = ?","testtest","1");的查询</br>
	 * 表示查询select * from autocomplete where KEY_WORDS = 'testtest' and Id = '1'
	 * @param query
	 * @param args
	 * @return
	 */
	public <T extends DBDomain> List<T> executeQuery(String query, String...args) {
		int length = args.length;
		if (length==0) {
			return executeQuery(query);
		}

		for(int i=0;i<length;i++) {
			query = query.replaceFirst("\\?", "'"+args[i]+"'");
		}
		return SQLiteUtils.rawQuery(getClass() , query ,null);
	}
	
	public final void loadFromCursor(Cursor cursor) {
		for (Field field : mTableInfo.getFields()) {
			final String fieldName = mTableInfo.getColumnName(field);
			Class<?> fieldType = field.getType();
			final int columnIndex = cursor.getColumnIndex(fieldName);

			if (columnIndex < 0) {
				continue;
			}

			field.setAccessible(true);

			try {
				boolean columnIsNull = cursor.isNull(columnIndex);

				Object value = null;

				if (columnIsNull) {
					field = null;
				}
				else if (fieldType.equals(Byte.class) || fieldType.equals(byte.class)) {
					value = cursor.getInt(columnIndex);
				}
				else if (fieldType.equals(Short.class) || fieldType.equals(short.class)) {
					value = cursor.getInt(columnIndex);
				}
				else if (fieldType.equals(Integer.class) || fieldType.equals(int.class)) {
					value = cursor.getInt(columnIndex);
				}
				else if (fieldType.equals(Long.class) || fieldType.equals(long.class)) {
					value = cursor.getLong(columnIndex);
				}
				else if (fieldType.equals(Float.class) || fieldType.equals(float.class)) {
					value = cursor.getFloat(columnIndex);
				}
				else if (fieldType.equals(Double.class) || fieldType.equals(double.class)) {
					value = cursor.getDouble(columnIndex);
				}
				else if (fieldType.equals(Boolean.class) || fieldType.equals(boolean.class)) {
					value = cursor.getInt(columnIndex) != 0;
				}
				else if (fieldType.equals(Character.class) || fieldType.equals(char.class)) {
					value = cursor.getString(columnIndex).charAt(0);
				}
				else if (fieldType.equals(String.class)) {
					value = cursor.getString(columnIndex);
				}
				else if (fieldType.equals(Byte[].class) || fieldType.equals(byte[].class)) {
					value = cursor.getBlob(columnIndex);
				}

				// Set the field value
				if (value != null) {
					field.set(this, value);
				}
			}
			catch (IllegalArgumentException e) {
			}
			catch (IllegalAccessException e) {
			}
			catch (SecurityException e) {
			}
		}

		if (mId != null) {
			DBManager.addEntity(this);
		}
	}
	
	@Override
	public String toString() {
		return mTableInfo.getTableName() + "@" + getId();
	}

	@Override
	public boolean equals(Object obj) {
		final DBDomain other = (DBDomain) obj;

		return this.mId != null && (this.mTableInfo.getTableName().equals(other.mTableInfo.getTableName()))
				&& (this.mId.equals(other.mId));
	}

	@Override
	public int hashCode() {
		int result = mId.hashCode();
		result = 31 * result + mTableInfo.hashCode();
		return result;
	}
}


<|start_filename|>app/src/main/java/com/test/saf/activity/AnnotationActivity.java<|end_filename|>
package com.test.saf.activity;

import android.os.Bundle;
import android.widget.TextView;

import com.safframework.injectview.annotations.InjectExtra;
import com.safframework.injectview.annotations.InjectView;
import com.safframework.router.RouterRule;
import com.safframework.tony.common.utils.Preconditions;
import com.test.saf.R;
import com.test.saf.app.BaseActivity;

import thereisnospon.codeview.CodeView;
import thereisnospon.codeview.CodeViewTheme;

/**
 * Created by <NAME> on 2016/11/22.
 */
@RouterRule(url={"annotationName/:anno_name"})
public class AnnotationActivity extends BaseActivity {

    @InjectView(R.id.text)
    CodeView text;

    @InjectView(R.id.title)
    TextView title;

    @InjectExtra(key = ANNO_NAME)
    String annotationName;

    public final static String ANNO_NAME = "anno_name";

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_annotation);

        initViews();
    }

    private void initViews() {

        if (Preconditions.isNotBlank(annotationName)) {
            title.setText(annotationName+"使用方法");
        }

        String content = getUsage(annotationName);

        if (Preconditions.isNotBlank(content)) {
            text.setTheme(CodeViewTheme.ANDROIDSTUDIO).fillColor();
            text.showCode(content);
        }
    }

    private String getUsage(String annotationName) {

        String result = null;
        if ("@Async".equals(annotationName)) {
            result = "    @Async\n" +
                    "    private void useAsync() {\n" +
                    "        Log.e(TAG, \" thread=\" + Thread.currentThread().getId());\n" +
                    "        Log.e(TAG, \"ui thread=\" + Looper.getMainLooper().getThread().getId" +
                    "());\n" +
                    "    }";
        } else if ("@Cacheable".equals(annotationName)) {
            result = "    @Cacheable(key = \"user\")\n" +
                    "    private User initData() {\n" +
                    "        User user = new User();\n" +
                    "        user.userName = \"tony\";\n" +
                    "        user.password = \"<PASSWORD>\";\n" +
                    "        return user;\n" +
                    "    }";
        }

        return result;
    }
}


<|start_filename|>saf-rxlifecycle/src/main/java/com/safframework/lifecycle/LifecyclePublisher.java<|end_filename|>
package com.safframework.lifecycle;

import android.support.annotation.IntDef;

import io.reactivex.processors.BehaviorProcessor;

/**
 * Created by <NAME> on 2017/5/25.
 */

public class LifecyclePublisher {

    public static final int ON_ATTACH = 0;
    public static final int ON_CREATE = 1;
    public static final int ON_CREATE_VIEW = 2;
    public static final int ON_START = 3;
    public static final int ON_RESUME = 4;
    public static final int ON_PAUSE = 5;
    public static final int ON_STOP = 6;
    public static final int ON_DESTROY_VIEW = 7;
    public static final int ON_DESTROY = 8;
    public static final int ON_DETACH = 9;

    private final BehaviorProcessor<Integer> behavior = BehaviorProcessor.create();

    @IntDef({ON_ATTACH, ON_CREATE, ON_CREATE_VIEW,
            ON_START, ON_RESUME,
            ON_PAUSE, ON_STOP,
            ON_DESTROY_VIEW, ON_DESTROY, ON_DETACH})
    public @interface Event {
    }

    public BehaviorProcessor<Integer> getBehavior() {
        return behavior;
    }

    public void onAttach() {
        behavior.onNext(ON_ATTACH);
    }

    public void onCreate() {
        behavior.onNext(ON_CREATE);
    }

    public void onCreateView() {
        behavior.onNext(ON_CREATE_VIEW);
    }

    public void onStart() {
        behavior.onNext(ON_START);
    }

    public void onResume() {
        behavior.onNext(ON_RESUME);
    }

    public void onPause() {
        behavior.onNext(ON_PAUSE);
    }

    public void onStop() {
        behavior.onNext(ON_STOP);
    }

    public void onDestroyView() {
        behavior.onNext(ON_DESTROY_VIEW);
    }

    public void onDestroy() {
        behavior.onNext(ON_DESTROY);
    }

    public void onDetach() {
        behavior.onNext(ON_DETACH);
    }
}

<|start_filename|>saf-core/src/main/java/com/safframework/saf/eventbus/EventBusUtils.java<|end_filename|>
/**
 * 
 */
package com.safframework.saf.eventbus;

import java.util.Map;
import java.util.Set;

/**
 * @author <NAME>
 *
 */
public class EventBusUtils {

	/**
	 * 查找Producer
	 * @param listener
	 * @return
	 */
	public static Map<Class<?>, EventProducer> findAllProducers(Object listener) {
		return AnnotatedFinder.findAllProducers(listener);
	}

	/**
	 * 查找Subscriber
	 * @param listener
	 * @return
	 */
    public static Map<Class<?>, Set<EventHandler>> findAllSubscribers(Object listener) {
        return AnnotatedFinder.findAllSubscribers(listener);
      }
}


<|start_filename|>saf-core/src/main/java/com/safframework/saf/utils/AsyncTaskExecutor.java<|end_filename|>
/**
 * 
 */
package com.safframework.saf.utils;

import android.annotation.SuppressLint;
import android.os.AsyncTask;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 推荐使用RxAsyncTask替换AsyncTask
 * @author <NAME>
 *
 */
@Deprecated
public class AsyncTaskExecutor {

	private static final int CORE_POOL_SIZE;
	private static final int MAXIMUM_POOL_SIZE;
	private static final int KEEP_ALIVE;
	private static final TimeUnit TIME_UNIT;

	private static final BlockingQueue<Runnable> concurrentPoolWorkQueue;
	private static final ThreadFactory concurrentThreadFactory;
    private static final ThreadPoolExecutor concurrentExecutor;
    
    private AsyncTaskExecutor() {}
    
    static {
    	CORE_POOL_SIZE = 5;
    	MAXIMUM_POOL_SIZE = 128;
    	KEEP_ALIVE = 1;
    	TIME_UNIT = TimeUnit.SECONDS;
    	
    	concurrentPoolWorkQueue = new LinkedBlockingQueue<Runnable>(10);
    	concurrentThreadFactory = new AsyncTaskThreadFactory();
    	concurrentExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE, 
				MAXIMUM_POOL_SIZE, KEEP_ALIVE, TIME_UNIT, concurrentPoolWorkQueue, concurrentThreadFactory);
    }

    /**
     * Concurrently executes AsyncTask on any Android version
     * @param task to execute
     * @param params for task
     * @return executing AsyncTask
     */
    @SuppressLint("NewApi")
    public static <Params, Progress, Result> AsyncTask<Params, Progress, Result>
    executeAsyncTask(AsyncTask<Params, Progress, Result> task,
    		Params... params) {
    	if (SAFUtils.isHoneycombOrHigher()) {
    		task.executeOnExecutor(concurrentExecutor, params);
    	} else {
    		task.execute(params);
    	}
    	return task;
    }

    private static class AsyncTaskThreadFactory implements ThreadFactory {
    	private final AtomicInteger count;
    	
    	{
    		count = new AtomicInteger(1);
    	}
    	
		@Override
		public Thread newThread(Runnable r) {
			return new Thread(r, "AsyncTask #" + count.getAndIncrement());
		}
    }
}


<|start_filename|>saf-queue/src/main/java/com/safframework/queue/Queue.java<|end_filename|>
package com.safframework.queue;

import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Process;

import java.util.concurrent.LinkedBlockingQueue;

/**
 * Created by tony on 2017/4/15.
 */

public class Queue {

    private Bundle                                 bundle = null;
    private HandlerThread                          operationHandlerThread = null;
    private Handler                                operationHandlerThreadHandler = null;
    private LinkedBlockingQueue<OperationRunnable> operationQueue = new LinkedBlockingQueue<>();
    private boolean         isRunning = false;
    private String          name = null;
    private int             priority = Process.THREAD_PRIORITY_DEFAULT;

    /**
     * 构造Queue
     * @param name
     */
    public Queue(String name) {
        this(name, Process.THREAD_PRIORITY_DEFAULT);
    }

    /**
     * 基于线程优先级构造Queue
     * @param name
     * @param priority
     */
    public Queue(String name, int priority) {
        this.name = name;
        this.priority = priority;
        bundle = new Bundle();
        operationHandlerThread = new HandlerThread(name, priority);
    }

    /**
     * 启动queue
     */
    public synchronized void start() {

        if(isRunning) return;

        isRunning = true;
        if(operationHandlerThread == null) {
            operationHandlerThread = new HandlerThread(name, priority);
        }
        operationHandlerThread.start();
        operationHandlerThreadHandler = new Handler(operationHandlerThread.getLooper());
        for(OperationRunnable op : operationQueue ){
            op.queueing(operationHandlerThreadHandler);
        }
        operationQueue.clear();
    }

    public boolean isActivated() {
        return isRunning;
    }

    /**
     * 停止queue，并且清空所有操作
     */
    public synchronized void stop() {

        if(!isRunning) return;

        isRunning = false;
        removeAllOperations();
        operationHandlerThread.quit();
        operationHandlerThread = null;
        operationHandlerThreadHandler = null;
        bundle.clear();
    }

    public boolean addOperation(Operation operation) {
        if(isRunning) {
            if(operationHandlerThreadHandler == null)
                return false;

            return operationHandlerThreadHandler.post(new OperationRunnable(this, operation));
        } else {
            return operationQueue.add(new OperationRunnable(this, operation));
        }
    }

    public boolean addOperationAtFirst(Operation operation) {
        if(isRunning) {
            if(operationHandlerThreadHandler == null)
                return false;

            return operationHandlerThreadHandler.postAtFrontOfQueue(new OperationRunnable(this, operation));
        } else {
            return operationQueue.add(new OperationRunnable(this, operation, OperationRunnable.Type.ATFIRST, null, 0));
        }
    }

    public boolean addOperationAtTime(Operation operation, long uptimeMillis) {
        if(isRunning) {
            if(operationHandlerThreadHandler == null)
                return false;

            return operationHandlerThreadHandler.postAtTime(new OperationRunnable(this, operation), uptimeMillis);
        } else {
            return operationQueue.add(new OperationRunnable(this, operation, OperationRunnable.Type.ATTIME, null, uptimeMillis));
        }
    }

    public boolean addOperationAtTime(Operation operation, Object token, long uptimeMillis) {
        if(isRunning) {
            if(operationHandlerThreadHandler == null)
                return false;

            return operationHandlerThreadHandler.postAtTime(new OperationRunnable(this, operation), token, uptimeMillis);
        } else {
            return operationQueue.add(new OperationRunnable(this, operation, OperationRunnable.Type.ATTIME_WITH_TOKEN, token, uptimeMillis));
        }
    }

    public boolean addOperationAfterDelay(Operation operation, long delayTimeMillis) {
        if(isRunning) {
            if(operationHandlerThreadHandler == null)
                return false;

            return operationHandlerThreadHandler.postDelayed(new OperationRunnable(this, operation), delayTimeMillis);
        } else {
            return operationQueue.add(new OperationRunnable(this, operation, OperationRunnable.Type.DELAY, null, delayTimeMillis));
        }
    }

    public void removeOperation(Operation operation) {
        if(isRunning) {
            if(operationHandlerThreadHandler == null)
                return ;
            operationHandlerThreadHandler.removeCallbacks(new OperationRunnable(this, operation));
        } else {
            operationQueue.remove(new OperationRunnable(this, operation));
        }
    }

    public void removeOperation(Operation operation, Object token) {
        if(isRunning) {
            if(operationHandlerThreadHandler == null)
                return;
            operationHandlerThreadHandler.removeCallbacks(new OperationRunnable(this, operation), token);
        } else {
            operationQueue.remove(new OperationRunnable(this, operation, OperationRunnable.Type.NORMAL, token, 0));
        }
    }

    public void removeAllOperations() {
        if(operationHandlerThreadHandler != null) {
            operationHandlerThreadHandler.removeCallbacksAndMessages(null);
        }
        operationQueue.clear();
    }

    public Bundle getBundle() {
        return bundle;
    }
}


<|start_filename|>saf-core/src/main/java/com/safframework/saf/view/LightDialog.java<|end_filename|>
/**
 * 
 */
package com.safframework.saf.view;

import android.app.AlertDialog;
import android.content.Context;

import com.safframework.saf.utils.SAFUtils;

/**
 * Holo Light theme的dialog
 * 用法如下：
 * <pre>
 * <code>
 * LightDialog dialog = LightDialog.create(this,"title","test");
 * dialog.show();
 * </code>
 * </pre>
 * @author <NAME>
 *
 */
public class LightDialog extends AlertDialog{

	  /**
	   * 创建LightDialog,在android4.0及以上的版本dialog采用Holo Light theme
	   *
	   * @param context
	   * @return light alert dialog
	   */
	  public static LightDialog create(Context context) {
	    LightDialog dialog;
	    if (SAFUtils.isICSOrHigher())
	      dialog = new LightDialog(context, 3);
	    else {
	      dialog = new LightDialog(context);
	      dialog.setInverseBackgroundForced(true);
	    }
	    return dialog;
	  }

	  public static LightDialog create(Context context, String title, String message) {
	    LightDialog dialog = create(context);
	    dialog.setTitle(title);
	    dialog.setMessage(message);
	    return dialog;
	  }

	  public static LightDialog create(Context context, int title, int message) {
	    LightDialog dialog = create(context);
	    dialog.setTitle(title);
	    dialog.setMessage(context.getString(message));
	    return dialog;
	  }

	  public static LightDialog create(Context context, int title, String message) {
	    LightDialog dialog = create(context);
	    dialog.setTitle(title);
	    dialog.setMessage(message);
	    return dialog;
	  }

	  public static LightDialog create(Context context, String title, int message) {
	    return create(context, title, context.getString(message));
	  }

	  protected LightDialog(Context context) {
	    super(context);
	  }

	  /**
	   * @param context
	   * @param theme
	   */
	  protected LightDialog(Context context, int theme) {
	    super(context, theme);
	  }

	  public LightDialog setPositiveButton(int text, OnClickListener listener) {
	    return setPositiveButton(getContext().getString(text), listener);
	  }

	  public LightDialog setPositiveButton(CharSequence text,
										   OnClickListener listener) {
	    setButton(BUTTON_POSITIVE, text, listener);
	    return this;
	  }

	  public LightDialog setPositiveButton(OnClickListener listener) {
	    return setPositiveButton(android.R.string.ok, listener);
	  }

	  public LightDialog setNegativeButton(int text, OnClickListener listener) {
	    return setNegativeButton(getContext().getString(text), listener);
	  }

	  public LightDialog setNegativeButton(CharSequence text,
										   OnClickListener listener) {
	    setButton(BUTTON_NEGATIVE, text, listener);
	    return this;
	  }

	  public LightDialog setNegativeButton(OnClickListener listener) {
	    return setNegativeButton(android.R.string.cancel, listener);
	  }
}


<|start_filename|>saf-core/src/main/java/com/safframework/saf/orm/DomainInfo.java<|end_filename|>
/**
 * 
 */
package com.safframework.saf.orm;

import android.app.Application;
import android.text.TextUtils;
import android.util.Log;

import com.safframework.saf.utils.ReflectionUtils;
import com.safframework.saf.utils.SAFUtils;

import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
 * @author <NAME>
 *
 */
public final class DomainInfo {

	public static final String TAG = "DomainInfo";
	private final static String DOMAIN_PACKAGE = "DOMAIN_PACKAGE";
	
	private Map<Class<? extends DBDomain>, TableInfo> mTableInfos = new HashMap<Class<? extends DBDomain>, TableInfo>();
	
	public DomainInfo(Application app) {
		if (loadDomainFromMetaData(app)) {
			Log.i(TAG,"DomainInfo loaded successfully.");
		} else {
			Log.i(TAG,"DomainInfo loaded failure.");
		}
	}
	
	private boolean loadDomainFromMetaData(Application app) {
		String domainPackage = SAFUtils.getMetaData(app, DOMAIN_PACKAGE);
		
		if (!TextUtils.isEmpty(domainPackage)) {
			loadDomainList(app, domainPackage);
		}

		return mTableInfos.size() > 0;
	}

	private void loadDomainList(Application app, String domainPackage) {
		final ClassLoader classLoader = app.getClass().getClassLoader();
		try {
			List<String>  domains = ReflectionUtils.getPackageAllClassName(app, domainPackage);
			if (domains!=null && domains.size()>0) {
				for(String domain:domains) {
					Class domainClass = Class.forName(domain, false, classLoader);
					mTableInfos.put(domainClass, new TableInfo(domainClass));
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
	
	public Collection<TableInfo> getTableInfos() {
		return mTableInfos.values();
	}

	public TableInfo getTableInfo(Class<? extends DBDomain> type) {
		return mTableInfos.get(type);
	}

	@SuppressWarnings("unchecked")
	public List<Class<? extends DBDomain>> getModelClasses() {
		return (List<Class<? extends DBDomain>>) mTableInfos.keySet();
	}

}


<|start_filename|>saf-core/src/main/java/com/safframework/saf/eventbus/ScheduledEvent.java<|end_filename|>
/**
 * 
 */
package com.safframework.saf.eventbus;

import java.util.concurrent.TimeUnit;

/**
 * @author <NAME>
 *
 */
public class ScheduledEvent {

	public long initialDelay;
	public long period;
	public TimeUnit unit = TimeUnit.MILLISECONDS;
}


<|start_filename|>saf-core/src/main/java/com/safframework/saf/rest/RestClient.java<|end_filename|>
/**
 * 
 */
package com.safframework.saf.rest;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.safframework.log.L;
import com.safframework.tony.common.utils.Preconditions;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.Proxy;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.URLEncoder;
import java.security.GeneralSecurityException;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.zip.GZIPInputStream;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import static java.net.HttpURLConnection.HTTP_BAD_REQUEST;
import static java.net.Proxy.Type.HTTP;

/**
 * 使用HttpURLConnection实现http的get、post、put、delete方法</br>
 * 同步调用get方法：
 * <pre>
 * <code>
 * RestClient request = RestClient.get(url);
 * String body = request.body();
 * </code>
 * </pre>
 * 异步调用get方法：
 * <pre>
 * <code>
 * 		RestClient.get(url,new HttpResponseHandler(){
 * 
 *			public void onSuccess(String content) {
 *			}
 *			
 *		});
 * </code>
 * </pre>
 * 同步调用post方法：post body内容为json
 * <pre>
 * <code>
 * RestClient request = RestClient.post(url);
 * request.acceptJson().contentType("application/json", null);
 * request.send(jsonString);
 * String body = request.body();
 * </code>
 * </pre>
 * 异步调用post方法：post body内容为json
 * <pre>
 * <code>
 * 		RestClient.post(url,json,new HttpResponseHandler(){
 *
 *			public void onSuccess(String content) {
 *			}
 *			
 *		});
 * </code>
 * </pre>
 * 使用gzip压缩的方法：
 * <pre>
 * <code>
 * RestClient request = RestClient.get(url);
 * request.acceptGzipEncoding().uncompress(true);
 * String body = request.body();
 * </code>
 * </pre>
 * @author <NAME>
 * 
 */
public class RestClient {

	private HttpURLConnection connection = null;
	private RestOutputStream output;

	private boolean multipart;
	private boolean ignoreCloseExceptions = true;
	private boolean uncompress = false;
	private boolean form;

	private URL url;
	private String requestMethod;
	private String httpProxyHost; // 代理服务器的url
	private int httpProxyPort;    // 代理服务器的端口

	private int bufferSize = 0x2000; // 8192

	private static SSLSocketFactory TRUSTED_FACTORY;
	private static HostnameVerifier TRUSTED_VERIFIER;

	/**
	 * 默认的ConnectionFactory
	 */
    static ConnectionFactory DEFAULT = new ConnectionFactory() {
      public HttpURLConnection create(URL url) throws IOException {
        return (HttpURLConnection) url.openConnection();
      }

      public HttpURLConnection create(URL url, Proxy proxy) throws IOException {
        return (HttpURLConnection) url.openConnection(proxy);
      }
    };

    private static ConnectionFactory CONNECTION_FACTORY = DEFAULT;

    /**
     * 可以自定义ConnectionFactory，只需实现ConnectionFactory接口即可
     * @param connectionFactory
     */
    public static void setConnectionFactory(final ConnectionFactory connectionFactory) {
      if (connectionFactory == null)
        CONNECTION_FACTORY = DEFAULT;
      else
        CONNECTION_FACTORY = connectionFactory;
    }

	private HttpURLConnection createConnection() {
		try {
			final HttpURLConnection connection;
			if (httpProxyHost != null)
				connection = CONNECTION_FACTORY.create(url, createProxy());
			else
				connection = CONNECTION_FACTORY.create(url);
			connection.setRequestMethod(requestMethod);
			connection.setReadTimeout(RestConstant.DEFAULT_READ_TIMEOUT);
			connection.setConnectTimeout(RestConstant.DEFAULT_CONNECTION_TIMEOUT);
			return connection;
		} catch (IOException e) {
			throw new RestException(e);
		}
	}

	/**
	 * after set connectionTimeout and readTimeout can throw java.net.SocketTimeoutException<br>
	 *
	 * @return
	 */
	public HttpURLConnection getConnection() {
		if (connection == null)
			connection = createConnection();
		return connection;
	}

	private Proxy createProxy() {
		return new Proxy(HTTP, new InetSocketAddress(httpProxyHost,httpProxyPort));
	}

	/**
	 * 配置http代理
	 * @param proxyHost
	 * @param proxyPort
	 * @return
	 */
    public RestClient useProxy(final String proxyHost, final int proxyPort) {
      if (connection != null)
        throw new IllegalStateException("The connection has already been created. This method must be called before reading or writing to the request.");

      this.httpProxyHost = proxyHost;
      this.httpProxyPort = proxyPort;
      return this;
    }

    /**
     * 配置HTTPS连接，信任所有证书
     *
     * @return RestClient
     * @throws RestException
     */
	public RestClient trustAllCerts() throws RestException {
		final HttpURLConnection connection = getConnection();
		if (connection instanceof HttpsURLConnection)
			((HttpsURLConnection) connection)
					.setSSLSocketFactory(getTrustedFactory());
		return this;
	}

    /**
     * 配置HTTPS连接，信任所有host
     *
     * @return RestClient
     */
	public RestClient trustAllHosts() {
		final HttpURLConnection connection = getConnection();
		if (connection instanceof HttpsURLConnection)
			((HttpsURLConnection) connection)
					.setHostnameVerifier(getTrustedVerifier());
		return this;
	}

	private static SSLSocketFactory getTrustedFactory()
			throws RestException {
		if (TRUSTED_FACTORY == null) {
			final TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {

				public X509Certificate[] getAcceptedIssuers() {
					return new X509Certificate[0];
				}

				public void checkClientTrusted(X509Certificate[] chain,
						String authType) {
					// Intentionally left blank
				}

				public void checkServerTrusted(X509Certificate[] chain,
						String authType) {
					// Intentionally left blank
				}
			} };
			try {
				SSLContext context = SSLContext.getInstance("TLS");
				context.init(null, trustAllCerts, new SecureRandom());
				TRUSTED_FACTORY = context.getSocketFactory();
			} catch (GeneralSecurityException e) {
				IOException ioException = new IOException(
						"Security exception configuring SSL context");
				ioException.initCause(e);
				throw new RestException(ioException);
			}
		}

		return TRUSTED_FACTORY;
	}

	private static HostnameVerifier getTrustedVerifier() {
		if (TRUSTED_VERIFIER == null)
			TRUSTED_VERIFIER = new HostnameVerifier() {

				public boolean verify(String hostname, SSLSession session) {
					return true;
				}
			};

		return TRUSTED_VERIFIER;
	}

	/**
	 * 创建 HTTP connection wrapper
	 *
	 * @param url
	 * @param method
	 * @throws RestException
	 */
	public RestClient(String url, String method)
			throws RestException {
		try {
			this.url = new URL(url);
			this.requestMethod = method;
		} catch (MalformedURLException e) {
			throw new RestException(e);
		}
	}

	/**
	 * 同步发起get请求
	 *
	 * @param url
	 * @return RestClient
	 * @throws RestException
	 */
	public static RestClient get(String url) throws RestException {
		L.d("get url=" + url);
		return new RestClient(url, RestConstant.METHOD_GET);
	}

	/**
	 * 异步发起get请求
	 * @param url
	 * @param callback
	 * @throws RestException
	 */
	public static void get(String url,HttpResponseHandler callback) {
		L.d("get url="+url);
		get(url, null, callback, RestConstant.DEFAULT_RETRY_NUM);
	}

	/**
	 * 异步发起get请求
	 * @param url
	 * @param customizedHeader
	 * @param callback
	 */
	public static void get(String url,Map<String, String> customizedHeader,HttpResponseHandler callback) {
		L.d("get url="+url);
		get(url,customizedHeader, callback, RestConstant.DEFAULT_RETRY_NUM);
	}

	private static void get(String url,Map<String, String> customizedHeader,HttpResponseHandler callback,int retryNum) {
		RestClient client = null;
		try {
			client = new RestClient(url, RestConstant.METHOD_GET);
			client.acceptGzipEncoding().uncompress(true);

			if (url.startsWith("https")) {
				//Accept all certificates
				client.trustAllCerts();
				//Accept all hostnames
				client.trustAllHosts();
			}

			if(Preconditions.isNotBlank(customizedHeader)) {
				for (Entry<String, String> item : customizedHeader.entrySet()) {
					client.getConnection().setRequestProperty(item.getKey(),item.getValue());
					L.i(item.getKey()+"="+item.getValue());
				}
			}

			String body = client.body();
			Map<String, List<String>> heads = client.getConnection().getHeaderFields();
			callback.onSuccess(body,heads);
		} catch (RestException e) {
			e.printStackTrace();
			L.e(e.getErrorCode(),e);
			if (RestException.RETRY_CONNECTION.equals(e.getErrorCode()) && retryNum > 0) {
				get(url,customizedHeader, callback, --retryNum);
			} else {
				callback.onFail(e);
			}
		} catch (Exception e) {
			e.printStackTrace();
			L.e("get method error!", e);

			if (Preconditions.isNotBlank(e.getMessage())) {
				callback.onFail(new RestException(e.getMessage()));
			}
		}
	}

	/**
	 * 异步发起get请求
	 * @param url
	 * @param callback
	 * @throws RestException
	 */
	public static void get(String url,HttpResponseRetryHandler callback) throws RestException {
		L.d("get url="+url);
		get(url,null, callback);
	}

	/**
	 * 异步发起get请求
	 * @param url
	 * @param customizedHeader
	 * @param callback
	 * @throws RestException
	 */
	public static void get(final String url, final Map<String, String> customizedHeader,final HttpResponseRetryHandler callback) throws RestException {
		RestClient client = null;

		int retryNum = callback.getRetryNum();
		try {
			client = new RestClient(url, RestConstant.METHOD_GET);
			client.acceptGzipEncoding().uncompress(true);

			if (url.startsWith("https")) {
				//Accept all certificates
				client.trustAllCerts();
				//Accept all hostnames
				client.trustAllHosts();
			}

			if(Preconditions.isNotBlank(customizedHeader)) {
				for (Entry<String, String> item : customizedHeader.entrySet()) {
					client.getConnection().setRequestProperty(item.getKey(),item.getValue());
					L.i(item.getKey()+"="+item.getValue());
				}
			}

			String body = client.body();
			Map<String, List<String>> heads = client.getConnection().getHeaderFields();
			callback.onSuccess(body,heads);
		} catch (RestException e) {
			e.printStackTrace();
			L.e(e.getErrorCode(),e);
			if (RestException.RETRY_CONNECTION.equals(e.getErrorCode()) && retryNum > 0) {
				callback.retry();
				retryNum--;
			} else {
				callback.onFail(e);
			}
		} catch (Exception e) {
			e.printStackTrace();
			L.e("get method error!", e);

			if (Preconditions.isNotBlank(e.getMessage())) {
				callback.onFail(new RestException(e.getMessage()));
			}
		}
	}

	/**
	 * 同步发起post请求
	 *
	 * @param url
	 * @return RestClient
	 * @throws RestException
	 */
	public static RestClient post(String url) throws RestException {
		L.d("post url="+url);
		return new RestClient(url, RestConstant.METHOD_POST);
	}

	/**
	 * 异步发起post请求
	 * @param url
	 * @param json 其中post body是json字符串
	 * @param callback
	 * @throws RestException
	 */
	public static void post(String url,JSONObject json,HttpResponseHandler callback) throws RestException {
		L.d("post url="+url+"\n"+"post body="+JSON.toJSONString(json));
		post(url, json, callback, RestConstant.DEFAULT_RETRY_NUM);
	}

	private static void post(String url,JSONObject json,HttpResponseHandler callback,int retryNum) throws RestException {
		RestClient request = new RestClient(url, RestConstant.METHOD_POST);
		request.acceptJson().contentType("application/json");
		request.acceptGzipEncoding().uncompress(true);

		if (url.startsWith("https")) {
			//Accept all certificates
			request.trustAllCerts();
			//Accept all hostnames
			request.trustAllHosts();
		}

		try {
			request.send(json);
			String body = request.body();
			Map<String, List<String>> heads = request.getConnection().getHeaderFields();
			callback.onSuccess(body,heads);
		} catch (RestException e) {
			e.printStackTrace();
			L.e(e.getErrorCode(),e);
			if (RestException.RETRY_CONNECTION.equals(e.getErrorCode()) && retryNum > 0) {
				post(url, json, callback, --retryNum);
			} else {
				callback.onFail(e);
			}
		} catch (Exception e) {
			e.printStackTrace();
			L.json(e);

			if (Preconditions.isNotBlank(e.getMessage())) {
				callback.onFail(new RestException(e.getMessage()));
			}
		}

	}

	/**
	 * 异步发起post请求
	 * @param url
	 * @param json
	 * @param callback
	 * @throws RestException
	 */
	public static void post(String url,JSONObject json,HttpResponseRetryHandler callback) throws RestException {
		L.d("post url="+url+"\n"+"post body="+JSON.toJSONString(json));
		RestClient request = new RestClient(url, RestConstant.METHOD_POST);
		request.acceptJson().contentType("application/json");
		request.acceptGzipEncoding().uncompress(true);

		if (url.startsWith("https")) {
			//Accept all certificates
			request.trustAllCerts();
			//Accept all hostnames
			request.trustAllHosts();
		}

		int retryNum = callback.getRetryNum();

		try {
			request.send(json);
			String body = request.body();
			Map<String, List<String>> heads = request.getConnection().getHeaderFields();
			callback.onSuccess(body,heads);
		} catch (RestException e) {
			e.printStackTrace();
			L.e(e.getErrorCode(),e);
			if (RestException.RETRY_CONNECTION.equals(e.getErrorCode()) && retryNum > 0) {
				callback.retry();
				retryNum--;
			} else {
				callback.onFail(e);
			}
		} catch (Exception e) {
			e.printStackTrace();
			L.json(e);

			if (Preconditions.isNotBlank(e.getMessage())) {
				callback.onFail(new RestException(e.getMessage()));
			}
		}
	}

	/**
	 * 异步发起post请求
	 * @param url
	 * @param map 以form形式传递数据
	 * @param callback
	 * @throws RestException
	 */
	public static void post(String url,Map<?, ?> map,HttpResponseHandler callback) throws RestException {
		L.d("post url="+url+"\n"+"form map="+map.toString());
		post(url, map, callback, RestConstant.DEFAULT_RETRY_NUM);
	}

	private static void post(String url,Map<?, ?> map,HttpResponseHandler callback, int retryNum) throws RestException {
		RestClient request = null;
		try {
			request = new RestClient(url, RestConstant.METHOD_POST).form(map);
			String body = request.body();
			Map<String, List<String>> heads = request.getConnection().getHeaderFields();
			callback.onSuccess(body,heads);
		} catch (RestException e) {
			e.printStackTrace();
			L.e(e.getErrorCode(),e);
			if (RestException.RETRY_CONNECTION.equals(e.getErrorCode()) && retryNum != 0) {
				post(url, map, callback, --retryNum);
			} else {
				callback.onFail(e);
			}
		} catch (Exception e) {
			e.printStackTrace();
			L.json(e);

			if (Preconditions.isNotBlank(e.getMessage())) {
				callback.onFail(new RestException(e.getMessage()));
			}
		}
	}

	/**
	 * 发起put请求
	 *
	 * @param url
	 * @return RestClient
	 * @throws RestException
	 */
	public static RestClient put(String url) throws RestException {
		return new RestClient(url, RestConstant.METHOD_PUT);
	}

	/**
	 * 发起delete请求
	 *
	 * @param url
	 * @return RestClient
	 * @throws RestException
	 */
	public static RestClient delete(String url) throws RestException {
		return new RestClient(url, RestConstant.METHOD_DELETE);
	}

	/**
	 * 将response返回的stream封装成BufferedInputStream
	 *
	 * @return stream
	 * @throws RestException
	 */
	public BufferedInputStream buffer() throws RestException {
		return new BufferedInputStream(stream(), bufferSize);
	}

	/**
	 * 返回response body的是否自动压缩
	 * @param uncompress
	 * @return
	 */
	public RestClient uncompress(final boolean uncompress) {
		this.uncompress = uncompress;
		return this;
	}

	/**
	 * 返回response body的stream，如果需要压缩返回的将是GZIPInputStream
	 *
	 * @return stream
	 * @throws RestException
	 */
	public InputStream stream() throws RestException {
		InputStream stream;
		if (code() < HTTP_BAD_REQUEST)
			try {
				stream = getConnection().getInputStream();
			} catch (IOException e) {
				throw new RestException(e);
			}
		else {
			stream = getConnection().getErrorStream();
			if (stream == null)
				try {
					stream = getConnection().getInputStream();
				} catch (IOException e) {
					throw new RestException(e);
				}
		}

		if (!uncompress
				|| !RestConstant.ENCODING_GZIP.equals(contentEncoding()))
			return stream;
		else
			try {
				return new GZIPInputStream(stream);
			} catch (IOException e) {
				throw new RestException(e);
			}
	}

	/**
	 * 从response中返回header的Content-Encoding
	 *
	 * @return
	 */
	public String contentEncoding() {
		return header(RestConstant.HEADER_CONTENT_ENCODING);
	}

	/**
	 * 返回HTTP Status Code
	 *
	 * @return the response code
	 * @throws RestException
	 */
	public int code() throws RestException {
		try {
			closeOutput();
			return getConnection().getResponseCode();
		} catch (IOException e) {
			throw new RestException(e);
		}
	}

	public int intHeader(final String name) throws RestException {
		return intHeader(name, -1);
	}

	public int intHeader(final String name, final int defaultValue)
			throws RestException {
		closeOutputQuietly();
		return getConnection().getHeaderFieldInt(name, defaultValue);
	}

	/**
	 * 从response中返回header的Content-Length
	 *
	 * @return response header value
	 */
	public int contentLength() {
		return intHeader(RestConstant.HEADER_CONTENT_LENGTH);
	}

	/**
	 * 创建ByteArrayOutputStream
	 *
	 * @return stream
	 */
	protected ByteArrayOutputStream byteStream() {
		final int size = contentLength();
		if (size > 0)
			return new ByteArrayOutputStream(size);
		else
			return new ByteArrayOutputStream();
	}

	/**
	 * 返回http response
	 *
	 * @return string
	 * @throws RestException
	 */
	public String body() throws RestException {
		return body(charset());
	}

	/**
	 * 返回指定charset的http response
	 *
	 * @param charset
	 * @return string
	 * @throws RestException
	 */
	public String body(final String charset) throws RestException {
		try {
			final ByteArrayOutputStream output = byteStream();
			if (getConnection().getResponseCode() != RestConstant.SUCCESS) {
				throw new RestException(RestException.RETRY_CONNECTION);
			}
			copy(buffer(), output);

			return output.toString(RestUtil.getValidCharset(charset));
		} catch (SocketTimeoutException e) {
			throw new RestException(e, RestException.RETRY_CONNECTION);

		} catch (IOException e) {
			throw new RestException(e);
		}
	}

	/**
	 * 设置{@link HttpURLConnection#setUseCaches(boolean)}的值
	 *
	 * @param useCaches
	 * @return RestClient
	 */
	public RestClient useCaches(final boolean useCaches) {
		getConnection().setUseCaches(useCaches);
		return this;
	}

	/**
	 * 设置header中accept的值
	 *
	 * @param value
	 * @return RestClient
	 */
	public RestClient accept(final String value) {
		return header(RestConstant.HEADER_ACCEPT, value);
	}

	/**
	 * 设置header中accept的值为application/json
	 *
	 * @return RestClient
	 */
	public RestClient acceptJson() {
		return accept(RestConstant.CONTENT_TYPE_JSON);
	}

	/**
	 * 设置header中Accept-Encoding的值为gzip
	 *
	 * @return RestClient
	 */
	public RestClient acceptGzipEncoding() {
		return acceptEncoding(RestConstant.ENCODING_GZIP);
	}

	/**
	 * 设置header中Accept-Encoding的值
	 *
	 * @param value
	 * @return RestClient
	 */
	public RestClient acceptEncoding(final String value) {
		return header(RestConstant.HEADER_ACCEPT_ENCODING, value);
	}

	/**
	 * 拷贝inputstream到outputstream
	 *
	 * @param input
	 * @param output
	 * @return RestClient
	 * @throws IOException
	 */
	protected RestClient copy(final InputStream input, final OutputStream output)
			throws IOException {
		return new CloseOperation<RestClient>(input, ignoreCloseExceptions) {

			@Override
			public RestClient run() throws IOException {
				final byte[] buffer = new byte[bufferSize];
				int read;
				while ((read = input.read(buffer)) != -1)
					output.write(buffer, 0, read);
				return RestClient.this;
			}
		}.call();
	}

	/**
	 * 拷贝reader到writer
	 *
	 * @param input
	 * @param output
	 * @return RestClient
	 * @throws IOException
	 */
	protected RestClient copy(final Reader input, final Writer output)
			throws IOException {
		return new CloseOperation<RestClient>(input, ignoreCloseExceptions) {

			@Override
			public RestClient run() throws IOException {
				final char[] buffer = new char[bufferSize];
				int read;
				while ((read = input.read(buffer)) != -1)
					output.write(buffer, 0, read);
				return RestClient.this;
			}
		}.call();
	}

	/**
	 * 从response header中返回Content-Type的charset参数
	 *
	 * @return charset or null if none
	 */
	public String charset() {
		return parameter(RestConstant.HEADER_CONTENT_TYPE,
				RestConstant.PARAM_CHARSET);
	}

	/**
	 * 从response header中返回指定的参数值
	 *
	 * @param headerName
	 * @param paramName
	 * @return parameter value or null if missing
	 */
	public String parameter(final String headerName, final String paramName) {
		return getParam(header(headerName), paramName);
	}

	/**
	 * 从header中获取参数值
	 *
	 * @param value
	 * @param paramName
	 * @return parameter value or null if none
	 */
	protected String getParam(final String value, final String paramName) {
		if (value == null || value.length() == 0)
			return null;

		final int length = value.length();
		int start = value.indexOf(';') + 1;
		if (start == 0 || start == length)
			return null;

		int end = value.indexOf(';', start);
		if (end == -1)
			end = length;

		while (start < end) {
			int nameEnd = value.indexOf('=', start);
			if (nameEnd != -1 && nameEnd < end
					&& paramName.equals(value.substring(start, nameEnd).trim())) {
				String paramValue = value.substring(nameEnd + 1, end).trim();
				int valueLength = paramValue.length();
				if (valueLength != 0)
					if (valueLength > 2 && '"' == paramValue.charAt(0)
							&& '"' == paramValue.charAt(valueLength - 1))
						return paramValue.substring(1, valueLength - 1);
					else
						return paramValue;
			}

			start = end + 1;
			end = value.indexOf(';', start);
			if (end == -1)
				end = length;
		}

		return null;
	}

	/**
	 * 获取response header中的值
	 *
	 * @param name
	 * @return response header
	 * @throws RestException
	 */
	public String header(final String name) throws RestException {
		closeOutputQuietly();
		return getConnection().getHeaderField(name);
	}

	/**
	 * 设置request header中的值
	 *
	 * @param name
	 * @param value
	 * @return RestClient
	 */
	public RestClient header(final String name, final String value) {
		getConnection().setRequestProperty(name, value);
		return this;
	}

	/**
	 * 获取所有的response headers
	 *
	 * @return map of response header names to their value(s)
	 * @throws RestException
	 */
	public Map<String, List<String>> headers() throws RestException {
		closeOutputQuietly();
		return getConnection().getHeaderFields();
	}

	protected RestClient closeOutputQuietly() throws RestException {
		try {
			return closeOutput();
		} catch (IOException e) {
			throw new RestException(e);
		}
	}

	/**
	 * 关闭output
	 *
	 * @return RestClient
	 * @throws RestException
	 * @throws IOException
	 */
	protected RestClient closeOutput() throws IOException {
		if (output == null)
			return this;
		if (multipart)
			output.write(RestConstant.CRLF + "--" + RestConstant.BOUNDARY
					+ "--" + RestConstant.CRLF);
		if (ignoreCloseExceptions)
			try {
				output.close();
			} catch (IOException ignored) {
				// Ignored
			}
		else
			output.close();
		output = null;
		return this;
	}

	/**
	 * 将json的内容写入post body
	 *
	 * @param json
	 * @return RestClient
	 * @throws RestException
	 * @throws IOException
	 */
	public RestClient send(JSONObject json) throws RestException, IOException {
		return send(JSON.toJSONString(json));
	}

	/**
	 * 将jsonString的内容写入post body
	 *
	 * @param jsonString
	 * @return RestClient
	 * @throws RestException
	 * @throws IOException
	 */
	public RestClient send(String jsonString) throws RestException, IOException {
		return send(jsonString.getBytes("UTF-8"));
	}

	/**
	 * 将字节数组写入post body
	 *
	 * @param input
	 * @return RestClient
	 * @throws RestException
	 */
	public RestClient send(final byte[] input) throws RestException {
		return send(new ByteArrayInputStream(input));
	}

	/**
	 * 将InputStream写入post body，并且InputStream流会在发送完毕后关闭
	 *
	 * @param input
	 * @return RestClient
	 * @throws RestException
	 */
	public RestClient send(final InputStream input) throws RestException {
		try {
			openOutput();
			copy(input, output);
		} catch (SocketTimeoutException e) {
			throw new RestException(e, RestException.RETRY_CONNECTION);

		} catch (IOException e) {
			throw new RestException(e);
		}
		return this;
	}

	/**
	 * 表单数据写入request body
	 *
	 * @param values
	 * @return RestClient
	 * @throws RestException
	 */
	public RestClient form(final Map<?, ?> values) throws RestException {
		return form(values, RestConstant.CHARSET_UTF8);
	}

	/**
	 * 表单数据写入request body
	 *
	 * @param values
	 * @param charset
	 * @return RestClient
	 * @throws RestException
	 */
	public RestClient form(final Map<?, ?> values, final String charset)
			throws RestException {
		if (!values.isEmpty())
			for (Entry<?, ?> entry : values.entrySet())
				form(entry, charset);
		return this;
	}

	public RestClient form(final Entry<?, ?> entry) throws RestException {
		return form(entry, RestConstant.CHARSET_UTF8);
	}

	public RestClient form(final Entry<?, ?> entry, final String charset)
			throws RestException {
		return form(entry.getKey(), entry.getValue(), charset);
	}

	/**
	 * 以name/value值键对作为表单数据，写入request body
	 * @param name
	 * @param value
	 * @return
	 * @throws RestException
	 */
	public RestClient form(final Object name, final Object value)
			throws RestException {
		return form(name, value, RestConstant.CHARSET_UTF8);
	}

	public RestClient form(final Object name, final Object value, String charset)
			throws RestException {
		final boolean first = !form;
		if (first) {
			contentType(RestConstant.CONTENT_TYPE_FORM, charset);
			form = true;
		}
		charset = RestUtil.getValidCharset(charset);
		try {
			openOutput();
			if (!first)
				output.write('&');
			output.write(URLEncoder.encode(name.toString(), charset));
			output.write('=');
			if (value != null)
				output.write(URLEncoder.encode(value.toString(), charset));
		} catch (IOException e) {
			throw new RestException(e);
		}
		return this;
	}

	/**
	 * 写multipart请求到request body
	 *
	 * @param name
	 * @param part
	 * @return RestClient
	 */
	public RestClient part(final String name, final String part) {
		return part(name, null, part);
	}

	/**
	 * 写multipart请求到request body
	 *
	 * @param name
	 * @param filename
	 * @param part
	 * @return RestClient
	 * @throws RestException
	 */
	public RestClient part(final String name, final String filename,
						   final String part) throws RestException {
		return part(name, filename, null, part);
	}

	/**
	 * 写multipart请求到request body
	 *
	 * @param name
	 * @param filename
	 * @param contentType
	 *            value of the Content-Type part header
	 * @param part
	 * @return RestClient
	 * @throws RestException
	 */
	public RestClient part(final String name, final String filename,
						   final String contentType, final String part) throws RestException {
		try {
			startPart();
			writePartHeader(name, filename, contentType);
			output.write(part);
		} catch (IOException e) {
			throw new RestException(e);
		}
		return this;
	}

	/**
	 * 写multipart请求到request body
	 *
	 * @param name
	 * @param part
	 * @return RestClient
	 * @throws RestException
	 */
	public RestClient part(final String name, final InputStream part)
			throws RestException {
		return part(name, null, null, part);
	}

	/**
	 * 写multipart请求到request body
	 *
	 * @param name
	 * @param filename
	 * @param contentType
	 *            value of the Content-Type part header
	 * @param part
	 * @return RestClient
	 * @throws RestException
	 */
	public RestClient part(final String name, final String filename,
						   final String contentType, final InputStream part)
			throws RestException {
		try {
			startPart();
			writePartHeader(name, filename, contentType);
			copy(part, output);
		} catch (IOException e) {
			throw new RestException(e);
		}
		return this;
	}

	/**
	 * 开始拼multipart部分
	 *
	 * @return RestClient
	 * @throws IOException
	 */
	protected RestClient startPart() throws IOException {
		if (!multipart) {
			multipart = true;
			contentType(RestConstant.CONTENT_TYPE_MULTIPART).openOutput();
			output.write("--" + RestConstant.BOUNDARY + RestConstant.CRLF);
		} else
			output.write(RestConstant.CRLF + "--" + RestConstant.BOUNDARY + RestConstant.CRLF);
		return this;
	}

	/**
	 * 写part header
	 *
	 * @param name
	 * @param filename
	 * @param contentType
	 * @return RestClient
	 * @throws IOException
	 */
	protected RestClient writePartHeader(final String name,
										 final String filename, final String contentType) throws IOException {
		final StringBuilder partBuffer = new StringBuilder();
		partBuffer.append("form-data; name=\"").append(name);
		if (filename != null)
			partBuffer.append("\"; filename=\"").append(filename);
		partBuffer.append('"');
		partHeader("Content-Disposition", partBuffer.toString());
		if (contentType != null)
			partHeader(RestConstant.HEADER_CONTENT_TYPE, contentType);
		return send(RestConstant.CRLF);
	}

	/**
	 * 写入multipart header到response body
	 *
	 * @param name
	 * @param value
	 * @return RestClient
	 * @throws RestException
	 * @throws IOException
	 */
	public RestClient partHeader(final String name, final String value)
			throws RestException, IOException {
		return send(name).send(": ").send(value).send(RestConstant.CRLF);
	}

	/**
	 * 设置header的Content-Type
	 *
	 * @param value
	 * @return RestClient
	 */
	public RestClient contentType(final String value) {
		return contentType(value,null);
	}

	/**
	 * 设置header的Content-Type
	 *
	 * @param value
	 * @param charset
	 * @return RestClient
	 */
	public RestClient contentType(final String value, final String charset) {
		if (charset != null && charset.length() > 0) {
			final String separator = "; " + RestConstant.PARAM_CHARSET + '=';
			return header(RestConstant.HEADER_CONTENT_TYPE, value + separator
					+ charset);
		} else
			return header(RestConstant.HEADER_CONTENT_TYPE, value);
	}

	/**
	 * 返回response的header中Content-Type的内容
	 *
	 * @return response header value
	 */
	public String contentType() {
		return header(RestConstant.HEADER_CONTENT_TYPE);
	}

	/**
	 * 打开output，在使用post时会用到
	 *
	 * @return RestClient
	 * @throws IOException
	 */
	protected RestClient openOutput() throws IOException {
		if (output != null)
			return this;
		getConnection().setDoOutput(true);
		getConnection().setUseCaches(false);// post 请求不能使用缓存
		final String charset = getParam(
				getConnection().getRequestProperty(RestConstant.HEADER_CONTENT_TYPE),
				RestConstant.PARAM_CHARSET);
		output = new RestOutputStream(getConnection().getOutputStream(), charset,
				bufferSize);
		return this;
	}

	public RestOutputStream getOutput() throws IOException {
		openOutput();
		return output;
	}

	public static void setRetryNum(int retry_num) {
		RestConstant.DEFAULT_RETRY_NUM = retry_num;
	}

	public static void setReadTimeout(int readTimeout) {
		RestConstant.DEFAULT_READ_TIMEOUT = readTimeout;
	}
	
	public static void setConnectionTimeout(int connectionTimeout) {
		RestConstant.DEFAULT_CONNECTION_TIMEOUT = connectionTimeout;
	}
}


<|start_filename|>saf-core/src/main/java/com/safframework/saf/orm/DatabaseHelper.java<|end_filename|>
/**
 * 
 */
package com.safframework.saf.orm;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;

import com.safframework.saf.utils.SAFUtils;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;


/**
 * @author <NAME>
 *
 */
public final class DatabaseHelper extends SQLiteOpenHelper {
	
	public static final String TAG = "DatabaseHelper";
	private final static String DB_NAME = "DB_NAME";
	private final static String DB_VERSION = "DB_VERSION";
	private final static String MIGRATION_PATH = "migrations";
	
	private static boolean doInitialUpgrade = false;
	private DBVersionPrefs dbVersionPrefs;

	public DatabaseHelper(Context context) {
		super(context, getDbName(context), null, getDbVersion(context));
		doInitialUpgrade = copyAttachedDatabase(context);
		dbVersionPrefs = DBVersionPrefs.get(context);
	}

	@Override
	public void onOpen(SQLiteDatabase db) {
		if (doInitialUpgrade) {
			doInitialUpgrade = false;
			onUpgrade(db, dbVersionPrefs.getDBVersion() , db.getVersion());
		} else {
			executePragmas(db);
		}
	};

	@Override
	public void onCreate(SQLiteDatabase db) {
		executePragmas(db);
		executeCreate(db);
		executeMigrations(db, -1, db.getVersion());
	}

	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
		executePragmas(db);
		executeCreate(db);
		executeMigrations(db, oldVersion, newVersion);
	}

	private static String getDbName(Context context) {
		String dbname = SAFUtils.getMetaData(context, DB_NAME);

		if (dbname == null) {
			Log.i(TAG,"DB_NAME not found. Defaulting name to 'saf_db.db'.");
			dbname = "saf_db.db";
		}

		return dbname;
	}

	private static int getDbVersion(Context context) {
		Integer dbversion = SAFUtils.getMetaData(context, DB_VERSION);

		if (dbversion == null || dbversion == 0) {
			Log.i(TAG,"DB_VERSION not found. Defaulting version to 1.");
			dbversion = 1;
		}

		return dbversion;
	}

	public boolean copyAttachedDatabase(Context context) {
		String dbName = getDbName(context);
		final File dbPath = context.getDatabasePath(dbName);

		// If the database already exists, return
		if (dbPath.exists()) {
			return false;
		}

		// Make sure we have a path to the file
		dbPath.getParentFile().mkdirs();

		// Try to copy database file
		try {
			final InputStream inputStream = context.getAssets().open(dbName);
			final OutputStream output = new FileOutputStream(dbPath);

			byte[] buffer = new byte[1024];
			int length;

			while ((length = inputStream.read(buffer)) > 0) {
				output.write(buffer, 0, length);
			}

			output.flush();
			output.close();
			inputStream.close();

			return true;
		}
		catch (IOException e) {
		}

		return false;
	}

	private void executePragmas(SQLiteDatabase db) {
		if (SQLiteUtils.FOREIGN_KEYS_SUPPORTED) {
			db.execSQL("PRAGMA foreign_keys=ON;");
			Log.i(TAG,"Foreign Keys supported. Enabling foreign key features.");
		}
	}

	private void executeCreate(SQLiteDatabase db) {
		db.beginTransaction();
		try {
			for (TableInfo tableInfo : DBManager.getTableInfos()) {
				String sql = SQLiteUtils.createTableDefinition(tableInfo);
				db.execSQL(sql);

				Log.i(TAG,sql);
			}
			db.setTransactionSuccessful();
		}
		finally {
			db.endTransaction();
		}
	}

	private boolean executeMigrations(SQLiteDatabase db, int oldVersion, int newVersion) {
		boolean migrationExecuted = false;
		try {
			final List<String> files = Arrays.asList(DBManager.getContext().getAssets().list(MIGRATION_PATH));
			Collections.sort(files, new NaturalOrderComparator());

			db.beginTransaction();
			try {
				for (String file : files) {
					try {
						int version = Integer.parseInt(file.replace(".sql", ""));

						if (version > oldVersion && version <= newVersion) {
							executeSqlScript(db, file);
							migrationExecuted = true;

							Log.i(TAG,file + " executed succesfully.");
						}
					}
					catch (NumberFormatException e) {
					}
				}
				db.setTransactionSuccessful();
			}
			finally {
				db.endTransaction();
			}
		}
		catch (IOException e) {
		}

		dbVersionPrefs.setDBVersion(db.getVersion());
		dbVersionPrefs.save();
		
		return migrationExecuted;
	}
	
	private void executeSqlScript(SQLiteDatabase db, String file) {
		try {
			final InputStream input = DBManager.getContext().getAssets().open(MIGRATION_PATH + "/" + file);
			final BufferedReader reader = new BufferedReader(new InputStreamReader(input));
			String line = null;

			while ((line = reader.readLine()) != null) {
				db.execSQL(line.replace(";", ""));
			}
		}
		catch (IOException e) {
		}
	}

}


<|start_filename|>saf-core/src/main/java/com/safframework/saf/eventbus/EventHandler.java<|end_filename|>
/*
 */
package com.safframework.saf.eventbus;

import java.lang.reflect.InvocationTargetException;

/**
 *
 * @author <NAME>
 * 
 */
class EventHandler {

  final Object target;

  final SubscriberMethod subscriberMethod;

  private final int hashCode;

  private boolean valid = true;

  EventHandler(Object target, SubscriberMethod method) {
    if (target == null) {
      throw new NullPointerException("EventHandler target cannot be null.");
    }
    if (method == null) {
      throw new NullPointerException("EventHandler method cannot be null.");
    }

    this.target = target;
    this.subscriberMethod = method;
    method.method.setAccessible(true);

    final int prime = 31;
    hashCode = (prime + method.hashCode()) * prime + target.hashCode();
  }

  public boolean isValid() {
    return valid;
  }

  public void invalidate() {
    valid = false;
  }

  /**
   * 处理event
   * @param event
   * @throws InvocationTargetException
   */
  public void handleEvent(Object event) throws InvocationTargetException {
    if (!valid) {
      throw new IllegalStateException(toString() + " has been invalidated and can no longer handle events.");
    }
    try {
      subscriberMethod.method.invoke(target, event);
    } catch (IllegalAccessException e) {
      throw new AssertionError(e);
    } catch (InvocationTargetException e) {
      if (e.getCause() instanceof Error) {
        throw (Error) e.getCause();
      }
      throw e;
    }
  }

  @Override 
  public String toString() {
    return "[EventHandler " + subscriberMethod + "]";
  }

  @Override 
  public int hashCode() {
    return hashCode;
  }

  @Override 
  public boolean equals(Object obj) {
    if (this == obj) {
      return true;
    }

    if (obj == null) {
      return false;
    }

    if (getClass() != obj.getClass()) {
      return false;
    }

    final EventHandler other = (EventHandler) obj;

    return subscriberMethod.equals(other.subscriberMethod) && target == other.target;
  }

}


<|start_filename|>app/src/main/java/com/test/saf/app/BaseActivity.java<|end_filename|>
package com.test.saf.app;

import android.os.Bundle;

import com.safframework.injectview.Injector;
import com.safframework.saf.app.SAFAppCompatActivity;


/**
 * Created by <NAME> on 15/11/19.
 */
public class BaseActivity extends SAFAppCompatActivity {

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    public void setContentView(int layoutResID) {
        super.setContentView(layoutResID);
        Injector.injectInto(this);
    }
}


<|start_filename|>saf-core/src/main/java/com/safframework/saf/utils/SAFUtils.java<|end_filename|>
/**
 * 
 */
package com.safframework.saf.utils;

import android.Manifest;
import android.annotation.TargetApi;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningAppProcessInfo;
import android.app.ActivityManager.RunningServiceInfo;
import android.app.AppOpsManager;
import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.location.Location;
import android.location.LocationManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.os.Environment;
import android.os.Looper;
import android.os.StatFs;
import android.provider.Settings;
import android.support.v4.app.ActivityCompat;
import android.telephony.TelephonyManager;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.Toast;

import com.alibaba.fastjson.JSON;
import com.safframework.tony.common.reflect.Reflect;
import com.safframework.tony.common.utils.Preconditions;

import java.io.DataOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;


/**
 * SAF的工具类
 * @author <NAME>
 *
 */
public class SAFUtils {

	public static boolean isFroyoOrHigher() {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO;
    }
	
	public static boolean isGingerbreadOrHigher() {
		return Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD;
	}
	
	public static boolean isHoneycombOrHigher() {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB;
    }
	
	public static boolean isICSOrHigher() {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH;
    }
	
	public static boolean isJellyBeanOrHigher() {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
    }
	
	@TargetApi(17)
	public static boolean isJellyBeanMR1OrHigher() {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1;
    }
	
	@TargetApi(18)
	public static boolean isJellyBeanMR2OrHigher() {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2;
    }
	
	@TargetApi(19)
	public static boolean isKitkatOrHigher() {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
    }
	
	@TargetApi(20)
	public static boolean isKitkatWatchOrHigher() {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH;
    }
	
	@TargetApi(21)
	public static boolean isLOrHigher() {
		return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
	}

	/**
	 * api level 22是android 5.1
	 * @return
     */
	@TargetApi(22)
	public static boolean isLMR1OrHigher() {
		return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1;
	}

	@TargetApi(23)
	public static boolean isMOrHigher() {
		return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
	}

	public static boolean isWiFiActive(Context context) { 
		WifiManager wm=null;
		try{
			wm = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
		}catch(Exception e){
			e.printStackTrace();
		}

		return wm!=null && wm.isWifiEnabled();
    }
	
	/**
	 * 安装apk
	 * @param fileName apk文件的绝对路径
	 * @param context
	 */
	public static void installAPK(String fileName, Context context) {
		Intent intent = new Intent(Intent.ACTION_VIEW);
		intent.setDataAndType(Uri.fromFile(new File(fileName)), "application/vnd.android.package-archive");
		context.startActivity(intent);
	}
	
	/**
	 * 判断某个应用当前是否正在运行
	 * @param context
	 * @param packageName
	 * @return
	 */
	public static boolean isAppRunning(Context context, String packageName) {
		if (packageName == null)
			return false;

		ActivityManager activityManager = (ActivityManager) context
				.getSystemService(Context.ACTIVITY_SERVICE);
		List<RunningAppProcessInfo> appProcesses = activityManager
				.getRunningAppProcesses();
		if (appProcesses == null)
			return false;
		for (RunningAppProcessInfo appProcess : appProcesses) {
			if (appProcess.processName.equals(packageName)) {
				return true;
			}
		}
		return false;
	}
	
	/**
	 * 判断服务是否运行
	 * @param mContext
	 * @param serviceName
	 * @return true为运行，false为不在运行
	 */
	public boolean isServiceRunning(Context mContext, String serviceName) {
		ActivityManager myManager = (ActivityManager) mContext
				.getSystemService(Context.ACTIVITY_SERVICE);
		ArrayList<RunningServiceInfo> runningService = (ArrayList<RunningServiceInfo>) myManager
				.getRunningServices(30);
		for (int i = 0; i < runningService.size(); i++) {
			String serName  = runningService.get(i).service.getClassName().toString();
			if (serName.equals(serviceName)) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 判断app是否处于前台,只使用了android 5.0以后
	 * 通过使用UsageStatsManager获取，此方法是android5.0A之后提供的API
	 * 必须：
	 * 1. 此方法只在android5.0以上有效
	 * 2. AndroidManifest中加入此权限<uses-permission xmlns:tools="http://schemas.android.com/tools" android:name="android.permission.PACKAGE_USAGE_STATS"
	 * tools:ignore="ProtectedPermissions" />
	 * 3. 打开手机设置，点击安全-高级，在有权查看使用情况的应用中，为这个App打上勾
	 *
	 * @param context     上下文参数
	 * @param packageName 需要检查是否位于栈顶的App的包名
	 * @return
	 */
	@TargetApi(21)
	public static boolean queryUsageStats(Context context, String packageName) {
		class RecentUseComparator implements Comparator<UsageStats> {
			@Override
			public int compare(UsageStats lhs, UsageStats rhs) {
				return (lhs.getLastTimeUsed() > rhs.getLastTimeUsed()) ? -1 : (lhs.getLastTimeUsed() == rhs.getLastTimeUsed()) ? 0 : 1;
			}
		}
		RecentUseComparator mRecentComp = new RecentUseComparator();
		long ts = System.currentTimeMillis();
		UsageStatsManager mUsageStatsManager = (UsageStatsManager) context.getSystemService("usagestats");
		List<UsageStats> usageStats = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_BEST, ts - 1000 * 10, ts);
		if (Preconditions.isBlank(usageStats)) {
			if (!havaPermissionForTest(context)) {
				Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
				intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
				context.startActivity(intent);
				Toast.makeText(context, "权限不够\n请打开手机设置，点击安全-高级，在有权查看使用情况的应用中，为这个App打上勾", Toast.LENGTH_SHORT).show();
			}
			return false;
		}
		Collections.sort(usageStats, mRecentComp);
		String currentTopPackage = usageStats.get(0).getPackageName();
		return currentTopPackage.equals(packageName);
	}

	/**
	 * 判断是否有用权限
	 *
	 * @param context 上下文参数
	 */
	@TargetApi(19)
	private static boolean havaPermissionForTest(Context context) {
		try {
			PackageManager packageManager = context.getPackageManager();
			ApplicationInfo applicationInfo = packageManager.getApplicationInfo(context.getPackageName(), 0);
			AppOpsManager appOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
			int mode = appOpsManager.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS, applicationInfo.uid, applicationInfo.packageName);
			return (mode == AppOpsManager.MODE_ALLOWED);
		} catch (PackageManager.NameNotFoundException e) {
			return true;
		}
	}
	
	/**
	 * 获取手机网络类型名称
	 * @param networkType
	 * @param mnc Mobile NetworkCode，移动网络码，共2位
	 * @return
	 */
	public static String getNetWorkName(int networkType,String mnc) {
		if (networkType == TelephonyManager.NETWORK_TYPE_UNKNOWN) {
			return "Network type is unknown";
		} else if (networkType == TelephonyManager.NETWORK_TYPE_CDMA) {
			return "电信2G";
		} else if (networkType == TelephonyManager.NETWORK_TYPE_EVDO_0) {
			return "电信3G";
		} else if (networkType == TelephonyManager.NETWORK_TYPE_GPRS || networkType == TelephonyManager.NETWORK_TYPE_EDGE) {
			if ("00".equals(mnc) || "02".equals(mnc)) {
				return "移动2G";
			} else if ("01".equals(mnc)) {
				return "联通2G";
			}
		} else if (networkType == TelephonyManager.NETWORK_TYPE_UMTS || networkType == TelephonyManager.NETWORK_TYPE_HSDPA) {
			return "联通3G";
		}
		return null;
	}
	
	/**
	 * 检测网络状态
	 * @param context
	 * @return
	 */
	public static boolean checkNetworkStatus(Context context){
		boolean resp = false;
		final ConnectivityManager connMgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
		NetworkInfo activeNetInfo = connMgr.getActiveNetworkInfo();   
		if (activeNetInfo != null && activeNetInfo.isAvailable()) {
			resp = true;
		}
		return resp;
	}
	
	/**
	 * 检测gps状态
	 * @param context
	 * @return
	 */
	public static boolean checkGPSStatus(Context context){
		boolean resp = false;
		LocationManager lm = (LocationManager)context.getSystemService(Context.LOCATION_SERVICE);  
        if(lm.isProviderEnabled(LocationManager.GPS_PROVIDER)){  
        	resp = true;            
        }  
        return resp;
	}
	
	/**
	 * 生成app日志tag
	 * @param cls
	 * @return
	 */
	public static String makeLogTag(Class<?> cls) {
		return cls.getSimpleName();
	}
	
	/**
	 * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
	 * @param context
	 * @param dpValue
	 * @return
	 */
	public static int dip2px(Context context, float dpValue) {
	  final float scale = context.getResources().getDisplayMetrics().density;
	  return (int) (dpValue * scale + 0.5f);
	}
	
	/**
	 * 根据手机的分辨率从  px(像素) 转成为dp
	 * @param context
	 * @param pxValue
	 * @return
	 */
	public static int px2dip(Context context, float pxValue) {
		final float scale = context.getResources().getDisplayMetrics().density;
	    return (int)(0.5F + pxValue / scale);
	}
	
	/**
	 * 根据手机的分辨率从 sp 的单位 转成为 px(像素)
	 * @param context
	 * @param spValue
	 * @return
	 */
	public static int sp2px(Context context, float spValue) {
		final float scale = context.getResources().getDisplayMetrics().density;
		return (int) (spValue * scale + 0.5f);
	}
	
	/**
	 * 根据手机的分辨率从  px(像素) 转成为sp
	 * @param context
	 * @param pxValue
	 * @return
	 */
	public static int px2sp(Context context, float pxValue) {
		final float scale = context.getResources().getDisplayMetrics().density;
		return (int) (pxValue / scale + 0.5f);
	}
	
    /**
     * 判断谷歌地图是否可用,某些国行的手机不支持谷歌地图的服务
     * @return
     */
	public static boolean googleMapAvailable() {
		boolean available = false;
		try{
			Class.forName("com.google.android.maps.MapActivity");
			available = true;
		} catch (Exception e)  {
		}
		return available;
	}
	
	/**
	 * 从Assets中读取文件
	 * @param context
	 * @param fileName
	 * @return
	 * @throws FileNotFoundException
	 */
	public static InputStream getFromAssets(Context context,String fileName)
			throws FileNotFoundException {
		InputStream inputStream = null;
		try {
			inputStream = context.getResources().getAssets().open(fileName);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return inputStream;
	}
	
	/**
	 * 将对象以json格式打印出来
	 * @param obj
	 * @return
	 */
	public static String printObject(Object obj) {
		return obj!=null?JSON.toJSONString(obj):"{}";
	}

	/**
	 * 对文件设置root权限
	 * @param filePath
	 * @return
	 */
	public static boolean upgradeRootPermission(String filePath) {
	    Process process = null;
	    DataOutputStream os = null;
	    try {
	        String cmd="chmod 777 " + filePath;
	        process = Runtime.getRuntime().exec("su"); //切换到root帐号
	        os = new DataOutputStream(process.getOutputStream());
	        os.writeBytes(cmd + "\n");
	        os.writeBytes("exit\n");
	        os.flush();
	        process.waitFor();
	    } catch (Exception e) {
	        return false;
	    } finally {
	        try {
	            if (os != null) {
	                os.close();
	            }
	            process.destroy();
	        } catch (Exception e) {
	        }
	    }
	    return true;
	}
	
	/**
	 * 判断当前线程是否为ui线程
	 * @return
	 */
	public static boolean isUIThread(){
    	long uiId = Looper.getMainLooper().getThread().getId();
    	long cId = Thread.currentThread().getId();
    	return uiId == cId;	
    }

	/**
	 * 关闭虚拟键盘
	 * @param context
	 * @param view
	 */
	public static void hideSoftInputFromWindow(Context context,View view) {
		InputMethodManager imm = (InputMethodManager)context.getSystemService(Context.INPUT_METHOD_SERVICE);
		imm.hideSoftInputFromWindow(view.getWindowToken(), 0);  
	}
	
	/**
	 * 关闭虚拟键盘
	 * @param context
	 * @param views
	 */
	public static void hideSoftInputFromWindow(Context context,View... views) {
		InputMethodManager imm = (InputMethodManager)context.getSystemService(Context.INPUT_METHOD_SERVICE);
		if (views!=null && views.length>0) {
			for (View view:views) {
				imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
			}
		}
	}
	
	/**
	 * 获取AndroidManifest.xml中<meta-data>元素的值
	 * @param context
	 * @param name
	 * @return
	 */
	public static <T> T getMetaData(Context context, String name) {
		try {
			final ApplicationInfo ai = context.getPackageManager().getApplicationInfo(context.getPackageName(),
					PackageManager.GET_META_DATA);

			if (ai.metaData != null) {
				return (T) ai.metaData.get(name);
			}
		}
		catch (Exception e) {
			System.out.print("Couldn't find meta-data: " + name);
		}

		return null;
	}
	
	/**
	 * 判断经纬度是否在中国
	 * @param mLocation
	 * @return
	 */
	public static boolean positionInChina(Location mLocation){

		return mLocation.getLatitude()>18.167 && mLocation.getLatitude()<53.55
				&& mLocation.getLongitude()>73.667 && mLocation.getLongitude()<135.033;
	}

	/**
	 * 判断经纬度是否在中国
	 * @param latitude
	 * @param longitude
     * @return
     */
	public static boolean positionInChina(double latitude,double longitude){

		return latitude>18.167 && latitude<53.55 && longitude>73.667 && longitude<135.033;
	}

	/**
	 * 判断是否存在sd卡
	 * @return
	 */
	public static boolean hasSdcard() {
		
		String status = Environment.getExternalStorageState();
		return Environment.MEDIA_MOUNTED.equals(status);
	}
	
	/**
	 * 获取手机可用的cpu数
	 * @return
	 */
	public static int getAvailableProcessors() {
		return Runtime.getRuntime().availableProcessors();
	}

	/**
	 * 获取全局的context，也就是Application Context
	 * @return
     */
	@TargetApi(14)
	public static Context getContext() {
		return Reflect.on("android.app.ActivityThread").call("currentApplication").get();
	}

	@TargetApi(23)
	public static boolean verifyStoragePermissions(Context context) {
		// Check if we have write permission
		int permission = ActivityCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE);

//		if (permission != PackageManager.PERMISSION_GRANTED) {
//			// We don't have permission so prompt the user
//			ActivityCompat.requestPermissions(
//					activity,
//					PERMISSIONS_STORAGE,
//					REQUEST_EXTERNAL_STORAGE
//			);
//		}

		return permission == PackageManager.PERMISSION_GRANTED;
	}

	/**
	 * 检查权限是否开启
	 *
	 * @param permission
	 * @return true or false
	 */
	public static boolean checkPermissions(Context context, String permission) {

		Context mContext = null;
		if (context==null) {
			if (SAFUtils.isICSOrHigher()) {
				mContext = getContext();
			} else {
				return false;
			}
		} else {
			mContext = context;
		}

		PackageManager localPackageManager = mContext.getApplicationContext().getPackageManager();
		return localPackageManager.checkPermission(permission, mContext.getApplicationContext().getPackageName()) == PackageManager.PERMISSION_GRANTED;
	}

	/**
	 * 获取当前进程的名称
	 * @param context
	 * @return
     */
	public static String getProcessName(Context context) {
		ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
		List<RunningAppProcessInfo> runningApps = am.getRunningAppProcesses();

		if (Preconditions.isNotBlank(runningApps)) {
			int myPid = android.os.Process.myPid();
			for (RunningAppProcessInfo proInfo : runningApps) {
				if (proInfo!=null && proInfo.pid == myPid) {
					return proInfo.processName;
				}
			}
		}

		return null;
	}

	/**
	 * 获取SD 卡内存
	 * @return
	 */
	public static long getAvailableSD() {
		if (!hasSdcard())
			return 0;

		String storageDirectory = Environment.getExternalStorageDirectory().toString();

		try {
			StatFs stat = new StatFs(storageDirectory);
			long avaliableSize = ((long) stat.getAvailableBlocks() * (long) stat
					.getBlockSize());
			return avaliableSize;
		} catch (RuntimeException ex) {
			return 0;
		}
	}

}


<|start_filename|>saf-queue/src/main/java/com/safframework/queue/OperationRunnable.java<|end_filename|>
package com.safframework.queue;

import android.os.Handler;
import android.os.Looper;

import java.lang.ref.WeakReference;

/**
 * Created by tony on 2017/4/15.
 */

public class OperationRunnable implements Runnable {

    private Operation operation = null;
    private Type type = Type.NORMAL;
    private Object token = null;
    private long time = 0;
    private WeakReference<Queue> owner;
    private static final Handler handler = new Handler(Looper.getMainLooper());

    protected OperationRunnable(Queue queue, Operation operation) {
        this(queue, operation, Type.NORMAL, null, 0);
    }

    public static void runOnUiThread(Runnable runnable) {
        handler.post(runnable);
    }

    public static void runOnUiThreadAfterDelay(Runnable runnable, long delayTimeMillis) {
        handler.postDelayed(runnable, delayTimeMillis);
    }

    public static void removeOperationOnUiThread(Runnable runnable) {
        handler.removeCallbacks(runnable);
    }

    public static void sleep(long sleepTimeMillis) {
        try {
            Thread.sleep(sleepTimeMillis);
        } catch (Exception e) {
        }
    }

    protected enum Type {

        NORMAL,
        ATFIRST,
        ATTIME,
        ATTIME_WITH_TOKEN,
        DELAY,
    }

    protected OperationRunnable(Queue queue, Operation operation, Type type, Object token, long time) {
        this.owner = new WeakReference<Queue>(queue);
        this.operation = operation;
        this.type = type;
        this.token = token;
        this.time = time;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        OperationRunnable that = (OperationRunnable) o;

        if (!operation.equals(that.operation)) return false;
        return token != null ? token.equals(that.token) : that.token == null;

    }

    @Override
    public int hashCode() {
        int result = operation.hashCode();
        result = 31 * result + (token != null ? token.hashCode() : 0);
        return result;
    }

    @Override
    public void run() {

        Queue queue = owner.get();
        if(queue != null && queue.isActivated()) {
            operation.run(queue, queue.getBundle());
        }

    }

    protected void queueing(Handler handler) {
        switch (type) {
            case NORMAL:
                handler.post(this);
                break;
            case ATFIRST:
                handler.postAtFrontOfQueue(this);
                break;
            case ATTIME:
                handler.postAtTime(this, time);
                break;
            case ATTIME_WITH_TOKEN:
                handler.postAtTime(this, token, time);
                break;
            case DELAY:
                handler.postDelayed(this, time);
                break;
            default:
                handler.post(this);
                break;
        }
    }
}


<|start_filename|>app/src/main/java/com/test/saf/domain/HttpResponse.java<|end_filename|>
package com.test.saf.domain;

import com.safframework.saf.parcel.AbstractParcelable;

/**
 * Created by <NAME> on 2016/12/1.
 */

public class HttpResponse extends AbstractParcelable {

    public boolean status;
    public int total;

}


<|start_filename|>saf-core/src/main/java/com/safframework/saf/config/SAFConstant.java<|end_filename|>
/**
 * 
 */
package com.safframework.saf.config;

/**
 * @author <NAME>
 *
 */
public class SAFConstant {

	//os类型
	public static final String SPECIAL_IMEI="000000000000000";
	
	/**
	 * 需要使用ImageLoader组件时,可以设置default_img_id的值,表示全局的默认图片
	 */
	public static int default_img_id;
	
	/** app存储目录/文件  可根据app的名称覆盖 默认使用saf作为文件名**/
	public static final String DIR = "/saf";
	public static final String CACHE_DIR = DIR + "/images";
}


<|start_filename|>app/src/main/java/com/test/saf/config/Config.java<|end_filename|>
package com.test.saf.config;

/**
 * Created by <NAME> on 2016/12/5.
 */

public class Config {

    public static float density; // 屏幕密度
    public static int height; // 屏幕高度
    public static int width; // 屏幕宽度
}


<|start_filename|>saf-core/src/main/java/com/safframework/saf/recyclerview/OnItemClickListener.java<|end_filename|>
package com.safframework.saf.recyclerview;

import android.support.v4.view.GestureDetectorCompat;
import android.support.v7.widget.RecyclerView;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;

/**
 * Created by <NAME> on 2016/11/27.
 */

public abstract class OnItemClickListener implements RecyclerView.OnItemTouchListener {

    private GestureDetectorCompat mGestureDetector;
    private RecyclerView recyclerView;

    public OnItemClickListener(RecyclerView view) {
        this.recyclerView = view;
        mGestureDetector = new GestureDetectorCompat(recyclerView.getContext(), new ItemTouchHelperGestureListener());
    }

    @Override
    public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
        mGestureDetector.onTouchEvent(e);
        return false;
    }

    @Override
    public void onTouchEvent(RecyclerView rv, MotionEvent e) {
        mGestureDetector.onTouchEvent(e);
    }

    @Override
    public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {

    }

    public abstract void onItemClick(RecyclerView.ViewHolder holder, int position);

    public abstract void onLongPress(RecyclerView.ViewHolder holder, int position);

    class ItemTouchHelperGestureListener extends GestureDetector.SimpleOnGestureListener {
        @Override
        public boolean onSingleTapUp(MotionEvent e) {
            View child = recyclerView.findChildViewUnder(e.getX(), e.getY());
            if (child != null) {
                int position = recyclerView.indexOfChild(child);
                onItemClick(recyclerView.getChildViewHolder(child), position);
            }
            return true;
        }

        @Override
        public void onLongPress(MotionEvent e) {
            View child = recyclerView.findChildViewUnder(e.getX(), e.getY());
            if (child != null) {
                int position = recyclerView.indexOfChild(child);
                OnItemClickListener.this.onLongPress(recyclerView.getChildViewHolder(child), position);
            }
        }
    }
}


<|start_filename|>saf-core/src/main/java/com/safframework/saf/utils/ToastUtils.java<|end_filename|>
/**
 * 
 */
package com.safframework.saf.utils;

import android.app.Activity;
import android.content.Context;
import android.text.TextUtils;
import android.widget.Toast;

import java.text.MessageFormat;

import static android.widget.Toast.LENGTH_LONG;
import static android.widget.Toast.LENGTH_SHORT;

/**
 * 在不是主线程的时候无法直接使用toast，但是可以使用该工具类实现toast。当然在主线程的时候也可以使用该类。
 * @author <NAME>
 * 
 */
public class ToastUtils {
	
	/**
	 * 
	 * @param activity
	 * @param resId string资源id
	 * @param duration
	 */
	private static void showToast(final Activity activity, final int resId,
			final int duration) {
		if (activity == null)
			return;

		final Context context = activity.getApplication();
		activity.runOnUiThread(new Runnable() {

			public void run() {
				Toast.makeText(context, resId, duration).show();
			}
		});
	}

	/**
	 * 
	 * @param activity
	 * @param message toast的内容
	 * @param duration
	 */
	private static void showToast(final Activity activity, final String message,
			final int duration) {
		if (activity == null)
			return;
		if (TextUtils.isEmpty(message))
			return;

		final Context context = activity.getApplication();
		activity.runOnUiThread(new Runnable() {

			public void run() {
				Toast.makeText(context, message, duration).show();
			}
		});
	}
	
	public static void showToast(final Context context, final int resId,
			final int duration) {
		if (context == null)
			return;

		((Activity) context).runOnUiThread(new Runnable() {
			public void run() {
				Toast.makeText(context.getApplicationContext(), resId,
						duration).show();
			}

		});
	}
	
	public static void showToast(final Context context, final String message,
			final int duration) {
		if (context == null)
			return;

		((Activity) context).runOnUiThread(new Runnable() {
			public void run() {
				Toast.makeText(context.getApplicationContext(), message,
						duration).show();
			}

		});

	}

	public static void showLong(final Activity activity, int resId) {
		showToast(activity, resId, LENGTH_LONG);
	}

	public static void showLong(final Activity activity, final String message) {
		showToast(activity, message, LENGTH_LONG);
	}

	public static void showLong(final Activity activity, final String message,
			final Object... args) {
		String formatted = MessageFormat.format(message, args);
		showToast(activity, formatted, LENGTH_LONG);
	}

	public static void showLong(final Activity activity, final int resId,
			final Object... args) {
		if (activity == null)
			return;

		String message = activity.getString(resId);
		showLong(activity, message, args);
	}
	
	public static void showLong(Context context, int resId) {
		showToast(context, resId, LENGTH_LONG);
	}

	public static void showLong(Context context, String message) {
		showToast(context, message, LENGTH_LONG);
	}

	public static void showShort(final Activity activity, final int resId) {
		showToast(activity, resId, LENGTH_SHORT);
	}

	public static void showShort(final Activity activity, final String message) {
		showToast(activity, message, LENGTH_SHORT);
	}

	public static void showShort(final Activity activity, final String message,
			final Object... args) {
		String formatted = MessageFormat.format(message, args);
		showToast(activity, formatted, LENGTH_SHORT);
	}

	public static void showShort(final Activity activity, final int resId,
			final Object... args) {
		if (activity == null)
			return;

		String message = activity.getString(resId);
		showShort(activity, message, args);
	}
	
	public static void showShort(Context context, int resId) {
		showToast(context, resId, LENGTH_SHORT);
	}

	public static void showShort(Context context, String message) {
		showToast(context, message, LENGTH_SHORT);
	}
}


<|start_filename|>saf-core/src/main/java/com/safframework/saf/orm/SQLiteUtils.java<|end_filename|>
/**
 * 
 */
package com.safframework.saf.orm;

import android.database.Cursor;
import android.os.Build;
import android.text.TextUtils;
import android.util.Log;

import com.safframework.saf.orm.annotation.Column;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * @author <NAME>
 *
 */
public class SQLiteUtils {

	public static final String TAG = "SQLiteUtils";
	public static final boolean FOREIGN_KEYS_SUPPORTED = Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO;
	
	public enum SQLiteType {
		INTEGER, REAL, TEXT, BLOB
	}
	
	@SuppressWarnings("serial")
	private static final HashMap<Class<?>, SQLiteType> TYPE_MAP = new HashMap<Class<?>, SQLiteType>() {
		{
			put(byte.class, SQLiteType.INTEGER);
			put(short.class, SQLiteType.INTEGER);
			put(int.class, SQLiteType.INTEGER);
			put(long.class, SQLiteType.INTEGER);
			put(boolean.class, SQLiteType.INTEGER);
			put(Byte.class, SQLiteType.INTEGER);
			put(Short.class, SQLiteType.INTEGER);
			put(Integer.class, SQLiteType.INTEGER);
			put(Long.class, SQLiteType.INTEGER);
			put(Boolean.class, SQLiteType.INTEGER);
			put(float.class, SQLiteType.REAL);
			put(double.class, SQLiteType.REAL);
			put(Float.class, SQLiteType.REAL);
			put(Double.class, SQLiteType.REAL);
			put(char.class, SQLiteType.TEXT);
			put(Character.class, SQLiteType.TEXT);
			put(String.class, SQLiteType.TEXT);
			put(byte[].class, SQLiteType.BLOB);
			put(Byte[].class, SQLiteType.BLOB);
		}
	};
	
	// Database creation
	public static String createTableDefinition(TableInfo tableInfo) {
		final ArrayList<String> definitions = new ArrayList<String>();

		for (Field field : tableInfo.getFields()) {
			String definition = createColumnDefinition(tableInfo, field);
			if (!TextUtils.isEmpty(definition)) {
				definitions.add(definition);
			}
		}

		return String.format("CREATE TABLE IF NOT EXISTS %s (%s);", tableInfo.getTableName(),
				TextUtils.join(", ", definitions));
	}
	
	@SuppressWarnings("unchecked")
	public static String createColumnDefinition(TableInfo tableInfo, Field field) {
		StringBuilder definition = new StringBuilder();

		Class<?> type = field.getType();
		final String name = tableInfo.getColumnName(field);
		final Column column = field.getAnnotation(Column.class);

		if (TYPE_MAP.containsKey(type)) {
			definition.append(name);
			definition.append(" ");
			definition.append(TYPE_MAP.get(type).toString());
		}

		if (!TextUtils.isEmpty(definition)) {
			if (column.length() > -1) {
				definition.append("(");
				definition.append(column.length());
				definition.append(")");
			}

			if (name.equals("Id")) {
				definition.append(" PRIMARY KEY AUTOINCREMENT");
			}
			
			if (column.notNull()) {
				definition.append(" NOT NULL");
			}
		}
		else {
			Log.i(TAG,"No type mapping for: " + type.toString());
		}

		return definition.toString();
	}
	
	public static void execSql(String sql) {
		DBManager.openDatabase().execSQL(sql);
	}

	public static <T extends DBDomain> T rawQuerySingle(Class<? extends DBDomain> type, String sql, String[] selectionArgs) {
		List<T> entities = rawQuery(type, sql, selectionArgs);

		if (entities.size() > 0) {
			return entities.get(0);
		}

		return null;
	}

	public static <T extends DBDomain> List<T> rawQuery(Class<? extends DBDomain> type, String sql, String[] selectionArgs) {
		Cursor cursor = DBManager.openDatabase().rawQuery(sql, selectionArgs);
		List<T> entities = parseCursor(type, cursor);
		cursor.close();

		return entities;
	}

	private static <T extends DBDomain> List<T> parseCursor(Class<? extends DBDomain> type,
			Cursor cursor) {
		final List<T> entities = new ArrayList<T>();

		try {
			Constructor<?> entityConstructor = type.getConstructor();

			if (cursor.moveToFirst()) {
				do {
					DBDomain entity = DBManager.getEntity(type, cursor.getLong(cursor.getColumnIndex("Id")));
					
					if(entity == null) {
						entity = (T) entityConstructor.newInstance();
					}

					entity.loadFromCursor(cursor);
					entities.add((T) entity);
				}
				while (cursor.moveToNext());
			}

		}
		catch (Exception e) {
		}

		return entities;
	}
}


<|start_filename|>saf-core/src/main/java/com/safframework/saf/async/RetryWithDelay.java<|end_filename|>
package com.safframework.saf.async;

import com.safframework.log.L;

import java.util.concurrent.TimeUnit;

import io.reactivex.Flowable;
import io.reactivex.annotations.NonNull;
import io.reactivex.functions.Function;


/**
 * Created by <NAME> on 2016/12/12.
 */

public class RetryWithDelay implements
        Function<Flowable<? extends Throwable>, Flowable<?>> {

    private final int maxRetries;
    private final int retryDelayMillis;
    private int retryCount;

    public RetryWithDelay(int maxRetries, int retryDelayMillis) {
        this.maxRetries = maxRetries;
        this.retryDelayMillis = retryDelayMillis;
    }

    @Override
    public Flowable<?> apply(@NonNull Flowable<? extends Throwable> flowable) throws Exception {
        return flowable
                .flatMap(new Function<Throwable, Flowable<?>>() {
                    @Override
                    public Flowable<?> apply(@NonNull Throwable throwable) throws
                            Exception {
                        if (++retryCount <= maxRetries) {
                            // When this Observable calls onNext, the original Observable will be
                            // retried (i.e. re-subscribed).
                            L.i("get error, it will try after " + retryDelayMillis
                                    + " millisecond, retry count " + retryCount);
                            return Flowable.timer(retryDelayMillis,
                                    TimeUnit.MILLISECONDS);
                        }
                        // Max retries hit. Just pass the error along.
                        return Flowable.error(throwable);
                    }
                });
    }
}


<|start_filename|>saf-core/src/main/java/com/safframework/saf/rest/RestException.java<|end_filename|>
/**
 * 
 */
package com.safframework.saf.rest;

import java.io.IOException;

/**
 * @author <NAME>
 *
 */
public class RestException extends RuntimeException{

	private static final long serialVersionUID = 8520390726356829268L;
	public static final String RETRY_CONNECTION = "retry";
	private String errorCode;

	protected RestException(IOException cause) {
		super(cause);
	}
	
	protected RestException(IOException cause,String code) {
		super(cause);
		this.errorCode = code;
	}

	protected RestException(String code) {
		super();
		this.errorCode = code;
	}
	
	public String getErrorCode() {
		return errorCode;
	}

	@Override
	public IOException getCause() {
		return (IOException) super.getCause();
	}
}


<|start_filename|>app/src/main/java/com/test/saf/fragment/GeneralAnnotationFragment.java<|end_filename|>
package com.test.saf.fragment;

import android.os.Bundle;
import android.support.v7.widget.DividerItemDecoration;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.safframework.injectview.Injector;
import com.safframework.injectview.annotations.InjectView;
import com.safframework.router.Router;
import com.safframework.saf.recyclerview.OnItemClickListener;
import com.test.saf.R;
import com.test.saf.adapter.AnnotationAdapter;
import com.test.saf.app.BaseFragment;

import java.util.ArrayList;
import java.util.List;


/**
 * Created by tony on 2016/11/20.
 */

public class GeneralAnnotationFragment extends BaseFragment {

    @InjectView(R.id.recyclerview)
    RecyclerView recyclerview;

    private List<String> data = new ArrayList<>();

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.fragment_general_annotation, container, false);
        Injector.injectInto(this, v);
        initData();

        return v;
    }

    private void initData() {

        data.clear();
        data.add("@Async");
        data.add("@Cacheable");
        data.add("@LogMethod");
        data.add("@Prefs");
        data.add("@Safe");
        data.add("@Trace");

        recyclerview.setLayoutManager(new LinearLayoutManager(mContext));//这里用线性显示 类似于listview
        recyclerview.setAdapter(new AnnotationAdapter(data));
        recyclerview.getLayoutManager().setAutoMeasureEnabled(true);
        recyclerview.setNestedScrollingEnabled(false);
        recyclerview.setHasFixedSize(false);
        recyclerview.addOnItemTouchListener(new OnItemClickListener(recyclerview){

            @Override
            public void onItemClick(RecyclerView.ViewHolder holder, int position) {
                String annotationName = (String) data.get(position);
                Router.getInstance().open("annotationName/"+annotationName);
            }

            @Override
            public void onLongPress(RecyclerView.ViewHolder holder, int position) {
            }
        });
        recyclerview.addItemDecoration(new DividerItemDecoration(
                mContext, DividerItemDecoration.VERTICAL));
    }
}


<|start_filename|>saf-core/src/main/java/com/safframework/saf/app/SAFApp.java<|end_filename|>
/**
 * 
 */
package com.safframework.saf.app;

import android.Manifest;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Build;
import android.telephony.TelephonyManager;
import android.util.Log;

import com.safframework.saf.config.SAFConstant;
import com.safframework.saf.utils.SAFUtils;
import com.safframework.tony.common.utils.Preconditions;

import java.util.ArrayList;
import java.util.List;

/**
 * SAFApp是自定义的Application,session可作为缓存存放app的全局变量<br>
 * SAFApp并不是每个app都需要使用,可自由选择,也可以继承它<br>
 * 如需使用SAFApp,则在AndroidManifest.xml中配置,<br>
 * 在application中增加android:name="cn.salesuite.saf.app.SAFApp"
 * 
 * @author <NAME>
 * 
 */
public class SAFApp extends Application {

	public List<Activity> activityManager;

	public String deviceid;  // 设备ID
	public String osVersion; // 操作系统版本
	public String mobileType;// 手机型号
	public String version;   // app的versionName
	public int versionCode;  // app的versionCode

	private static SAFApp instance;

	/**
	 * @see Application#onCreate()
	 */
	@Override
	public void onCreate() {
		super.onCreate();
		init();
	}

	public void init() {
		instance = this;

		activityManager = new ArrayList<Activity>();
		
		PackageManager manager = this.getPackageManager();
		try {
			PackageInfo info = manager.getPackageInfo(this.getPackageName(), 0);
			deviceid = getDeviceId();
			osVersion = Build.VERSION.RELEASE;
			mobileType = Build.MODEL;
			if (null != info) {
				version = info.versionName;
				versionCode = info.versionCode;
			}
		} catch (NameNotFoundException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 获取手机的设备号（imei）
	 * 
	 * @return
	 */
	@TargetApi(23)
	private String getDeviceId() {
		String  imei = null;

		if (SAFUtils.isMOrHigher()) {
			if (getPackageManager().checkPermission(Manifest.permission.READ_PHONE_STATE,
					getPackageName()) == PackageManager.PERMISSION_GRANTED) {
				TelephonyManager mphonemanger = ((TelephonyManager) getSystemService(TELEPHONY_SERVICE));
				if (mphonemanger !=null) {
					imei = mphonemanger.getDeviceId();
				}
			} else {
				Log.e("SAFApp","no android.permission.READ_PHONE_STATE permission");
			}
		} else {
			TelephonyManager mphonemanger = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
			if (mphonemanger != null) {
				imei = mphonemanger.getDeviceId();
			}
		}

		if (Preconditions.isBlank(imei)) {
			imei = SAFConstant.SPECIAL_IMEI;
		}

		return imei;
	}

	public static SAFApp getInstance() {
		return instance;
	}
}

<|start_filename|>app/src/main/java/com/test/saf/fragment/PermissionFragment.java<|end_filename|>
package com.test.saf.fragment;

import android.Manifest;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;

import com.jakewharton.rxbinding2.view.RxView;
import com.safframework.injectview.Injector;
import com.safframework.injectview.annotations.InjectView;
import com.safframework.permission.RxPermissions;
import com.test.saf.R;
import com.test.saf.app.BaseFragment;

import java.util.concurrent.TimeUnit;

import io.reactivex.annotations.NonNull;
import io.reactivex.functions.Consumer;

/**
 * Created by <NAME> on 2017/6/23.
 */

public class PermissionFragment extends BaseFragment {

    @InjectView(R.id.text1)
    TextView text1;

    @InjectView(R.id.text2)
    TextView text2;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.fragment_permission, container, false);
        Injector.injectInto(this, v);

        initViews();

        return v;
    }

    private void initViews() {

        RxView.clicks(text1).throttleFirst(500, TimeUnit.MILLISECONDS)
                .compose(new RxPermissions(mContext).ensure(Manifest.permission.CAMERA))
                .subscribe(new Consumer<Boolean>() {
                    @Override
                    public void accept(@NonNull Boolean aBoolean) throws Exception {

                        if (aBoolean) {
                            Toast.makeText(mContext,"获取摄像头权限成功",Toast.LENGTH_SHORT).show();
                        } else {
                            Toast.makeText(mContext,"获取摄像头权限失败",Toast.LENGTH_SHORT).show();
                        }
                    }
                });

        RxView.clicks(text2).throttleFirst(500, TimeUnit.MILLISECONDS)
                .compose(new RxPermissions(mContext).ensure(Manifest.permission.CALL_PHONE))
                .subscribe(new Consumer<Boolean>() {
                    @Override
                    public void accept(@NonNull Boolean aBoolean) throws Exception {

                        if (aBoolean) {
                            Toast.makeText(mContext,"获取打电话权限成功",Toast.LENGTH_SHORT).show();
                        } else {
                            Toast.makeText(mContext,"获取打电话权限失败",Toast.LENGTH_SHORT).show();
                        }
                    }
                });
    }
}


<|start_filename|>saf-core/src/main/java/com/safframework/saf/app/package-info.java<|end_filename|>
/**
 * SAF框架基类的Activity和application
 */
package com.safframework.saf.app;

<|start_filename|>saf-queue/src/main/java/com/safframework/queue/Operation.java<|end_filename|>
package com.safframework.queue;

import android.os.Bundle;

/**
 * Created by tony on 2017/4/15.
 */

public interface Operation {

    void run(Queue queue, Bundle bundle);
}


<|start_filename|>app/src/main/java/com/test/saf/menu/MenuManager.java<|end_filename|>
package com.test.saf.menu;

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;

import com.test.saf.R;
import com.test.saf.fragment.CacheFragment;
import com.test.saf.fragment.EventBusFragment;
import com.test.saf.fragment.GeneralAnnotationFragment;
import com.test.saf.fragment.HomeFragment;
import com.test.saf.fragment.LogFragment;
import com.test.saf.fragment.PermissionFragment;
import com.test.saf.fragment.RouterFragment;
import com.test.saf.fragment.SqliteORMFragment;

/**
 * Created by tony on 2016/11/20.
 */

public class MenuManager {

    private static MenuManager instance = null;
    private FragmentManager fragmentManager;
    private MenuType curType;

    public enum MenuType {

        HOME("SAF介绍",false),
        ANNOTATION("通用注解",true),
        EVENTBUS("Event Bus",true),
        SQLITE("Sqlite ORM",true),
        ROUTER("Router",true),
        CACHE("Cache",true),
        LOG("日志框架L",true),
        PERMISSION("权限框架",true);

        public final String title;
        public final boolean removed;

        MenuType(String title,boolean removed) {
            this.title = title;
            this.removed = removed;
        }

        public String getTitle() {
            return title;
        }

        public boolean isRemoved() {
            return removed;
        }
    }

    private MenuManager(FragmentManager fragmentManager) {
        this.fragmentManager = fragmentManager;
        curType = MenuType.HOME;
    }

    public static MenuManager getInstance(FragmentManager fragmentManager) {
        if (instance == null) {
            instance = new MenuManager(fragmentManager);
        }

        return instance;
    }

    public MenuType getCurType() {

        return curType;
    }


    public boolean show(MenuType type) {
        if (curType.equals(type)) {
            return true;
        } else {
            hide(curType);
        }

        Fragment fragment = (Fragment) fragmentManager.findFragmentByTag(type.getTitle());
        if (fragment == null) {
            fragment = create(type);
            if (fragment == null) {
                return false;
            }
        }

        fragmentManager.beginTransaction().show(fragment).commit();
        curType = type;
        return true;
    }

    private Fragment create(MenuType type) {
        Fragment fragment = null;
        switch (type) {
            case HOME:
                fragment = new HomeFragment();
                break;

            case ANNOTATION:
                fragment = new GeneralAnnotationFragment();
                break;

            case EVENTBUS:
                fragment = new EventBusFragment();
                break;

            case SQLITE:
                fragment = new SqliteORMFragment();
                break;

            case ROUTER:
                fragment = new RouterFragment();
                break;

            case CACHE:
                fragment = new CacheFragment();
                break;

            case LOG:
                fragment = new LogFragment();
                break;

            case PERMISSION:
                fragment = new PermissionFragment();
                break;

            default:
                break;
        }
        fragmentManager.beginTransaction().add(R.id.content_frame, fragment, type.getTitle()).commitAllowingStateLoss();
        return fragment;
    }

    private void hide(MenuType type) {
        Fragment fragment = (Fragment) fragmentManager.findFragmentByTag(type.getTitle());
        if (fragment != null) {
            if (type.isRemoved()) {
                fragmentManager.beginTransaction().remove(fragment).commit();
            } else {
                FragmentTransaction ft = fragmentManager.beginTransaction();
                // ft.addToBackStack(type.getTitle());
                ft.hide(fragment);
                ft.commit();
            }
        }
    }

    /**
     * 判断某个fragment是否存在
     *
     * @param type
     * @return
     */
    public boolean isFragmentExist(MenuType type) {
        Fragment fragment = (Fragment) fragmentManager.findFragmentByTag(type.getTitle());

        return fragment!=null;
    }

    /**
     * 返回菜单的总数
     *
     * @return
     */
    public int getMenuCount() {

        if (MenuType.values() != null) {
            return MenuType.values().length;
        }

        return 0;
    }

}


<|start_filename|>saf-core/src/main/java/com/safframework/saf/concurrent/BackgroundPriorityThreadFactory.java<|end_filename|>
/**
 * 
 */
package com.safframework.saf.concurrent;

import android.os.Process;

import com.safframework.tony.common.utils.Preconditions;

import java.util.concurrent.ThreadFactory;

/**
 * 使用该类时，app需要增加权限：&ltuses-permission android:name="android.permission.RAISED_THREAD_PRIORITY"/>
 * @author <NAME>
 *
 */
public class BackgroundPriorityThreadFactory implements ThreadFactory {
	
	public String threadName;
	
	public BackgroundPriorityThreadFactory() {
	}
	
	public BackgroundPriorityThreadFactory(String threadName) {
		this.threadName = threadName;
	}

	@Override
	public Thread newThread(Runnable r) {
		
		if (Preconditions.isNotBlank(threadName)) {
			return new Thread(r,threadName) {
				@Override
				public void run() {
					Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
					super.run();
				}
			};
		} else {
			return new Thread(r) {
				@Override
				public void run() {
					Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
					super.run();
				}
			};
		}
		
	}
}


<|start_filename|>saf-core/src/main/java/com/safframework/saf/eventbus/Subscribe.java<|end_filename|>
/**
 * 
 */
package com.safframework.saf.eventbus;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 消息的订阅者默认使用ThreadMode.PostThread（在当前的thread运行）
 * @author <NAME>
 *
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Subscribe {
	ThreadMode value() default ThreadMode.PostThread;
}
