在Activity的mainThread中,我的图像处理器和Exo播放器(用于播放音乐) 用过的。 OS-9(Nokia)设备中的螺母无法正常工作imageHandler->连接到Workhandler的图像处理器。 代码下方: WorkHandler
public class WorkHandler extends android.os.HandlerThread {
private Handler workHandler = null;
private volatile List<WorkMessageProxy> messageProxyList;
public WorkHandler() {
super("WorkHandler", Process.THREAD_PRIORITY_BACKGROUND);
start();
workHandler = new Handler(getLooper()) {
@Override
public void handleMessage(Message msg) {
if (messageProxyList != null) {
for (WorkMessageProxy workMessageProxy : messageProxyList) {
Log.i("workHandler", "handleMessage: Message: " + msg.toString());
workMessageProxy.handleMessage(msg);
}
}
}
};
}
public void post(Runnable run) {
workHandler.post(run);
}
public void postAtFrontOfQueue(Runnable runnable) {
workHandler.postAtFrontOfQueue(runnable);
}
public void postDelayed(Runnable runnable, long delay) {
workHandler.postDelayed(runnable, delay);
}
public void postAtTime(Runnable runnable, long time) {
workHandler.postAtTime(runnable, time);
}
public void addMessageProxy(WorkMessageProxy proxy) {
initMessageProxyList();
messageProxyList.add(proxy);
}
public void removeMessageProxy(WorkMessageProxy proxy) {
initMessageProxyList();
messageProxyList.remove(proxy);
}
private void initMessageProxyList() {
if (messageProxyList == null) {
messageProxyList = new ArrayList<>();
}
}
public Handler getHandler() {
return workHandler;
}
public interface WorkMessageProxy {
void handleMessage(Message msg);
}
}
ImageHandler
public class ImageFrameHandler implements WorkHandler.WorkMessageProxy {
private static final String TAG = "ImageFrameHandler";
private Resources resources;
private int[] resArray;
private int width;
private int height;
public boolean isRunning;
public final WorkHandler workHandler;
private File[] files;
private static final int FILE = 0;
private static final int RES = 1;
private int type;
private boolean isOpenCache;
@Retention(RetentionPolicy.SOURCE)
@IntDef({FILE, RES})
public @interface Operation {
}
ImageFrameHandler(@Operation int type) {
this.type = type;
imageCache = new ImageCache();
workHandler = new WorkHandler();
}
@Deprecated
public ImageFrameHandler() {
imageCache = new ImageCache();
workHandler = new WorkHandler();
}
private ImageCache imageCache;
private Handler handler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
if (onImageLoadListener != null) {
onImageLoadListener.onImageLoad(bitmapDrawable);
}
switch (msg.what) {
case RES:
load((int[]) msg.obj);
break;
case FILE:
load((File[]) msg.obj);
break;
}
}
};
private volatile float frameTime;
private volatile float delayTime;//in millisecond
private volatile int index = 0;
private volatile BitmapDrawable bitmapDrawable;
private OnImageLoadListener onImageLoadListener;
private boolean loop;
/**
* load frame form file directory;
*
* @param width request width
* @param height request height
* @param fileDir file directory
* @param fps The number of broadcast images per second
* @param onPlayFinish finish callback
*/
@Deprecated
public void loadImage(final String fileDir, int width, int height, int fps,
OnImageLoadListener onPlayFinish) {
if (!isRunning) {
isRunning = true;
this.width = width;
this.height = height;
loadImage(fileDir, fps, onPlayFinish);
}
}
/**
* load frame form file directory;
*
* @param fileDir file directory
* @param fps The number of broadcast images per second
* @param onPlayFinish finish callback
*/
@Deprecated
public void loadImage(final String fileDir, int fps, OnImageLoadListener onPlayFinish) {
if (!isRunning && !TextUtils.isEmpty(fileDir) && fps > 0) {
isRunning = true;
File dir = new File(fileDir);
if (dir.exists() && dir.isDirectory()) {
File[] files = dir.listFiles();
loadImage(files, width, height, fps, onPlayFinish);
}
}
}
/**
* load frame form file files Array;
*
* @param width request width
* @param height request height
* @param files files Array
* @param fps The number of broadcast images per second
* @param onPlayFinish finish callback
*/
@Deprecated
public void loadImage(final File[] files, int width, int height, int fps,
OnImageLoadListener onPlayFinish) {
if (!isRunning) {
setImageFrame(files, width, height, fps, onPlayFinish);
load(files);
}
}
/**
* load frame form file files Array;
*
* @param files files Array
* @param fps The number of broadcast images per second
* @param onPlayFinish finish callback
*/
@Deprecated
public void loadImage(final File[] files, int fps, OnImageLoadListener onPlayFinish) {
loadImage(files, width, height, fps, onPlayFinish);
}
private void setImageFrame(final File[] files, int width, int height, float fps,
OnImageLoadListener onPlayFinish) {
this.width = width;
this.height = height;
if (imageCache == null) {
imageCache = new ImageCache();
}
this.onImageLoadListener = onPlayFinish;
frameTime = 1000f / fps + 0.5f;
workHandler.addMessageProxy(this);
this.files = files;
}
/**
* load frame form file resources Array;
*
* @param resArray resources Array
* @param width request width
* @param height request height
* @param fps The number of broadcast images per second
* @param onPlayFinish finish callback
*/
@Deprecated
public void loadImage(Resources resources, @RawRes int[] resArray, int width, int height, int fps,
OnImageLoadListener onPlayFinish) {
loadImage(resources, resArray, width, height, fps, onPlayFinish);
}
private void setImageFrame(Resources resources, @RawRes int[] resArray, int width, int height,
float fps,
OnImageLoadListener onPlayFinish) {
this.width = width;
this.height = height;
this.resources = resources;
if (imageCache == null) {
imageCache = new ImageCache();
}
this.onImageLoadListener = onPlayFinish;
frameTime = 1000f / fps + 0.5f;
workHandler.addMessageProxy(this);
this.resArray = resArray;
}
/**
* load frame form file resources Array;
*
* @param resArray resources Array
* @param fps The number of broadcast images per second
* @param onPlayFinish finish callback
*/
@Deprecated
public void loadImage(Resources resources, @RawRes int[] resArray, int fps,
OnImageLoadListener onPlayFinish) {
if (!isRunning) {
setImageFrame(resources, resArray, width, height, fps, onPlayFinish);
load(resArray);
}
}
/**
* loop play frame
*
* @param loop true is loop
*/
public ImageFrameHandler setLoop(boolean loop) {
if (!isRunning) {
this.loop = loop;
}
return this;
}
/**
* stop play frame
*/
public void stop() {
workHandler.getHandler().removeCallbacksAndMessages(null);
workHandler.removeMessageProxy(this);
handler.removeCallbacksAndMessages(null);
resources = null;
isRunning = false;
if (onImageLoadListener != null) {
onImageLoadListener.onPlayStop();
}
}
public void pause() {
isRunning = false;
workHandler.getHandler().removeCallbacksAndMessages(null);
// workHandler.removeMessageProxy(this);
handler.removeCallbacksAndMessages(null);
}
public void start() {
if (!isRunning) {
isRunning = true;
switch (type) {
case FILE:
load(files);
break;
case RES:
load(resArray);
break;
}
}
}
private void load(final File[] files) {
Log.i(TAG, "load: files");
Message message = Message.obtain();
message.obj = files;
message.what = FILE;
workHandler.getHandler().sendMessage(message);
workHandler.postDelayed(this::start, 10000);
}
private void load(@RawRes int[] res) {
Message message = Message.obtain();
message.obj = res;
message.what = RES;
workHandler.getHandler().sendMessage(message);
}
private void loadInThreadFromFile(final File[] files) {
Log.i(TAG, "loadInThreadFromFile: " + files.length);
Log.i(TAG, "loadInThreadFromFile: " + index);
if (index < files.length) {
File file = files[index];
//Log.i(TAG, "loadInThreadFromFile: " + file != null ? file.getAbsolutePath() : "");
if (file.isFile() && file.exists() && isPicture(file)) {
if (bitmapDrawable != null) {
imageCache.mReusableBitmaps.add(new SoftReference<>(bitmapDrawable.getBitmap()));
}
long start = System.currentTimeMillis();
bitmapDrawable = BitmapLoadUtils.decodeSampledBitmapFromFile(file.getAbsolutePath(), width, height, imageCache, isOpenCache);
long end = System.currentTimeMillis();
long diff = end - start;
//Log.i(TAG, "loadInThreadFromFile: frameTime: " + frameTime + "\tdelayTime: " + delayTime + "\tdiff=" + diff);
//Log.i(TAG, "loadInThreadFromFile: mod:" + ANIMATED_FRAME + "~" + Math.round(ANIMATED_FRAME));
float time = 0;
Log.i(TAG, "loadInThreadFromFile: ANIMATED_FRAME: " + (index % Math.round(ANIMATED_FRAME)));
if (index % Math.round(ANIMATED_FRAME) != 0) {
time = frameTime;
} else {
Log.e(TAG, "Have delay @" + index);
time = delayTime;
}
float updateTime = (time - diff) > 0 ? (time - diff) : 0;
Message message = Message.obtain();
message.what = FILE;
message.obj = files;
handler.sendMessageAtTime(message, (int) (SystemClock.uptimeMillis() + updateTime));
//Log.i(TAG, "loadInThreadFromFile: updateTime:" + updateTime + "\tindex:" + index + "\tlength:" + files.length);
index++;
} else {
index++;
loadInThreadFromFile(files);
}
} else {
if (loop) {
Log.i(TAG, "loadInThreadFromFile: loop: IF");
index = 0;
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
loadInThreadFromFile(files);
}
}, (long) delayTime);
} else {
Log.i(TAG, "loadInThreadFromFile: loop=> " + loop);
index++;
bitmapDrawable = null;
frameTime = 0;
if (onImageLoadListener != null) {
onImageLoadListener.onPlayFinish();
}
isRunning = false;
onImageLoadListener = null;
}
}
if (onImageLoadListener != null) {
Log.i(TAG, "loadInThreadFromFile: index: " + index);
onImageLoadListener.onIndexChange(index);
}
}
private void loadInThreadFromRes(final int[] resIds) {
if (index < resIds.length) {
int resId = resIds[index];
if (bitmapDrawable != null) {
imageCache.mReusableBitmaps.add(new SoftReference<>(bitmapDrawable.getBitmap()));
}
long start = System.currentTimeMillis();
bitmapDrawable = BitmapLoadUtils.decodeSampledBitmapFromRes(resources, resId, width, height, imageCache, isOpenCache);
long end = System.currentTimeMillis();
float updateTime = (frameTime - (end - start)) > 0 ? (frameTime - (end - start)) : 0;
Message message = Message.obtain();
message.what = RES;
message.obj = resIds;
handler.sendMessageAtTime(message, index == 0 ? 0 : (int) (SystemClock.uptimeMillis() + updateTime));
index++;
} else {
if (loop) {
index = 0;
loadInThreadFromRes(resIds);
} else {
index++;
bitmapDrawable = null;
frameTime = 0;
if (onImageLoadListener != null) {
onImageLoadListener.onPlayFinish();
}
isRunning = false;
onImageLoadListener = null;
}
}
}
private boolean isPicture(File file) {
return file.getName().endsWith("png") || file.getName().endsWith("jpg");
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case RES:
loadInThreadFromRes((int[]) msg.obj);
break;
case FILE:
Log.i("ImageFrameHandler", "msg.obj; " + msg.obj.toString());
loadInThreadFromFile((File[]) msg.obj);
break;
}
}
public void setFps(float fps) {
Log.e(TAG, "setFps: " + fps);
//frameTime = 1000f / fps + 0.5f;
frameTime = 1000f / fps;
}
/**
* @param dps seconds in Sec
*/
public void setDelays(float dps) {
Log.e(TAG, "setDelays: " + dps);
delayTime = dps * 1000.0f;
}
public void setOnImageLoaderListener(OnImageLoadListener onPlayFinish) {
this.onImageLoadListener = onPlayFinish;
}
public interface OnImageLoadListener {
void onImageLoad(BitmapDrawable drawable);
void onPlayStop();
void onPlayFinish();
void onIndexChange(int index);
}
public static class FileHandlerBuilder implements FrameBuild {
private int width;
private int height;
private float fps = 30;
private float delays = 2;
private File[] files;
private OnImageLoadListener onPlayFinish;
private ImageFrameHandler imageFrameHandler;
private int startIndex;
private int endIndex;
public FileHandlerBuilder(@NonNull File[] files) {
if (files.length == 0) {
throw new IllegalArgumentException("fileDir is not empty");
}
this.files = files;
createHandler();
}
public FileHandlerBuilder(@NonNull String fileDir) {
Log.i(TAG, "FileHandlerBuilder: fileDir");
if (TextUtils.isEmpty(fileDir)) {
throw new IllegalArgumentException("fileDir is not empty");
}
File dir = new File(fileDir);
if (dir.exists() && dir.isDirectory()) {
files = dir.listFiles();
}
createHandler();
}
@Override
public FrameBuild setLoop(boolean loop) {
imageFrameHandler.setLoop(loop);
return this;
}
@Override
public FrameBuild stop() {
imageFrameHandler.stop();
return this;
}
// Just change the length of the array
@Override
public FrameBuild setStartIndex(int startIndex) {
if (startIndex >= files.length) {
throw new IllegalArgumentException("startIndex is not big to files length");
}
this.startIndex = startIndex;
return this;
}
@Override
public FrameBuild setEndIndex(int endIndex) {
if (endIndex > files.length) {
throw new IllegalArgumentException("endIndex is not big to files length");
}
if (endIndex <= startIndex) {
throw new IllegalArgumentException("endIndex is not to small startIndex");
}
this.endIndex = endIndex;
return this;
}
@Override
public FrameBuild setWidth(int width) {
this.width = width;
return this;
}
@Override
public FrameBuild setHeight(int height) {
this.height = height;
return this;
}
@Override
public FrameBuild setFps(float fps) {
this.fps = fps;
imageFrameHandler.setFps(fps);
return this;
}
@Override
public FrameBuild setDelays(float dps) {
this.delays = dps;
imageFrameHandler.setDelays(dps);
return this;
}
@Override
public FrameBuild setOnImageLoaderListener(OnImageLoadListener onPlayFinish) {
this.onPlayFinish = onPlayFinish;
return this;
}
@Override
public FrameBuild openLruCache(boolean isOpenCache) {
imageFrameHandler.openLruCache(isOpenCache);
return this;
}
@Override
public ImageFrameHandler build() {
if (!imageFrameHandler.isRunning) {
clip();
imageFrameHandler.setImageFrame(files, width, height, fps, onPlayFinish);
}
return imageFrameHandler;
}
private void createHandler() {
if (imageFrameHandler == null) {
Log.i(TAG, "createHandler: ");
imageFrameHandler = new ImageFrameHandler(FILE);
}
}
@Override
public FrameBuild clip() {
if (startIndex >= 0 && endIndex > 0 && startIndex < endIndex) {
files = split(files, startIndex, endIndex);
}
return this;
}
File[] split(File[] resArray, int start, int end) {
File[] ints = new File[end - start];
int index = 0;
for (int i = start; i < end; i++) {
ints[index] = resArray[i];
index++;
}
return ints;
}
}
private void openLruCache(boolean isOpenCache) {
this.isOpenCache = isOpenCache;
}
/**
* Transform into build builder mode
* <p>
* Two kinds are file
* Resource
*/
public static class ResourceHandlerBuilder implements FrameBuild {
@NonNull
private final Resources resources;
private int width;
private int height;
private float fps = 30;
private int[] resArray;
private OnImageLoadListener onPlayFinish;
private ImageFrameHandler imageFrameHandler;
private int startIndex;
private int endIndex;
public ResourceHandlerBuilder(@NonNull Resources resources, @NonNull @RawRes int[] resArray) {
if (resArray.length == 0) {
throw new IllegalArgumentException("resArray is not empty");
}
this.resources = resources;
this.resArray = resArray;
createHandler();
}
@Override
public FrameBuild setLoop(boolean loop) {
imageFrameHandler.setLoop(loop);
return this;
}
@Override
public FrameBuild stop() {
imageFrameHandler.stop();
return this;
}
@Override
public FrameBuild setStartIndex(int startIndex) {
if (startIndex >= resArray.length) {
throw new IllegalArgumentException("startIndex is not to big resArray length");
}
this.startIndex = startIndex;
return this;
}
@Override
public FrameBuild setEndIndex(int endIndex) {
if (endIndex > resArray.length) {
throw new IllegalArgumentException("endIndex is not big to resArray length");
}
if (endIndex <= startIndex) {
throw new IllegalArgumentException("endIndex is not to small startIndex");
}
this.endIndex = endIndex;
return this;
}
@Override
public FrameBuild clip() {
if (startIndex >= 0 && endIndex > 0 && startIndex < endIndex) {
resArray = split(resArray, startIndex, endIndex);
}
return this;
}
@Override
public FrameBuild setWidth(int width) {
this.width = width;
return this;
}
@Override
public FrameBuild setHeight(int height) {
this.height = height;
return this;
}
@Override
public FrameBuild setFps(float fps) {
this.fps = fps;
imageFrameHandler.setFps(fps);// There is a double calculation here. I want a better way to support dynamic change later.
return this;
}
@Override
public FrameBuild setDelays(float dps) {
return null;
}
@Override
public FrameBuild openLruCache(boolean isOpenCache) {
imageFrameHandler.openLruCache(isOpenCache);
return this;
}
@Override
public FrameBuild setOnImageLoaderListener(OnImageLoadListener onPlayFinish) {
this.onPlayFinish = onPlayFinish;
return this;
}
@Override
public ImageFrameHandler build() {
if (!imageFrameHandler.isRunning) {
clip();
imageFrameHandler.setImageFrame(resources, resArray, width, height, fps,
onPlayFinish);
}
return imageFrameHandler;
}
private void createHandler() {
if (imageFrameHandler == null) {
imageFrameHandler = new ImageFrameHandler(RES);
}
}
int[] split(int[] resArray, int start, int end) {
int[] ints = new int[end - start];
int index = 0;
for (int i = start; i < end; i++) {
ints[index] = resArray[i];
index++;
}
return ints;
}
}
}
我在“活动”中同时使用了以下两种方法:
Exoplayer初始化:
private void prepareExoPlayerFromURL() {
Log.i(TAG, "prepareExoPlayerFromURL: musicFilePath: " + musicFilePath);
try {
if (!FileUtils.createValidFile(musicFilePath).exists()) {
Log.i(TAG, "[prepareExoPlayerFromURL] is not Valid File URL");
Snackbar.make(toolbar, R.string.err_failed_to_pick_audio, Snackbar.LENGTH_LONG).show();
onAudioRemove();
return;
}
if (exoPlayer != null) {
MediaSource mediaSource = audioSource.createMediaSource(Uri.fromFile(FileUtils.createValidFile(musicFilePath)));
ClippingMediaSource clippingMediaSource = new ClippingMediaSource(mediaSource, 0, (long) totalDurationInSec * 1000 * 1000);
exoPlayer.prepare(clippingMediaSource);
btnMute.setVisibility(View.VISIBLE);
if (exoPlayer.getVolume() > 0) {
btnMute.setImageResource(R.drawable.ic_unmute);
}
/* TODO Write Permission */
joinAudio();
}
} catch (Throwable e) {
e.printStackTrace();
}
//initMediaControls();
}
**播放音乐**
private void playMusic() {
Log.i(TAG, "playMusic: isForeGround: " + isForeGround);
if (isForeGround && this.layLoad.getVisibility() != View.VISIBLE && this.errorView.getVisibility() != View.VISIBLE &&
this.exoPlayer != null && !this.exoPlayer.isLoading()) {
Log.i(TAG, "playMusic: TRUE");
exoPlayer.seekTo(0);
exoPlayer.setPlayWhenReady(true);
} else {
Log.i(TAG, "playMusic: exoPlayer is null");
}
}
ResetMusic当ImageHandler完成一次时:
private void resetMusic() {
Log.i(TAG, "resetMusic: " + musicFilePath);
if (isForeGround && this.layLoad.getVisibility() != View.VISIBLE && this.errorView.getVisibility() != View.VISIBLE &&
this.exoPlayer != null) {
Log.i(TAG, "resetMusic: getApplicationLooper: "+exoPlayer.getApplicationLooper().getThread().getName());
Log.i(TAG, "resetMusic: getPlaybackLooper: "+exoPlayer.getPlaybackLooper().getThread().getName());
Log.i(TAG, "resetMusic: TRUE");
exoPlayer.seekTo(0);
exoPlayer.setPlayWhenReady(true);
} else {
Log.i(TAG, "resetMusic: exoPlayer is null");
}
}
在onCreate()方法中用于Acvitiy的图像处理程序
imageFrameHandler = new ImageFrameHandler.FileHandlerBuilder(SlideShowUtility.getTempDirPath(this))
// .setFps((slideShowUtility.getVideoImages().size() - selectedImages.size()) * 1.0f / (selectedImages.size() - 1) * 1.0f)
.setFps(frameRate)
.setDelays(seconds)
.setWidth((int) (mWidth * mulX))
.setHeight((int) (mHeight * mulY))
.setLoop(true)
.build();
-> Exo播放器在位置0重置时,ResetMusic方法已发出警告。