第八章 多线程

如何使用多线程

一、AsyncTask

        AsyncTask是Android提供的一种异步消息处理工具,可以非常简单地从子线程切换到主线程。AsyncTask <Params,Progress,Result> 类指定三个泛型参数:
    ◆  Params —— 在执行AsyncTask时需要传入的参数,是一个可以改变size的数组;
    ◆  Progress —— 后台任务执行时,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为进度单位;
    ◆  Result —— 当任务执行完毕后,如果需要对结果进行返回,则使用这里指定的泛型作为返回值类型。
class DownloadTask extends AsyncTask<Void,Integer,Boolean>{
        .....
}
        上面这个我们自定义的一个简单的AsyncTask,第一个泛型参数指定为Void,表示执行AsyncTask的时候不需要传入参数给后台;第二个泛型参数指定为Integer,表示使用整型数据来作为进度显示单位;第三个泛型参数指定为Boolean,则表示使用布尔型数据来反馈执行结果。

二、子线程和主线程的通信

        很多时候,我们需要把子线程的数据在UI上展示出来,而我们也知道子线程中是不能直接进行UI操作的,这种情况下,我们就需要用异步线程来处理。下面是一个下载的子线程,我们要把子线程中的下载进度实时展示在UI上:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private EditText mEditText;
    private Button mButton;
    private ProgressBar mProgressBar;
    private String mUrlString;
    private Handler mHandler = new DownloadHandler(this);
    private TextView mTextView;

    public TextView getTextView() {
        return mTextView;
    }

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

        mEditText = (EditText) findViewById(R.id.edit_text);
        mButton = (Button) findViewById(R.id.button);
        mProgressBar = (ProgressBar) findViewById(R.id.progressBar);
        mTextView = (TextView) findViewById(R.id.textView);
        mButton.setOnClickListener(this);

        mEditText.setText("http://download.sj.qq.com/upload/connAssitantDownload/upload/MobileAssistant_1.apk");
    }

    @Override
    public void onClick(View v) {
        mUrlString = mEditText.getText().toString();
        Log.i("我是URL",mUrlString);

        switch (v.getId()){
            case R.id.button:
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        download(mUrlString);
                    }
                }).start();
                break;
        }
    }

    private void download(String urlString) {
        try {
            URL url = new URL(urlString);
            URLConnection connection = url.openConnection();
            InputStream inputStream = connection.getInputStream();

            int contentLength = connection.getContentLength();    //要下载的文件大小
            Log.i("文件大小",contentLength+"");

            String downloadFoldersName = Environment.getExternalStorageDirectory() + File.separator + "songhaoran" +File.separator;
            File file = new File(downloadFoldersName);
            if (!file.exists()){
                file.mkdir();    //如果没有文件夹,创建文件夹
            }

            //下载后的文件名
            String fileName = downloadFoldersName + "test.apk";
            File apkFile = new File(fileName);
            if (apkFile.exists()){
                apkFile.delete();   //如果这个文件存在,删除这个文件
            }

            int downloadSize = 0;
            byte[] bytes = new byte[1024];
            int length;

            OutputStream outputStream = new FileOutputStream(fileName);     //创建文件
            while ((length = inputStream.read(bytes)) != -1){              //读取每次循环的字节流的大小,当length1!=0,就一直循环
                outputStream.write(bytes,0,length);                       //把数据通过循环写入到bytes中
                downloadSize += length;                                  //累加每次循环的数据大小
                int progress = downloadSize*100/contentLength;          //计算出下载进度
                Log.i("我是进度", String.valueOf(progress));
                mProgressBar.setProgress(progress);    //这里直接就可以在子线程更新UI,因为在setProgress方法的源码中,已经封装了post异步机制

                Message message = new Message();
                message.obj = progress;
                message.what = 0;
                mHandler.sendMessage(message);
            }
            Log.i("下载完成","ok");
            inputStream.close();
            outputStream.close();

        } catch (IOException e) {
            e.printStackTrace();
            Log.i("下载失败","fail");
        }
    }

    //handler类要写成静态的内部类,防止泄漏
    public static class DownloadHandler extends Handler{       

        public final WeakReference<MainActivity> mActivity;    //弱引用

        public DownloadHandler(MainActivity activity) {
            mActivity = new WeakReference<>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            MainActivity mainActivity = mActivity.get();

            switch (msg.what){
                case 0:
                    int progress = (int) msg.obj;
                    mainActivity.getTextView().setText(progress+"%");
                    if (progress == 100){
                        Toast.makeText(mainActivity,"download success",Toast.LENGTH_SHORT).show();
                    }
                    break;
            }

        }
    }
}


        点击下载,开启一个子线程,在子线程中执行download()方法。在download()方法中,首先打开URL的连接,创建一个inputString来接收数据;接下来,我们要创建下载文件在手机中的路径和文件名,如果手机中没有指定文件夹就创建该文件夹,如果该路径下要下载的文件已存在,则删除该文件重新下载;
        设置好下载环境,先创建一个字节流byte[ ],用outputString创建下载文件; 接下来执行while循环,读取上次循环得到的bytes作为判断条件;在循环中,写入每次循环的数据到bytes,累加每次循环的数据大小,这样就可以算出下载进度progress,然后用Handler把progress发送给指定位置;
        接下来就是重点了,利用Handler发送progress给主线程,主线程接收到progress后在UI上展示出来。


拾遗:

1.  在整理、重构代码时,按F6可以将一个类移到其他地方;
2.  UI线程是不安全的。也就是说,如果想要更新应用程序里的UI元素,则必须在主线程中进行,否则就会出现异常;
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值