毕业设计:下载

一、主要思路

        访问网络资源并且下载下来的功能是通过OkHttp来实现的,具体操作在之前的文章中有。最麻烦的是各组件之间的交互问题。例如,我设置一个存放线程的列表,每建立一个下载事项就新建一个对应线程,并将其放到下载列表中,然后线程会自动执行到文件下载完毕。现在问题来了,我需要将下载进程实时反馈到“任务”面板上,并且还要能通过“任务”面板上的暂停/开始按钮操作线程,这怎么实现?

        似乎能想到的唯一办法就是通过Handler实现。我在主线程里面定义一个Handler,初始化的时候改变这个Handler的handleMessage()方法,让它每接收到一个值为1的Message就致使adapter执行notifyDataSetChanged()方法。当然这里的这个adapter是和“任务”面板的listView绑定起来的。然后我想方设法地把这个handler通过参数传递到线程列表的Thread对象里面,然后每当Thread执行的进度发生变化(也就是progress向前推进),就通过这个handler发送一个值为1的Message,于是这两者就关联起来了。至于暂停/开始按钮的点击事件,是在adapter的getView()方法写好的,一旦触发就会去找线程列表,找到对应的线程并执行暂停/开始操作。

二、线程和线程的死锁问题

        因为原来的suspend()方法和stop()方法容易导致死锁,所以在这个版本被废弃了,想要实现类似的功能只能自己通过设置标志位、wait()方法和synchronized来实现。

        如代码中所示,resume(),suspend(),和stop()方法都只是修改运行状态(status)。在run()方法中,首先判断是否为stop,如果为假则再判断是否为suspend,如果为真则执行wait()方法给其他线程让路,如果为假则执行所需执行的内容。方法本身是不复杂的,我为了给程序添加对应的功能让其看起来有些复杂。

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;

import com.example.vcloud_3_25.MainActivity;
import com.example.vcloud_3_25.utils.L;
import com.example.vcloud_3_25.utils.TaskManager;

public class DownloadThread implements Runnable {
    public static final int STOP = -1;
    public static final int SUSPEND = 0;
    public static final int RUNNING = 1;
    protected int status = 1;
    private String name;
    private String fileName;
    private int fileId;
    private int progress;
    private String fileType;
    
    private Handler mHandler;//

    private MainActivity activity;

    public DownloadThread(MainActivity context,Handler handler, String fileName, int fileId,String fileType) {
        this.mHandler = handler;
        
        this.activity = context;

        TaskManager.count++;
        this.name = "DownloadThread-" + TaskManager.count;

        this.fileId = fileId;
        this.fileName = fileName;
        this.fileType = fileType;

        progress = 0;
    }

    public String getFileType(){
        return this.fileType;
    }
    public int getProgress() {
        return progress;
    }

    public String getName() {
        return name;
    }

    public String getFileName() {
        return fileName;
    }

    public void setFileName(String fileName) {
        this.fileName = fileName;
    }

    public int getFileId() {
        return fileId;
    }

    public void setFileId(int fileId) {
        this.fileId = fileId;
    }

    @Override
    public boolean equals(Object o) {
        DownloadThread t2 = (DownloadThread) o;
        return t2.getName() == this.getName();
    }

    @Override
    public void run() {

        OkHttpClient client = new OkHttpClient();
        final Request request = new Request.Builder().url(
                "http://192.168.23.1:8080/VCloud/files/" + fileId).build();
        Call call = client.newCall(request);
        call.enqueue(new Callback() {

            @Override
            public void onResponse(Call call, Response response) {
                L.d("on response");
                long sum = 0;
                long total;
                int len = 0;

                InputStream is = response.body().byteStream();

                try {
                    total = response.body().contentLength();

                    File file = new File(Environment
                            .getExternalStorageDirectory(), "VcloudDownload/"
                            + fileName);
                    byte[] buf = new byte[128];
                    FileOutputStream fos = new FileOutputStream(file);

                    L.d("start download");
                    while (status != STOP & (len = is.read(buf)) != -1) {
                        if (status == SUSPEND) {
                            try {
                                synchronized (this) {
                                    wait(500);
                                }

                            } catch (InterruptedException e) {
                                L.d(e.getMessage());
                            }
                        } else {

                            fos.write(buf, 0, len);
                            sum += len;
                            double temp = sum / (double) total;
                            int recentProgress = (int) (temp * 100);
                            if (recentProgress != progress) {
                                progress = recentProgress;
                                L.d("sum:" + sum + ",  progress:" + progress);
                                Message msg = mHandler.obtainMessage(1);
                                mHandler.sendMessage(msg);

                            }

                        }
                    }

                    fos.flush();
                    fos.close();
                    is.close();

                } catch (IOException ioe) {
                    ioe.printStackTrace();
                    L.d(ioe.getMessage());
                }

                L.d("download success!");
                progress = 100;
                mHandler.sendMessage(mHandler.obtainMessage(1));
            }

            @Override
            public void onFailure(Call call, IOException e) {
                L.d("enqueue failure!");
                L.d(e.getMessage());
            }
        });

    }

    public synchronized void resume() {
        status = RUNNING;
        notifyAll();
        L.d("Thread " + name + "resume!");
    }

    public void suspend() {
        status = SUSPEND;
        L.d("Thread " + name + "suspend!");
    }

    public void stop() {
        status = STOP;
        L.d("Thread " + name + "stop!");
    }

}

三、DownloadListAdapter

        这个ListViewAdapter比较特殊,它需要一个List<DownloadThread>来构造,而这个List就是从主线程里面传过来的。它里面的item和List里面的Thread是一一对应的,因此不仅数据随时同步(通过notifyDataSetChanged就能实现),而且对按钮绑定监听器也能直接对每一个线程进行控制。

        ViewHolder其实是比较难理解的一个东西。之前我使用的adapter是直接继承自SimpleAdapter所以没有使用到这个方法。根据我的理解,holder应该是一个指向ListView中item的指针,每次新增加一个item这个指针就会指向新增加的item,所以对holder中的成员操作实际就是对item中的成员操作。值得注意的是,对button设置监听器的时候,对button的操作一定是要基于onClick(View v)中的v,而不是holder.button,否则不管触发的是哪个item的button,都只会有最后一个item作出反应。

package com.example.vcloud_3_25.utils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import thread.DownloadThread;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;

import com.example.vcloud_3_25.MainActivity;
import com.example.vcloud_3_25.R;

public class DownloadListAdapter extends BaseAdapter {

    private List<DownloadThread> mList;
    private MainActivity context;
    private ViewHolder holder = null;

    public DownloadListAdapter(MainActivity context) {
        this.mList = context.mTaskManager.mDownList;
        this.context = context;
    }

    @Override
    public int getCount() {
        return mList.size();
    }

    @Override
    public Object getItem(int position) {
        return mList.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View view = convertView;
        if (view == null) {
            LayoutInflater inflater = (LayoutInflater) context
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            view = inflater.inflate(R.layout.task_item, parent, false);

            holder = new ViewHolder();
            holder.fileName = (TextView) view
                    .findViewById(R.id.textView_taskName);
            holder.fileImag = (ImageView) view.findViewById(R.id.imag_task);
            holder.progressBar = (ProgressBar) view
                    .findViewById(R.id.progressBar_task);
            holder.progressCount = (TextView) view
                    .findViewById(R.id.textView_progress);
            holder.button = (Button) view.findViewById(R.id.button_task);
            holder.complete = (TextView) view
                    .findViewById(R.id.textView_complete);

            view.setTag(holder);

        } else {
            holder = (ViewHolder) view.getTag();
        }

        final DownloadThread thread = (DownloadThread) getItem(position);
        if (thread != null) {
            holder.fileName.setText(thread.getFileName());
            holder.fileImag.setImageResource(FileHelper.getFileImag("other"));
            holder.progressBar.setProgress(thread.getProgress());
            holder.fileImag.setImageResource(FileHelper.getFileImag(thread
                    .getFileType()));
            holder.progressBar.setMax(100);
            holder.progressCount.setText(thread.getProgress() + "%");
            holder.button.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {
                    if (((TextView) v).getText().equals("暂停")) {
                        ((TextView) v).setText("开始");

                        // 暂停该线程
                        thread.suspend();
                    } else {
                        ((TextView) v).setText("暂停");

                        // 继续执行该线程
                        thread.resume();
                    }
                }
            });
            if (thread.getProgress() == 100) {
                holder.button.setVisibility(View.INVISIBLE);
                holder.complete.setVisibility(View.VISIBLE);
            }
        }

        return view;
    }

    public class ViewHolder {
        public TextView fileName;
        public ImageView fileImag;
        public ProgressBar progressBar;
        public TextView progressCount;
        public Button button;
        public TextView complete;
    }
}

四、TaskManager

        TaskManager其实是一个管理所有任务的类,所以添加线程到下载列表之类的操作都是基于这个类实现的。目前还在实现上传功能,所以给上传列表留了一个空。

package com.example.vcloud_3_25.utils;

import java.util.ArrayList;
import java.util.List;

import thread.DownloadThread;
import thread.UploadThread;

import com.example.vcloud_3_25.MainActivity;

public class TaskManager {
    public List<DownloadThread> mDownList;
    public List<UploadThread> mUpList;
    private MainActivity context;
    public static int count = 0;

    public TaskManager(MainActivity activity) {
        context = activity;
        init();
    }

    public void init() {
        mDownList = new ArrayList<DownloadThread>();
        mUpList = new ArrayList<UploadThread>();
    }
}

五、MainActivity

        核心方法都说过了,所以只贴Handler的部分。

public Handler mHandler;
mHandler = new Handler(){

    @Override
    public void handleMessage(Message msg) {
        if(msg.what==1){
            downloadAdapter.notifyDataSetChanged();
        }
    }
            
};

六、手机如何访问电脑上的Tomcat

电脑端下载一个免费WIFI,我使用的是腾讯管家的。打开免费WIFI,让手机连到这个网上,至此手机和电脑就连到同个局域网了。打开电脑的网络管理能看到这个局域网中电脑的IP,例如我的电脑端是192.168.23.1,而手机端是192.168.23.2。打开服务器,手机端访问192.168.23.1:8080,如果出现tomcat欢迎页面即成功。

如果电脑能访问到tomcat页面而手机端不能,多半是电脑防火墙的问题,关了它。

如果电脑端都不能访问,多半是tomcat配置出错了,最好的方法是重新解压重新绑定到Eclipse。

posted @ 2017-04-17 13:59  viaduct  阅读(260)  评论(0编辑  收藏  举报