Android Studio + Eclipse 实现类似微博主页功能APP

  我在简书也发布了一份,喜欢黑色背景的同学可以看一下

  这其实是大二下的一个课设,内容是实现实时的图片、视频以及地点上传,而我的理解就是实现一个类似于微信朋友圈或是微博的主界面的App、 但自己学得太久,用了4个月时间(19.1-19.5),主要的时间用于来搞Android。而现在目前方向变了,想系统学习一下JavaWeb前后端以及框架,写个随笔来记录一下这个Demo的关键点,方便自己日后查看一下。
  现在也学了其他的知识,其实后端的工作其实可以用InteliJ IDEA+Maven+Tomcat+Mysql来搭建,就不需要引入jar包,用框架去整合比较方便。

  这Demo的 Android的xml配置文件 相当于 web的前端。但和前端不同的是,Android需要线程进行和对服务器的通话。而主线程需要更新界面部件,只能我们自己新建一个子线程来处理,并且用messageHandler来负责服务器返回信息的处理。而上传文件/获取信息的接口 其实就是我们web的网址,我们只要将传出去的数据格式化为json数据传输给服务器Tomcat,经过服务器的验证后,用json数据返回,App就能实现在浏览器登录的效果,并且跳转。
而我这个demo没有将服务器放在云服务器中,所以测试的时候也比较麻烦,而且测试建议用真机,在电脑上跑As模拟器的确太卡,以下是项目的地址

Android Studio 前端 github 地址

Eclipse 后端 github 地址

这2个地址中都包含了我自己以及在网上找的小Demo。所以导入的时候,可以全部导入,
也可以只导入部分的,其中在Eclipse也有使用说明,也有对应数据库的sql文件。
导入部分内容:

Android Studio:导入 projectthree 和 okhttputils

Eclipse:导入 MyWebTest

最值得注意的是:

  1. Android Studiookhttputils 项目中的 gradle文件 中 要 任意 **注释 **掉其中的一句
    因为两个项目Gradle文件引用的jar相同 导致jar包冲突
implementation 'com.zhy:okhttputils:2.6.2'
implementation 'com.squareup.okio:okio:2.2.2'
implementation("com.squareup.okhttp3:okhttp:3.14.1")
  1. 在Eclipse 中 要 修改 Tomcat 中的server.xml 的 配置文件 将上传的文件路径改为 电脑磁盘的特定的路径 因为每次Tomcat每次接收到的文件都是存储在其默认目录中的,如果Tomcat关闭并重新启动,它会将默认的目录下的文件清空,所以需要设置它的路径 设置它的Context
    所以在这个Demo中 我将上传到的文件 存储在 F://Picture//Upload 文件中

在这里插入图片描述

      <Host appBase="webapps" autoDeploy="true" name="localhost" unpackWARs="true" xmlNamespaceAware="false" xmlValidation="false">
        <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" pattern="%h %l %u %t &quot;%r&quot; %s %b" prefix="localhost_access_log" suffix=".txt"/>
       
      <Context docBase="F://Picture//Upload" path="/upload"/>
      <Context docBase="layuiDemo" path="/ui" reloadable="true" source="org.eclipse.jst.jee.server:layuiDemo"/></Host>

3.这个Demo 我用的是Mysql8.0,因为当时没有学Maven 需要手动引入一下jar包, 其次可以在util包中的JDBCUtil中修改自己的数据库属性

public class JDBCUtil {

	private static String url="jdbc:mysql://localhost:3306/demodatabase?serverTimezone=UTC&&characterEncoding=utf-8";
//	private static String url="jdbc:mysql://172.16.86.194:3306/demodatabase?serverTimezone=UTC";
	private static String driverClass="com.mysql.cj.jdbc.Driver";
	private static String username="root";
	private static String password="Password";
	private static Connection con;

jar.png


4.更改AndroidStudio 中 Projectthree 中的 上传url 更改为 本机IP地址
LoginServletActivity 
UploadActivity
UploadVidioActivity
RegisterServletActivity
AppHomeFragment
AppFindFragment3
private final static String Url="http://本机IP地址:8080/MyWebTest/queryServlet";
  1. 好了 最麻烦的一步来了 因为搞云服务器 需要形成一个 PAN(personal area network)
  • 你需要保持电脑有网 能用wifi 并且关闭防火墙
  • 将Android 的 projectthree导入到 手机 中(默认是Android 9.0 pie)
  • 将电脑的热点打开 并用测试手机连接上该电脑wifi 就可以运行了
    wifi.png
  • 其实也可以借同学的手机来开一下热点 然后你的电脑和测试手机都连接同一个热点 也可以实现。但前提还是得关防火墙

万事俱备 只欠东风

看一下效果图吧

  1. 登录界面
      登录界面的 icon 可以去阿里巴巴矢量标签库中查找。登录交换的的代码与其他界面的类似,用apach的HttpClient来实现比较基础的json传输,例如:登录、注册、以及图片视频url的获取。在上传的界面其中的效果在主页也是等同的用到了MainApplication来存储用户的用户名以及密码信息,并且在MainApplication中对baidu地图以及ImageLoader图片加载的初始化
public class MainApplication extends Application {
    private final static String TAG="MainApplication";

    private static MainApplication mApp;

    public HashMap<String,String> UserinfoMap=new HashMap<String,String>();

    // 利用单例模式获取当前应用的唯一实例
    public static MainApplication getInstance() {
        return mApp;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        // 在打开应用时对静态的应用实例赋值
        mApp = this;
        Log.d(TAG, "onCreate");
        initImageLoader();

        SDKInitializer.initialize(this);
        //自4.3.0起,百度地图SDK所有接口均支持百度坐标和国测局坐标,用此方法设置您使用的坐标类型.
        //包括BD09LL和GCJ02两种坐标,默认是BD09LL坐标。
        SDKInitializer.setCoordType(CoordType.BD09LL);
    }

    @Override
    public void onTerminate() {
        Log.d(TAG, "onTerminate");
        super.onTerminate();
    }

    private void initImageLoader() {
        ImageLoaderConfiguration configuration = ImageLoaderConfiguration.createDefault(this);
        ImageLoader.getInstance().init(configuration);
    }
}
public class LoginServletActivity extends AppCompatActivity implements View.OnClickListener {

    private final static String TAG="LoginServletActivity";
    private EditText et_username;
    private EditText et_password;
    private TextView tv_result;

//    private static String url="http://172.16.86.194:8080/MyWebTest/loginServlet";
    private static String url="http://你的ip地址:8080/MyWebTest/loginServlet";

    private final static int Login=1;
    private final static int Fail=2;
    String username=null;
    String password=null;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login_servlet);
        et_username=findViewById(R.id.et_username);
        et_password=findViewById(R.id.et_password);
        tv_result=findViewById(R.id.tv_result);
        findViewById(R.id.btn_login).setOnClickListener(this);
        findViewById(R.id.btn_register).setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        if (v.getId()==R.id.btn_login){
            username=et_username.getText().toString().trim();
            password=et_password.getText().toString().trim();
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {

                            HttpClient httpClient = new DefaultHttpClient();
                            HttpPost httpPost = new HttpPost(url);
                            List<NameValuePair> list = new ArrayList<NameValuePair>();
                            list.add(new BasicNameValuePair("username", username));
                            list.add(new BasicNameValuePair("password", password));
                            //实例化
                            final UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list, "utf-8");
                            httpPost.setEntity(entity);
                            HttpResponse httpResponse = httpClient.execute(httpPost);
                            if (httpResponse.getStatusLine().getStatusCode() == 200) {
                                HttpEntity entity1 = httpResponse.getEntity();
                                String row = EntityUtils.toString(entity1, "utf-8");
                                Message message = new Message();
                                message.what = Login;
                                message.obj = row;
                                handler.sendMessage(message);

                            } else {
                                Message message = new Message();
                                message.what = Fail;
                                handler.sendMessage(message);
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }).start();
//            }

        }else if(v.getId()==R.id.btn_register){
            Intent intent=new Intent(this,RegisterServletActivity.class);
            startActivity(intent);
        }
    }

    private Handler handler=new Handler(){
        @Override
        public void handleMessage(Message msg) {
            if(msg.what==Login){
                String row=(String)msg.obj;
                String[] info=row.split("#");

                if(info[1].equalsIgnoreCase("SUCC")){
                    tv_result.setText("Login SUCC");
                    Toast.makeText(LoginServletActivity.this,"Succ",Toast.LENGTH_SHORT).show();

                    MainApplication.getInstance().UserinfoMap=new HashMap<String,String>();
                    MainApplication.getInstance().UserinfoMap.put("username",username);
                    MainApplication.getInstance().UserinfoMap.put("password",password);

                    Intent intent=new Intent(LoginServletActivity.this,AppMainActivity2.class);
                    Bundle bundle=new Bundle();
                    bundle.putString("username",username);
                    bundle.putString("password",password);
//                    Toast.makeText(LoginServletActivity.this,username+" "+password,Toast.LENGTH_SHORT).show();
                    intent.putExtras(bundle);
                    startActivity(intent);
                }else{
                    tv_result.setText("Login Fail 请检查用户名和密码是否正确");
                    Toast.makeText(LoginServletActivity.this,"Fail",Toast.LENGTH_SHORT).show();
                }
            }else if(msg.what==Fail){
                tv_result.setText("服务器繁忙");
//                Toast.makeText(LoginServletActivity.this,"Succ",Toast.LENGTH_SHORT).show();
            }
        }
    };
}
  1. 第一次登录

第一次登录成功后会提示授权信息 在AppMainActivity2用了PermissionRequest进行权限的提示,gradle引用了 implementation ‘com.github.franmontiel:PersistentCookieJar:v1.0.1’ 需要编译后才能生效,并用Fragment来主要来实现界面的切换
登录成功后

@RuntimePermissions
public class AppMainActivity2 extends AppCompatActivity {
    private static final String TAG="AppMainActivity";
    private FragmentTabHost tabHost;
    public String username=null;
    public String password=null;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_app_main);

        Intent intent=getIntent();
        Bundle temp=intent.getExtras();
        username=temp.getString("username");
        password=temp.getString("password");


        Bundle bundle=new Bundle();//用于传递信息
        bundle.putString("tag",TAG);
        bundle.putString("username",username);
        bundle.putString("password",password);

        tabHost=findViewById(android.R.id.tabhost);
        //把内容放在标签栏正上方
        tabHost.setup(this,getSupportFragmentManager(),R.id.fl_content);

        //放置fragment
        tabHost.addTab(getTabView(R.string.home,R.drawable.tab_home_selector), AppHomeFragment.class,bundle);
        tabHost.addTab(getTabView(R.string.find,R.drawable.tab_find_selector), AppFindFragment3.class,bundle);
        tabHost.addTab(getTabView(R.string.message,R.drawable.tab_message_selector), AppMessageFragment.class,bundle);
        tabHost.addTab(getTabView(R.string.me,R.drawable.tab_me_selector), AppMeFragment.class,bundle);

        //不设置各标签的之间的分隔线
        tabHost.getTabWidget().setShowDividers(LinearLayout.SHOW_DIVIDER_NONE);

        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
            //initMap();
        } else {
            AppMainActivity2PermissionsDispatcher.ApplySuccessWithCheck(this);
        }
    }

    private TabSpec getTabView(int textId,int imageId){
        String text=getResources().getString(textId);
        Drawable drawable=getResources().getDrawable(imageId);

        drawable.setBounds(0,0,drawable.getMinimumWidth(),drawable.getMinimumHeight());
        View item_tabbar=getLayoutInflater().inflate(R.layout.item_tabbar,null);
        TextView tv_item=item_tabbar.findViewById(R.id.tv_item_tabbar);

        tv_item.setCompoundDrawables(null, drawable, null, null);
        // 生成并返回该标签按钮对应的标签规格
        return tabHost.newTabSpec(text).setIndicator(item_tabbar);
    }



    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        // NOTE: delegate the permission handling to generated method
        AppMainActivity2PermissionsDispatcher.onRequestPermissionsResult(this, requestCode, grantResults);
    }

    /**
     * 申请权限成功时
     */
    @NeedsPermission(Manifest.permission.ACCESS_COARSE_LOCATION)
    void ApplySuccess() {
        //initMap();
    }

    /**
     * 申请权限告诉用户原因时
     * @param request
     */
    @OnShowRationale(Manifest.permission.ACCESS_COARSE_LOCATION)
    void showRationaleForMap(PermissionRequest request) {
        showRationaleDialog("使用此功能需要打开定位的权限", request);
    }

    /**
     * 申请权限被拒绝时
     *
     */
    @OnPermissionDenied(Manifest.permission.ACCESS_COARSE_LOCATION)
    void onMapDenied() {
        Toast.makeText(this,"你拒绝了权限,该功能不可用",Toast.LENGTH_LONG).show();
    }

    /**
     * 申请权限被拒绝并勾选不再提醒时
     */
    @OnNeverAskAgain(Manifest.permission.ACCESS_COARSE_LOCATION)
    void onMapNeverAskAgain() {
        AskForPermission();
    }

    /**
     * 告知用户具体需要权限的原因
     * @param messageResId
     * @param request
     */
    private void showRationaleDialog(String messageResId, final PermissionRequest request) {
        new AlertDialog.Builder(this)
                .setPositiveButton("确定", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(@NonNull DialogInterface dialog, int which) {
                        request.proceed();//请求权限
                    }
                })
                .setNegativeButton("取消", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(@NonNull DialogInterface dialog, int which) {
                        request.cancel();
                    }
                })
                .setCancelable(false)
                .setMessage(messageResId)
                .show();
    }

    /**
     * 被拒绝并且不再提醒,提示用户去设置界面重新打开权限
     */
    private void AskForPermission() {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("当前应用缺少定位权限,请去设置界面打开\n打开之后按两次返回键可回到该应用哦");
        builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                return;
            }
        });
        builder.setPositiveButton("设置", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                intent.setData(Uri.parse("package:" + AppMainActivity2.this.getPackageName())); // 根据包名打开对应的设置界面
                startActivity(intent);
            }
        });
        builder.create().show();
    }
}

3.正常的主页界面

通过设置NineGridAdapter适配器通过NineGridTestLayout来初始化图片、Framelayout初始化视频,在item_grid.xml中配置好每个部件的大小和位置,并将数据库得到的信息进行解析并设置对应的listener
如果解析后数据是图片格式就将
NineGridTestLayoutandroid:visibility="visiable"
Framelayout的android:visibility="gone"
如果解析后数据是视频格式就 反之

public class NineGridItem implements Serializable {
    private static final long serialVersionUID = 2189052605715370758L;
    public boolean isShowAll = false;

    public String uid;//上传用户 id
    public String time;//上传时间
    public List<String> urlList = new ArrayList<>();//url列表
    public String text;//描述
    public String location;//地点
    public String type;//类型 图片和视频
    public String url;//
    public boolean bPressed;//是否按下
    public int id;// item id
    private static int seq=0;//是否超过九个
}
public class NineGridAdapter extends RecyclerView.Adapter<NineGridAdapter.ViewHolder> implements View.OnClickListener, View.OnLongClickListener{

    private int CLICK = 0; // 正常点击
    private int DELETE = 1; // 点击了删除按钮
    private Context mContext;
    private List<NineGridItem> mList;
    protected LayoutInflater inflater;

    public NineGridAdapter(Context context) {
        mContext = context;
        inflater = LayoutInflater.from(context);
    }

    public void setList(List<NineGridItem> list) {
        mList = list;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View convertView = inflater.inflate(R.layout.item_grid, parent, false);
        ViewHolder viewHolder = new ViewHolder(convertView);
        return viewHolder;
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {


        NineGridItem item=mList.get(position);
        holder.tv_uid.setText(item.getUid());
        holder.tv_text.setText(item.getText());
        holder.tv_location.setText(item.getLocation());
        holder.tv_time.setText(item.getTime());


        if(item.getType().equals("图片")){
            holder.layout.setIsShowAll(mList.get(position).isShowAll);
            holder.layout.setUrlList(mList.get(position).urlList);
        }else{
            holder.fl_video.setVisibility(View.VISIBLE);
            getImage(holder.iv_videopic,item.getUrlList().get(0));
            holder.fl_video.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Intent intent=new Intent(mContext, MoviePlayActivity.class);
                    Bundle bundle=new Bundle();
                    bundle.putString("path",mList.get(position).getUrlList().get(0));
                    intent.putExtras(bundle);
                    mContext.startActivity(intent);
                }
            });


        }
        holder.tv_delete.setVisibility((item.bPressed) ? View.VISIBLE : View.GONE);
        holder.tv_delete.setId(item.id * 10 + DELETE);
        holder.ll_item.setId(item.id * 10 + CLICK);
        holder.tv_delete.setOnClickListener(this);
//         列表项的点击事件需要自己实现
        holder.ll_item.setOnClickListener(this);
//         列表项的长按事件需要自己实现
        holder.ll_item.setOnLongClickListener(this);

    }

    public class ViewHolder extends RecyclerView.ViewHolder {

        NineGridTestLayout layout;
        public TextView tv_uid;
        public TextView tv_delete;
        public TextView tv_location;
        public TextView tv_text;
        public TextView tv_time;
        public LinearLayout ll_item;
        public FrameLayout fl_video;
        public ImageView iv_pause;
        public ImageView iv_videopic;
        public TextView tv_url;


        public ViewHolder(View itemView) {
            super(itemView);
            layout = (NineGridTestLayout) itemView.findViewById(R.id.layout_nine_grid);
            ll_item=itemView.findViewById(R.id.ll_item);
            tv_uid=itemView.findViewById(R.id.tv_uid);
            tv_delete=itemView.findViewById(R.id.tv_delete);
            tv_text=itemView.findViewById(R.id.tv_text);
            tv_location=itemView.findViewById(R.id.tv_location);
            tv_time=itemView.findViewById(R.id.tv_time);
            fl_video=itemView.findViewById(R.id.fl_video);
            iv_pause=itemView.findViewById(R.id.iv_pause);
            iv_videopic=itemView.findViewById(R.id.iv_videopic);
            tv_url=itemView.findViewById(R.id.tv_url);

        }
    }

    private int getListSize(List<NineGridItem> list) {
        if (list == null || list.size() == 0) {
            return 0;
        }
        return list.size();
    }

    @Override
    public int getItemCount() {
        return getListSize(mList);
    }

    //根据列表项编号获得当前位置序号
    private int getPosition(int item_id){
        int pos=0;
        for(int i=0;i<mList.size();i++){
            if(mList.get(i).id==item_id){
                pos=i;
                break;
            }
        }
        return pos;
    }

    public void getImage(ImageView iv_image,String path){

        FFmpegMediaMetadataRetriever retriever = new  FFmpegMediaMetadataRetriever();
        try {
            retriever.setDataSource(path);
            Bitmap bitmap = retriever.getFrameAtTime(100000,FFmpegMediaMetadataRetriever.OPTION_CLOSEST_SYNC );  //这个时间就是第一秒的
            iv_image.setImageBitmap(bitmap);
        } catch (Exception e) {
            e.printStackTrace();
        }
        finally{
            retriever.release();
        }

    }

    @Override
    public void onClick(View v) {

            int position = getPosition((int) v.getId() / 10);
            int type = (int) v.getId() % 10;
            if (type == CLICK) { // 正常点击,则触发点击监听器的onItemClick方法
                if (mOnItemClickListener != null) {
                    mOnItemClickListener.onItemClick(v, position);
                }
            } else if (type == DELETE) { // 点击了删除按钮,则触发删除监听器的onItemDeleteClick方法
                if (mOnItemDeleteClickListener != null) {
                    mOnItemDeleteClickListener.onItemDeleteClick(v, position);
                }
            }
    }

    @Override
    public boolean onLongClick(View v) {
        int position = getPosition((int) v.getId() / 10);
        if (mOnItemLongClickListener != null) {
            mOnItemLongClickListener.onItemLongClick(v, position);
        }
        return true;
    }


    // 声明列表项的点击监听器对象
    private RecyclerExtras.OnItemClickListener mOnItemClickListener;

    public void setOnItemClickListener(RecyclerExtras.OnItemClickListener listener) {
        this.mOnItemClickListener = listener;
    }

    // 声明列表项的长按监听器对象
    private RecyclerExtras.OnItemLongClickListener mOnItemLongClickListener;

    public void setOnItemLongClickListener(RecyclerExtras.OnItemLongClickListener listener) {
        this.mOnItemLongClickListener = listener;
    }

    // 声明列表项的删除监听器对象
    private RecyclerExtras.OnItemDeleteClickListener mOnItemDeleteClickListener;

    public void setOnItemDeleteClickListener(RecyclerExtras.OnItemDeleteClickListener listener) {
        this.mOnItemDeleteClickListener = listener;
    }
}

3.1 在主页的界面上能 实现图片以及视频的展示
Gilde可以通过url 获取图片并且显示
FFmpegMediaMetadataRetriever可以通过url获取视频资源的第一帧 然后在首页显示的视频播放图标,只是将两张图片拼接出来的,然后如果点击图片或者视频才会继续向服务器请求新的数据。其实在实际的开发中,图片应该使用缩略图和原图来存储,当用户点击缩略图才放大显示原图,这样可以加速界面的刷新,也省了加载的时间

implementation 'com.github.wseemann:FFmpegMediaMetadataRetriever:1.0.14'
implementation 'com.github.bumptech.glide:glide:4.9

3.2
布局方面使用SwipeRefreshLayout用于刷新以及RecyclerView用于放置布局文件,可以实现下拉刷新界面并且 长按组件可以右上角出现删除图标,布局的话有参考了一下网上别人的Demo,学习一下别人对于图片的裁剪实现类似微信朋友圈的九宫格布局layout以及适配器adapter;更主要实现就是先将每个 用户发送的信息 设置为一个item的布局,然后通过类似于list添加到RecyclerView,并且为item设置viewHolder,在viewHolder中对于每个item进行对图片和视频不同的初始化。

public class AppHomeFragment extends Fragment implements View.OnClickListener,OnItemClickListener, OnItemLongClickListener, OnItemDeleteClickListener,OnRefreshListener{
    private static final String TAG="HomeFragment";
    private int conunt=5;
    protected View mView;
    protected Context mContext;
    private ArrayList<Picinfo> PublicArray=new ArrayList<Picinfo>();//数据链表
    private ArrayList<Picinfo> AllArray=new ArrayList<Picinfo>();//数据链表
    private static int download=1;

//    private final static String Url="http://172.16.86.194:8080/MyWebTest/downloadServlet";
//    private final static String Url2="http://172.16.86.194:8080/upload";
    private final static String Url="http://你的ip地址:8080/MyWebTest/downloadServlet";
    private final static String Url2="http://你的ip地址:8080/upload";

    private SwipeRefreshLayout srl_dynamic;//转圈圈
    private RecyclerView rv_dynamic; //循环视图
    private RecyclerView.LayoutManager rv_manager;//布局管理器
    private NineGridAdapter adapter;//适配器
    private ArrayList<NineGridItem> mList=new ArrayList<NineGridItem>();


    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        mContext=getActivity();
        mView=inflater.inflate(R.layout.fragment_app_home,container,false);
        srl_dynamic=mView.findViewById(R.id.srl_dynamic);
        srl_dynamic.setOnRefreshListener((SwipeRefreshLayout.OnRefreshListener) this);
        srl_dynamic.setColorSchemeResources(
                R.color.red, R.color.orange, R.color.green, R.color.blue);
        rv_dynamic=mView.findViewById(R.id.rv_dynamic);

        GetfromMysql();

        return mView;
    }

    private void initRecyclerDynamic() {
        rv_manager = new LinearLayoutManager(mContext);
        rv_dynamic.setLayoutManager(rv_manager);
        adapter = new NineGridAdapter(mContext);
        adapter.setList(mList);
        rv_dynamic.setAdapter(adapter);

        // 设置线性列表的点击监听器
        adapter.setOnItemClickListener(this);
        // 设置线性列表的长按监听器
        adapter.setOnItemLongClickListener(this);
        // 设置线性列表的删除按钮监听器
        adapter.setOnItemDeleteClickListener(this);
        rv_dynamic.setItemAnimator(new DefaultItemAnimator());
        // 给rv_dynamic添加列表项之间的空白装饰
        rv_dynamic.addItemDecoration(new SpacesItemDecoration(1));

    }

    @Override
    public void onRefresh() {
        // 延迟若干秒后启动刷新任务
        mHandler.postDelayed(mRefresh, 2000);
    }
    private Handler mHandler = new Handler(); // 声明一个处理器对象
    // 定义一个刷新任务
    private Runnable mRefresh = new Runnable() {
        @Override
        public void run() {
            srl_dynamic.setRefreshing(false);

            mList=new ArrayList<NineGridItem>();
            GetfromMysql();

            Toast.makeText(mContext,"刷新成功",Toast.LENGTH_SHORT).show();
        }
    };



    private void GetfromMysql(){
        new Thread(new Runnable() {
            @Override
            public void run() {
                try{
                    //客户端
                    HttpClient httpClient=new DefaultHttpClient();
                    //post方式
                    HttpPost httpPost=new HttpPost(Url);
                    //传输数据
                    List<NameValuePair> list=new ArrayList<NameValuePair>();
                    list.add(new BasicNameValuePair("download","download"));
                    list.add(new BasicNameValuePair("count",String.valueOf(conunt++)));

                    UrlEncodedFormEntity entity=new UrlEncodedFormEntity(list,"utf-8");
                    httpPost.setEntity(entity);

                    //回应
                    HttpResponse httpResponse=httpClient.execute(httpPost);
                    if(httpResponse.getStatusLine().getStatusCode()==200){

                        HttpEntity entity1=httpResponse.getEntity();
                        String jstr= EntityUtils.toString(entity1,"utf-8");
                        Message message=new Message();
                        message.what=download;
                        message.obj=jstr;
                        messageHander.sendMessage(message);
                    }
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }).start();
    }

    private Handler messageHander=new Handler(){
        @Override
        public void handleMessage(Message msg) {
            if(msg.what==download){
                initPublicArray((String) msg.obj);//译码
                initRecyclerDynamic(); // 初始化动态线性布局的循环视图
            }
        }

    };

    private void initPublicArray(String jstr) {
        //ali size() org length()
        JSONArray array= JSON.parseArray(jstr);
        for(int i=0;i<array.size();i++){

            JSONObject jsonObject=array.getJSONObject(i);
            String uid=jsonObject.getString("uid");
            String time=jsonObject.getString("time");
            String url=jsonObject.getString("url");
            String text=jsonObject.getString("text");
            String location=jsonObject.getString("location");
            String type=jsonObject.getString("type");
            Log.e(TAG,uid+"  "+url);
            Log.w(TAG,uid+"  "+url);
//            url="http://172.16.86.194:8080/upload"+url;


            String []urls=url.split("#");
            List<String> urlList = new ArrayList<String>();
            for(int j=0;j<urls.length;j++){
//                urlList.add("http://172.16.86.194:8080/upload"+urls[j]);
                urlList.add(Url2+urls[j]);
            }

            NineGridItem item=new NineGridItem(uid,time,urlList,text,location,type);
            mList.add(item);


//            Picinfo picinfo=new Picinfo(uid,text,url,location,false);
//            PublicArray.add(picinfo);
//            AllArray.add(picinfo);
        }

    }

    @Override
    public void onItemClick(View view, int position) {
        String desc = String.format("您点击了第%d项,标题是%s", position + 1,
               mList.get(position).getText());
        Toast.makeText(getActivity(), desc, Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onItemLongClick(View view, int position) {

        NineGridItem item=mList.get(position);
        item.bPressed=!item.bPressed;
        mList.set(position,item);
        adapter.notifyItemChanged(position);

    }

    @Override
    public void onItemDeleteClick(View view, int position) {
        mList.remove(position);
        adapter.notifyItemRemoved(position);
    }

    @Override
    public void onClick(View v) {

    }
}

4.实现定位
  我是用buidu地图来实现定位的功能的,看着baiduAPI来慢慢实现定位,需要你去百度地图开发平台申请一个密钥放在mainfests中,一个密钥只能对于一个App。并且 在当你选择上传 图片/视频 的时候会自动将地点填入,其实还有挺多功能能实现的,有兴趣可以去baidu地图开发平台了解一下

        <!-- 百度地图密钥 -->
        <meta-data
            android:name="com.baidu.lbsapi.API_KEY"
            android:value="PtUdzaPVt3yQGDgEiGZA9pzgO8Fp6sE4" />
        <service
            android:name="com.baidu.location.f"
            android:enabled="true"
            android:process=":remote" />
public class AppMessageFragment extends Fragment implements View.OnClickListener {
    private static final String TAG="MessageFragment";
    protected View mView;//声明一个视图对象
    protected Context mContext;//声明一个上下文对象
    private String[] Permissionrequest={Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.WRITE_EXTERNAL_STORAGE};
    private MapView mapView;//地图
    private BaiduMap baiduMap;
    private TextView tv_loccity;

    //防止每次定位都重新设置中心点和marker
    private boolean isFirstLocation = true;
    //初始化LocationClient定位类
    private LocationClient mLocationClient = null;
    //BDAbstractLocationListener为7.2版本新增的Abstract类型的监听接口,原有BDLocationListener接口
    private BDLocationListener myListener = new MyLocationListener();
    //经纬度
    private double lat;
    private double lon;


    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        mContext=getActivity();
        mView=inflater.inflate(R.layout.fragment_app_message,container,false);

        mView.findViewById(R.id.btn_picupload).setOnClickListener(this);
        mView.findViewById(R.id.btn_vedioupload).setOnClickListener(this);
        mapView=mView.findViewById(R.id.mv_mapview);
        baiduMap=mapView.getMap();
        tv_loccity=mView.findViewById(R.id.tv_loccity);
        initMap();

        return mView;
    }



    @Override
    public void onClick(View v) {
        if(v.getId()==R.id.btn_picupload){
             Intent intent=new Intent(mContext, UploadActivity.class);
             Bundle bundle=new Bundle();
             bundle.putString("city",tv_loccity.getText().toString().trim());
             intent.putExtras(bundle);
             startActivity(intent);
        }

        if(v.getId()==R.id.btn_vedioupload){
            Intent intent=new Intent(mContext, UploadVideoActivity.class);
            Bundle bundle=new Bundle();
            bundle.putString("city",tv_loccity.getText().toString().trim());
            intent.putExtras(bundle);
            startActivity(intent);
        }
    }
    private void initMap() {
        //普通地图
        baiduMap.setMapType(BaiduMap.MAP_TYPE_NORMAL);
        //卫星地图
        //baiduMap.setMapType(BaiduMap.MAP_TYPE_SATELLITE);
        //空白地图, 基础地图瓦片将不会被渲染。在地图类型中设置为NONE,将不会使用流量下载基础地图瓦片图层。使用场景:与瓦片图层一起使用,节省流量,提升自定义瓦片图下载速度。
        //baiduMap.setMapType(BaiduMap.MAP_TYPE_NONE);
        //开启交通图
        baiduMap.setTrafficEnabled(true);
        //关闭缩放按钮
        mapView.showZoomControls(false);

        // 开启定位图层
        baiduMap.setMyLocationEnabled(true);
        //声明LocationClient类
        mLocationClient = new LocationClient(mContext);
        //注册监听函数
        mLocationClient.registerLocationListener(myListener);
        initLocation();
        //开始定位
        mLocationClient.start();
    }

    private void initLocation() {
        LocationClientOption option = new LocationClientOption();
        //可选,默认高精度,设置定位模式,高精度,低功耗,仅设备
        option.setLocationMode(LocationClientOption.LocationMode.Hight_Accuracy);
        //可选,默认gcj02,设置返回的定位结果坐标系
        option.setCoorType("bd09ll");
        //可选,默认0,即仅定位一次,设置发起定位请求的间隔需要大于等于1000ms才是有效的
        int span = 5000;
        option.setScanSpan(span);
        //可选,设置是否需要地址信息,默认不需要
        option.setIsNeedAddress(true);
        //可选,默认false,设置是否使用gps
        option.setOpenGps(true);
        //可选,默认false,设置是否当GPS有效时按照1S/1次频率输出GPS结果
        option.setLocationNotify(true);
        //可选,默认false,设置是否需要位置语义化结果,可以在BDLocation.getLocationDescribe里得到,结果类似于“在北京天安门附近”
        option.setIsNeedLocationDescribe(true);
        //可选,默认false,设置是否需要POI结果,可以在BDLocation.getPoiList里得到
        option.setIsNeedLocationPoiList(true);
        //可选,默认true,定位SDK内部是一个SERVICE,并放到了独立进程,设置是否在stop的时候杀死这个进程,默认不杀死
        option.setIgnoreKillProcess(false);
        //可选,默认false,设置是否收集CRASH信息,默认收集
        option.SetIgnoreCacheException(false);
        //可选,默认false,设置是否需要过滤GPS仿真结果,默认需要
        option.setEnableSimulateGps(false);
        mLocationClient.setLocOption(option);
    }

    /**
     * 实现定位监听 位置一旦有所改变就会调用这个方法
     * 可以在这个方法里面获取到定位之后获取到的一系列数据
     */
    public class MyLocationListener implements BDLocationListener {

        @Override
        public void onReceiveLocation(BDLocation location) {
            //获取定位结果
            location.getTime();    //获取定位时间
            location.getLocationID();    //获取定位唯一ID,v7.2版本新增,用于排查定位问题
            location.getLocType();    //获取定位类型
            location.getLatitude();    //获取纬度信息
            location.getLongitude();    //获取经度信息
            location.getRadius();    //获取定位精准度
            location.getAddrStr();    //获取地址信息
            location.getCountry();    //获取国家信息
            location.getCountryCode();    //获取国家码
            location.getCity();    //获取城市信息
            location.getCityCode();    //获取城市码
            location.getDistrict();    //获取区县信息
            location.getStreet();    //获取街道信息
            location.getStreetNumber();    //获取街道码
            location.getLocationDescribe();    //获取当前位置描述信息
            location.getPoiList();    //获取当前位置周边POI信息

            location.getBuildingID();    //室内精准定位下,获取楼宇ID
            location.getBuildingName();    //室内精准定位下,获取楼宇名称
            location.getFloor();    //室内精准定位下,获取当前位置所处的楼层信息
            //经纬度
            lat = location.getLatitude();
            lon = location.getLongitude();

            //这个判断是为了防止每次定位都重新设置中心点和marker
            if (isFirstLocation) {
                isFirstLocation = true;
                //设置并显示中心点
                setPosition2Center(baiduMap, location, true);
            }

            tv_loccity.setText(location.getCity()+"  "+location.getDistrict()+"  "+location.getStreet());
        }
    }

    /**
     * 设置中心点和添加marker
     *
     * @param map
     * @param bdLocation
     * @param isShowLoc
     */
    public void setPosition2Center(BaiduMap map, BDLocation bdLocation, Boolean isShowLoc) {
        MyLocationData locData = new MyLocationData.Builder()
                .accuracy(bdLocation.getRadius())
                .direction(bdLocation.getRadius()).latitude(bdLocation.getLatitude())
                .longitude(bdLocation.getLongitude()).build();
        map.setMyLocationData(locData);

        if (isShowLoc) {
            LatLng ll = new LatLng(bdLocation.getLatitude(), bdLocation.getLongitude());
            MapStatus.Builder builder = new MapStatus.Builder();
            builder.target(ll).zoom(18.0f);
            map.animateMapStatus(MapStatusUpdateFactory.newMapStatus(builder.build()));
        }
    }

    @Override
    public void onResume() {
        mapView.onResume();
        super.onResume();
    }
    @Override
    public void onPause() {
        mapView.onPause();
        super.onPause();
    }
    @Override
    public void onDestroy() {
        super.onDestroy();
        //在activity执行onDestroy时执行mMapView.onDestroy(),实现地图生命周期管理
        // 退出时销毁定位
        mLocationClient.unRegisterLocationListener(myListener);
        mLocationClient.stop();
        // 关闭定位图层
        baiduMap.setMyLocationEnabled(false);
        mapView.onDestroy();
        mapView = null;
    }
}

5.文件添加上传图片 因和主页界面类似就不放代码
  上传图片/视频,都是依赖于okhttputil来实现的,这是一个基于okhttp来开发的工具,其实也可以直接用okhttp。我主要用它来实现文件的上传,可以点击图片实现进入相册模式,实现的过程是将图片的url传入另一个activity中处理,并且设置这个activity的背景是纯黑,通过改变图片的width以及height 实现放大效果

 public void multiFileUpload() {
        //String mBaseUrl="http://172.16.86.49:8001/upload";
        //mBaseUrl="http://172.16.86.194:8080/MyWebTest/uploadServlet";

        String Allurl="";
        for(int i=0;i<fileName.size();i++)
        {
            Allurl+="/"+username+"/"+fileName.get(i)+"#";
        }

        Map<String, String> params = new HashMap<>();
        params.put("uid",username);
        params.put("time", DateUtil.getNowDateTime());
        params.put("location",tv_cityloc.getText().toString().trim());
        params.put("type",type);
        params.put("text",et_text.getText().toString().trim());
//        params.put("Allurl",Allurl);

        String url = mBaseUrl;

        //Log.e(TAG,Calendar.getInstance()+name);
        for(int i=0;i<arrayList.size();i++)
        {
            String s=arrayList.get(i);
            String name=s.substring(s.lastIndexOf("/")+1);
            File file=new File(s);
            if (!file.exists()||!file.exists())
            {
                return;
            }

            if(i==(arrayList.size()-1)){
                params.put("Allurl",Allurl);
            }

            OkHttpUtils.post()//
                    .addFile("mFile", fileName.get(i), file)//
//                .addFile("mFile", "2.txt", file2)//
                    .url(url)
                    .params(params)//
                    .build()//
                    .execute(new UploadVideoActivity.MyStringCallback());
        }

    }

6.视频上传
  点击视频可以进入电影模式来播放,手机会自动来实现屏幕的翻转。这个比较难实现,我也是在网上找了很多demo来学习,但最后还是没能找到想要的,还是按着自己买的书一步一步打下来,但还是对于Android比较底层的方法不太了解,最后虽然实现效果,为了赶课设,但自己对这方面还是有些许欠缺


7.**Android 主要的配置文件**
  • 主 build.gradle
    com.android.tools.build:gradle:3.2.1 手动可以设置到自己需要的版本号 我当时是设置为4.10.1的
buildscript {
    repositories {
        google()
        jcenter()
        
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.2.1'
        
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        google()
        jcenter()
     
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}
  • AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.projectthree">

    <!-- 互联网 1-->
    <uses-permission android:name="android.permission.INTERNET" />
    <!-- 查看网络状态 11-->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <!-- 开关网络状态 1-->
    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />

    <!-- 定位 11-->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

    <!--用于申请调用A-GPS模块 0-->
    <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"></uses-permission>
    <!--用于申请获取蓝牙信息进行室内定位-->
    <uses-permission android:name="android.permission.BLUETOOTH"></uses-permission>
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"></uses-permission>
    <uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" />
    <uses-permission android:name="android.permission.WAKE_LOCK"/>
    <uses-permission android:name="android.permission.GET_TASKS" />
    <uses-permission android:name="android.permission.WRITE_SETTINGS" />

    <!-- 查看手机状态 1-->
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <!-- 下载时不提示通知栏 -->
    <uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />
    <!-- 拍照 -->
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-feature android:name="android.hardware.camera.autofocus" />
    <!-- 录像/录音 -->
    <uses-permission android:name="android.permission.RECORD_VIDEO"/>
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <!-- 震动 -->
    <uses-permission android:name="android.permission.VIBRATE" />
    <!-- SD卡 1-->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
    <!-- 安装应用请求,Android8.0需要 -->
    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme"
        android:networkSecurityConfig="@xml/network_security_config"
        android:name=".MainApplication">
        <uses-library android:name="org.apache.http.legacy"
            android:required="false"/>
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <!-- 百度地图密钥 -->
        <meta-data
            android:name="com.baidu.lbsapi.API_KEY"
            android:value="PtUdzaPVt3yQGDgEiGZA9pzgO8Fp6sE4" />
        <service
            android:name="com.baidu.location.f"
            android:enabled="true"
            android:process=":remote" />

        <activity android:name=".AppMainActivity" android:theme="@style/AppCompatTheme"/>
        <activity android:name=".LoginServletActivity" android:theme="@style/AppCompatTheme"/>
        <activity android:name=".RegisterServletActivity" android:theme="@style/AppCompatTheme"/>
        <activity android:name=".UploadActivity" android:theme="@style/AppCompatTheme"/>

        <activity
            android:name=".MoviePlayActivity"
            android:configChanges="orientation|keyboardHidden|screenSize|screenLayout"
            android:screenOrientation="sensor"
            android:theme="@style/FullScreenTheme"
            android:supportsPictureInPicture="true" />

        <activity android:name=".MyGalleryActivity" android:theme="@style/AppCompatTheme"/>
        <activity android:name=".RecyclerViewExampleActivity" android:theme="@style/AppCompatTheme"/>
        <activity android:name=".SecondActivity" android:theme="@style/AppCompatTheme"/>
        <activity android:name=".AppMainActivity2" android:theme="@style/AppCompatTheme"/>
        <activity android:name=".UploadVideoActivity" android:theme="@style/AppCompatTheme"/>

    </application>

</manifest>
  • projectthree/build.gradle
apply plugin: 'com.android.application'

android {
    compileSdkVersion 28

    defaultConfig {
        applicationId "com.example.z2"
        minSdkVersion 16
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }

    compileOptions {

        sourceCompatibility JavaVersion.VERSION_1_8

        targetCompatibility JavaVersion.VERSION_1_8

    }
    lintOptions{
        abortOnError false
    }

    useLibrary 'org.apache.http.legacy'
}
allprojects {
    repositories {
        maven { url "https://jitpack.io" }


    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])

    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'

    implementation files('libs/httpclient-4.2.5.jar')
    implementation project(':okhttputils')
//    implementation files('libs/okhttputils-2_6_2.jar')
//    implementation files('libs/okhttp-3.4.1.jar')
//    implementation files('libs/okio-1.9.0.jar')

    implementation 'com.google.code.gson:gson:2.3.1'
    implementation 'com.alibaba:fastjson:1.1.54.android'
    implementation 'com.squareup.okhttp3:okhttp-urlconnection:3.14.1'
    implementation 'com.github.franmontiel:PersistentCookieJar:v1.0.1'
//    implementation 'com.github.bumptech.glide:glide:4.0.0'
//    implementation "com.android.support:recyclerview-v7:28.0.0"
    implementation "com.android.support:design:28.0.0"


    implementation "com.android.support:recyclerview-v7:28.0.0"
    implementation "com.android.support:cardview-v7:28.0.0"
    implementation "com.android.support:palette-v7:28.0.0"

    implementation 'com.github.wseemann:FFmpegMediaMetadataRetriever:1.0.14'
    implementation 'com.github.bumptech.glide:glide:4.9.0'
    implementation 'com.android.support:animated-vector-drawable:28.0.0'
    implementation 'com.android.support:support-v4:28.0.0'
    implementation 'com.github.chrisbanes:PhotoView:1.3.0'

    // https://mvnrepository.com/artifact/com.nostra13.universalimageloader/universal-image-loader
    implementation group: 'com.nostra13.universalimageloader', name: 'universal-image-loader', version: '1.9.3'



    implementation files('libs/BaiduLBS_Android.jar')
    implementation files('libs/component_common_sdk_1.0.0.jar')
    implementation files('libs/IndoorscapeAlbumPlugin.jar')
    implementation 'com.github.hotchemi:permissionsdispatcher:2.2.0'
    annotationProcessor 'com.github.hotchemi:permissionsdispatcher-processor:2.2.0'
}
  • okhttputils/build.gradle (需要注释1行代码 若能成功允许则不需要)
apply plugin: 'com.android.library'
//apply plugin: 'com.novoda.bintray-release'//添加


android {
    compileSdkVersion 28

    defaultConfig {
        minSdkVersion 15
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    lintOptions{
        abortOnError false
        warning 'InvalidPackage'
    }

}


task clearJar(type: Delete) {
    delete 'build/outputs/okhttputils.jar'
}

task makeJar(type: Copy) {
    from('build/intermediates/bundles/release/')
    into('build/outputs/')
    include('classes.jar')
    rename ('classes.jar', 'okhttputils-2_6_2.jar')
}

makeJar.dependsOn(clearJar, build)



dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
//    compile 'com.squareup.okhttp3:okhttp:3.3.1'
    //尝试注释这一行
    implementation 'com.zhy:okhttputils:2.6.2'
    //尝试注释这一行
    implementation 'com.squareup.okio:okio:2.2.2'
    //尝试注释这一行
    implementation("com.squareup.okhttp3:okhttp:3.14.1")

    testImplementation("com.squareup.okhttp3:mockwebserver:3.14.1")
}

8.数据库
  当时打这个demo的时候还没认真系统学习数据库,只会简单的增删查改,现在看以前的表结构,还是有时间的时候再去解耦合吧

CREATE TABLE `userinfo` (
  `ids` int(255) NOT NULL AUTO_INCREMENT,
  `uid` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `upw` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  PRIMARY KEY (`ids`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;

CREATE TABLE `uploadinfo` (
  `ids` int(255) NOT NULL AUTO_INCREMENT,
  `uid` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `time` datetime NOT NULL,
  `url` varchar(255) NOT NULL,
  `text` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
  `location` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `type` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  PRIMARY KEY (`ids`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8;

userinfo.png

uploadinfo.png

小结

  这个Demo吃了我很多时间,从学习数据库到javaweb再到Android,每一步都很赶也很急,感觉自己对于Android不会的地方还有许多,要不停看书、测试、去Github找demo,去stackoverflow找解决方法,去Maven repository找gradle等等,感觉自己失去同时期的许多机会,也许有连续几晚因为bug而失眠,但你说有没有学到东西,答案是肯定的能学到的,好的就是培养了自己独立思考的能力,以及以后面对问题的从容和淡定。
  现在写随笔记录一下,是因为自己想深入学习一下JavaWeb前后端,不想再打比较肤浅的sql语句以及比较生硬的交互界面,所以感觉会很长时间不会再碰Android。Android里面的东西的确太多,对于图片的修剪,视频的传输格式,自定义相册以及视频播放器等等,不深入理解是很难打得出来的。而我也是参考了挺多demo,心里也不想充当工具人的角色。Android每年一个版本,Android 10.0 Q已经发布了,确实也需要去了解一下和9.0 Pie有什么区别和功能呢,想学精通有点难度,例如Bluetooth、NFC、Flutter等等这些都需要投入一定的精力去学习。而查询的Fragment也没有实现动态查询也是比较可惜。
  最后测试机MI5C收尾

  • 5
    点赞
  • 38
    收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:数字20 设计师:CSDN官方博客 返回首页
评论

打赏作者

Mr丶LuoT4

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值