Android开发高级进阶—多线程(实现简单下载器)

Android开发高级进阶——多线程(实现简单下载器)

每个Android应用在被启动时都会创建一个线程,这个线程称为主线程或UI线程,Android应用的所有操作都会运行在这个线程中。但是为了保证UI的流畅性,通常会将耗时操作放到子线程中,例如IO操作、网络请求等。而几乎每个Android应用都会涉及到网络请求等耗时操作,所以多线程对于Android来说变得至关重要。

什么是多线程?

线程:是进程中单一的连续控制流程/执行路径。

多线程:多个线程并行执行。

二.为什么要使用多线程?

使用多线程可以提高效率,并且不会使程序出现卡顿现象(比如ANR)。

三.什么时候使用多线程?

Android3.0以及以后的版本中,禁止在主线程执行网络请求,否则会抛出异常,可见在UI线程中执行耗时操作是不推荐的行为。所以,在进行与耗时操作同步进行的操作时(即并行)使用多线程。

四.如何使用多线程?

我们经常说Android中的主线程是线程不安全的,所以只能在主线程中更新UI。那么如何更新主线程且保证线程是安全的呢?

Android中提供了保证线程安全的几种解决方案:

  • 使用Handler实现线程之间的通信。
  • Activity.runOnUiThread(Runnable):一般在Activity的Thread中运用。
  • View.post(Runnable)
  • View.postDelayed(Runnable, long)

Android中的线程分为主线程(UI线程)和工作线程。

  • 主线程(UI线程):程序运行时被创建的线程。
  • 工作线程:自己创建的线程。

以上两个线程之间的通信最基本的有两种:

Thread和Runnable

这里通过实现一个简单的下载器来学习Thread和Runnable。

这个下载器就一个界面,包含一个输入框,一个进度条,用来显示下载进度,用来输入下载地址,一个按钮,用来开始下载。

界面代码如下:activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="16dp"
android:layout_marginTop="16dp"
tools:context="com.trampcr.downloaddemo.MainActivity">

<EditText
    android:id="@+id/et_url"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="20dp"
    android:hint="请输入下载地址" />

<ProgressBar
    android:id="@+id/pb_down_load"
    style="@style/Widget.AppCompat.ProgressBar.Horizontal"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_below="@id/et_url"
    android:layout_marginTop="30dp"
    android:max="100" />

<TextView
    android:id="@+id/tv_progress"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_below="@id/pb_down_load"
    android:layout_marginTop="20dp"
    android:text="下载进度"
    android:textColor="#000000" />

<Button
    android:id="@+id/btn_start_download"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_below="@id/tv_progress"
    android:layout_centerHorizontal="true"
    android:layout_marginTop="50dp"
    android:background="@drawable/btn_style"
    android:text="开始下载"
    android:textColor="#000000" />

</RelativeLayout>

细心的人可能会注意到这里的按钮用了一个背景@drawable/btn_style,这里是自定义按钮的形状。代码如下:btn_style.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:topLeftRadius="10dp"
    android:radius="8dp"
    android:topRightRadius="10dp"
    android:bottomLeftRadius="10dp"
    android:bottomRightRadius="10dp" />
<stroke android:color="#000000"
    android:width="0.7dp"/>
</shape>

MainActivity.java

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

//public static final String DOWNLOAD_URL = "http://psoft.33lc.com:801/small/rootexplorer_33lc.apk";
private Button mBtnStartDownload;
private EditText mEtUrl;
private String mUrl;
private ProgressBar mPbDownload;
private TextView mTvProgress;

private Handler mHandler = new DownloadHandler(this);

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

    mEtUrl = (EditText) findViewById(R.id.et_url);
    mBtnStartDownload = (Button) findViewById(R.id.btn_start_download);
    mPbDownload = (ProgressBar) findViewById(R.id.pb_down_load);
    mTvProgress = (TextView) findViewById(R.id.tv_progress);

    mBtnStartDownload.setOnClickListener(this);
}

@Override
public void onClick(View v) {
    mUrl = mEtUrl.getText().toString().trim();
    new Thread(new Runnable() {
        @Override
        public void run() {
            download(mUrl);
        }
    }).start();
}

private void download(String mUrl) {
    try {
        URL url = new URL(mUrl);
        URLConnection urlConnection = url.openConnection();
        int contentLength = urlConnection.getContentLength(); //下载文件大小
        InputStream inputStream= urlConnection.getInputStream();
        String downloadFolderName = Environment.getExternalStorageDirectory() + File.separator + "trampcr" + File.separator;
        File file = new File(downloadFolderName);
        if (!file.exists()){
            file.mkdir();
        }
        String fileName = downloadFolderName + "zxm.apk";
        File apkFile = new File(fileName);
        if (apkFile.exists()) {
            apkFile.delete();
        }
        int downloadSize = 0;
        byte[] buff = new byte[1024];
        int length = 0;
        OutputStream outputStream = new FileOutputStream(fileName);
        while ((length = inputStream.read(buff)) != -1) {
            outputStream.write(buff, 0, length);
            downloadSize += length;
            int progress = downloadSize * 100 / contentLength;
            Message msg = mHandler.obtainMessage();
            msg.what = 0;
            msg.obj = progress;
            mHandler.sendMessage(msg);
        }
        outputStream.close();
        inputStream.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

public static class DownloadHandler extends Handler{
    public final WeakReference<MainActivity> weakRefActivity;

    public DownloadHandler(MainActivity mainActivity) {
        weakRefActivity = new WeakReference<MainActivity>(mainActivity);
    }

    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        MainActivity activity = weakRefActivity.get();//判断是否被回收
        switch (msg.what){
            case 0:
                int progress = (int) msg.obj;
                activity.mPbDownload.setProgress(progress);
                activity.mTvProgress.setText("下载进度:" + progress + "%");
                if (progress == 100){
                    Toast.makeText(activity, "下载完成", Toast.LENGTH_LONG).show();
                }
                break;
            }
        }
    }
}

所需要的权限:

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

效果如如下:

注:这里面用到了一个我不太熟悉的知识:WeakReference的理解与使用

文/trampcr(简书作者)
原文链接:http://www.jianshu.com/p/6a558a23084a
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值