Creación de una aplicación de cliente de noticias con los protocolos QUIC, MVVM y Data Binding [parte 1] | Daniel Mendoza Octubre de 2020

La tecnología siempre ha sido más rápida, los teléfonos móviles pasan por enormes bloques con botones y una empresa en miniatura que hace un cuádruple, que se puede usar en la bolsa para realizar cualquier tara (e incluso decepciones). Este tipo de avances se limitan a que cada uno de ellos está conectado y ha sido conectado, pero esto también es posible en ausencia de los protocolos de comunicación que se incluirán en la información que brinden las partes.

El protocolo QUIC (tYse puede compartir con HTTP / 3) creado inicialmente por Google y convertido a un proyecto de código cerrado. El interés de este protocolo es que la transmisión de datos de implementación a UDP (la diferencia entre sus analistas que están relacionados con TCP), hace que las conexiones sean menos latentes (tiene velocidad).

Cualquier empresa ha podido soportar QUIC en sus servidores, pero la cuenta del cliente, como nuestros Preparativos Nuestras Apps para adoptar este nuevo protocolo de transferencia de datos, también es posible hacerlo sin compatibilidad con dispositivos HTTP 1 y 2. Este artículo muestra cómo crear un cliente compatible con QUIC usando MVVM y Data Binding.

  • Estudio de Android
  • Una cuenta del desarrollador en newsapi.org

Primero que nada, necesitamos crear un nuevo proyecto de Android Sudio con una Actividad Vacía, orientado a Android 6 o versiones superiores.

Nuevo proyecto

Para encontrar nuestro objetivo, depende de las siguientes dependencias:

  • RecyclerView: para descargar nuestras noticias en una lista
  • SwipeRefreshLayout: para mejorar la capacidad de reflejar noticias
  • HQUIC: Para conectarse con la API REST mediana QUIC
  • capitán: Para usar el enlace de datos con Kotlin
  • Ciclo de vida: Para usar MVVM

Primero hacemos coincidir las dependencias de RecyclerView y SwipeRefreshLayout, agregando las siguientes líneas a nuestra compilación de archivo. Haga clic en el nivel de la aplicación:

dependencies{
implementation "androidx.recyclerview:recyclerview:1.1.0"
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
}

Para habilitar el SDK de HQUIC, aceptamos descargar el repositorio de Maven de Huawei a nuestro archivo build.gradle a nivel de proyecto

buildscript {repositories {maven { url 'https://developer.huawei.com/repo/' }// This linegoogle()jcenter()}dependencies {classpath 'com.android.tools.build:gradle:3.3.2'}}}allprojects {repositories {maven { url 'https://developer.huawei.com/repo/' }// and this onegoogle()jcenter()}}task clean(type: Delete) {delete rootProject.buildDir}

Ahora estamos de acuerdo en la dependencia de HQUIC para construir el nuestro. Gradle el nivel de la aplicación.

implementation 'com.huawei.hms:hquic-provider:5.0.0.300'

Para usar Data Binding con Kotlin, necesitamos un complemento para su archivo build.gradle a nuestro nivel de aplicación.

// asegurate de agregar el plugin de kapt después del plugin de kotlin
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'

Sin embargo, depende de las dependencias de Cape y MVVM

dependencies {
...
implementation "android.arch.lifecycle:extensions:1.1.1"
kapt 'com.android.databinding:compiler:3.1.4'

}

Por última vez, obtiene el soporte para View Binding y Data Binding dentro de buildFeatures

android {
...
buildFeatures{
dataBinding true
viewBinding true
}
}

Queremos crear un cliente llamado HQUICService, basado en el proporcional a Huawei, esta clase abstrae las funcionalidades de QUIC y le permite hacer peticiones REST de la forma.

Nota: Si el servidor QUIC, HQUIC está ejecutando una petición HTTP / 2, no es un problema implementar el SDK SDK.

class HQUICService(val context: Context) {

private val TAG = "HQUICService"

private val DEFAULT_PORT = 443

private val DEFAULT_ALTERNATEPORT = 443

private val executor: Executor = Executors.newSingleThreadExecutor()

private var cronetEngine: CronetEngine? = null

private var callback: UrlRequest.Callback? = null

/**
* Asynchronous initialization.
*/
init {
HQUICManager.asyncInit(
context,
object : HQUICManager.HQUICInitCallback {
override fun onSuccess() {
Log.i(TAG, "HQUICManager asyncInit success")
}

override fun onFail(e: Exception?) {
Log.w(TAG, "HQUICManager asyncInit fail")
}
})
}

/**
* Create a Cronet engine.
*
*
@param url URL.
*
@return cronetEngine Cronet engine.
*/
private fun createCronetEngine(url: String): CronetEngine? {
if (cronetEngine != null) {
return cronetEngine
}
val builder = CronetEngine.Builder(context)
builder.enableQuic(true)
builder.addQuicHint(getHost(url), DEFAULT_PORT, DEFAULT_ALTERNATEPORT)
cronetEngine = builder.build()
return cronetEngine
}

/**
* Construct a request
*
*
@param url Request URL.
*
@param method method Method type.
*
@return UrlRequest urlrequest instance.
*/
private fun builRequest(
url: String,
method: String,
headers: HashMap<String, String>?,
body:ByteArray?
): UrlRequest? {
val cronetEngine: CronetEngine? = createCronetEngine(url)
val requestBuilder = cronetEngine?.newUrlRequestBuilder(url, callback, executor)
requestBuilder?.apply {
setHttpMethod(method)
if(method=="POST"){
body?.let {
setUploadDataProvider(UploadDataProviders.create(ByteBuffer.wrap(it)), executor) }
}
headers?.let{
for (key in it.keys) {
addHeader(key, headers[key])
}
}
return build()
}
return null
}

/**
* Send a request to the URL.
*
*
@param url Request URL.
*
@param method Request method type.
*/
fun sendRequest(url: String, method: String, headers: HashMap<String, String>?=null,body:ByteArray?=null) {
Log.i(TAG, "callURL: url is " + url + "and method is " + method)
val urlRequest: UrlRequest? = builRequest(url, method, headers,body)
urlRequest?.apply { urlRequest.start() }
}

/**
* Parse the domain name to obtain the host name.
*
*
@param url Request URL.
*
@return host Host name.
*/
private fun getHost(url: String): String? {
var host: String? = null
try {
val url1 = URL(url)
host = url1.host
} catch (e: MalformedURLException) {
Log.e(TAG, "getHost: ", e)
}
return host
}

fun setCallback(mCallback: UrlRequest.Callback?) {
callback = mCallback
}
}

El siguiente paso es definir el modelo de datos. Es cierto que las clases de datos de Kotlin han sido un proceso muy simple.

data class Article(val author:String,
val title:String,
val description:String,
val url:String,
val _time:String){
//El campo time viene en formato UTC, por lo que tendremos que formatearlo antes de mostrarlo.
val time:String
get() {
val date= SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.getDefault()).parse(_time)
return date?.toString() ?: ""
}
}

También informaremos al cliente de la noticia de que se implementará la aplicación de newsapi.org. Esta es la urgencia de UrlRequest.Callback, que cubrirá todos los eventos de la petición.

class NewsClient(context: Context): UrlRequest.Callback() {
var hquicService: HQUICService? = null
val CAPACITY = 10240
val TAG="NewsDownloader"
var response:StringBuilder=java.lang.StringBuilder()
var listener:NewsClientListener?=null

init {
hquicService = HQUICService(context)
hquicService?.setCallback(this)
}

fun getNews(url: String, method:String){
hquicService?.sendRequest(url,method)
}

override fun onRedirectReceived(
request: UrlRequest,
info: UrlResponseInfo,
newLocationUrl: String
) {
request.followRedirect()
}

override fun onResponseStarted(request: UrlRequest, info: UrlResponseInfo) {
Log.i(TAG, "onResponseStarted: ")
val byteBuffer = ByteBuffer.allocateDirect(CAPACITY)
request.read(byteBuffer)

}

override fun onReadCompleted(
request: UrlRequest,
info: UrlResponseInfo,
byteBuffer: ByteBuffer
) {
Log.i(TAG, "onReadCompleted: method is called")
val readed=String(byteBuffer.array(), byteBuffer.arrayOffset(), byteBuffer.position())
response.append(readed)
request.read(ByteBuffer.allocateDirect(CAPACITY))
}

override fun onSucceeded(request: UrlRequest?, info: UrlResponseInfo?) {
//If everything is ok you can read the response body
val json=JSONObject(response.toString())
val array=json.getJSONArray("articles")
val list=ArrayList<Article>()
for (i in 0 until array.length()){
val article=array.getJSONObject(i)
val author=article.getString("author")
val title=article.getString("title")
val description=article.getString("description")
val time=article.getString("publishedAt")
val url=article.getString("url")
list.add(Article(author, title, description, url, time))
}
listener?.onSuccess(list)
}

override fun onFailed(request: UrlRequest, info: UrlResponseInfo, error: CronetException) {
//If someting fails you must report the error
listener?.onFailure(error.toString())
}

public interface NewsClientListener{
fun onSuccess(news:ArrayList<Article>)
fun onFailure(error: String)
}

Tan pronto como sea posible, en el método onReadCompleted, acordaron que los datos se incluirían en el StringBuilder.

response.append(readed)

El SDK de HQUIC solo lee la cantidad de bytes a la carretera, porque el método onReadCompleted será muy diferente y siempre tendrá una respuesta completa.

Uno que ha recibido todos los datos del formulario de satisfacción se llama en el método onSucceeded. De las felicitaciones, fallará enFailed.

Esto es todo por ahora, participaremos en la Parte 2

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *