从0到1开发一款APPAndroid开发经验谈Android开发

【从 0 开始开发一款直播 APP】12 直播封面上传

2017-05-11  本文已影响232人  菜鸟窝

本文为菜鸟窝作者蒋志碧的连载。“从 0 开始开发一款直播 APP ”系列来聊聊时下最火的直播 APP,如何完整的实现一个类"腾讯直播"的商业化项目


直播封面上传功能运行效果

直播界面讲解

直播标题
    当前直播的标题,内容简介
直播封面
    封面图片会在如上图显示以及直播列表上显示
是否录制
    直播结束之后需要观看直播,就需要录制,直播结束之后录制的视频文件就叫点播,可以对视频进行永久的存储
摄像头直播
    根据主播需求打开前置摄像头还是后置摄像头
录屏直播
    只要用于屏幕录播,例如:游戏 在5.0以上
录制清晰度
    码流和分辨率(流畅,超清,高清)决定

界面布局不贴了,读者自己看着界面做,这里需要提示一下,界面上用到的自定义控件在文章中有讲,请戳链接。


【从 0 开始开发一款直播 APP】13 Android 6.0 运行时权限
【从 0 开始开发一款直播 APP】14 animation-list 逐帧动画自定义Switch控件


直播封面上传

直播封面上传功能有以下几个:
运行时权限验证
上传本地图片
上传相机图片

1、运行时权限验证

PublishPresenter # checkPublishPermission()

//1、权限通过 ActivityCompat 类的 checkSelfPermission() 方法判断是否有所需权限。
//2、权限请求是通过 ActivityCompat 类中的 requestPermissions() 方法,在OnRequestPermissionsResultCallback # onRequestPermissionsResult() 方法中回调。
//----------------------------split line---------------------------------------
@Override
public boolean checkPublishPermission(Activity activity) {
    if (Build.VERSION.SDK_INT >= 23) {
        List<String> permissions = new ArrayList<>();
        //写入外部存储设备权限(保存封面图片)
        if (PackageManager.PERMISSION_GRANTED != ActivityCompat.checkSelfPermission(activity, android.Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
            permissions.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
        }
        //相机权限
        if (PackageManager.PERMISSION_GRANTED != ActivityCompat.checkSelfPermission(activity, Manifest.permission.CAMERA)) {
            permissions.add(Manifest.permission.CAMERA);
        }
        //ActivityCompat.requestPermissions() 请求权限
        if (permissions.size() != 0) {
            ActivityCompat.requestPermissions(activity
                    , permissions.toArray(new String[0]),
                    Constants.WRITE_PERMISSION_REQ_CODE);
            return false;
        }
    }
    return true;
}

PublishActivity # onRequestPermissionsResult()
onRequestPermissionsResult() 处理请求权限

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);

    switch (requestCode) {
        //写入外部存储权限
        case Constants.WRITE_PERMISSION_REQ_CODE:
            for (int ret : grantResults) {
                if (ret != PackageManager.PERMISSION_GRANTED) {
                    return;
                }
            }
            mPermission = true;
            break;
    }
}

2、上传图片(相机、本地)

依然采用 MVP 架构。

【从 0 开始开发一款直播 APP】5.1 MVP 完全解析 -- 实现直播登录

View — IPublishView

public interface IPublishView extends BaseView{

    Activity getActivity();
    /**
     * 定位成功
     * @param location 位置
     */
    void doLocationSuccess(String location);
    /**
     * 定位失败
     */
    void doLocationFailed();
    /**
     * 图片上传成功
     * @param url 路径
     */
    void doUploadSuccess(String url);
    /**
     * 图片上传失败
     * @param url 路径
     */
    void doUploadFailed(String url);
    /**
     * 结束页面
     */
    void finishActivity();
}

Presenter — IPublishPresenter

public abstract class IPublishPresenter implements BasePresenter {
    protected BaseView mBaseView;
    public IPublishPresenter(BaseView baseView) {
        mBaseView = baseView;
    }
    /**
     * 检查推流权限
     * @param activity
     * @return
     */
    public abstract boolean checkPublishPermission(Activity activity);
    /**
     * 裁剪图片
     * @param imgUri 图片地址
     * @return
     */
    public abstract Uri cropImage(Uri imgUri);
    /**
     * 选择图片方式:相机、相册
     * @param mPermission 权限
     * @param type 类型
     * @return
     */
    public abstract Uri pickImage(boolean mPermission,int type);
    /**
     * 上传图片
     * @param path 图片路径
     */
    public abstract void doUploadPic(String path);
}

图片上传请求实体类


图片要上传到服务端,就需要请求网络,对图片上传封装一个请求实体。

public class UploadPicRequest  extends IRequest {
    //http://live.demo.cniao5.com/Api/Image/upload
    //请求参数:userId type file
    public UploadPicRequest(int requestId, String userId, int type, File file) throws FileNotFoundException {
        mRequestId = requestId;
        mParams.put("userId",userId);
        mParams.put("type",type);
        mParams.put("file",file);
    }
    @Override
    public String getUrl() {
        return getHost() + "Image/upload";
    }
    @Override
    public Type getParserType() {
        return new TypeToken<Response<UploadResp>>() {}.getType();
    }
}

PublishPresenter 具体实现

图片裁剪请查看:详细解释如何通过Android自带的方式来实现图片的裁剪——原理分析+解决方案

public class PublishPresenter extends IPublishPresenter {
    private IPublishView mIPublishView;
    private boolean mUploading = false;
    private String TAG = PublishPresenter.class.getSimpleName();
    public PublishPresenter(IPublishView iPublishView) {
        super(iPublishView);
        this.mIPublishView = iPublishView;
    }

    @Override
    public void start() {
    }

    @Override
    public void finish() {
        mIPublishView.finishActivity();
    }
  
    /**
     * 直接调用系统的图片裁剪功能
     * @param uri
     * @return
     */
    @Override
    public Uri cropImage(Uri uri) {
        Uri cropUri = createCoverUri("_crop");
        Intent intent = new Intent("com.android.camera.action.CROP");
        intent.setDataAndType(uri, "image/*");//可以选择图片类型,如果是 * 表明所有类型的图片
        intent.putExtra("crop", "true");//设置在开启的Intent中设置显示的 view 可裁剪
        intent.putExtra("aspectX", 750);//裁剪图片的比例
        intent.putExtra("aspectY", 550);
        intent.putExtra("outputX", 750);//裁剪图片的宽
        intent.putExtra("outputY", 550);
        intent.putExtra("scale", true);//是否保持比例
        intent.putExtra("return-data", false);//是否返回bitmap
        intent.putExtra(MediaStore.EXTRA_OUTPUT, cropUri);//保存图片到指定uri
        intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());//输出格式
        mIPublishView.getActivity().startActivityForResult(intent, Constants.CROP_CHOOSE);//启动裁剪功能
        return cropUri;
    }
    //存储封面图片并保存uri地址
    private Uri createCoverUri(String preFileName) {
        String filename = ImUserInfoMgr.getInstance().getUserId() + preFileName + ".jpg";
        String path = Environment.getExternalStorageDirectory() + "/cniao_live";
        File outputImage = new File(path, filename);
        if (ContextCompat.checkSelfPermission(mIPublishView.getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE)
                != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(mIPublishView.getActivity(), new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, Constants.WRITE_PERMISSION_REQ_CODE);
            return null;
        }
        try {
            File pathFile = new File(path);
            if (!pathFile.exists())
                pathFile.mkdirs();

            if (outputImage.exists())
                outputImage.delete();
        } catch (Exception e) {
            e.printStackTrace();
            mIPublishView.showMsg("生成封面失败");
        }
        return Uri.fromFile(outputImage);
    }
    //选择封面图来源(相机、相册)
    @Override
    public Uri pickImage(boolean mPermission, int type) {
        Uri fileUri = null;
        if (!mPermission) {
            mIPublishView.showMsg("权限不足");
            return null;
        }
        switch (type) {
            //相机
            case Constants.PICK_IMAGE_CAMERA:
                fileUri = createCoverUri("");
                //启动相机
                Intent intent_photo = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                intent_photo.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);
                mIPublishView.getActivity().startActivityForResult(intent_photo, Constants.PICK_IMAGE_CAMERA);
                break;
            //本地相册
            case Constants.PICK_IMAGE_LOCAL:
                fileUri = createCoverUri("_select");
                //打开相册
                Intent intent_album = new Intent("android.intent.action.GET_CONTENT");
                intent_album.setType("image/*");
                mIPublishView.getActivity().startActivityForResult(intent_album, Constants.PICK_IMAGE_LOCAL);
                break;
        }
        return fileUri;
    }
    //上传封面图
    @Override
    public void doUploadPic(String path) {
        mUploading = true;
        try {
            final UploadPicRequest request = new UploadPicRequest(1000,
                    ACache.get(mIPublishView.getContext()).getAsString("user_id"),
                    Constants.LIVE_COVER_TYPE,new File(path));
            AsyncHttp.instance().post(request, new AsyncHttp.IHttpListener() {
                @Override
                public void onStart(int requestId) {

                }
                @Override
                public void onSuccess(int requestId, Response response) {
                    if (response!=null) {
                        UploadResp resp = (UploadResp) response.getData();
                        Log.i(TAG, "onSuccess url:" + resp.getUrl());
                        mIPublishView.doUploadSuccess(resp.getUrl());
                    }else {
                        Log.i(TAG, "onSuccess url:");
                    }
                }
                @Override
                public void onFailure(int requestId, int httpStatus, Throwable error) {
                    Log.i(TAG, "onFailure :" + error);
                    mIPublishView.doLocationFailed();
                }
            });
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }
}

封面图片上传实现

整个大致流程:首先检测权限,权限添加之后点击封面图片调用封面选择对话框,会出现三个按钮(相机、相册、取消)。
相机:打开相机功能进行拍照,然后确定之后会调用系统裁剪功能对图片进行裁剪,接着会将图片进行保存,裁剪之后再点击确定就会调用上传图片功能。
相册:打开相册选择,选择之后会调用系统裁剪功能对图片进行裁剪,接着会将图片进行保存,裁剪之后再点击确定就会调用上传图片功能。
取消:点击取消则退出当前对话框。

public class PublishActivity extends BaseActivity implements View.OnClickListener, IPublishView{
    //图片封面文字
    private TextView mTvPicTip;
    private Dialog mPicDialog;//选择封面对话框(照相机、相册、取消)
    private ImageView mImgCover;//封面图
    private Uri mFileUri, mCropUri;//原始图片文件uri,裁剪之后的图片文件uri
    private boolean mPermission = false;//权限监测

    private PublishPresenter mPublishPresenter;
    private String TAG = PublishActivity.class.getSimpleName();

    @Override
    protected void setActionBar() {
    }

    @Override
    protected void setListener() {
        mImgCover.setOnClickListener(this);
    }

    @Override
    protected void initData() {
        //初始化PublishPresenter
        mPublishPresenter = new PublishPresenter(this);
        //检测权限
        mPermission = mPublishPresenter.checkPublishPermission(this);
        String strCover = ACache.get(this).getAsString("head_pic");
        if (!TextUtils.isEmpty(strCover)) {
            Log.e(TAG, "head_pic:" + strCover);
            Glide.with(this).load(strCover).into(mImgCover);
            mTvPicTip.setVisibility(View.GONE);
        } else {
            mImgCover.setImageResource(R.drawable.publish_background);
        }
    }

    @Override
    protected void initView() {
        mTvPicTip = obtainView(R.id.tv_pic_tip);
        mImgCover = obtainView(R.id.cover);
        //初始化图片选择对话框
        initPhotoDialog();
    }

    /**
     * 封面图片选择对话框
     */
    private void initPhotoDialog() {
        //对话框初始化及样式设置
        mPicDialog = new Dialog(this, R.style.float_dialog);
        //对话框布局
        mPicDialog.setContentView(R.layout.dialog_pic_choose);
        WindowManager windowManager = getWindowManager();
        Display display = windowManager.getDefaultDisplay();
        Window window = mPicDialog.getWindow();
        WindowManager.LayoutParams lp = window.getAttributes();
        window.setGravity(Gravity.BOTTOM);
        lp.width = display.getWidth();
        mPicDialog.getWindow().setAttributes(lp);
        //按钮初始化并添加点击事件
        mPicDialog.findViewById(R.id.tv_chose_camera).setOnClickListener(this);
        mPicDialog.findViewById(R.id.tv_pic_lib).setOnClickListener(this);
        mPicDialog.findViewById(R.id.tv_dialog_cancel).setOnClickListener(this);
    }

    @Override
    protected int getLayoutId() {
        return R.layout.activity_publish;
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            //封面图片选择
            case R.id.cover:
                mPicDialog.show();
                break;
            //相机,对拍摄图片地址进行存储,并对图片进行裁剪
            case R.id.tv_chose_camera:
                mFileUri = mPublishPresenter.pickImage(mPermission, Constants.PICK_IMAGE_CAMERA);
                mPicDialog.dismiss();
                break;
            //相册,从本地相册选择图片作为封面,并对图片进行裁剪,并将地址进行保存
            case R.id.tv_pic_lib:
                mFileUri = mPublishPresenter.pickImage(mPermission, Constants.PICK_IMAGE_LOCAL);
                mPicDialog.dismiss();
                break;
            //取消对话框按钮,表示不添加封面图片
            case R.id.tv_dialog_cancel:
                mPicDialog.dismiss();
                break;
        }
    }
    //相机和相册选择结果回调
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (resultCode == RESULT_OK) {
            switch (requestCode) {
                //选择相机图片
                case Constants.PICK_IMAGE_CAMERA:
                    mCropUri = mPublishPresenter.cropImage(mFileUri);
                    Log.d(TAG, "cropImage->path camera:" + mCropUri.getPath());
                    Log.d(TAG,"PICK_IMAGE_CAMERA 选择相机图片成功");
                    break;
                //选择本地相册图片
                case Constants.PICK_IMAGE_LOCAL:
                    String path = OtherUtils.getPath(this, data.getData());
                    if (null != path) {
                        Log.d(TAG, "cropImage->path local:" + path);
                        File file = new File(path);
                        mCropUri = mPublishPresenter.cropImage(Uri.fromFile(file));
                    }
                    Log.e(TAG,"PICK_IMAGE_LOCAL 选择本地图片成功");
                    break;
                //上传相机/相册图片
                case Constants.CROP_CHOOSE:
                    mTvPicTip.setVisibility(View.GONE);
                    Log.d(TAG, "cropImage->path crop:" + mCropUri.getPath());
                    mPublishPresenter.doUploadPic(mCropUri.getPath());
                    Log.d(TAG,"CROP_CHOOSE 上传图片成功");
                    break;
            }
        }
    }

    @Override
    public void doUploadSuccess(String url) {
        //加载封面图
        Glide.with(this).load(url).into(mImgCover);
    }

    @Override
    public void doUploadFailed(String url) {
        showMsg("直播封面上传失败");
    }
    //其他某些实现方法已被删,代码量太大,只贴出主要代码
    //......
}

运行效果

在控制台可以查看到打印的 Log 信息,先选择图片,然后裁剪之后进行上传。上传到服务端会返回一个 url 地址。打开 url 可以在浏览器中查看到图片。



在手机上找到 cniao_live 文件夹,并且有刚刚上传过的图片。

详情请转至 GitHub 特训营戳>>http://www.cniao5.com/hd/h5/android/fkcxy.html
上一篇下一篇

猜你喜欢

热点阅读