九、连接外部世界——网络

我们生活在数字通信时代。手持设备在交流中发挥着巨大的作用,并影响着人们的互动方式。在前一章中,我们讨论了安卓的一个强大功能——识别用户的位置并根据位置定制服务。在这一章中,我们将重点介绍安卓设备最有用和最强大的功能之一——联网和与外部世界的连接。

虽然我们将简要介绍网络连接的重要概念和对网络的安卓框架支持,但我们将特别关注各种内置第三方库的配置和使用。我们还将学习如何从网址加载图像,并在我们创建的示例应用中显示它。

我们将涵盖以下内容:

  • 网络连接
  • Android 框架对网络的支持
  • 内置库的使用
  • 第三方图书馆的使用

网络连接

了解和识别用户所连接的网络的状态和类型对于为用户提供丰富的体验至关重要。安卓框架为我们提供了几个类,我们可以用它们来查找网络的细节:

  • ConnectivityManager
  • NetworkInfo

ConnectivityManager提供网络连接状态及其变化的信息,而NetworkInfo提供网络类型的信息——移动或无线网络。

以下代码片段有助于确定网络是否确实可用,以及设备是否连接到网络:

fun isOnline(): Boolean {
    val connMgr = getSystemService(Context.CONNECTIVITY_SERVICE) as  
    ConnectivityManager
    val networkInfo = connMgr.activeNetworkInfo
    return networkInfo != null && networkInfo.isConnected
}

isOnline()方法根据ConnectivityManager返回的结果返回Boolean—真或假。connMgr实例与NetworkInfo一起用于查找网络信息。

清单权限

访问网络和发送/接收数据需要访问互联网和网络状态的许可。必须在清单文件中定义以下权限,应用才能利用设备的网络:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

internet 权限允许应用通过启用网络套接字进行通信,而访问网络状态权限则允许它查找有关可用网络的信息。

安卓框架为应用提供了管理网络数据的默认意图MANAGE_NETWORK_USAGE。处理意图的活动可以针对应用来实施:

  <intent-filter>
   <action android:name="android.intent.action.MANAGE_NETWORK_USAGE" />
   <category android:name="android.intent.category.DEFAULT" />
  </intent-filter>

截击库

通过 HTTP 协议与网络服务器通信并以字符串、JSON 和图像的形式交换信息的能力使应用更具交互性,并为用户提供丰富的体验。安卓有一个名为Volley的内置 HTTP 库,可以开箱即用地进行这种信息交换。

除了使信息交换更容易之外,Volley还为用户提供了更容易的方法来处理请求的整个生命周期,即计划、取消、优先化等等。

Volley is quite good for lightweight network operations and makes information exchange easier. For huge downloads and streaming operations, developers should use Download Manager.

同步适配器

保持应用中的数据与 web 服务器同步,使开发人员能够为用户提供丰富的体验。安卓框架提供同步适配器,使数据同步能够以定义的周期间隔发生。

Volley类似,同步适配器具备处理数据传输生命周期的所有设施,并提供无缝的数据交换手段。

Sync adapter implementation typically contains a stub authenticator, a stub content provider, and a sync adapter.

第三方图书馆

除了安卓框架的内置支持,我们还有相当多的第三方库可以用来处理网络操作。其中,Square 的Picasso和 bumptech 的Glide是广泛使用的图像下载和缓存库。

在本节中,我们将重点关注这两个库的实现——PicassoGlide——以从特定的 URL 加载图像并在我们的示例应用中显示它。

Networking calls should never be done on the main thread. Doing so will result in the app becoming less responsive and create Application Not Responding conditions. Instead, we should create separate worker threads which handle such network calls and provide information as and when the request is attended to.

毕加索

在这个示例项目中,让我们了解如何使用 Square 中的Picasso库,并从指定的 URL 加载图像。

让我们创建一个新的安卓项目,并将其称为 ImageLoader。我们需要确保 Kotlin 的支持被检查。

对于图像加载器示例,我们可以通过选择空活动来继续:

让我们将活动命名为MainActivity,默认情况下会出现该活动,并且将 XML 命名为activity_main:

用户界面——可扩展标记语言

生成的默认 XML 代码将包含一个TextView。我们需要稍微调整一下 XML 代码,用一个“T2”代替TextView。该ImageView将提供占位符,用于显示使用Picasso从网址获取的图像。

下面的 XML 代码显示默认的 XML 包含TextView;我们将用ImageView取代TextView:

*<?*xml version="1.0" encoding="utf-8"*?>
* <android.support.constraint.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="match_parent"
     tools:context="com.natarajan.imageloader.MainActivity">

     <TextView
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:text="Hello World!"
         app:layout_constraintBottom_toBottomOf="parent"
 app:layout_constraintLeft_toLeftOf="parent"
         app:layout_constraintRight_toRightOf="parent"
         app:layout_constraintTop_toTopOf="parent" />

 </android.support.constraint.ConstraintLayout>

带有ImageView的修改后的 XML 看起来像下面代码块中显示的那个。我们可以通过从小部件中拖动ImageView或者通过在 XML 布局中键入代码来轻松添加它。在ImageView中,我们将其标记为显示启动器图标作为占位符:

*<?*xml version="1.0" encoding="utf-8"*?>
* <android.support.constraint.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="match_parent"
     tools:context="com.natarajan.imageloader.MainActivity">

     <ImageView
         android:id="@+id/imageView"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         app:srcCompat="@mipmap/ic_launcher"
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintLeft_toLeftOf="parent"
         app:layout_constraintRight_toRightOf="parent"
         app:layout_constraintTop_toTopOf="parent"
         tools:layout_editor_absoluteX="139dp"
         tools:layout_editor_absoluteY="219dp" /> 
 </android.support.constraint.ConstraintLayout>

ImageViewer在占位符中显示启动器图标,标记为从网址加载时显示图像。只要我们在 XML 中进行更改,就会显示启动器图标:

build.gradle

我们需要在build.gradle依赖项中添加实现com.square.picasso.picasso:2.71828。2.71828 版本是撰写本文时的最新版本。为了确保我们使用最新版本,谨慎的做法是检查http://square.github.io/picasso/并在 Gradle 依赖项中使用最新版本。

我们需要在build.gradle文件的依赖项部分添加以下行,以使Picasso可用于我们的应用:

实施com.squareup.picasso:picasso:2.71828

修改后的build.gradle文件应该如下图所示:

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation"org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
    implementation 'com.android.support:appcompat-v7:26.1.0'
    implementation 'com.android.support.constraint:constraint-layout:1.1.0'
    implementation 'com.squareup.picasso:picasso:2.71828'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.1'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' }

Kotlin 码

生成的默认 Kotlin 代码将有名为MainActivity的类文件。该类文件扩展了AppCompatActivity,提供了支持库动作栏功能。

代码在onCreate方法中加载activity_main中定义的 XML,并在加载时显示它。setContentView读取activity_main中定义的 XML 内容,并在加载时显示ImageView:

package com.natarajan.imageloader

 import android.support.v7.app.AppCompatActivity
 import android.os.Bundle
 import kotlinx.android.synthetic.main.activity_main.*

 class MainActivity : AppCompatActivity() {

     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         setContentView(R.layout.activity_main)
     }
 }

我们已经通过将默认的TextView替换为ImageView对 XML 进行了更改。我们需要在我们的 Kotlin 代码中反映这些变化,并通过使用Picasso实现图像的加载。

我们需要为我们的程序添加ImageViewPicasso导入来使用这些组件:

import android.widget.ImageView
import com.squareup.picasso.Picasso

由于我们已经导入了Picasso并确保添加了依赖项,我们应该能够通过一行代码Picasso.get().load("URL").into(ImageView)加载日期:

Picasso.get().load("http://i.imgur.com/DvpvklR.png").into(imageView);

毕加索图像加载的最终修改 Kotlin 类应如下所示:

package com.natarajan.imageloader 
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.widget.ImageView
import com.squareup.picasso.Picasso
import kotlinx.android.synthetic.main.activity_main.*

 class MainActivity : AppCompatActivity() {

 override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         setContentView(R.layout.*activity_main*)
        Picasso.get().load("http://i.imgur.com/DvpvklR.png").into(imageView);
    }
 }

清单权限

我们需要确保我们的应用已经添加了访问互联网的权限。这是必需的,因为我们将从指定的网址下载图像,并将其显示在我们的ImageViewer中。

我们已经详细介绍了所需的清单权限。让我们继续添加此权限:

    <uses-permission android:name="android.permission.INTERNET"></uses-permission>

修改后的 XML 应该如下所示:

*<?*xml version="1.0" encoding="utf-8"*?>
* <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.natarajan.imageloader">

     <uses-permission android:name="android.permission.INTERNET">
    </uses-permission>

     <application
         android:allowBackup="true"
         android:icon="@mipmap/ic_launcher"
         android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round"
         android:supportsRtl="true"
         android:theme="@style/AppTheme">
         <activity android:name=".MainActivity">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                  <category  
 android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
     </application>
  </manifest>

现在我们都已经完成了对 XML、Kotlin 代码、build.gradleAndroidManifest文件的更改,是时候启动我们的应用并通过Picasso了解图像的无缝加载过程了。

运行应用后,我们应该能够看到我们的设备加载页面,显示应用名称 ImageLoader,并显示以下网址中显示的图像:

滑音

Glide来自 bumptech 的是另一个非常受欢迎的图像加载库。我们将考虑使用Glide并从特定的网址加载图像。

让我们继续在我们的build.gradle和其他相关文件中进行Glide所需的更改。

build.gradle

我们需要添加插件kotlin-kapt并在我们应用的build.gradle文件中添加依赖项。一旦我们同步所做的更改,我们应该能够在代码中使用Glide并加载图像。

Glide库使用标注处理。注释处理有助于生成样板代码,并使代码更容易理解。为了观察运行时工作的实际代码,开发人员可以检查生成的代码并理解库生成的样板代码:

apply plugin: 'kotlin-kapt' implementation 'com.github.bumptech.glide:glide:4.7.1' kapt "com.github.bumptech.glide:compiler:4.7.1" 

The Glide library talks about adding the annotation processor along with Glide in the dependencies. This is applicable for Java. For Kotlin, we need to add the kapt Glide compiler as shown in the code block.

修改后的build.gradle依赖关系应该如下所示:

dependencies {
     implementation fileTree(dir: 'libs', include: ['*.jar'])
     implementation"org.jetbrains.kotlin:kotlin-stdlib-
     jre7:$kotlin_version"
     implementation 'com.android.support:appcompat-v7:27.1.1'
     implementation 'com.android.support.constraint:constraint-
     layout:1.1.0'
     implementation 'com.squareup.picasso:picasso:2.71828'
     implementation 'com.github.bumptech.glide:glide:4.7.1'
     kapt "com.github.bumptech.glide:compiler:4.7.1" 
 testImplementation 'junit:junit:4.12'
     androidTestImplementation 'com.android.support.test:runner:1.0.1'
     androidTestImplementation 
 'com.android.support.test.espresso:espresso-core:3.0.1' }

在项目级build.gradle文件中,我们需要在repositories部分添加mavenCentral() ,如图所示:

allprojects {
     repositories {
         google()
         mavenCentral()
         jcenter()
     }

我们已经完成了对build.gradle文件的更改;我们应该对proguard-rules.pro文件做如下补充。proguard-rules.pro文件使开发人员能够通过移除对应用中未使用和不想要的代码的引用来缩小 APK 大小。

为了保证Glide模块是而不是不受 proguard 萎缩的影响,我们需要明确提到 app 要求我们保留Glide的引用。*-*keep命令确保在构建中保留对Glide和相应模块的引用:

-keep public class * implements com.bumptech.glide.module.GlideModule
 -keep public class * extends com.bumptech.glide.module.AppGlideModule
 -keep public enum com.bumptech.glide.load.ImageHeaderParser$** {
   **[] $VALUES;
   public *;
 }
# for DexGuard only -keepresourcexmlelements manifest/application/meta-data@value=GlideModule

Kotlin 码

我们定义了一个单独的类ImageLoaderGlideModule,它扩展了AppGlideModule()。类上的注释@GlideModule使应用能够访问GlideApp实例。GlideApp实例可用于我们应用中的各种活动:

package com.natarajan.imageloader
*/**
  ** Created by admin on 4/14/2018. **/* import com.bumptech.glide.annotation.GlideModule
 import com.bumptech.glide.module.AppGlideModule

@GlideModule
 class ImageLoaderGlideModule : AppGlideModule()

我们需要在MainActivity Kotlin 类中进行以下更改,以使图像由Glide加载,并在应用启动时显示。

Picasso类似,Glide也有一个简单的语法用于从指定的网址加载图像:

GlideApp.with(this).load("URL").into(imageView);

修改后的MainActivity Kotlin 类应该如下图所示:

package com.natarajan.imageloader

 import android.support.v7.app.AppCompatActivity
 import android.os.Bundle
 import kotlinx.android.synthetic.main.activity_main.*

 class MainActivity : AppCompatActivity() {

 override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         setContentView(R.layout.activity_main)

     if(imageView != null){

   GlideApp.with(this).load("http://goo.gl/gEgYUd").into(imageView);
       }
    }
 }

我们已经完成了Glidebuild.gradleProguard.rules和 Kotlin 类文件所需的所有更改。我们应该看到应用从指定的网址加载图像并显示在ImageView中,如图所示:

摘要

联网和连接外部世界是安卓设备非常强大的功能。我们介绍了网络基础知识,检查网络状态,可用网络类型,以及安卓框架提供的执行网络操作的内置功能。

我们还详细讨论了第三方库PicassoGlide,图像加载库,并在我们的应用中介绍了这些库的实现。

在下一章中,我们将致力于开发一个简单的待办事项应用,并讨论各种概念,如 listview、dialog 等,并学习如何在应用中使用它们。