QUIC 프로토콜을 사용하여 알림을 만들 때 사용[Kotlin]

QUIC es un nuevo protocolo de transporte basado en UDP que permite mejorar la velocidad de las conexiones de red debido a la baja latencia de UDP. QUIC은 빠른 UDP 인터넷 연결을 의미하며 이전보다 먼저 convierta en un estándar.
Las características clave de QUIC 아들:
  • Tiempo de establecimiento de conxión drásticamente reducido
  • Control de congestion mejorado
  • Multiplexación sin bloqueo de cabecera de línea
  • 연결 마이그레이션

  • Algunas empresas están comenzando a accir QUIC en sus servidores, ahora desde el lado del cliente, es hora de estar preparados y agregar soporte QUIC a nuestras aplicaciones. Esta vez construiremos una aplicación de noticias con soporte QUIC usando el kit HQUIC.
    HQUIC nos permite conectarnos con servicios web a través del protocolo QUIC, si el control remoto no es compatible con QUIC, el kit usará HTTP 2 en su lugar automáticamente.

    요구 사항 사전


  • Android Studio 프로젝트
  • Una cuenta de desarrollador en newsapi.org

  • Agregar las Dependencias requeridas



    Este ejemplo requerirá las siguientes Dependencias:
  • RecyclerView: Para mostrar todas las novedades en una lista.
  • SwipeRefreshLayout: Para permitir que el usuario actualice la pantalla con un gesto de deslizar.
  • HQUIC:El kit que nos permite conectarnos con servicios a través de QUIC

  • HQUIC는 Huawei가 우수한 build.gradle을 사용하여 Agregar el repositorio를 사용할 수 있도록 합니다.

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


    Ahora agregue las dependsencias requeridas a nivel de aplicación build.gradle

    implementation 'com.huawei.hms:hquic-provider:5.0.0.300'
    implementation "androidx.recyclerview:recyclerview:1.1.0"
    implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
    


    디세난도 라 UI



    대부분의 목록은 RecyclerView의 요소 목록에 포함되어 있으며, 요소를 제거하기 위해 요소를 준비하는 데 사용됩니다. Esta vista básica solo mostrará el título, una breve introducción y la fecha de publicación:

    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="5dp">
    <TextView
            android:id="@+id/title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="4dp"
            android:text="TextView"
            android:textSize="24sp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.0"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    <TextView
            android:id="@+id/content"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="12dp"
            android:text="Content"
            android:textSize="20sp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/title" />
    <TextView
            android:id="@+id/time"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="16dp"
            android:text="TextView"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.0"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/content" />
    </androidx.constraintlayout.widget.ConstraintLayout>
    


    Para el diseño de la actividad, el diseño contendrá un SwipeRefresLayout con el RecyclerView dentro

    <?xml version="1.0" encoding="utf-8"?>
    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
        <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
            android:layout_height="match_parent"
            android:layout_width="match_parent"
            android:id="@+id/swipeRefreshLayout">
    
            <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/recyclerNews"
                android:layout_width="match_parent"
                android:layout_height="match_parent">
    </androidx.recyclerview.widget.RecyclerView>
    </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
    </FrameLayout>
    


    Para mostrar los artículos en una vista de reciclador, necesitarás un adaptador, el adaptador informará el evento de clic de cualquier elemento al oyente dado.

    class NewsAdapter(val news:ArrayList<Article>,var listener: NewsViewHolder.onNewsClickListener?): RecyclerView.Adapter<NewsAdapter.NewsViewHolder>() {
    class NewsViewHolder(itemView: View, var listener:onNewsClickListener?) : RecyclerView.ViewHolder(itemView), View.OnClickListener {
            public fun init(article: Article){
                itemView.title.text=article.title
                itemView.content.text=article.description
                val date= SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.getDefault()).parse(article.time)
                itemView.time.text=date?.toString()
                itemView.setOnClickListener(this)
            }
    interface onNewsClickListener{
                fun onClickedArticle(position: Int)
            }
    override fun onClick(v: View?) {
                listener?.onClickedArticle(adapterPosition)
            }
        }
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NewsViewHolder {
            val view=LayoutInflater.from(parent.context)
                .inflate(R.layout.item_view,parent,false)
            return NewsViewHolder(view,listener)
        }
    override fun onBindViewHolder(holder: NewsViewHolder, position: Int) {
            holder.init(news[position])
        }
    override fun getItemCount(): Int {
            return news.size
        }
    }
    


    HQUIC 서비스



    Usaremos la clase HQUICService는 HQUIC의 데모를 보여주기 위해 사용됩니다. Esta clase abstrae las capacidades del kit HQUIC y nos permite realizar una solicitud fácilmente.

    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): UrlRequest? {
            val cronetEngine: CronetEngine? = createCronetEngine(url)
            val requestBuilder= cronetEngine?.newUrlRequestBuilder(url, callback, executor)
            requestBuilder?.apply {
                setHttpMethod(method)
                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) {
            Log.i(TAG, "callURL: url is " + url + "and method is " + method)
            val urlRequest: UrlRequest? = builRequest(url, method)
            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
        }
    }
    


    Descargando las 알림



    Definiremos una clase auxiliar para manejar la solicitud y analiza la respuesta en una ArrayList. El kit HQUIC leerá una cierta cantidad de bytes por vez, para respuestas grandes, el método onReadCompleted se llamará más de una vez.

    data class Article(val author:String,
                       val title:String,
                       val description:String,
                       val url:String,
                       val time:String)
    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)
        }
    }
    


    Haciendo la solicitud



    Definir propiedades de solicitud.

    private val API_KEY="YOUR_API_KEY"
    private val URL = "https://newsapi.org/v2/top-headlines?apiKey=$API_KEY"
    private val METHOD = "GET"
    


    Llama a la función getNews para iniciar la solicitud, si todo va bien, la lista de noticias se entregará en la devolución de llamada onSuccess. 오류가 발생하면 장애 시 발전량을 활성화하십시오.

    private fun getNews() {
        val country=Locale.getDefault().country
        val url= "$URL&country=$country"
        Log.e("URL",url)
        val downloader=NewsClient(this)
        downloader.apply {
            listener=this@MainActivity
            getNews(url,METHOD)
        }
    }
    override fun onSuccess(news: ArrayList<Article>) {
        this.news.apply {
            clear()
            addAll(news)
        }
        runOnUiThread{
            swipeRefreshLayout.isRefreshing=false
            loadingDialog?.dismiss()
            adapter?.notifyDataSetChanged()
        }
    }
    override fun onFailure(error: String) {
        val errorDialog=AlertDialog.Builder(this).apply {
            setTitle(R.string.error_title)
            val message="${getString(R.string.error_message)} \n $error"
            setMessage(message)
            setPositiveButton(R.string.ok){ dialog, _ ->
                dialog.dismiss()
            }
            setCancelable(false)
            create()
            show()
        }
    }
    


    결론



    Es solo cuestión de tiempo para que el protocolo QUIC se convierta en el nuevo estándar de conexiones a Internet. Por ahora, puedes preparar tus aplicaciones para accidirlo y brindar la mejor experiencia de usuario.
    Prueba 엘 데모: https://github.com/danms07/HQUICNews



    Les compartimos el link al articulo 원본
    https://forums.developer.huawei.com/forumPortal/en/topicview?tid=0202352465220930189&fid=0101187876626530001

    좋은 웹페이지 즐겨찾기