Android开发系列——实战篇11:多线程与异步机制

本文介绍安卓的多线程与异步任务处理的机制

目录

  • 一、多线程
    • 1.主线程与子线程
    • 2.Handler用法详解
      • 使用Handler发送post请求
      • 使用Handler处理Message消息

一、多线程

1.主线程与子线程

每个 Android 应用都有一个主线程,负责处理界面(包括测量和绘制视图)、协调用户互动以及接收生命周期事件。

主线程一般被用于操作UI界面,与用户进行交互,所以也被称为UI线程。

与此相对的,在一些耗时比较久的任务处理中,比如从网络上下载资源、进行后台服务等,这些是用户看不到的地方。我们不能直接将这些任务放在主线程中进行处理,不然会耗费大量资源并且出现错误,降低用户体验。

所以一般遇到这些任务,需要在主线程之外重新开一个子线程进行任务处理,也就是所谓的非UI线程。

:子线程(非UI线程)里的操作是不能直接对UI界面进行更新的。

通过一个从网络上下载图片的案例来理解主线程与子线程。

主要逻辑:在界面上创建一个Button按钮,点击后从网络上下载一幅图片。

主要步骤:
1.在布局文件中创建按钮和图片视图,给按钮添加事件。
2.在Java文件中编写事件处理逻辑。
3.在AndroidManifest清单文件中添加网络权限。

activity_main.xml文件

 <Button
        android:id="@+id/bt1"
        adnroid:onClick="download"
        android:layout_width="128dp"
        android:layout_height="wrap_content"
        android:layout_alignParentStart="true"
        android:layout_alignParentTop="true"
        android:layout_alignParentEnd="true"
        android:layout_marginStart="134dp"
        android:layout_marginTop="100dp"
        android:layout_marginEnd="134dp"
        android:text="Download" />

 <ImageView
        android:id="@+id/image"
        android:layout_width="300dp"
        android:layout_height="200dp"
        android:layout_centerInParent="true"
        android:background="@color/colorAccent" />

效果:
Android开发系列——实战篇11:多线程与异步机制_第1张图片

Java类文件

package com.example.handler;

import androidx.appcompat.app.AppCompatActivity;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;

import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;

public class MainActivity extends AppCompatActivity {

    //实例化ImageView对象
    ImageView mimageView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initView();
    }

    private void initView() {
        //初始化方法中将imageView对象绑定视图
        mimageView = (ImageView)findViewById(R.id.image);
    }

    public void download(View view){//按键处理函数
        //创建字符串变量,将网络图片的地址传入
        String urlstr = "https://www.baidu.com/s?wd=%E4%BB%8A%E6%97%A5%E6%96%B0%E9%B2%9C%E4%BA%8B&tn=SE_Pclogo_6ysd4c7a&sa=ire_dl_gh_logo&rsv_dl=igh_logo_pc";
        URL url = null;
        try {//捕获异常
            //实例化URL对象,将地址传入
            url = new URL(urlstr);
            //创建URLConnection对象,进行连接
            URLConnection con = url.openConnection();
            //创建输入流对象接受输入流
            InputStream in = con.getInputStream();
            //创建流对象解码输入流
            Bitmap bm = BitmapFactory.decodeStream(in);
            //将解码后的流(图片)传入视图
            mimageView.setImageBitmap(bm);
        } catch (Exception e) {
            e.printStackTrace();
        }


    }


}

AndroidManifest清单文件

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

这样基本逻辑就已经完善了,但是实际编译过程中却无法实现功能,查看日志可以发现这一行:

在高版本的安卓中,网络请求是不能放在主线程里的,一定要开启一个子线程去处理网络任务。

开启子线程后的Java类文件

package com.example.handler;

import androidx.appcompat.app.AppCompatActivity;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;

import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;

public class MainActivity extends AppCompatActivity {

    //实例化ImageView对象
    ImageView mimageView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initView();
    }

    private void initView() {
        //初始化方法中将imageView对象绑定视图
        mimageView = (ImageView)findViewById(R.id.image);
    }

    public void download(View view){//按键处理函数

        //创建一个内部类开启子线程
        Thread t = new Thread(){
            @Override
            public void run(){
                String urlStr = "https://www.baidu.com/s?wd=%E4%BB%8A%E6%97%A5%E6%96%B0%E9%B2%9C%E4%BA%8B&tn=SE_Pclogo_6ysd4c7a&sa=ire_dl_gh_logo&rsv_dl=igh_logo_pc";
                URL url = null;
                try {//捕获异常
                    //实例化URL对象,将地址传入
                    url = new URL(urlStr);
                    //创建URLConnection对象,进行连接
                    URLConnection con = url.openConnection();
                    //创建输入流对象接受输入流
                    InputStream in = con.getInputStream();
                    //创建流对象解码输入流
                    Bitmap bm = BitmapFactory.decodeStream(in);
                    //将解码后的流(图片)传入视图
                    mimageView.setImageBitmap(bm);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

        };
        t.start();
    }


}

但是依旧会进行报错,原因在于:
子线程是不能对UI界面直接进行更新的。、如果要对UI界面进行更新,一定是子线程给主线程发消息,由主线程对界面进行更新。

UI线程与非UI线程之间通过Handler进行通信。

完整的Java类代码

package com.example.handler;

import androidx.appcompat.app.AppCompatActivity;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.ImageView;

import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;

public class MainActivity extends AppCompatActivity {

    //实例化ImageView对象
    ImageView mimageView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initView();
    }

    //创建一个Handler对象
    Handler h = new Handler(){
        @Override
        public void handleMessage(Message msg){
            //接受来自子线程中Message的消息并强制转换为数据流类型
            Bitmap bm = (Bitmap)msg.obj;
            //更新视图
            mimageView.setImageBitmap(bm);
        }
    };

    private void initView() {
        //初始化方法中将imageView对象绑定视图
        mimageView = (ImageView)findViewById(R.id.image);
    }

    public void download(View view){//按键处理函数

        //创建一个内部类开启子线程
        Thread t = new Thread(){
            @Override
            public void run(){
                //直接复制网络图片的地址传入参数
                String urlStr = "http://img4.imgtn.bdimg.com/it/u=1906469856,4113625838&fm=26&gp=0.jpg";
                URL url = null;
                try {//捕获异常
                    //实例化URL对象,将地址传入
                    url = new URL(urlStr);
                    //创建URLConnection对象,进行连接
                    URLConnection con = url.openConnection();
                    //创建输入流对象接受输入流
                    InputStream in = con.getInputStream();
                    //创建流对象解码输入流
                    Bitmap bm = BitmapFactory.decodeStream(in);
                    //将解码后的流(图片)传入视图
                    //mimageView.setImageBitmap(bm);

                    //创建一个Message对象用来发送消息
                    Message m = new Message();
                    m.obj = bm;
                    //向主线程发送消息
                    h.sendMessage(m);

                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

        };
        t.start();
    }


}

这样效果就出来了:
Android开发系列——实战篇11:多线程与异步机制_第2张图片

2.Handler用法详解

使用Handler发送post请求

通过第一节的例子可以看到在主线程与子线程之间使用Handler这个消息处理器进行消息的传递与处理。
第一节的例子中是使用Handler的Message方法进行发送消息,在子线程中进行网络资源下载后传递消息给主线程进行UI更新。
Handler还有其他的请求方法,如post请求。

通过一个实例来理解post请求的一些方法。

主要功能:通过post请求实现一个图片的轮播行为。

使用Handler处理Message消息

你可能感兴趣的:(Android开发系列)