上门洗车APP --- Androidclient开发 之 项目结构介绍

上门洗车APP --- Androidclient开发 之 项目结构介绍

前言

尽管公司项目较紧,但还是抽空给大家继续更新。

o_O"~ 欢迎大家的关注,非常高兴和大家共同学习。前面给大家分享了项目中的以下内容:

上门洗车APP --- Androidclient开发 前言及业务简介

上门洗车APP --- Androidclient开发 之 网络框架封装介绍(一)

上门洗车APP --- Androidclient开发 之 网络框架封装介绍(二)

之前有非常多朋友私信过来说想打包一份源代码学习。因为本项目也是还在开发中,进度不是非常快。后台那边的一系列接口须要调试,自己也是时间精力有限,还请朋友见谅。本篇博文给大家介绍项目的  总体文件夹结构。界面开发、项目中所使用的  自己定义控件、技术点 等,同一时候也会打包一份眼下最新的源代码供感兴趣的朋友学习。

关于项目文件夹结构

首先我们来看下项目的文件夹结构,给大家做一些简单的介绍:

                  

因为文件夹过多,我们分了两张图展示。先对源代码包的管理进行介绍:

org.gaochun.activity  -------> 管理Activity

org.gaochun.adapter -------> 通用万能Adapter

org.gaochun.android.http  -------> Android-Async-Http框架

org.gaochun.android.http.manager  -------> 通用数据管理(常量、URL、网络请求 等)

org.gaochun.android.http.network -------> 自己定义回调接口

org.gaochun.android.http.request -------> 请求參数管理

org.gaochun.model -------> 实体Bean管理

org.gaochun.parser -------> 解析接口及抽象解析器管理

org.gaochun.parser.impl -------> 解析器管理

org.gaochun.receiver -------> 监听推送消息的Receiver

org.gaochun.ui -------> 标题栏及Application管理

org.gaochun.utils  -------> 经常使用工具类

org.gaochun.widget -------> 自己定义控件管理(AlertDialog、圆形ImageView、带声音的Toast等)

部分技术点

有木有感觉层次还是比較清晰的呢?好了,不扯淡!给出了这些包中各自的职责后。相信朋友能够非常清晰的找到相应所须要了解的类。

这里大致介绍两个包中的内容,org.gaochun.adapter 和 org.gaochun.widget ,其它包在之前介绍网络框架中大致有提到,看例如以下两个包中的内容:

            

一个是通用的Adapter和通用的ViewHolder缓存类。一个是自己定义的提示框、圆形ImageView、带声音的Toast 。

有时候项目里面有好多好多ListView,记得曾经一种傻逼的做法是往 adapter 包中塞各种 OrderAdapter,HistoryAdapter,...... 然后每一个 adapter 都是 extends BaseAdapter,假设有一百个Adapter,我去,这他妈是个什么概念。。。。嘿嘿,后来学乖了哟。

关于通用Adapter可參考:https://github.com/JoanZapata/base-adapter-helper


CommonAdapter:

package org.gaochun.adapter;

import java.util.List;

import android.content.Context;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;

/**
 * 通用 Adapter
 * https://github.com/JoanZapata/base-adapter-helper
 * @param <T>
 */
public abstract class CommonAdapter<T> extends BaseAdapter{

    protected LayoutInflater mInflater;
    protected Context mContext;
    protected List<T> mDatas;
    protected final int mItemLayoutId;

    /**
     * 初始化通用Adapter
     * @param context   上下文
     * @param mDatas    须要显示的数据集合
     * @param itemLayoutId  子布局文件
     */
    public CommonAdapter(Context context, List<T> mDatas, int itemLayoutId)
    {
        this.mContext = context;
        this.mInflater = LayoutInflater.from(mContext);
        this.mDatas = mDatas;
        this.mItemLayoutId = itemLayoutId;
    }

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

    @Override
    public T getItem(int position)
    {
        return mDatas.get(position);
    }

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

    @Override
    public View getView(int position, View convertView, ViewGroup parent)
    {
        //从ViewHolder中获取控件view。若为空则创建
        final ViewHolder viewHolder = getViewHolder(position, convertView,parent);

        Log.i("gao_chun", position+"");
        convert(viewHolder, getItem(position));

        return viewHolder.getConvertView();

    }

    /**
     * 抽取出getView中间改变的部分
     * @param helper    holder缓存对象
     * @param item      Bean对象
     */
    public abstract void convert(ViewHolder helper, T item);

    /**
     * 获得ViewHolder中的view
     * @param position
     * @param convertView
     * @param parent
     * @return
     */
    private ViewHolder getViewHolder(int position, View convertView,ViewGroup parent)
    {
        return ViewHolder.get(mContext, convertView, parent, mItemLayoutId,position);
    }

}

ViewHolder:

package org.gaochun.adapter;

import android.content.Context;
import android.graphics.Bitmap;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import com.nostra13.universalimageloader.core.ImageLoader;

/**
 * 通用 ViewHolder 缓存类
 * @author gao_chun
 *
 */
public class ViewHolder{

    private ImageLoader imageLoader = ImageLoader.getInstance();

    private final SparseArray<View> mViews;
    private int mPosition;
    private View mConvertView;

    private ViewHolder(Context context, ViewGroup parent, int layoutId,int position)
    {
        this.mPosition = position;
        this.mViews = new SparseArray<View>();
        mConvertView = LayoutInflater.from(context).inflate(layoutId, parent,false);
        mConvertView.setTag(this);   // setTag
    }

    /**
     * 拿到一个ViewHolder对象
     *
     * @param context
     * @param convertView
     * @param parent
     * @param layoutId
     * @param position
     * @return
     */
    public static ViewHolder get(Context context, View convertView,ViewGroup parent, int layoutId, int position)
    {
        if (convertView == null){
            //创建ViewHolder对象 ,并做View缓存
            return new ViewHolder(context, parent, layoutId, position);
        }
        return (ViewHolder)convertView.getTag();
    }

    public View getConvertView()
    {
        return mConvertView;
    }

    /**
     * 通过控件的Id获取对于的控件,假设没有则增加views
     *
     * @param viewId
     * @return
     */
    public <T extends View> T getView(int viewId){

        View view = mViews.get(viewId);
        if (view == null){
            view = mConvertView.findViewById(viewId);
            mViews.put(viewId, view);
        }
        return (T)view;
    }

    /**
     * 为TextView设置字符串
     *
     * @param viewId
     * @param text
     * @return
     */
    public ViewHolder setText(int viewId, String text)
    {
        TextView view = getView(viewId);
        view.setText(text);
        return this;
    }

    /**
     * 为ImageView设置图片
     *  setImageResource
     * @param viewId
     * @param drawableId
     * @return
     */
    public ViewHolder setImageResource(int viewId, int drawableId)
    {
        ImageView view = getView(viewId);
        view.setImageResource(drawableId);
        return this;
    }

    /**
     * 为ImageView设置图片
     *  setImageBitmap
     * @param viewId
     * @param drawableId
     * @return
     */
    public ViewHolder setImageBitmap(int viewId, Bitmap bm)
    {
        ImageView view = getView(viewId);
        view.setImageBitmap(bm);
        return this;
    }

    /**
     * 为ImageView设置图片
     *  setImageByUrl
     * @param viewId
     * @param drawableId
     * @return
     */
   /* public ViewHolder setImageByUrl(int viewId, String url)
    {
        ImageLoader.getInstance(3, Type.LIFO).loadImage(url,(ImageView) getView(viewId));
        return this;
    }*/

    public ViewHolder setImageByUrl(int viewId, String url){
        imageLoader.displayImage(url,(ImageView)getView(viewId));
        return this;
    }

    public int getPosition(){

        return mPosition;
    }

}

这里还要给大家推荐一个技巧:ListView中按钮监听器 设置 及 优化,有个须要注意的地方是 setTag 能够记录信息。

以下是实现带声音的Toast控件类源代码,大家能够下载源代码了解学习:

package org.gaochun.widget;

import com.washcar.R;

import android.content.Context;
import android.media.MediaPlayer;
import android.util.DisplayMetrics;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

/**
 * 自己定义声音提示 Toast控件
 * @version 0.1
 * @created gao_chun
 */
public class SoundToast extends Toast{

	private MediaPlayer mPlayer;
	private boolean isSound;

	public SoundToast(Context context){

		this(context, false);
	}

	public SoundToast(Context context, boolean isSound){
		super(context);

		this.isSound = isSound;

		mPlayer = MediaPlayer.create(context, R.raw.newdatatoast);
		mPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener(){
			@Override
			public void onCompletion(MediaPlayer mp){
				mp.release();   //释放资源
			}
		});
	}

	@Override
	public void show()
	{
		super.show();
		if (isSound){
			mPlayer.start();
		}
	}

	/**
	 * 获取控件实例
	 *
	 * @param context
	 * @param text  提示消息
	 * @param isSound   是否播放声音
	 * @param duration  时长
	 * @return
	 */
	public static SoundToast show(Context context, CharSequence text, boolean isSound, int duration){

		SoundToast result = new SoundToast(context, isSound);
		LayoutInflater inflate = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
		DisplayMetrics dm = context.getResources().getDisplayMetrics();
		View v = inflate.inflate(R.layout.layout_toast, null);	//载入Toast布局
		v.setMinimumWidth(dm.widthPixels);  //设置控件最小宽度为手机屏幕宽度
		TextView tv = (TextView) v.findViewById(R.id.tv_lable);
		tv.setText(text);
		result.setView(v);
		result.setDuration(duration);// 设置 显示多长时间;
		//result.setGravity(Gravity.CENTER,0,0);
		return result;
	}

}

注:语音提示播报功能已经实现,在接收到server推送的订单后自己主动播报。因为眼下方便測试,在首页点击 订单中 按钮便可看到效果。部分自己定义控件这里也只是多的介绍了。感兴趣的童鞋能够下载了研究研究,没准以后在自己项目中就用到了。关于语音播报,在讯飞平台注冊了ID,之前简介过一些使用方法:Android语音播报、后台播报、语音识别 。大家能够參考下。

关于界面

关于res文件夹下的界面,想了想,貌似也没有什么好说的了,仅仅须要掌握一些经常使用的布局技巧,熟练使用Android中的布局属性,基本上简单的界面没有什么太大的问题。因为使用了 dimens 做适配,仅仅须要将使用dp的地方,换成如:android:padding="@dimen/dimen_20_dip" 就能够了。如登录界面:

<?xml version="1.0" encoding="utf-8"?

>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/background" >

    <LinearLayout
        android:id="@+id/layout_table"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/dimen_30_dip"
        android:orientation="vertical" >

        <include layout="@layout/layout_line_horizonal" />

        <EditText
            android:id="@+id/edit_username"
            style="@style/text_16"
            android:layout_width="match_parent"
            android:layout_height="@dimen/dimen_75_dip"
            android:background="@color/white"
            android:hint="@string/text_hint_username"
            android:inputType="phone"
            android:maxLength="11"
            android:padding="@dimen/dimen_20_dip"
            android:textColorHint="@color/text_hint_color" />

        <EditText
            android:id="@+id/edit_password"
            style="@style/text_16"
            android:layout_width="match_parent"
            android:layout_height="@dimen/dimen_75_dip"
            android:layout_marginTop="@dimen/row_margin"
            android:background="@color/white"
            android:hint="@string/text_hint_password"
            android:inputType="textPassword"
            android:padding="@dimen/dimen_20_dip"
            android:textColorHint="@color/text_hint_color" />

        <include layout="@layout/layout_line_horizonal" />
    </LinearLayout>

    <Button
        android:id="@+id/button_login"
        style="@style/text_white_18"
        android:layout_width="match_parent"
        android:layout_height="@dimen/dimen_60_dip"
        android:layout_below="@+id/layout_table"
        android:layout_marginTop="@dimen/dimen_65_dip"
        android:background="@drawable/button_selector"
        android:gravity="center"
        android:onClick="onClick"
        android:text="@string/text_login" />

</RelativeLayout>

个人中心界面:

<?

xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/background" >

    <RelativeLayout
        android:id="@+id/layout_top"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/white" >

        <RelativeLayout
            android:id="@+id/layout_table"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@drawable/person_bg"
            android:padding="@dimen/dimen_20_dip" >

            <org.gaochun.widget.RoundImageView
                android:id="@+id/iv_avatar"
                android:layout_width="@dimen/dimen_135_dip"
                android:layout_height="@dimen/dimen_135_dip"
                android:layout_marginLeft="@dimen/dimen_20_dip"
                android:layout_marginRight="@dimen/dimen_10_dip"
                android:scaleType="centerCrop"
                android:src="@drawable/gao_chun" />

            <TextView
                android:id="@+id/tv_user_name"
                style="@style/text_white_16"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="@dimen/dimen_60_dip"
                android:layout_marginTop="@dimen/dimen_30_dip"
                android:layout_toRightOf="@+id/iv_avatar"
                android:text="gao_chun"
                android:textStyle="bold" />

            <TextView
                android:id="@+id/tv_user_money"
                style="@style/text_white_16"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_below="@+id/tv_user_name"
                android:layout_marginLeft="@dimen/dimen_45_dip"
                android:layout_marginTop="@dimen/dimen_20_dip"
                android:layout_toRightOf="@+id/iv_avatar"
                android:text="剩余金额:12345"
                android:textStyle="bold" />
        </RelativeLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@+id/layout_table"
            android:orientation="horizontal"
            android:padding="@dimen/dimen_20_dip" >

            <TextView
                style="@style/text_16"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="@dimen/dimen_25_dip"
                android:layout_weight="1"
                android:text="优评:2" />

            <View
                android:layout_width="1px"
                android:layout_height="match_parent"
                android:layout_marginRight="@dimen/dimen_40_dip"
                android:background="@color/line_gray" />

            <TextView
                style="@style/text_16"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="中评:4" />

            <View
                android:layout_width="1px"
                android:layout_height="match_parent"
                android:layout_marginRight="@dimen/dimen_40_dip"
                android:background="@color/line_gray" />

            <TextView
                style="@style/text_16"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="差评:0" />
        </LinearLayout>
    </RelativeLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/layout_top"
        android:layout_marginTop="@dimen/dimen_30_dip"
        android:orientation="vertical" >

        <View
            android:layout_width="match_parent"
            android:layout_height="1px"
            android:background="@color/line_gray" />

        <TextView
            android:id="@+id/tv_my_order"
            style="@style/text_black_16"
            android:layout_width="match_parent"
            android:layout_height="@dimen/dimen_75_dip"
            android:background="@drawable/row_selector"
            android:clickable="true"
            android:drawableLeft="@drawable/person_order"
            android:drawablePadding="@dimen/dimen_40_dip"
            android:gravity="center_vertical"
            android:onClick="onClick"
            android:padding="@dimen/dimen_25_dip"
            android:text="@string/text_my_order" />

        <TextView
            android:id="@+id/tv_my_vip"
            style="@style/text_black_16"
            android:layout_width="match_parent"
            android:layout_height="@dimen/dimen_75_dip"
            android:layout_marginTop="@dimen/row_margin"
            android:background="@drawable/row_selector"
            android:clickable="true"
            android:drawableLeft="@drawable/person_vip"
            android:drawablePadding="@dimen/dimen_40_dip"
            android:gravity="center_vertical"
            android:onClick="onClick"
            android:padding="@dimen/dimen_25_dip"
            android:text="@string/text_my_vip" />

        <TextView
            android:id="@+id/tv_my_notify"
            style="@style/text_black_16"
            android:layout_width="match_parent"
            android:layout_height="@dimen/dimen_75_dip"
            android:layout_marginTop="@dimen/row_margin"
            android:background="@drawable/row_selector"
            android:clickable="true"
            android:drawableLeft="@drawable/person_inform"
            android:drawablePadding="@dimen/dimen_40_dip"
            android:gravity="center_vertical"
            android:onClick="onClick"
            android:padding="@dimen/dimen_25_dip"
            android:text="@string/text_my_information" />

        <TextView
            android:id="@+id/tv_my_more"
            style="@style/text_black_16"
            android:layout_width="match_parent"
            android:layout_height="@dimen/dimen_75_dip"
            android:layout_marginTop="@dimen/row_margin"
            android:background="@drawable/row_selector"
            android:clickable="true"
            android:drawableLeft="@drawable/person_more"
            android:drawablePadding="@dimen/dimen_40_dip"
            android:gravity="center_vertical"
            android:onClick="onClick"
            android:padding="@dimen/dimen_25_dip"
            android:text="@string/text_more" />

        <include layout="@layout/layout_line_horizonal" />
    </LinearLayout>

    <Button
        android:id="@+id/button_submit"
        style="@style/text_white_18"
        android:layout_width="match_parent"
        android:layout_height="@dimen/dimen_65_dip"
        android:layout_alignParentBottom="true"
        android:layout_marginBottom="@dimen/dimen_40_dip"
        android:background="@drawable/button_selector"
        android:gravity="center"
        android:onClick="onClick"
        android:text="@string/text_exit" />

</RelativeLayout>

关于其它技术点,日后总结了看有没有必要给大家再介绍下,先就到这里吧!

源代码下载:http://download.csdn.net/download/gao_chun/8861137

【注:转载注明gao_chun的BLOG http://blog.csdn.net/gao_chun/article/details/46711649

时间: 2024-10-20 21:09:10

上门洗车APP --- Androidclient开发 之 项目结构介绍的相关文章

上门洗车APP --- Androidclient开发 之 网络框架封装介绍(二)

上门洗车APP --- Androidclient开发 之 网络框架封装介绍(二) 前几篇博文中给大家介绍了一下APP中的基本业务及开发本项目使用的网络架构: 上门洗车APP --- Androidclient开发 前言及业务简单介绍 上门洗车APP --- Androidclient开发 之 网络框架封装介绍(一) 本篇接着给大家分享网络框架封装.相信感兴趣的朋友已经对上篇博文中的一些开源项目有了些许了解.这里继续为大家介绍关于GenericDataManager 通用网络管理类中的 data

上门洗车APP --- Android客户端开发 之 项目结构介绍

上门洗车APP --- Android客户端开发 之 项目结构介绍 前言 虽然公司项目较紧,但还是抽出时间给大家继续更新.     o_O"~ 感谢大家的关注,很高兴和大家共同学习.前面给大家分享了项目中的以下内容: 上门洗车APP --- Android客户端开发 前言及业务简介 上门洗车APP --- Android客户端开发 之 网络框架封装介绍(一) 上门洗车APP --- Android客户端开发 之 网络框架封装介绍(二) 之前有很多朋友私信过来说想打包一份源码学习,由于本项目也是还

家庭洗车APP --- Androidclient开展 之 网络框架包介绍(一)

家庭洗车APP --- Android客户端开发 之 网络框架包介绍(一) 上篇文章中给大家简单介绍了一些业务.上门洗车APP --- Android客户端开发 前言及业务简单介绍,本篇文章给大家介绍下网络框架.之前也了解过一些开源网络通讯架构.也大概看了一部分源代码.比方Afinal.Volley.AndBase.Android-async-http等,感觉各自都有各自的优劣,自己也曾封装过一些简单的网络架构,感觉有非常多地方须要注意和优化.这里就不贴出来献丑了,感兴趣的朋友能够去查阅学习上面

上门洗车APP --- Android客户端开发 之 网络框架封装(二)

上门洗车APP --- Android客户端开发 之 网络框架封装(二) 前几篇博文中给大家介绍了一下APP中的基本业务及开发本项目使用的网络架构: 上门洗车APP --- Android客户端开发 前言及业务简介 上门洗车APP --- Android客户端开发 之 网络框架封装介绍(一) 本篇接着给大家分享网络框架封装,相信感兴趣的朋友已经对上篇博文中的一些开源项目有了些许了解,这里继续为大家介绍关于GenericDataManager 通用网络管理类中的 dataRequest 方法 和

上门洗车APP --- Android客户端开发 前言及业务简介

上门洗车APP --- Android客户端开发 前言及业务简介 最近有些小累,私自接了一个项目,利用空余时间在开发,也比较乏力,时间和精力上有时候分配不过来,毕竟公司的事情要忙,只能自己抽时间来完成了,男人嘛,累点好,舒服是留给死人的(套用了一句逼格的话,o_O"~). 项目目前还在开发中,接口的调试以及业务的分析,框架的搭建,客户端这边已进行的差不多了,明天端午节,先祝大家节日快乐,汗~,看了下时间,貌似博客写完发表已经是端午节了,好吧,咕嘟咕嘟...... 大晚上的也是睡不着,写写博客,总

上门洗车APP --- Android客户端开发 之 网络框架封装介绍(一)

上门洗车APP --- Android客户端开发 之 网络框架封装介绍(一) 上篇文章中给大家简单介绍了一些业务,上门洗车APP --- Android客户端开发 前言及业务简介,本篇文章给大家介绍下网络框架,之前也了解过一些开源网络通讯架构,也大概看了一部分源码,比如Afinal.Volley.AndBase.Android-async-http等,感觉各自都有各自的优劣,自己也曾封装过一些简单的网络架构,感觉有很多地方需要注意和优化,这里就不贴出来献丑了,感兴趣的朋友可以去查阅学习上面所说的

上门洗车App 竟然是块大肥肉!

http://www.leiphone.com/k-xiche-app-idea.html 打车App.租车App.防违规App我们见得多,但洗车App你一定没听过,之前在一次创业路演上碰到一个做上门洗车App的,下面的投资人很是不解,“给人上门洗车还要花时间做个App,价格却跟传统洗车店收的一样,这有利可图么?”跟那个创业者深聊过后才发现,上门洗车App这事,搞不好是个暴利行当. 笔者下载这个App试用了一下,抛开UI设计.抛开创意体验,直奔它的核心功能——预约洗车,填好手机号.车牌号.预约洗

.NET Core实战项目之CMS 第十三章 开发篇-在MVC项目结构介绍及应用第三方UI

作为后端开发的我来说,前端表示真心玩不转,你如果让我微调一个位置的样式的话还行,但是让我写一个很漂亮的后台的话,真心做不到,所以我一般会选择套用一些开源UI模板来进行系统UI的设计.那如何套用呢?今天就以我们系列实战教程中的CMS系统为例来应用第三方的后台模板LayuiCMS2.0为例来进行实战演练吧! 本文已收录至<.NET Core实战项目之CMS 第一章 入门篇-开篇及总体规划> 作者:依乐祝 首发地址 "DotNetCore实战"公众号 原文地址:https://w

spring boot 依赖环境和项目结构介绍

1.环境介绍 使用 Spring Boot 开发项目需要有两个基础环境和一个开发工具,这两个环境是指 Java 编译环境和构建工具环境,一个开发工具是指 IDE 开发工具. Spring Boot 2.0 要求 Java 8 作为最低版本,需要在本机安装 JDK 1.8 并进行环境变量配置,同时需要安装构建工具编译 Spring Boot 项目,最后准备个顺手的 IDE 开发工具即可. 1.1.构建工具 构建工具是一个把源代码生成可执行应用程序的自动化工具,Java 领域中主要有三大构建工具:A