Preface

RxHttp Is based on RxJava2+Retrofit 2.9.0+OkHttp 4.9.0 Lightweight implementation , Perfect compatibility MVVM Architecture of network request encapsulation class library , Cabinet and delicate , Simple and easy to use , Easy to handle network requests .

GitHub

https://github.com/kongpf8848/RxHttp

Bright spot

  • Very little code , Insufficient class library size 100kb, But it's good enough for most of APP Network request task for , Concentrated is the essence _^_

  • Perfect compatibility MVVM,MVC framework , compatible Kotlin and Java,Kotlin+MVVM+RxHttp The combination is more sour and refreshing ,MVVM The official recommendation , Hold tight Google The thighs are right

  • Perfect solution to the thorny problem of generic type erasure , Restore the real type of a generic

  • Born to support web requests and Activity,Fragment Life cycle binding , When the interface is destroyed, the network request callback will be cancelled automatically

  • Natural support BaseUrl, Supports dynamic incoming Url

  • Support customization OkHttpClient.Builder, Highly customizable network request parameters

  • Support Glide Share one with network request OkHttpClient, make the best of OkHttpClient Thread pool and connection pool of , Most of the time, one App One OkHttpClient That's enough

  • Support GET,POST,PUT,DELETE Etc , Support file upload and progress monitoring , Support to upload multiple files at the same time , Support Uri Upload

  • Support file download and progress monitoring , Support large file download , Support breakpoint download

Use requirement

Project based on AndroidX,Java8+,minSdkVersion>=21

Use

implementation 'com.github.kongpf8848:RxHttp:1.0.11'

To configure ( Optional )

 RxHttpConfig.getInstance()
/**
* Failed retries
*/
.maxRetries(3)
/**
* Time interval between failed retries
*/
.retryDelayMillis(200)
/**
* Customize OkHttpClient.Builder(),RxHttp Support customization OkHttpClient.Builder(),
* If not defined , Then use RxHttp default OkHttpClient.Builder()
*/
.builder(OkHttpClient.Builder().apply {
connectTimeout(60, TimeUnit.SECONDS)
readTimeout(60, TimeUnit.SECONDS)
writeTimeout(60, TimeUnit.SECONDS)
/**
* DEBUG In mode , Add log interceptor , It is recommended to use RxHttp Medium FixHttpLoggingInterceptor, Use OkHttp Of HttpLoggingInterceptor When uploading and downloading, there will be IOException problem
*/
if (BuildConfig.DEBUG) {
addInterceptor(FixHttpLoggingInterceptor().apply {
level = FixHttpLoggingInterceptor.Level.BODY
})
}
})

Based on using

  • GET/POST/PUT/DELETE/ Upload request
 RxHttp.getInstance()
/**
* get: Request type , for get,post,put,delete,upload, They correspond to each other GET/POST/PUT/DELETE/ Upload request
* context: Context , for Context,Activity or Fragment type , When context by Activity or Fragment Network request and lifecycle binding
*/
.get(context)
/**
* request url, Such as https://www.baidu.com
*/
.url("xxx")
/**
* Request parameter key value pairs , The type is Map<String, Any?>?, Such as hashMapOf("name" to "jack")
*/
.params(map)
/**
* Each network request corresponds to tag value , for null, Used for subsequent manual operation according to tag Cancel the specified network request
*/
.tag("xxx")
/**
* HttpCallback: Network callback , Parameters xxx To return the data model corresponding to the data ,
* similar RxJava Medium Observer,onComplete Only in onNext Execute after callback , If there is an error, it will only call back onError But not onComplete
*/
.enqueue(object : HttpCallback<xxx>() {
/**
* http Callback at the beginning of the request
*/
override fun onStart() { } /**
* http Callback on successful request
*/
override fun onNext(response: xxx?) { } /**
* http Callback on request failure
*/
override fun onError(e: Throwable?) { } /**
* http Call back when the request completes successfully
*/
override fun onComplete() { } /**
* Upload progress callback , The request type is upload It will be called back when it's done
*/
override fun onProgress(readBytes: Long, totalBytes: Long) { }
})
  • Download request
 RxHttp.getInstance()
/**
* download: Request type , Download request
* context: Context , If you don't need to bind to the lifecycle , It should be delivered applicationContext
*/
.download(context)
/**
* Save the path
*/
.dir(dir)
/**
* Save the file name
*/
.filename(filename)
/**
* Whether it is a breakpoint download , The default is false
*/
.breakpoint(true)
/**
* Download address , Such as http://study.163.com/pub/ucmooc/ucmooc-android-official.apk
*/
.url(url)
/**
* request Tag
*/
.tag(null)
/**
* Download callback
*/
.enqueue(object: DownloadCallback() {
/**
* Callback at the start of download
*/
override fun onStart() { } /**
* Call back when the download is complete
*/
override fun onNext(response: DownloadInfo?) { } /**
* Call back when download fails
*/
override fun onError(e: Throwable?) { } /**
* When the download is complete, call back
*/
override fun onComplete() { } /**
* Download progress callback
*/
override fun onProgress(readBytes: Long, totalBytes: Long) { } })
  • Cancel the request
 /**
* tag:Any?, request Tag, Corresponding to... In the network request Tag value
* If not null, Cancel the specified network request ,
* If null, Cancel all network requests
*/
RxHttp.getInstance().cancelRequest(tag)

Project practice

Here we assume that the data format returned by the server is {"code":xxx,"data":T,"msg":""}, among code Is the response code , integer , be equal to 200 When it comes to success , The rest are failures ,data The corresponding data type is generic (boolean,int,double,String, object { }, Array [ ] Other types )

{
"code": 200,
"data":T,
"msg": ""
}

Corresponding Response Class is

class TKResponse<T>(val code:Int,val msg: String?, val data: T?) : Serializable {
companion object{
const val STATUS_OK=200
}
fun isSuccess():Boolean{
return code== STATUS_OK
}
}
  • MVC project

    • Definition MVCHttpCallback, It is used to call back the result of network request to UI Interface
    abstract class MVCHttpCallback<T> {
    private val type: Type
    init {
    val arg = TypeUtil.getType(javaClass)
    type = TypeBuilder
    .newInstance(TKResponse::class.java)
    .addTypeParam(arg)
    .build()
    } fun getType(): Type {
    return this.type
    } /**
    * Callback at the beginning of the request , You can load here loading Dialog, etc , Null implementation by default
    */
    open fun onStart() {} /**
    * Abstract method , Request successful callback , The returned content is generic , Corresponding TKResponse Of data
    */
    abstract fun onSuccess(result: T?) /**
    * Abstract method , Request failed callback , The returned content is code( Error code ),msg( error message )
    */
    abstract fun onFailure(code: Int, msg: String?) /**
    * Upload progress callback , Null implementation by default
    */
    open fun onProgress(readBytes: Long, totalBytes: Long) {} /**
    * Callback on request completion , This method will not be called back until the request is successful , Null implementation by default
    */
    open fun onComplete() {} }
    • Define the network interface , encapsulation GET/POST Wait for network request
    object MVCApi {
    /**
    * GET request
    * context: Context
    * url: request url
    * params: parameter list , for null
    * tag: Identify a network request
    * callback: Network request callback
    */
    inline fun <reified T> httpGet(
    context: Context,
    url: String,
    params: Map<String, Any?>?,
    tag: Any? = null,
    callback: MVCHttpCallback<T>
    ) {
    RxHttp.getInstance().get(context)
    .url(url)
    .params(params)
    .tag(tag)
    .enqueue(simpleHttpCallback(callback))
    } /**
    * POST request
    * context: Context
    * url: request url
    * params: parameter list , for null
    * tag: Identify a network request
    * callback: Network request callback
    */
    inline fun <reified T> httpPost(
    context: Context,
    url: String,
    params: Map<String, Any?>?,
    tag: Any? = null,
    callback: MVCHttpCallback<T>
    ) {
    RxHttp.getInstance().post(context)
    .url(url)
    .params(params)
    .tag(tag)
    .enqueue(simpleHttpCallback(callback))
    } ...... inline fun <reified T> simpleHttpCallback(callback: MVCHttpCallback<T>): HttpCallback<TKResponse<T>> {
    return object : HttpCallback<TKResponse<T>>(callback.getType()) {
    override fun onStart() {
    super.onStart()
    callback.onStart()
    } override fun onNext(response: TKResponse<T>?) {
    if (response != null) {
    if (response.isSuccess()) {
    callback.onSuccess(response.data)
    } else {
    return onError(ServerException(response.code, response.msg))
    } } else {
    return onError(NullResponseException(TKErrorCode.ERRCODE_RESPONSE_NULL, TKErrorCode.ERRCODE_RESPONSE_NULL_DESC))
    } } override fun onError(e: Throwable?) {
    handleThrowable(e).run {
    callback.onFailure(first, second)
    }
    } override fun onComplete() {
    super.onComplete()
    callback.onComplete()
    } override fun onProgress(readBytes: Long, totalBytes: Long) {
    super.onProgress(readBytes, totalBytes)
    callback.onProgress(readBytes, totalBytes)
    }
    }
    }
    • stay View Layering as Activity Call the network interface
    MVCApi.httpGet(
    context = baseActivity,
    url = TKURL.URL_GET,
    params = null,
    tag = null,
    callback = object : MVCHttpCallback<List<Banner>>() {
    override fun onStart() {
    LogUtils.d(TAG, "onButtonGet onStart() called")
    } override fun onSuccess(result: List<Banner>?) {
    Log.d(TAG, "onButtonGet onSuccess() called with: result = $result")
    } override fun onFailure(code: Int, msg: String?) {
    Log.d(TAG, "onButtonGet onFailure() called with: code = $code, msg = $msg")
    } override fun onComplete() {
    Log.d(TAG, "onButtonGet onComplete() called")
    } })

    For specific use, please refer to demo Code ,demo There are detailed examples in MVC How to use the project RxHttp

  • MVVM project

    • Definition Activity Base class BaseMvvmActivity
    abstract class BaseMvvmActivity<VM : BaseViewModel, VDB : ViewDataBinding> : AppCompatActivity(){
    lateinit var viewModel: VM
    lateinit var binding: VDB protected abstract fun getLayoutId(): Int final override fun onCreate(savedInstanceState: Bundle?) {
    onCreateStart(savedInstanceState)
    super.onCreate(savedInstanceState)
    binding = DataBindingUtil.setContentView(this, getLayoutId())
    binding.lifecycleOwner = this
    createViewModel()
    onCreateEnd(savedInstanceState)
    } protected open fun onCreateStart(savedInstanceState: Bundle?) {}
    protected open fun onCreateEnd(savedInstanceState: Bundle?) {} /**
    * establish ViewModel
    */
    private fun createViewModel() {
    val type = findType(javaClass.genericSuperclass)
    val modelClass = if (type is ParameterizedType) {
    type.actualTypeArguments[0] as Class<VM>
    } else {
    BaseViewModel::class.java as Class<VM>
    }
    viewModel = ViewModelProvider(this).get(modelClass)
    } private fun findType(type: Type): Type?{
    return when(type){
    is ParameterizedType -> type
    is Class<*> ->{
    findType(type.genericSuperclass)
    }
    else ->{
    null
    }
    }
    } }
    • Definition ViewModel Base class of BaseViewModel
    open class BaseViewModel(application: Application) : AndroidViewModel(application) {
    /**
    * Network warehouse
    */
    protected val networkbaseRepository: NetworkRepository = NetworkRepository.instance /**
    * Context
    */
    protected var context: Context = application.applicationContext }
    • Define the network warehouse , Encapsulating network interfaces
    /**
    * MVVM Architecture network warehouse
    * UI->ViewModel->Repository->LiveData(ViewModel)->UI
    */
    class NetworkRepository private constructor() { companion object {
    val instance = NetworkRepository.holder
    } private object NetworkRepository {
    val holder = NetworkRepository()
    } inline fun <reified T> wrapHttpCallback(): MvvmHttpCallback<T> {
    return object : MvvmHttpCallback<T>() { }
    } inline fun <reified T> newCallback(liveData: MutableLiveData<TKState<T>>): HttpCallback<TKResponse<T>> {
    val type = wrapHttpCallback<T>().getType()
    return object : HttpCallback<TKResponse<T>>(type) {
    override fun onStart() {
    liveData.value = TKState.start()
    } override fun onNext(response: TKResponse<T>?) {
    liveData.value = TKState.response(response)
    } override fun onError(e: Throwable?) {
    liveData.value = TKState.error(e)
    } override fun onComplete() { /**
    * Pro - , Don't do anything here , Don't give LiveData assignment , prevent onNext Corresponding LiveData Data is covered ,
    * stay TKState class handle Method will handle callbacks in particular , Don't worry
    */
    } override fun onProgress(readBytes: Long, totalBytes: Long) {
    liveData.value = TKState.progress(readBytes, totalBytes)
    }
    }
    } inline fun <reified T> httpGet(
    context: Context,
    url: String,
    params: Map<String, Any?>?,
    tag: Any? = null
    ): MutableLiveData<TKState<T>> {
    val liveData = MutableLiveData<TKState<T>>()
    RxHttp.getInstance()
    .get(context)
    .url(url)
    .params(params)
    .tag(tag)
    .enqueue(newCallback(liveData))
    return liveData
    } inline fun <reified T> httpPost(
    context: Context,
    url: String,
    params: Map<String, Any?>?,
    tag: Any? = null
    ): MutableLiveData<TKState<T>> {
    val liveData = MutableLiveData<TKState<T>>()
    RxHttp.getInstance().post(context)
    .url(url)
    .params(params)
    .tag(tag)
    .enqueue(newCallback(liveData))
    return liveData
    } inline fun <reified T> httpPostForm(
    context: Context,
    url: String,
    params: Map<String, Any?>?,
    tag: Any? = null
    ): MutableLiveData<TKState<T>> {
    val liveData = MutableLiveData<TKState<T>>()
    RxHttp.getInstance().postForm(context)
    .url(url)
    .params(params)
    .tag(tag)
    .enqueue(newCallback(liveData))
    return liveData
    } inline fun <reified T> httpPut(
    context: Context,
    url: String,
    params: Map<String, Any?>?,
    tag: Any? = null
    ): MutableLiveData<TKState<T>> {
    val liveData = MutableLiveData<TKState<T>>()
    RxHttp.getInstance().put(context)
    .url(url)
    .params(params)
    .tag(tag)
    .enqueue(newCallback(liveData))
    return liveData
    } inline fun <reified T> httpDelete(
    context: Context,
    url: String,
    params: Map<String, Any?>?,
    tag: Any? = null
    ): MutableLiveData<TKState<T>> {
    val liveData = MutableLiveData<TKState<T>>()
    RxHttp.getInstance().delete(context)
    .params(params)
    .url(url)
    .tag(tag)
    .enqueue(newCallback(liveData))
    return liveData
    } /**
    * Upload
    * Support uploading multiple files ,map Corresponding value The type is File Type or Uri type
    * Monitor the upload progress
    val map =Map<String,Any>()
    map.put("model", "xiaomi")
    map.put("os", "android")
    map.put("avatar",File("xxx"))
    map.put("video",uri)
    */
    inline fun <reified T> httpUpload(
    context: Context,
    url: String,
    params: Map<String, Any?>?,
    tag: Any? = null
    ): MutableLiveData<TKState<T>> {
    val liveData = MutableLiveData<TKState<T>>()
    RxHttp.getInstance().upload(context)
    .url(url)
    .params(params)
    .tag(tag)
    .enqueue(newCallback(liveData))
    return liveData
    } /**
    * download
    * context: Context , If you don't need to bind to the lifecycle , It should be delivered applicationContext
    * url: Download address
    * dir: Local directory path
    * filename: Save the file name
    * callback: Download progress callback
    * md5: Download the file MD5 value
    * breakpoint: Whether breakpoint download is supported , The default is true
    */
    fun httpDownload(context: Context, url: String, dir: String, filename: String, callback: DownloadCallback, md5: String? = null, breakPoint: Boolean = true, tag: Any? = null) {
    RxHttp.getInstance().download(context).dir(dir).filename(filename).breakpoint(breakPoint).md5(md5).url(url).tag(tag).enqueue(callback)
    } }
    • Definition TKState class , Used to convert network callbacks to LiveData
    /**
    * take HttpCallback The callback is converted to the corresponding LiveData
    */
    class TKState<T> { var state: Int = 0
    var code = TKErrorCode.ERRCODE_UNKNOWN
    var msg: String? = null
    var data: T? = null
    var progress: Long = 0
    var total: Long = 0 @JvmOverloads
    constructor(state: Int, data: T? = null, msg: String? = "") {
    this.state = state
    this.data = data
    this.msg = msg
    } constructor(state: Int, throwable: Throwable?) {
    this.state = state
    handleThrowable(throwable).run {
    this@TKState.code = first
    this@TKState.msg = second
    }
    } constructor(state: Int, progress: Long, total: Long) {
    this.state = state
    this.progress = progress
    this.total = total
    } fun handle(handleCallback: HandleCallback<T>.() -> Unit) {
    val callback = HandleCallback<T>()
    callback.apply(handleCallback)
    when (state) {
    START -> {
    callback.onStart?.invoke()
    }
    SUCCESS -> {
    callback.onSuccess?.invoke(data)
    }
    FAIL -> {
    callback.onFailure?.invoke(code, msg)
    }
    PROGRESS -> {
    callback.onProgress?.invoke(progress, total)
    }
    }
    if (state == SUCCESS || state == FAIL) {
    callback.onComplete?.invoke()
    }
    } open class HandleCallback<T> {
    var onStart: (() -> Unit)? = null
    var onSuccess: ((T?) -> Unit)? = null
    var onFailure: ((Int, String?) -> Unit)? = null
    var onComplete: (() -> Unit)? = null
    var onProgress: ((Long, Long) -> Unit)? = null fun onStart(callback: (() -> Unit)?) {
    this.onStart = callback
    } fun onSuccess(callback: ((T?) -> Unit)?) {
    this.onSuccess = callback
    } fun onFailure(callback: ((Int, String?) -> Unit)?) {
    this.onFailure = callback
    } fun onComplete(callback: (() -> Unit)?) {
    this.onComplete = callback
    } fun onProgress(callback: ((Long, Long) -> Unit)?) {
    this.onProgress = callback
    }
    } companion object {
    const val START = 0
    const val SUCCESS = 1
    const val FAIL = 2
    const val PROGRESS = 3 fun <T> start(): TKState<T> {
    return TKState(START)
    } fun <T> response(response: TKResponse<T>?): TKState<T> {
    if (response != null) {
    if (response.isSuccess()) {
    return TKState(SUCCESS, response.data, null)
    } else {
    return error(ServerException(response.code, response.msg))
    } } else {
    return error(NullResponseException(TKErrorCode.ERRCODE_RESPONSE_NULL, TKErrorCode.ERRCODE_RESPONSE_NULL_DESC))
    } } fun <T> error(t: Throwable?): TKState<T> {
    return TKState(FAIL, t)
    } fun <T> progress(progress: Long, total: Long): TKState<T> {
    return TKState(PROGRESS, progress, total)
    }
    } }
    • After a series of packages , Last in View Layering as Activity in ViewModel call Repository The interface
     viewModel.testPost(hashMapOf(
    "name" to "jack",
    "location" to "shanghai",
    "age" to 28)
    )
    .observeState(this) {
    onStart {
    LogUtils.d(TAG, "onButtonPost() onStart called")
    }
    onSuccess {
    LogUtils.d(TAG, "onButtonPost() onSuccess called:${it}")
    }
    onFailure { code, msg ->
    ToastHelper.toast("onButtonPost() onFailure,code:${code},msg:${msg}")
    }
    onComplete {
    LogUtils.d(TAG, "onButtonPost() onComplete called")
    }
    }

    Specific use should also refer to demo Code ,demo There are detailed examples in MVVM How to use the project RxHttp

Download is highly recommended Demo Code ,Demo There are detailed examples in , demonstration MVVM And MVC How architecture is used RxHttp, If this article helps you , You can think about giving me some praise

Demo

https://github.com/kongpf8848/RxHttp

RxHttp - Lightweight 、 Scalable 、 Easy to use 、 Perfect compatibility MVVM、MVC Architecture of the network encapsulation class library more related articles

  1. Web Uploader - The function is all ready , Perfect compatibility IE The upload component of

    File uploads are websites and Web Common features of applications , There has never been a perfect file upload component , So let a lot of developers encounter a headache browser compatibility problem . WebUploader By Baidu FEX Team developed a model with HTML ...

  2. Prism Lightweight extensible code library .

    Official website :http://prismjs.com/ Prism It's lightweight and easy to use JavaScript Class library ,minified and gzipped Only after compression 1.5kb size , Even if you add language ...

  3. Dapper Perfect compatibility Oracle, Execute stored procedures , And return the result set .

    Dapper Perfect compatibility Oracle, Execute stored procedures , And return the result set . This problem , It's been bothering me for two days . Just used Dapper When , I feel very good . Especially with .net 4.0 New characteristics dynamic, Let me generate a generic collection ...

  4. Picaso Perfect compatibility OkHttp3.3, Cache optimization is the same - Tamic Developer"s Blog

    Why in Fresco,Glide In such a powerful context , I think of it again Picasso, Why did you write this article ? It's because the project recently adopted square The company's RxAndroid,Retrfit and OKhttp, I have to think of ...

  5. input Input box border-radius Attribute in IE8 Perfect compatibility under

    In our work, we found that most of the search boxes have rounded corners , So it's easy for inexperienced front-end people to think of , to input add border-radius Attribute is solved . That's right. It's true , But don't forget border-radi ...

  6. Floating layer of css How to write it , Perfect compatibility IE6~10

    Using the absolute positioning difference between elements by one pixel , Use different colors to make the effect of floating layer small triangle , Perfectly compatible with all browsers ! html part : <div class="poptip"> <span cla ...

  7. 【AOS Basic application platform 】 It's intact AOS Tag library , Perfect compatibility with standard tag library

    [ Jinma square AOS Development platform ] today ① It's intact AOS Tag library , Perfect compatibility with standard tag library .② New development of the sub page based on the dynamic generation of the main page of the secondary navigation menu function .#AOS Development platform #

  8. js Perfectly compatible with the copy function of the browser

    1,js combination swf Copy function , Perfect compatibility with Firefox , Google ,360,ie8, Examples of use :(ps: introduce copy.swf More important , File gate   Decompression password :http://www.bieanju.com/, In order to prevent 360 Delete ...

  9. IE6 Perfect compatibility css3 Fillet and shadow properties htc plug-in unit PIE.htc

    1.( recommend :)css plug-in unit PIE.htc, This is the perfect compatibility css3 The fillet and shadow properties in IE6 The effect of using in the environment , But here's the thing : The following code must be written in html Of documents head tag , Otherwise it will not work ( You can't refer to the following from the outside ...

  10. ASUS 200 The system motherboard is perfectly compatible M.2 install Win7 System

    although Windows 10 The installation probability of the system is increasing , But classic Windows 7 There are still a large number of users . Especially in China , Windows 7 It is still the preferred system for many computer users . Enduring Windows ...

Random recommendation

  1. Reprint :Centos7 Compile configuration from scratch Memcached

    preface Memcached Is a high performance distributed memory object caching system , For dynamic Web Application to reduce database load . It reduces the number of database reads by caching data and objects in memory , To improve dynamics . Speed of database driven websites . Memca ...

  2. edit Ext form ( One )——— Add and delete rows and columns dynamically

    One . Add and delete lines dynamically stay ext In the table , Dynamic addition of rows is mainly bound to tables store of , Through to store Add or delete data sets , Can realize the dynamic addition and deletion of table rows .   (1) Dynamically add rows to a table   gridS ...

  3. backbone Library Learning -Events

    backbone The framework of the library http://www.cnblogs.com/nuysoft/archive/2012/03/19/2404274.html Let's start with backbone Of Events Module start ...

  4. CentOS Mysql Automatic backup .txt

    http://fly.b3log.org/articles/2011/10/25/1319505473928.html MySql Database backup : explain : I'm going to put MySql Database storage directory /var/lib ...

  5. Fragment A brief introduction to the use of 【Android】

    Fragment It is used more and more in actual project development , Now for a brief introduction Layout file : <LinearLayout xmlns:android="http://schemas.android.com ...

  6. C Language array memory initialization

    Memory initialization is inevitable , But there's no need to memset, Just write it like this :char* szRemoteFile = new char[MAX_LENGTH](); http://blog.csdn.net/pa ...

  7. Crgwin Introduction and installation

    Crgwin brief introduction Cygwin It's a windows Classes running on the platform UNIX Simulation environment , yes cygnus solutions Free software developed by the company ( The famous tools developed by the company include eCos, But now it has been Redhat Acquisition ...

  8. WPF Developed lottery program ( A good example of hand training ) Source code attached

    Preface WPF yes .NET The latest interface development library , The development interface is very flexible ! But learning WPF It's also very difficult . At the invitation of a friend , Write a small program . The program is small , Five zang organs ,WPF A glimpse of the flexibility of development . It's a good reference for beginners , ...

  9. avro 1.8.2 (js)

    5 month 15 Yesterday's avro 1.8.2 Already included js Version code . Tsinghua University image address : https://mirrors.tuna.tsinghua.edu.cn/apache/avro/avro-1.8.2 ...

  10. OpenSuSE Linux Lower installation Oracle10g Steps for

    OpenSuSE Linux Lower installation Oracle10g Steps for : --root user --1.vi etc/profile Add a script : if [ \$USER = "oracle" ]; ...