做了一个仿抖音的app,刚刚二期开发结束了,项目名叫 琅琊短视频。项目用到了阿里趣视频,今天朋友问一录制视频之后返回闪退,索性拿出一点时间记录一下,方便看。
录制视频之后,进入发布页面,返回闪退或者发布也闪退:
经过至少二百人提问题和测试之后,趣视频的代码即使你写的和阿里官方给出的文档写的一模一样,也会有机型在录制视频返回之后闪退。why?开始公测之后发现闪退的问题,我当然是先认为是自己的问题,把代码梳理了一遍又一遍,完美啊,没问题啊。
后来,把代码一点点全都注释掉之后,用穷举法发现了问题:在onDestory()的时候,阿里的一些资源回收,在一些机型是回收不动的,直接闪退。但是!不回收,在另外一些机型会闪退。最后,四个回收资源的方法,我保留了三个,注释掉一个,在几百人的测试中,没有再发现问题。
附代码如下:
//销毁回收
@Override
protected void onDestroy() {
super.onDestroy();
if (mCompose != null) {
mCompose.release();
//根据阿里11.1号更新版本添加
mCompose = null;
}
// if (aliyunIThumbnailFetcher != null) {
// aliyunIThumbnailFetcher.release();
// }
if (mAsyncTaskOnCreate != null) {
mAsyncTaskOnCreate.cancel(true);
mAsyncTaskOnCreate = null;
}
if (mAsyncTaskResult != null) {
mAsyncTaskResult.cancel(true);
mAsyncTaskResult = null;
}
}
我以为阿里趣视频版本更新那么快,新版本不会了,不,还是会。那么这里的经验就值得借鉴了,起码你知道问题出在回收方法上,具体如何做,还是要真机测试。
上传视频耗时长:
和阿里官方文档写的一样,也会在个别机型出现耗时间长。前提是你的代码能优化的都做了,那么,你下载对应版本的阿里demo,进行真机测试,可能会出现阿里的demo也会及较慢。那么束手就擒吧,首先这种手机的比例很低(公司一百五十人参与测试,只发现一个,客户方面只发现一个小众机型),我发现并且真机测试过的只有红米note7PRO这一款有问题,上传十五秒视频要两三分钟,也还可以。
更换音乐列表为自己服务器的数据:
在AliyunSvideoMusic依赖中,com.aliyun.apsaravideo.music.music包下面的MusicLoader类,我改造如下:
package com.aliyun.apsaravideo.music.music;
import android.content.Context;
import android.os.AsyncTask;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;
import com.aliyun.apsaravideo.music.R;
import com.aliyun.apsaravideo.music.utils.SharedPreferenceUtils;
import com.aliyun.common.utils.CommonUtil;
import com.aliyun.common.utils.StorageUtils;
import com.aliyun.common.utils.ToastUtil;
import com.aliyun.downloader.DownloaderManager;
import com.aliyun.downloader.FileDownloaderCallback;
import com.aliyun.downloader.FileDownloaderModel;
import com.aliyun.svideo.base.http.EffectService;
import com.aliyun.svideo.base.http.MusicFileBean;
import com.google.gson.Gson;
import com.liulishuo.filedownloader.BaseDownloadTask;
import com.lzy.okgo.OkGo;
import com.lzy.okgo.callback.StringCallback;
import com.lzy.okgo.model.HttpParams;
import com.lzy.okgo.model.Response;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class MusicLoader {
public static final int EFFECT_MUSIC = 5; //音乐
private final MusicQuery mMusicQuery;
private LoadCallback callback;
private final Context mContext;
public EffectService mService = new EffectService();
public void loadAllMusic() {
loadLocalMusic();
loadMoreOnlineMusic();
}
public MusicLoader(Context context) {
mContext = context;
mMusicQuery = new MusicQuery(context);
}
/**
* 加载本地音乐
*/
public void loadLocalMusic() {
mMusicQuery.setOnResProgressListener(new MusicQuery.OnResProgressListener() {
@Override
public void onResProgress(ArrayList<MusicFileBean> musics) {
List<EffectBody<MusicFileBean>> effectBodyList = new ArrayList<>();
for (MusicFileBean musicFileBean : musics) {
effectBodyList.add(new EffectBody<MusicFileBean>(musicFileBean, true));
}
if (callback != null) {
callback.onLoadLocalMusicCompleted(effectBodyList);
}
}
});
mMusicQuery.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
private int pageSize = 25;
private int pageNo = 1;
/**
* 是否正在加载网络音乐
*/
private boolean isLoadingMusic;
private boolean isMusicEnd;
/**
* 搜索音乐
*/
public void searchOnlineMusic(String keyWord) {
loadOnlineMusic(1, 25, keyWord);
}
public void loadMoreOnlineMusic() {
if (isLoadingMusic || isMusicEnd) {
return;
}
loadOnlineMusic(pageNo, pageSize, "");
}
/**
* 加载网络音乐 小肥龙音乐
*/
List<MusicFileBean> result = new ArrayList<>();
private void loadOnlineMusic(int pageNo, final int pageSize, final String keyWord) {
isLoadingMusic = true;
HttpParams params = new HttpParams();
params.put("page", pageNo);
params.put("token", SharedPreferenceUtils.getToken(mContext));
OkGo.<String>post("http://www.1yft.com/Api/Video/getmusiclist").tag(mContext).params(params).execute(new StringCallback() {
@Override
public void onSuccess(Response<String> response) {
List<MusicLineBean> musicLineBeanList = new ArrayList<>();
Gson gs = new Gson();
BaseBean jsonObject = gs.fromJson(response.body(), BaseBean.class);//把JSON字符串转为对象
/* musicLineBeanList = (List<MusicLineBean>) gs.fromJson(String.valueOf(response.body()), new TypeToken<List<MusicLineBean>>() {
}.getType());*/
musicLineBeanList = (List<MusicLineBean>) jsonObject.data;
isLoadingMusic = false;
if (TextUtils.isEmpty(keyWord) && musicLineBeanList.size() < pageSize) {
isMusicEnd = true;
}
//项目中需要的music
for (int i = 0; i < musicLineBeanList.size(); i++) {
MusicFileBean musicFileBean = new MusicFileBean();
musicFileBean.setMusicId(musicLineBeanList.get(i).getId());
musicFileBean.setTitle(musicLineBeanList.get(i).getMusic_name());
musicFileBean.setArtist(musicLineBeanList.get(i).getMusic_author());
musicFileBean.setImage(musicLineBeanList.get(i).getMusic_image());
musicFileBean.setPath(musicLineBeanList.get(i).getMusic_url());
musicFileBean.setDuration(Integer.parseInt(String.valueOf(Math.round(Double.parseDouble(musicLineBeanList.get(i).getMusic_second())))));
result.add(musicFileBean);
}
List<EffectBody<MusicFileBean>> effectBodyList = new ArrayList<>();
List<FileDownloaderModel> modelsTemp = DownloaderManager.getInstance().getDbController().getResourceByType(EFFECT_MUSIC);
for (MusicFileBean musicFileBean : result) {
EffectBody<MusicFileBean> effectBody = new EffectBody<>(musicFileBean, false);
for (FileDownloaderModel fileDownloaderModel : modelsTemp) {
if (musicFileBean.getMusicId().equals(fileDownloaderModel.getDownload()) && new File(fileDownloaderModel.getPath()).exists()) {
musicFileBean.setPath(fileDownloaderModel.getPath());
effectBody.setLocal(true);
}
}
effectBodyList.add(effectBody);
}
if (callback != null) {
if (TextUtils.isEmpty(keyWord)) {
callback.onLoadNetMusicCompleted(effectBodyList);
} else {
callback.onSearchNetMusicCompleted(effectBodyList);
}
}
MusicLoader.this.pageNo++;
MusicLoader.this.pageSize++;
}
@Override
public void onError(Response<String> response) {
super.onError(response);
isLoadingMusic = false;
}
});
}
/**
* 下载音乐
*
* @param musicFileBean
* @param callback
*/
public void downloadMusic(final MusicFileBean musicFileBean, final FileDownloaderCallback callback) {
if (!CommonUtil.hasNetwork(mContext)) {
ToastUtil.showToast(mContext, R.string.aliyun_network_not_connect);
return;
}
if (CommonUtil.SDFreeSize() < 10 * 1000 * 1000) {
Toast.makeText(mContext, R.string.aliyun_no_free_memory, Toast.LENGTH_SHORT).show();
return;
}
/*
* 下载地址通过musicId进行比对
* */
String url = "";
for (int i = 0; i < result.size(); i++) {
if (musicFileBean.musicId.equals(result.get(i).getMusicId())) {
url = result.get(i).getPath();
}
}
if (TextUtils.isEmpty(url)) {
ToastUtil.showToast(mContext, "播放地址为空");
return;
}
final FileDownloaderModel downloaderModel = new FileDownloaderModel();
downloaderModel.setUrl(url);
downloaderModel.setDownload(musicFileBean.getMusicId());
downloaderModel.setName(musicFileBean.title);
downloaderModel.setIsunzip(0);
downloaderModel.setDuration(musicFileBean.duration);
downloaderModel.setPath(StorageUtils.getFilesDirectory(mContext) + "/music/" + musicFileBean.title);
downloaderModel.setDescription(musicFileBean.artist);
downloaderModel.setEffectType(EFFECT_MUSIC);
if (downloaderModel == null) {
Log.e("MusicLoader", "downloaderModel is null");
}
final FileDownloaderModel model = DownloaderManager.getInstance().addTask(downloaderModel, url);
if (model == null) {
Log.e("MusicLoader", "model is null");
}
if (DownloaderManager.getInstance().isDownloading(model.getTaskId(), model.getPath())) {
return;
}
DownloaderManager.getInstance().startTask(model.getTaskId(), new FileDownloaderCallback() {
@Override
public void onFinish(int downloadId, String path) {
callback.onFinish(downloadId, path);
}
@Override
public void onStart(int downloadId, long soFarBytes, long totalBytes, int preProgress) {
callback.onStart(downloadId, soFarBytes, totalBytes, preProgress);
}
@Override
public void onProgress(int downloadId, long soFarBytes, long totalBytes, long speed,
int progress) {
callback.onProgress(downloadId, soFarBytes, totalBytes, speed, progress);
}
@Override
public void onError(BaseDownloadTask task, Throwable e) {
ToastUtil.showToast(mContext, R.string.aliyun_download_failed);
DownloaderManager.getInstance().deleteTaskByTaskId(model.getTaskId());
DownloaderManager.getInstance().getDbController().deleteTask(model.getTaskId());
callback.onError(task, e);
}
});
}
public interface LoadCallback {
void onLoadLocalMusicCompleted(List<EffectBody<MusicFileBean>> loacalMusic);
void onLoadNetMusicCompleted(List<EffectBody<MusicFileBean>> netMusic);
void onSearchNetMusicCompleted(List<EffectBody<MusicFileBean>> result);
}
public void setCallback(LoadCallback callback) {
this.callback = callback;
}
}
MusicFileBean类:
package com.aliyun.svideo.base.http;
public class MusicFileBean {
public MusicFileBean(String title, String artist, String musicId,String image) {
this.title = title;
this.artist = artist;
this.musicId = musicId;
this.image = image;
}
public MusicFileBean() {
}
/**
* id标识
*/
public int id;
/**
* 显示名称
*/
public String title;
/**
* 文件名称
*/
public String displayName;
/**
* 音乐文件的路径
*/
public String path;
/**
* 媒体播放总时间
*/
public int duration;
/**
* 艺术家
*/
public String artist;
/**
* 音乐id
*/
public String musicId;
/**
* 音乐图片地址
*/
public String image;
public long size;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getDisplayName() {
return displayName;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
public int getDuration() {
return duration;
}
public void setDuration(int duration) {
this.duration = duration;
}
public String getArtist() {
return artist;
}
public void setArtist(String artist) {
this.artist = artist;
}
public String getMusicId() {
return musicId;
}
public void setMusicId(String musicId) {
this.musicId = musicId;
}
public long getSize() {
return size;
}
public void setSize(long size) {
this.size = size;
}
public String getImage() {
return image;
}
public void setImage(String image) {
this.image = image;
}
}