在上一篇文章中,我们讲解了如何加载本地图片,那么在实际项目中 ImageView
大多数使用场景是加载网络图片,网络图片其实就是存储在服务器上的文件,我们需要从服务器获取到文件的二进制输入流 Inpustream
,然后将其转化为 ImageView
可以加载的 Bitmap
对象。实现网络图片的加载。
这篇文章我们通过使用原始的网络连接和使用第三库来简单讲解 ImageView
网络图片的加载。
先上代码(主要分为三大步骤):
Android
系统规定网络请求操作需要在子线程完成。主要是因为网络请求属于耗时操作,如果在主线程发起网络请求会导致主线程在网络请求期间,无法及时响应用户的操作,Activity
声明的 Handler
对象把在子线从网络获取到的 Bitmap
对象,转移到 UI
线程。UI
。public class ImageNetActivity extends AppCompatActivity {
/**
* 7.要知道 这里现在是子线程 更新UI的操作需要在主线程(UI线程)完成。
* 在 Activity 中声明 Handler 对象,并复写它的 handleMessage 方法
*/
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == 1010) {
Bitmap bitmap = (Bitmap) msg.obj;
setImageView(bitmap);
}
}
};
private ImageView mImageView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_image_net);
mImageView = findViewById(R.id.image);
loadImageUrl("https://cdn.pixabay.com/photo/2017/05/09/23/02/dog-2299482_960_720.jpg");
}
private void loadImageUrl(final String imageUrl) {
// Android 系统强制网络请求需要在子线程操作
new Thread(new Runnable() {
@Override
public void run() {
InputStream inputStream = null;
try {
// 1. 把传过来的路径转成URL
URL url = new URL(imageUrl);
// 2.通过URL 建立网络连接
// --> url.openConnection() 返回 URLConnection
// ,它是一个抽象类,这里需要通过Http协议建立连接,需要它的实现类 HttpURLConnection
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
// 3. 使用GET方法访问网络
urlConnection.setRequestMethod("GET");
// 配置网络超时时间为 10秒
urlConnection.setConnectTimeout(10000);
//4. 获取Http协议 响应码
int responseCode = urlConnection.getResponseCode();
// Http 协议 规定 响应码为200时 请求成功
if (responseCode == 200) {
// 5. 得知 请求资源成功后,获取图片文件输入流
inputStream = urlConnection.getInputStream();
// 6. 把获取到的 文件输入流 通过 系统Api 转换为 ImageView 可以识别的 Bitmap 对象
Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
// 7.要知道 这里现在是子线程 更新UI的操作需要再主线程(UI线程)完成。
Message message = mHandler.obtainMessage();
message.obj = bitmap;
message.what = 1010;//
mHandler.sendMessage(message);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (inputStream != null) {
try {
//关闭流
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}).start();
}
}
/**
* 8.利用从 Mesage 从子线程中携带回来的 Bitmap 对象,在UI线程设置图片
*/
private void setImageView(Bitmap bitmap) {
mImageView.setImageBitmap(bitmap);
}
}
上面是创建一个空的 Activity
,布局文件中只有一个 ImageView
控件。 注意:在 Android
中 主线程 也叫 UI
线程。 UI
线程是响应用户操作的线程,一旦在 UI
线程中存在好在操作,就会阻塞 UI
线程,导致无法及时响应用户操作事件。所以在 Android
系统 4.0 后,强制网络请求操作必须在子线程。但问题是:所有更新 UI
的操作又必须在 UI
线程,这就是我们必须把网络请求的结果,转移到主线程才能更新 UI
。怎么转移呢? 那就是 Handler
。这个现在知道怎么用就行,后面我们会仔细讲解。
上面的代码中,利用系统自带 ULRConnection
请求网络请求的步骤注释已经很详细了。可仔细了解其网络请求步骤,大致的套路是一样的。
特别特别特别注意:网络请求是需要权限的,你需要在 AndroidManifet.xml
文件中声明一句用户权限。至于权限的概念后面我们会细聊。现在只需要在 AndroidManifet.xml
文件申明即可。
<uses-permission android:name="android.permission.INTERNET"/>
我们来思考几个问题,如果在真实项目中,我们这样加载图片你觉得可以吗?
........
答案是:不可以。
所以我们需要一个比较完善的图片加载系统,这个系统最基础的要包括图片的缓存策略:先从网络请求图片,在手机内存中和SD卡中各自保存一份图片资源。当重启应用时,如果图片存在SD卡中,就可以从SD卡中直接获取图片加载。并且SD卡所能存储的图片总数是一定的,会不断的根据策略去舍去图片的存留。
还有非常重要的一点:从图片加载库的使用者角度讲,使用者无需关心内部到底是使用内存缓存,还是SD卡缓存,或是直接从网络获取的。这对于使用者来讲,内部的一切用户并不需要知道。使用者只需要知道加载图片的接口。
对于图片加载框架,内部实现是极其复杂的,目前我们并不需要了解其内部实现方式。
下面我们就使用最常用的图片加载框架 Glide
来完成我们图片加载框架使用的演示。
我们要知道,因为Android是开源的,所以会产生各种各样的第三方框架,而我们不能盲目的去使用,要根据实际情况,从这之中挑选出最优的、最适合自己项目的框架,合理有效的去使用各种资源。而我们推荐的 Glide
是经过不断的和其他框架对比所挑选出来性价比最高的!
目前国内主流的第三方网络图片加载库有 Glide
(主推)、 ImageLoader
、 Picasso
、 Volley
、 Fresco
等,感兴趣的小伙伴可以去搜索一下这些加载库的全方面对比,百度一哈比比皆是,我们就不再这里将网上的一些大神所对比的实际内容再复述一遍啦。下面请跟我走4步,完成你人生中第一次加载网络图片吧!!!
1.首先我们要通过依赖 Glide
图片加载库。
Glide github 官方地址
2.在官方文档中我们找到需要依赖的 Glide
库地址。
implementation 'com.github.bumptech.glide:glide:4.9.0'
3.将依赖地址放置到 app 模块下的 build.gradle 中如图:
添加完成后,我们点击 右上角的 Sync Now ,从网络下载依赖库到本地,并依赖到 app 模块。
4.我们在创建的空 Activity
当中,为 ImageView
控件利用 Glide
加载图片。
okay,搞定!!使用第三图片加载库是不是很简单。
其实里面的大致操作就是我们在第一个问题中书写的代码,里面多的就是各种缓存策略和逻辑处理。