软件工程作业的一些说明

2019-05-05  本文已影响0人  啦啦哇哈哈

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

MVC架构

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

<?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层

        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上各个大神的分享,从中汲取了很多知识和开发技巧,这次遇到的一些问题的难点,就借鉴了许多大神的做法,开源真是伟大!

上一篇 下一篇

猜你喜欢

热点阅读