添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
相关文章推荐
重情义的数据线  ·  RuoYi-Vue 2.3 ...·  3 年前    · 
暴躁的奔马  ·  android ...·  3 年前    · 
  • 在H5(某个 WebView )中也可以修改这个头像。
  • 分为上传成功,上传失败和取消上传的功能。 先想想自己的这个功能,实现1和2只需要将上传的逻辑写在一个公共的类就行(例如: LoadUtil.java ),要实现3的功能,首先想到也是在一个 WebViewActivity 中实现上传,不是直接跟在某个 NativeActivity 中上传的逻辑一样?仔细想想,发现其实不是这样的,一个 WebViewActivity 对应的会是很多的H5 的界面(例如: http://wwww.baidu.com 和 http://www.google.com 可能都是在同一个 WebViewAcitivity )。这样就会导致我需要在公用的 WebViewActivity 中加上图片剪切加上图片上传借口的调用。同样的,我实现1和2的时候也是需要在两个不同的 Activity 中去实现图片的裁剪和调用上传接口。又或者说来个更直接的,如果我图片的裁剪是系统自带的,那么我们还需要在 onActivityResult 中来取得最后裁剪的数据。根据自己的需求,想了一个代码量比较少的做法。可以自己写一个 BaseActivity ,将图片的截取、图片的获得、调用图片上传的接口都写在里面。然后再写一个工具类来实现图片的上传接口,最后在根据每种不同的情况来处理上传成功,失败以及取消的操作。
    这篇文章主要涉及到知识:
  • 图片的截取和Bitmap的生成
  • 上传文件的协议以及实现
  • Native和H5复用
  • 图片的截取和Bitmap的生成

    对于系统的图片的截取

    1
    2
    3
    4
    5
    6
    7
    8
    9
    Intent intent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
    intent.setType("image/*"); //打开的是系统的相册
    intent.putExtra("crop", "true"); //使用系统自带的裁剪
    intent.putExtra("aspectX", 1);
    intent.putExtra("aspectY", 1); //两行表示裁剪宽高比为1:1
    intent.putExtra("outputX", WIDTH);
    intent.putExtra("outputY", HEIGTH); //裁剪以后输出的宽高值
    intent.putExtra("return-data", true); // 返回得到的数据
    startActivityForResult(intent, REQUSET_CODE);

    然后就可以在 onActivityResult(int requestCode,int responseCode,Intent data) 中调用了。现在需要的是得到 Bitmap 的值。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    if (data != null)
    {
    Bitmap bitmap = null;
    if (data.hasExtra("data")) // 首先会判断data中是否存在"data"值
    {
    bitmap = data.getParcelableExtra("data"); // 存在就可以直接取了
    }
    else // 不存在的时候则是可以通过BitmapFactory来获取bitmap的值
    {
    BitmapFactory.Options options = new BitmapFactory.Options();
    InputStream is = null;
    try
    {
    is = getContentResolver().openInputStream(data.getData());
    bitmap = BitmapFactory.decodeStream(is, null, options);
    }
    catch (Exception e)
    {
    }
    finally
    {
    try
    {
    is.close();
    }
    catch (Exception e)
    {
    }
    }
    }

    上传文件的协议以及实现

    分为两个部分,请求头和请求的正文。
    Headers(请求标头):
    会有一个Content-Type字段
    Content-Type:multipart/form-data;boundary= 一串每次请求不一样的字符串
    TextView(请求正文):
    请求正文里面的信息就复杂一点了。
    第一行:
    两个减号+ 与请求头一样的字符串 +回车符号 (markdown中不支持写减号)
    第二行:
    Content-Disposition: form-data; name=\”file\”+; filename=\” + filename + \” + 回车符号(这里面的file的值根据你的后台的接口而写,filename则是你的上传的图像所在的位置)
    第三行:
    Content-Type: image/jpeg+两个回车符号(注意是两个回车符号)
    第四行:
    将图片转化为流写入。
    第五行:
    回车符号+ 与请求头一样的字符串 +两个减号+回车符号
    这样所有的信息写好以后,就完成了图片上传的协议。接下来就是读取后台返回来的信息解析出来就行。
    使用 HttpURLConnection 实现。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    URL url = new URL(uploadUrl);
    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    conn.setRequestMethod("POST");
    // 允许input,output
    conn.setDoInput(true);
    conn.setDoOutput(true);
    conn.setUseCaches(false);
    // 设置请求头的信息
    conn.setRequestProperty("Connection", "Keep-Alive");
    // conn.setRequestProperty("Charset", "UTF-8");
    // conn.setRequestProperty("ENCTYPE", "multipart/form-data");
    conn.setRequestProperty("Accept-Charset", "utf-8");
    conn.setRequestProperty("contentType", "utf-8");
    conn.setRequestProperty("Content-Transfer-Encoding", "binary");
    conn.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary);
    // 上传的信息,ds就是上传文件的流
    DataOutputStream ds = new DataOutputStream(conn.getOutputStream());
    // twoHyphens="--",boundary是前面请求头中的相同的信息,end="\r\n"
    // 第一行
    ds.writeBytes(twoHyphens + boundary + end);
    // 第二行
    ds.writeBytes("Content-Disposition: form-data; name=\"upload\"; filename=\"" + filename + "\"" + end);
    // 第三行
    ds.writeBytes("Content-Type: image/jpeg" + end + end);
    // 第四行,将Bitmap写入输出流,相当于将Bitmap上传
    bitmap.compress(CompressFormat.JPEG, 100, ds);
    // 第五行
    ds.writeBytes(end+twoHyphens + boundary + twoHyphens + end);
    ds.flush();
    // 得到输入流,即得到服务器返回的数据
    InputStream is = conn.getInputStream();
    StringBuffer sb = new StringBuffer();
    String s = null;
    BufferedReader reader = new BufferedReader(new InputStreamReader(is));
    while ((s = reader.readLine()) != null)
    {
    sb.append(s);
    }
    String result = sb.toString();

    这样就按照上面的协议完成了一个头像的上传。

    Native和H5复用

    现在的应用确实越来越多的APP 跟H5 紧密相连,因此也有越来越多的功能需要结合H5 一起来实现。上传头像是一个很常见的功能。前面得到的结论是做一个公共的 BaseActivity 提供给 NativeActivity 和 WebViewActivity 来使用。然后在 BaseActivity 里面写上我们的图片的截取以及 onActivityResult 。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public class BaseActivity extends Activity
    {
    public void pickUpPic()
    {
    // 上面操作中的系统的图片的截取
    }
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data)
    {
    // 上面操作中的得到Bitmap的操作
    }
    }

    大概的框架就是这样,只需要在 NativeActivity 和 WebViewActivity 相应的位置调用 pickUpPic 就行了。

    优化

    这样就结束了?No.对于只是上传头像来说这样或许是结束了,但是还有很多的问题还没有考虑到。

  • 请求的参数?
  • 返回的结果,成功或失败提示有差别吗?
  • 用户在网速慢的情况下取消上传的操作?
  • 友好的提示用户上传的信息?
  • 请求参数

    pickUpPic 作为唯一的入口,因此需要将所有的参数(例如:请求的头像的宽高比、返回的大小、入口的类型等等)。因此需要全部写在 pickUpPic 里面。

    1
    2
    3
    4
    5
    public void pickUpPic(Params parama)
    {
    // 将带入的参数设置一些信息
    // 带入的参数设置截取的图像大小等等信息
    }

    这样就能满足所有很多不同的需求了。设置的信息也可以带入 onActivityResult 中,在里面调用上传头像接口时使用。

    提示信息

    注意在UI 线程中是无法处理网络操作的,我们现在的成功,失败以及用户的取消操作都是在子线程中操作的(也就是上传的时候才知道成功失败等等)。所以最先想到了使用一个 Handler 来操作,这样的话,还需要在 BaseActivity 中加一个 handler 来处理我们返回来的信息,同时主要到我们可能会有中不同的结果,success,fail,cancle。还有我们的 NativeActivity 和 WebViewActivity 两个不同的 Activity的操作也应该有很大的不同的。大概可以写一个这样的 Handler`。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    private Handler handler = new Handler()
    {
    @Override
    public void handleMessage(Message msg)
    {
    switch(msg.what)
    {
    case SUCCESS:
    // do somenthing
    listener.onSuccess(msg.obj);
    break;
    case FAIL:
    // do something
    listener.onFail(msg.obj);
    break;
    case CANCLE:
    // do something
    listener.onCancle(msg.obj);
    break;
    default:
    break;
    }
    }
    }

    只需要分别在 NativeActivity 和 WebViewActivity 中去实现不同的操作就行了。
    然后,大概就是这样了。