Android编程思想,面向对象程序设计第六篇——封装(下)功能模块化,MVC分离

来源:互联网 发布:淘宝用户名字怎么改 编辑:程序博客网 时间:2024/04/26 06:14

    上一节讲到了布局模块化,在实际开发中还有一个非常重要的是功能模块化。特别是团队开发的情况下,如果你没有进行模块化处理而是直接把某个功能的实现写在

Activity里面的话,时间长了发现很多都是重复性开发。明明同一个功能,却出现了你在你的Activity写你的实现方法,他在他的Activity写他的实现方法,浪费了大把时间。而且一旦这个功能点需要修改的时候,需要同时修改非常多的逻辑代码,还不一定都改的对。

    所以正确的作法是把功能点封装载一个专门的类里面,这个类可以用接口和方法提供输入输出就可以了。这就是所谓的MVC。M是model负责处理数据逻辑,V是视图

负责处理界面,C是控制器负责连接数据和视图。

   如下面的一个下载器类:


监听下载状态的接口,也就是MVC中的C(控制器)

public interface IDownloadApkInterProgress {
         void onDownloadWait(DownloadGameInfoBean apkInfoBean);
         void onDownloadStart(DownloadGameInfoBean apkInfoBean);
         void onDownloadProgress(DownloadGameInfoBean apkInfoBean);
         void onDownloadEnd(DownloadGameInfoBean dib);
         void onDownloadFailed(DownloadGameInfoBean apkInfoBean, String errMsg);
         void onDownloadPaused(DownloadGameInfoBean apkInfoBean);
         void onDownloadDelete(DownloadGameInfoBean apkInfoBean);
}


处理下载的业务逻辑并通过IDownloadApkInterProgress 接口输出下载进度信息,也就是MVC中的M(模型)

public class DownloadApkRequest extends IRequest {
    private Context mContext;
    private Handler mHandler;
    private BaseGameInfoBean mBaseGameInfoBean;
    private IDownloadApkInterProgress mListener;
    private DownloadManager.IDownloadPause mPauseListener;
    private long mProgressUpdateTime = 0;
    private long mProgressUpdatePos  = 0;
    private boolean mIsRunning = false;
    private long mDateTime;
    private long mCurrentTime;
    private int mFrom;//在哪个界面点击下载
    public DownloadApkRequest(Context context, Handler handler, int from,BaseGameInfoBean baseGameInfoBean, IDownloadApkInterProgress listener) {
        mContext           = context;
        mHandler           = handler;
        mBaseGameInfoBean = baseGameInfoBean;
        mListener          = listener;
        mFrom              = from;
        init();
    }


    private void init() {
            infoBean = new DownloadGameInfoBean();
            infoBean.setSavePath(CommonHelper.getApkPath(ShuoWanApplication.getAppContext(),mBaseGameInfoBean.getGameName()));
            infoBean.setState(DownloadManager.TO_DOWNLOAD);
            infoBean.setCurrentPos(0);
            infoBean.setEndPos(mBaseGameInfoBean.getGameSize());
            //基础信息
            infoBean.setGameId(mBaseGameInfoBean.getGameId());
            infoBean.setGameName(mBaseGameInfoBean.getGameName());
            infoBean.setPkgName(mBaseGameInfoBean.getPkgName());
            infoBean.setGameVersionName(mBaseGameInfoBean.gameVersionName);
            infoBean.setVersionCode(mBaseGameInfoBean.versionCode);
            infoBean.setGameDownloadUrl(mBaseGameInfoBean.gameDownloadUrl);
            infoBean.setGameIconUrl(mBaseGameInfoBean.gameIconUrl);
            infoBean.setGameDownloadNum(mBaseGameInfoBean.gameDownloadNum);
            infoBean.setGameLanguage(mBaseGameInfoBean.gameLanguage);
            infoBean.setGameStar(mBaseGameInfoBean.getGameStar());
            infoBean.setGameScore(mBaseGameInfoBean.getGameScore());
            infoBean.setFromInterface(mFrom);
            new File(infoBean.getSavePath()).delete();
            notifyDownloadWait(infoBean);
    }


    public String getPackageName() {
        return mBaseGameInfoBean.pkgName;
    }
    public String getUrl(){
        return mBaseGameInfoBean.gameDownloadUrl;
    }


    @Override
    public int getPriority() {
        return PRIORITY.PRIORITY_LOW;
    }


    @Override
    public boolean execute() throws Exception {
        //获取provider的状态,根据状态下载
        mIsRunning = true;
        infoBean.setNetState(CommonHelper.isNetworkAvailable(mContext) ? CommonHelper.isWifi(mContext) ? CommonHelper.NET_WIFI : CommonHelper.NET_MOBILE: CommonHelper.NET_ERROR);
        infoBean.setSpeed(0);
        File saveFile = null;
        InputStream inStream = null;
        BufferedRandomAccessFile raFile = null;
        try {
            saveFile = new File(infoBean.getSavePath());
            if (!saveFile.exists()) {
                saveFile.createNewFile();
            }
            long startPos = saveFile.length();
            long avTotalSize = Storage.recordAvailableSpace(mContext);
            //如果空间不足
            if (avTotalSize <= mBaseGameInfoBean.gameSize - startPos) {
                infoBean.setState(DownloadManager.DOWNLOAD_MEMORY_OUT);
                notifyDownloadFailed(infoBean, "空间不足");
                return false;
            }

//连接服务器下载文件
            URL httpUrl = new URL(infoBean.gameDownloadUrl.replaceAll(" ", "%20"));
            HttpURLConnection http = (HttpURLConnection) httpUrl.openConnection();
            http.setConnectTimeout(5 * 1000);
            http.setRequestMethod("GET");
            http.setRequestProperty("Accept", "image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*");    //设置客户端可以接受的返回数据类型
            http.setRequestProperty("Accept-Language", "zh-CN");    //设置客户端使用的语言问中文
            http.setRequestProperty("Referer", mBaseGameInfoBean.gameDownloadUrl);    //设置请求的来源,便于对访问来源进行统计
            http.setRequestProperty("Charset", "UTF-8");    //设置通信编码为UTF-8
            http.setRequestProperty("Range", "bytes=" + startPos + "-");
            http.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)");    //客户端用户代理
            http.setRequestProperty("Connection", "Keep-Alive");    //使用长连接
            inStream = http.getInputStream();
            //http.getContentLength()获得从startPos开始到结尾的长度,所以要加上startPos才是总长度
            infoBean.setEndPos(http.getContentLength() + startPos);
            infoBean.setCurrentPos(startPos);
            infoBean.setState(DownloadManager.DOWNLOADING);
            
            notifyDownloadStart(infoBean);
//通知开始下载
            byte[] buffer = new byte[1024 * 32];    //设置本地数据缓存的大小为32k
            int offset = 0;
            raFile = new BufferedRandomAccessFile(saveFile, "rwd");
            raFile.seek(startPos);
            mProgressUpdatePos = startPos;
            while (mIsRunning && (offset = inStream.read(buffer, 0, buffer.length)) != -1) {
                raFile.write(buffer, 0, offset);
                mCurrentTime = System.currentTimeMillis();
                infoBean.setCurrentPos(infoBean.getCurrentPos() + offset);
                mDateTime = mCurrentTime - mProgressUpdateTime;


                if(mDateTime >300){
                    long speed = (infoBean.getCurrentPos() - mProgressUpdatePos)*1000/mDateTime;
                    if(speed < 0){
                        speed = 0;
                    }
                    infoBean.setSpeed(speed);
                    mProgressUpdatePos  = infoBean.getCurrentPos();
                    mProgressUpdateTime   = mCurrentTime;
                    
                    //控制界面进度刷新频率
                    notifyDownloadProgress(infoBean); //通知进度更新
                }
            }
            if (!mIsRunning) {
               
                infoBean.setState(DownloadManager.DOWNLOAD_PAUSE);
                notifyDownloadPause(infoBean);//通知暂停下载
            } else {
                infoBean.setState(DownloadManager.DOWNLOADED);
                
                notifyDownloadDone(infoBean);//通知下载完成
            }
            raFile.close();
            inStream.close();
            if(TextUtils.isEmpty(infoBean.getPkgName())){
                String pkg = CommonHelper.getPkgByPath(mContext,infoBean.getSavePath());
                if(!TextUtils.isEmpty(pkg)){
                    infoBean.setPkgName(pkg);
                    
                    notifyDownloadDone(infoBean);
                }
            }
        } catch (Exception e) {
            infoBean.setState(DownloadManager.DOWNLOAD_ERROR);
            
            notifyDownloadFailed(infoBean, "下载错误");
            try {
                if (raFile != null) raFile.close();
                if (inStream != null) inStream.close();
            } catch (Exception e1) {
            }
            e.printStackTrace();
        }
        return false;
    }


    public void notifyDownloadPause(final DownloadGameInfoBean apkInfoBean) {
        
        if (mPauseListener != null) {
            if (mHandler != null) {
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        if (mListener != null) {
                            mListener.onDownloadPaused(apkInfoBean);
                        }
                        mPauseListener.onPaused();
                    }
                });
            } else {
                if (mListener != null) {
                    mListener.onDownloadPaused(apkInfoBean);
                }
                mPauseListener.onPaused();
            }
        } else if (mListener != null) {
            if (mHandler != null) {
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        mListener.onDownloadPaused(apkInfoBean);
                    }
                });
            } else {
                mListener.onDownloadPaused(apkInfoBean);
            }
        }
    }


    public void exit(DownloadManager.IDownloadPause listener) {
        mPauseListener = listener;
        if (mPauseListener != null) {
            mPauseListener.onPauseing();
        }
        mIsRunning = false;
    }


    private void notifyDownloadStart(final DownloadGameInfoBean apkInfoBean) {
        if (mListener != null) {
            if (mHandler != null) {
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        mListener.onDownloadStart(apkInfoBean);
                    }
                });
            } else {
                mListener.onDownloadStart(apkInfoBean);
            }
        }
    }


    private void notifyDownloadWait(final DownloadGameInfoBean apkInfoBean) {
        if (mListener != null) {
            if (mHandler != null) {
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        mListener.onDownloadWait(apkInfoBean);
                    }
                });
            } else {
                mListener.onDownloadWait(apkInfoBean);
            }
        }
    }


    private void notifyDownloadProgress(final DownloadGameInfoBean apkInfoBean) {
        if (mListener != null) {
            if (mHandler != null) {
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        mListener.onDownloadProgress(apkInfoBean);
                    }
                });
            } else {
                mListener.onDownloadProgress(apkInfoBean);
            }
        }
    }


    private void notifyDownloadDone(final DownloadGameInfoBean apkInfoBean) {
        if (mListener != null) {
            if (mHandler != null) {
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        mListener.onDownloadEnd(apkInfoBean);
                    }
                });
            } else {
                mListener.onDownloadEnd(apkInfoBean);
            }
        }
    }


    private void notifyDownloadFailed(final DownloadGameInfoBean apkInfoBean, final String errMsg) {
        if (mListener != null) {
            if (mHandler != null) {
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        mListener.onDownloadFailed(apkInfoBean, errMsg);
                    }
                });
            } else {
                mListener.onDownloadFailed(apkInfoBean, errMsg);
            }
        }
    }


    public boolean getIsRunning(){
        return mIsRunning;
    }
}

   

最后就是在需要知道下载进度信息的界面接收下载数据也就是MVC中的V(视图)

public DownloadActivity extends BaseActivity implements IDownloadApkInterProgress {


@Override

protected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    new DownloadApkRequest(context, handler, from,baseGameInfoBean, this).execute();}
@Overridepublic void onDownloadStart(DownloadGameInfoBean apkInfoBean) {    }@Overridepublic void onDownloadProgress(DownloadGameInfoBean apkInfoBean) {    }@Overridepublic void onDownloadEnd(DownloadGameInfoBean apkInfoBean) {        }   @Overridepublic void onDownloadFailed(DownloadGameInfoBean apkInfoBean, String errMsg) {    }@Overridepublic void onDownloadPaused(DownloadGameInfoBean apkInfoBean) {    }@Overridepublic void onDownloadWait(DownloadGameInfoBean apkInfoBean) {    }@Overridepublic void onDownloadDelete(DownloadGameInfoBean apkInfoBean) {    }@Overridepublic void onInstall(String pkg) {    }@Overridepublic void onRemove(String pkg) {    }

}



这是一个比较明显的例子,在遇到下载的功能时由于还需要很多其他操作(比如数据库操作),往往还比较容易想到要单独封装出来。往往是一些看似很简单的功能却容易疏忽了。比如跳转Activity,很多人觉得跳转个Activity这么简单的事情就直接写在Activity里面就行了,何必还要封装这么麻烦。但是,当Activity越来越多,相互之间跳转越频繁时,问题就出来了。我写的这几个Activity要跳转到ActivityDemo,你写的几个Actiivty也要跳转到ActivityDemo。然后在ActivityDemo接收intent传来的参数的时候,我写两个参数,你写两个参数。突然有一天会发现在跳转到ActivityDemo的时候出现了空指针错误。原因就是他在跳转ActivityDemo的时候Intent里没有传我在ActivityDemo里面要获取的参数。他在写跳转的时候根本不知道你在别的地方也写了跳转ActivityDemo的代码,所以也不知道要传你那几个参数。所以比较好的办法就是封装一个管理跳转方法的类。

public class ActivityUtils {
/** * 游戏详情(普通) * * @param context * @param baseGameInfoBean */public static void startNormalGameDetailActivity(Context context, BaseGameInfoBean baseGameInfoBean, String gameId) { Intent intent = new Intent(context, NormalGameDetailActivity.class); intent.putExtra(NormalGameDetailActivity.EXTRA_GAME, baseGameInfoBean); intent.putExtra(NormalGameDetailActivity.EXTRA_GAME_ID, gameId); ((Activity)context).startActivityForResult(intent,0);}public static void startTaskGameDetailActivity(Context context, BaseGameInfoBean baseGameInfoBean) { Intent intent = new Intent(context, TaskGameDetailActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.putExtra(TaskGameDetailActivity.EXTRA_GAME_INFO, baseGameInfoBean); context.startActivity(intent);}public static void startYinDaoActivity(Context context) { Intent intent = new Intent(context, YinDaoActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intent);}
}

不管是谁要实现Activity跳转都要使用ActivityUtils里面的跳转方法,那么不管是谁使用,都可以看得一清二楚要传什么参数,要注意些什么。

所以追根到底封装就是为了提高代码的可读性、可复用性、可维护性、可扩展性。


1 0
原创粉丝点击