美文网首页
软件工程作业的一些说明

软件工程作业的一些说明

作者: 啦啦哇哈哈 | 来源:发表于2019-05-05 22:45 被阅读0次

    软件工程作业要求写一款APP,安卓的应用开发也一直是自己的兴趣,所以整个作业的完成过程中还是很兴奋的。我们的APP是基于gank.io网站提供的文章数据接口,和美女图片接口,完成的一款开发技术干货APP,下面简单说一下完成作业过程中值得说的几个点。

    MVC架构

    作业要求使用MVC架构,MVC是Model-View-Controller的简写,在android里面,MVC很好理解,

    • Model是一个个数据实体类,它承载了应用中的数据,我们的APP中包括:用户的数据实体类User,封装的网络请求类(net包下的),文章的数据实体类GankModel,收藏的实体类SaveModel以及一些其他的常量类,和一些View的Adapter类,应该都属于Model层,Model为视图界面(View)层提供了根本的东西:


    • View就是用户能直接看到的视图界面,就对应android里面的一个个View,界面的布局写在xml文件里面,我们的App里面自定义的View就只有一个EmptyRecyclerView,处理了一些没有数据的空页面的情况,剩下的View都是用的是Android SDK中的UI控件,能够顺利完成我们的需求了:



      下面是很多布局文件,我们所有的视图都依赖这些xml:



      简单看一个其中的例子:
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:background="@color/gray"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingBottom="@dimen/activity_vertical_margin"
        tools:context=".controller.activity.LoginActivity">
    
    
        <ScrollView
            android:id="@+id/login_form"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
    
            <LinearLayout
                android:padding="10dp"
                android:id="@+id/email_login_form"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical">
    
                <ImageView
                    android:layout_gravity="center_horizontal"
                    android:src="@drawable/icon_app_ironman"
                    android:id="@+id/iv_login_icon"
                    android:layout_width="150dp"
                    android:layout_height="150dp" />
    
                <TextView
                    android:layout_marginTop="10dp"
                    android:layout_gravity="center_horizontal"
                    android:id="@+id/tv_welcome"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textSize="18sp"
                    android:textStyle="italic|bold"
                    android:text="欢迎来到干货营!"/>
                <android.support.design.widget.TextInputLayout
                    android:layout_marginTop="10dp"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content">
    
                    <AutoCompleteTextView
                        android:id="@+id/tv_account"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:hint="@string/prompt_email"
                        android:inputType="textEmailAddress"
                        android:maxLines="1"
                        android:singleLine="true" />
    
                </android.support.design.widget.TextInputLayout>
    
                <android.support.design.widget.TextInputLayout
                    android:layout_marginTop="10dp"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content">
    
                    <EditText
                        android:id="@+id/et_password"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:hint="@string/prompt_password"
                        android:imeActionId="6"
                        android:imeOptions="actionUnspecified"
                        android:inputType="textPassword"
                        android:maxLines="1"
                        android:singleLine="true" />
    
                </android.support.design.widget.TextInputLayout>
                <RelativeLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content">
    
                    <CheckBox
                        android:id="@+id/cb_remember"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_alignParentRight="true"
                        android:text="记住密码" />
                </RelativeLayout>
                <Button
                    android:id="@+id/btn_sign_in"
                    android:elevation="10dp"
                    android:background="@drawable/shape_login_btn"
                    style="?android:textAppearanceSmall"
                    android:textColor="@color/white"
                    android:textSize="16sp"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="16dp"
                    android:text="@string/action_sign_in"
                    android:textStyle="bold" />
    
                <Button
                    android:id="@+id/btn_register"
                    android:elevation="10dp"
                    android:background="@drawable/shape_login_btn"
                    style="?android:textAppearanceSmall"
                    android:textColor="@color/white"
                    android:textSize="16sp"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="16dp"
                    android:text="@string/action_register"
                    android:textStyle="bold" />
            </LinearLayout>
        </ScrollView>
    
    

    这就是View层

    • Controller层自然就是安卓中最重要的Activity或者Fragment



      Controller顾名思义,它负责控制,相当于View层和Model层的中间层,View层的数据来源于Model层,它需要Controller层把Model的数据交给View,而用户的一些点击操作滑动操作可能改变Model层的数据,那也需要Controller层来扮演这个角色,将用户的操作转化成对数据的改变。典型的例子就是Activity中各种Button的setOnclickListener里面做的事情,就十分鲜明的说明了Activity作为Controller的作用。比如这是登录界面的两个Button的Listener:

            btn_register.setOnClickListener {
                startActivity(Intent(this, RegisterActivity::class.java))
            }
            btn_sign_in.setOnClickListener { onLogin() }
    

    点击第一个Button注册,就跳转到注册的Activity,点击第二个Button登录,就进入登录状态,onLogin里面处理的登录的检查和跳转等等。

    但事实上在写的过程中很难避免Activity或者Fragment要做一些视图的操作,比如setVisibility这些,因为xml几乎不具备可编程性。Controller层和View层的耦合还是比较严重的,所以其实有更好的一些其他的架构。。MVC这种模式事实上还是有很大的劣势的,至少在Android中是这样。

    大概我们作业的结构就介绍完了:


    下面再谈谈其他的几个点:

    关于Kotlin

    不得不说Kotlin包装的一些语法糖,吃起来真甜,目前还没有接触到多少Kotlin的高级特性,但是一些简单的特性,就已经让人爱不释手了。举个最简单的例子在Model层会有大量的数据实体类,我们拿最重要的这个GankModel举例子,Java语言是这样的:

    public class GankModel {
        private boolean error;
        private List<ResultsEntity> results;
    
        public void setError(boolean error) {
            this.error = error;
        }
    
        public void setResults(List<ResultsEntity> results) {
            this.results = results;
        }
    
        public boolean getError() {
            return error;
        }
    
        public List<ResultsEntity> getResults() {
            return results;
        }
    
        public static class ResultsEntity implements Serializable{
            public ResultsEntity() {
            }
    
            public ResultsEntity(String _id, String createdAt, String desc, String publishedAt, String source, String type, String url, boolean used, String who, List<String> images) {
                this._id = _id;
                this.createdAt = createdAt;
                this.desc = desc;
                this.publishedAt = publishedAt;
                this.source = source;
                this.type = type;
                this.url = url;
                this.used = used;
                this.who = who;
                this.images = images;
            }
    
            private String _id;
            private String createdAt;
            private String desc;
            private String publishedAt;
            private String source;
            private String type;
            private String url;
            private boolean used;
            private String who;
            private List<String> images;
    
            public void set_id(String _id) {
                this._id = _id;
            }
    
            public void setCreatedAt(String createdAt) {
                this.createdAt = createdAt;
            }
    
            public void setDesc(String desc) {
                this.desc = desc;
            }
    
            public void setPublishedAt(String publishedAt) {
                this.publishedAt = publishedAt;
            }
    
            public void setSource(String source) {
                this.source = source;
            }
    
            public void setType(String type) {
                this.type = type;
            }
    
            public void setUrl(String url) {
                this.url = url;
            }
    
            public void setUsed(boolean used) {
                this.used = used;
            }
    
            public void setWho(String who) {
                this.who = who;
            }
    
            public void setImages(List<String> images) {
                this.images = images;
            }
    
            public String get_id() {
                return _id;
            }
    
            public String getCreatedAt() {
                return createdAt;
            }
    
            public String getDesc() {
                return desc;
            }
    
            public String getPublishedAt() {
                return publishedAt;
            }
    
            public String getSource() {
                return source;
            }
    
            public String getType() {
                return type;
            }
    
            public String getUrl() {
                return url;
            }
    
            public boolean getUsed() {
                return used;
            }
    
            public String getWho() {
                return who;
            }
    
            public List<String> getImages() {
                return images;
            }
        }
    }
    
    

    嗯 它太长了。。kotlin呢,就这么几行

    class GankModel {
       
        var error: Boolean = false
        var results: List<ResultsEntity>? = null
        class ResultsEntity(_id: String, createdAt: String, desc: String, publishedAt: String, source: String, type: String, url: String, var used: Boolean, who: String, images: List<String>) : Serializable {
            var _id: String? = _id
            var createdAt: String? = createdAt
            var desc: String? = desc
            var publishedAt: String? = publishedAt
            var source: String? = source
            var type: String? = type
            var url: String? = url
            var who: String? = who
            var images: List<String>? = images
        }
    }
    

    他免去了各种繁琐而重复的get set,让人能把精力放在有意义的事情上,另外还有一个很舒服的就是kotlin的可空类型(就是上图中的那个带问号的类型),它的使用免去了大量的Java中为了避免空指针异常的判空If语句,也让人倍感清新。大量的语法糖也就不再举例子了,Kotlin的学习还在路上!(比较尴尬的是,kotlin在和Java交互中有一些坑,在这次作业App的开发中也遇到了一个导致崩溃的可空类型和Java的交互,需要特别注意!)

    项目第一次接触的的一些第三方的好用的库

    登录模块借助Bmob后端云bmob后端云
    ,收藏模块为了避免大量数据库的没有意义的增删改查代码,也是使用了Android中比较常用的realm数据库框架realm
    ,都在Application中做了初始化。


    这是我们的Bmobuser实体类
    open class User : BmobUser() {
        var age: Int = 0
        //true男、false女
        var gender: Boolean = false
        var intro: String? = null
        var nickname: String? = null
    }
    

    下面是为realm封装的一个工具类:

    public class DBManager {
    
        /**
         * 收藏数据,保存到数据库中
         *
         * @param entity
         */
        public static void save(final SaveModel entity) {
            Realm realm = Realm.getDefaultInstance();
            realm.executeTransaction(new Realm.Transaction() {
                @Override
                public void execute(Realm realm) {
                    realm.copyToRealm(entity);
                }
            });
        }
    
        /**
         * 取消收藏,从数据库中删除
         *
         * @param entity
         */
        public static void cancelSave(final SaveModel entity) {
            Realm realm = Realm.getDefaultInstance();
            realm.beginTransaction();
            SaveModel resultsEntity = realm.where(SaveModel.class).equalTo("_id", entity.get_id()).findFirst();
            resultsEntity.deleteFromRealm();
            realm.commitTransaction();
        }
    
        /**
         * 获取数据中所搜收藏记录
         *
         * @return
         */
        public static List<SaveModel> getAllData() {
            Realm realm = Realm.getDefaultInstance();
            realm.beginTransaction();
            RealmResults<SaveModel> results = realm.where(SaveModel.class).findAll();
            realm.commitTransaction();
            return results;
        }
    
        /**
         * 根据主键查询某条记录
         *
         * @return
         */
        public static SaveModel queryModel(String _id) {
            Realm realm = Realm.getDefaultInstance();
            realm.beginTransaction();
            SaveModel result = realm.where(SaveModel.class).equalTo("_id", _id).findFirst();
            realm.commitTransaction();
            return result;
        }
    }
    
    

    项目的细节

    都是Android中的一些常见的View的使用,一些简单的网络请求的封装,Handler Recyclerview Fragment Activity等等都可以看我之前发的博客的知识总结,这感觉是很难说完的。
    项目的一个难点是美女图片的加载,在那么多图片的情况下,有可能出现out of memory以及加载的卡顿,这方面一个是借助Glide的缓存策略去避免反复加载,另外是RecyclerView的缓存和回收去避免过多图片驻留内存。

    由于平时还有各种学业压力,所以完成项目过程中有一段时间是很赶进度的,难免有些代码功能是实现了,但是在代码的整洁性,结构的清晰性上都有一定可以提升的空间(就是有些代码写的比较丑。。),日后会继续努力去完善的,但是最终的作品是完成了,希望能有一个好的成绩~感谢大家的齐心协力!
    最后还要感谢github上各个大神的分享,从中汲取了很多知识和开发技巧,这次遇到的一些问题的难点,就借鉴了许多大神的做法,开源真是伟大!

    相关文章

      网友评论

          本文标题:软件工程作业的一些说明

          本文链接:https://www.haomeiwen.com/subject/gdmkoqtx.html