Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
I
ImageVideoBanner
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
jiangjiantao
ImageVideoBanner
Commits
d75937a0
Commit
d75937a0
authored
Apr 23, 2021
by
委座-江
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
解决部分内存泄漏
增加二级缓存
parent
61e53d36
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
21 changed files
with
2155 additions
and
263 deletions
+2155
-263
build.gradle
imageVideoBanner/build.gradle
+1
-1
AndroidManifest.xml
imageVideoBanner/src/main/AndroidManifest.xml
+1
-2
BaseApplication.java
...ain/java/com/widget/imagevideobanner/BaseApplication.java
+19
-0
MainActivity.java
...c/main/java/com/widget/imagevideobanner/MainActivity.java
+8
-4
ImageVideoBanner.java
.../com/widget/imagevideobanner/banner/ImageVideoBanner.java
+12
-68
ImageVideoFragment.java
...om/widget/imagevideobanner/banner/ImageVideoFragment.java
+72
-126
MyVideoView.java
.../java/com/widget/imagevideobanner/banner/MyVideoView.java
+50
-0
BannerBean.java
...ain/java/com/widget/imagevideobanner/bean/BannerBean.java
+20
-17
BitmapCache.java
...n/java/com/widget/imagevideobanner/cache/BitmapCache.java
+257
-0
DiskLruCache.java
.../com/widget/imagevideobanner/cache/disk/DiskLruCache.java
+943
-0
StrictLineReader.java
.../widget/imagevideobanner/cache/disk/StrictLineReader.java
+196
-0
Util.java
...ain/java/com/widget/imagevideobanner/cache/disk/Util.java
+77
-0
BannerLogFileUtils.java
...a/com/widget/imagevideobanner/log/BannerLogFileUtils.java
+87
-0
BannerLogFileUtils.java
...com/widget/imagevideobanner/utils/BannerLogFileUtils.java
+0
-43
BitmapUtils.java
...n/java/com/widget/imagevideobanner/utils/BitmapUtils.java
+1
-1
IMMLeaks.java
...main/java/com/widget/imagevideobanner/utils/IMMLeaks.java
+177
-0
LifecycleCallbacksAdapter.java
...get/imagevideobanner/utils/LifecycleCallbacksAdapter.java
+36
-0
MD5Util.java
.../main/java/com/widget/imagevideobanner/utils/MD5Util.java
+113
-0
ThrowableUtils.java
...ava/com/widget/imagevideobanner/utils/ThrowableUtils.java
+84
-0
load_fail.png
imageVideoBanner/src/main/res/drawable-mdpi/load_fail.png
+0
-0
item_video_view.xml
imageVideoBanner/src/main/res/layout/item_video_view.xml
+1
-1
No files found.
imageVideoBanner/build.gradle
View file @
d75937a0
apply
plugin:
'com.android.
library
'
apply
plugin:
'com.android.
application
'
android
{
android
{
compileSdkVersion
28
compileSdkVersion
28
...
...
imageVideoBanner/src/main/AndroidManifest.xml
View file @
d75937a0
...
@@ -6,9 +6,9 @@
...
@@ -6,9 +6,9 @@
<uses-permission
android:name=
"android.permission.WRITE_EXTERNAL_STORAGE"
/>
<uses-permission
android:name=
"android.permission.WRITE_EXTERNAL_STORAGE"
/>
<uses-permission
android:name=
"android.permission.READ_EXTERNAL_STORAGE"
/>
<uses-permission
android:name=
"android.permission.READ_EXTERNAL_STORAGE"
/>
<application
<application
android:allowBackup=
"true"
android:allowBackup=
"true"
android:name=
".BaseApplication"
android:icon=
"@mipmap/ic_launcher"
android:icon=
"@mipmap/ic_launcher"
android:label=
"@string/app_name"
android:label=
"@string/app_name"
android:roundIcon=
"@mipmap/ic_launcher_round"
android:roundIcon=
"@mipmap/ic_launcher_round"
...
@@ -28,7 +28,6 @@
...
@@ -28,7 +28,6 @@
</activity>
</activity>
</application>
</application>
</manifest>
</manifest>
\ No newline at end of file
imageVideoBanner/src/main/java/com/widget/imagevideobanner/BaseApplication.java
0 → 100644
View file @
d75937a0
package
com
.
widget
.
imagevideobanner
;
import
android.app.Application
;
public
class
BaseApplication
extends
Application
{
private
static
BaseApplication
mApp
;
@Override
public
void
onCreate
()
{
super
.
onCreate
();
mApp
=
this
;
}
public
static
BaseApplication
getApplication
()
{
return
mApp
;
}
}
imageVideoBanner/src/main/java/com/widget/imagevideobanner/MainActivity.java
View file @
d75937a0
...
@@ -6,6 +6,7 @@ import android.support.v7.app.AppCompatActivity;
...
@@ -6,6 +6,7 @@ import android.support.v7.app.AppCompatActivity;
import
com.widget.imagevideobanner.banner.ImageVideoBanner
;
import
com.widget.imagevideobanner.banner.ImageVideoBanner
;
import
com.widget.imagevideobanner.bean.BannerBean
;
import
com.widget.imagevideobanner.bean.BannerBean
;
import
com.widget.imagevideobanner.utils.IMMLeaks
;
import
java.util.ArrayList
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.List
;
...
@@ -22,17 +23,20 @@ public class MainActivity extends AppCompatActivity {
...
@@ -22,17 +23,20 @@ public class MainActivity extends AppCompatActivity {
setDatas
();
setDatas
();
banner
.
addData
(
list
);
banner
.
addData
(
list
);
banner
.
startBanner
();
banner
.
startBanner
();
//修复inputManager引发的内存泄漏
IMMLeaks
.
fixFocusedViewLeak
(
BaseApplication
.
getApplication
());
}
}
private
void
setDatas
()
{
private
void
setDatas
()
{
for
(
int
i
=
0
;
i
<
1
;
i
++)
{
for
(
int
i
=
0
;
i
<
2
;
i
++)
{
BannerBean
listBean
=
new
BannerBean
();
BannerBean
listBean
=
new
BannerBean
();
if
(
i
==
0
)
{
if
(
i
==
0
)
{
String
ur
l
=
Environment
.
getExternalStorageDirectory
()
+
"/Video_Compressed_converseBanner_2021_01_05_03_05_11_0.mp4"
;
String
ur
i
=
"android.resource://"
+
getPackageName
()
+
"/"
+
R
.
raw
.
default_guide
;
listBean
.
setUrl
(
ur
l
);
listBean
.
setUrl
(
ur
i
);
listBean
.
setType
(
1
);
listBean
.
setType
(
1
);
list
.
add
(
listBean
);
list
.
add
(
listBean
);
}
else
if
(
i
==
1
)
{
}
else
{
String
url
=
Environment
.
getExternalStorageDirectory
()
+
"/vertical_test.mp4"
;
String
url
=
Environment
.
getExternalStorageDirectory
()
+
"/vertical_test.mp4"
;
listBean
.
setUrl
(
url
);
listBean
.
setUrl
(
url
);
listBean
.
setType
(
1
);
listBean
.
setType
(
1
);
...
...
imageVideoBanner/src/main/java/com/widget/imagevideobanner/banner/ImageVideoBanner.java
View file @
d75937a0
...
@@ -2,7 +2,6 @@ package com.widget.imagevideobanner.banner;
...
@@ -2,7 +2,6 @@ package com.widget.imagevideobanner.banner;
import
android.content.Context
;
import
android.content.Context
;
import
android.content.res.TypedArray
;
import
android.content.res.TypedArray
;
import
android.graphics.Bitmap
;
import
android.media.MediaPlayer
;
import
android.media.MediaPlayer
;
import
android.os.Bundle
;
import
android.os.Bundle
;
import
android.os.Handler
;
import
android.os.Handler
;
...
@@ -13,28 +12,24 @@ import android.support.v4.app.Fragment;
...
@@ -13,28 +12,24 @@ import android.support.v4.app.Fragment;
import
android.support.v4.app.FragmentActivity
;
import
android.support.v4.app.FragmentActivity
;
import
android.support.v4.app.FragmentManager
;
import
android.support.v4.app.FragmentManager
;
import
android.support.v4.app.FragmentStatePagerAdapter
;
import
android.support.v4.app.FragmentStatePagerAdapter
;
import
android.text.TextUtils
;
import
android.util.AttributeSet
;
import
android.util.AttributeSet
;
import
android.view.LayoutInflater
;
import
android.view.LayoutInflater
;
import
android.view.MotionEvent
;
import
android.view.MotionEvent
;
import
android.view.View
;
import
android.view.View
;
import
android.widget.FrameLayout
;
import
android.widget.FrameLayout
;
import
com.bumptech.glide.util.Util
;
import
com.widget.imagevideobanner.R
;
import
com.widget.imagevideobanner.R
;
import
com.widget.imagevideobanner.bean.BannerBean
;
import
com.widget.imagevideobanner.bean.BannerBean
;
import
com.widget.imagevideobanner.
utils.BitmapUtils
;
import
com.widget.imagevideobanner.
cache.BitmapCache
;
import
java.util.ArrayList
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.List
;
import
java.util.Timer
;
import
java.util.Timer
;
import
java.util.TimerTask
;
import
java.util.TimerTask
;
import
java.util.concurrent.Callable
;
import
java.util.concurrent.ExecutionException
;
import
java.util.concurrent.Executors
;
import
java.util.concurrent.Future
;
import
java.util.concurrent.ScheduledExecutorService
;
import
java.util.concurrent.ScheduledExecutorService
;
import
wseemann.media.FFmpegMediaMetadataRetriever
;
public
class
ImageVideoBanner
extends
FrameLayout
implements
ViewPager
.
OnPageChangeListener
,
com
.
widget
.
imagevideobanner
.
banner
.
ImageVideoFragment
.
OnVideoCompletionListener
{
public
class
ImageVideoBanner
extends
FrameLayout
implements
ViewPager
.
OnPageChangeListener
,
com
.
widget
.
imagevideobanner
.
banner
.
ImageVideoFragment
.
OnVideoCompletionListener
{
private
static
final
int
UPTATE_VIEWPAGER
=
0
;
private
static
final
int
UPTATE_VIEWPAGER
=
0
;
...
@@ -77,8 +72,6 @@ public class ImageVideoBanner extends FrameLayout implements ViewPager.OnPageCha
...
@@ -77,8 +72,6 @@ public class ImageVideoBanner extends FrameLayout implements ViewPager.OnPageCha
return
true
;
return
true
;
}
}
});
});
}
}
public
void
replaceData
(
List
<
com
.
widget
.
imagevideobanner
.
bean
.
BannerBean
>
listBean
)
{
public
void
replaceData
(
List
<
com
.
widget
.
imagevideobanner
.
bean
.
BannerBean
>
listBean
)
{
...
@@ -97,56 +90,12 @@ public class ImageVideoBanner extends FrameLayout implements ViewPager.OnPageCha
...
@@ -97,56 +90,12 @@ public class ImageVideoBanner extends FrameLayout implements ViewPager.OnPageCha
return
;
return
;
}
}
for
(
BannerBean
bannerBean
:
mList
)
{
for
(
BannerBean
bannerBean
:
mList
)
{
if
(
bannerBean
.
getType
()
==
1
&&
null
==
bannerBean
.
getLoadingImage
()
)
{
if
(
bannerBean
.
getType
()
==
1
)
{
setLoadingImages
(
bannerBean
);
BitmapCache
.
getInstance
(
mContext
).
addCache
(
bannerBean
.
getUrl
()
);
}
}
}
}
}
}
/**
* 获取视频的第一帧作为截图
*
* @param bannerBean
*/
private
void
setLoadingImages
(
final
BannerBean
bannerBean
)
{
if
(
sExecutor
==
null
)
{
sExecutor
=
Executors
.
newScheduledThreadPool
(
5
);
}
Future
<
Boolean
>
submit
=
sExecutor
.
submit
(
new
Callable
<
Boolean
>()
{
@Override
public
Boolean
call
()
{
try
{
String
url
=
bannerBean
.
getUrl
();
if
(!
url
.
contains
(
"android.resource"
))
{
FFmpegMediaMetadataRetriever
mmr
=
new
FFmpegMediaMetadataRetriever
();
mmr
.
setDataSource
(
url
);
mmr
.
extractMetadata
(
FFmpegMediaMetadataRetriever
.
METADATA_KEY_ALBUM
);
mmr
.
extractMetadata
(
FFmpegMediaMetadataRetriever
.
METADATA_KEY_ARTIST
);
Bitmap
b
=
mmr
.
getFrameAtTime
();
// frame at 2 seconds
bannerBean
.
setLoadingImage
(
BitmapUtils
.
getBytes
(
b
));
Bitmap
bx
=
BitmapUtils
.
createBlurBitmap
(
b
,
40
);
bannerBean
.
setLoadingBlurImage
(
BitmapUtils
.
getBytes
(
bx
));
b
.
recycle
();
bx
.
recycle
();
mmr
.
release
();
}
return
true
;
}
catch
(
Exception
e
)
{
return
false
;
}
}
});
try
{
submit
.
get
();
}
catch
(
InterruptedException
e
)
{
e
.
printStackTrace
();
}
catch
(
ExecutionException
e
)
{
e
.
printStackTrace
();
}
}
@Override
@Override
public
void
onPageScrolled
(
int
position
,
float
positionOffset
,
int
positionOffsetPixels
)
{
public
void
onPageScrolled
(
int
position
,
float
positionOffset
,
int
positionOffsetPixels
)
{
...
@@ -155,7 +104,6 @@ public class ImageVideoBanner extends FrameLayout implements ViewPager.OnPageCha
...
@@ -155,7 +104,6 @@ public class ImageVideoBanner extends FrameLayout implements ViewPager.OnPageCha
@Override
@Override
public
void
onPageSelected
(
int
position
)
{
public
void
onPageSelected
(
int
position
)
{
autoCurrIndex
=
position
;
autoCurrIndex
=
position
;
// Toast.makeText(context,"position="+position,Toast.LENGTH_SHORT).show();
if
(
mList
.
get
(
position
).
getType
()
==
1
)
{
if
(
mList
.
get
(
position
).
getType
()
==
1
)
{
//如果是视频
//如果是视频
stopBanner
();
stopBanner
();
...
@@ -175,7 +123,6 @@ public class ImageVideoBanner extends FrameLayout implements ViewPager.OnPageCha
...
@@ -175,7 +123,6 @@ public class ImageVideoBanner extends FrameLayout implements ViewPager.OnPageCha
if
(
msg
.
arg1
!=
0
)
{
if
(
msg
.
arg1
!=
0
)
{
mViewPager
.
setCurrentItem
(
msg
.
arg1
);
mViewPager
.
setCurrentItem
(
msg
.
arg1
);
}
else
{
}
else
{
//false 当从末页调到首页时,不显示翻页动画效果,
mViewPager
.
setCurrentItem
(
msg
.
arg1
,
false
);
mViewPager
.
setCurrentItem
(
msg
.
arg1
,
false
);
}
}
break
;
break
;
...
@@ -202,7 +149,6 @@ public class ImageVideoBanner extends FrameLayout implements ViewPager.OnPageCha
...
@@ -202,7 +149,6 @@ public class ImageVideoBanner extends FrameLayout implements ViewPager.OnPageCha
}
}
public
void
startBanner
()
{
public
void
startBanner
()
{
//如果第一页是视频 不用定时器
if
(
mList
.
get
(
0
).
getType
()
==
0
)
{
if
(
mList
.
get
(
0
).
getType
()
==
0
)
{
startBanner
(
period
);
startBanner
(
period
);
}
}
...
@@ -211,7 +157,7 @@ public class ImageVideoBanner extends FrameLayout implements ViewPager.OnPageCha
...
@@ -211,7 +157,7 @@ public class ImageVideoBanner extends FrameLayout implements ViewPager.OnPageCha
public
void
startBanner
(
long
delay
)
{
public
void
startBanner
(
long
delay
)
{
stopBanner
();
stopBanner
();
timer
=
new
Timer
();
timer
=
new
Timer
();
createTimerTask
();
//创建定时器
createTimerTask
();
timer
.
schedule
(
timerTask
,
delay
,
period
);
timer
.
schedule
(
timerTask
,
delay
,
period
);
}
}
...
@@ -233,7 +179,6 @@ public class ImageVideoBanner extends FrameLayout implements ViewPager.OnPageCha
...
@@ -233,7 +179,6 @@ public class ImageVideoBanner extends FrameLayout implements ViewPager.OnPageCha
@Override
@Override
public
void
onVideoCompletion
(
MediaPlayer
mp
)
{
public
void
onVideoCompletion
(
MediaPlayer
mp
)
{
if
(
mList
.
size
()
==
1
)
{
if
(
mList
.
size
()
==
1
)
{
//当集合中只有一个视频时,循环播放
mAdapter
.
getFragment
().
circulationPlayer
();
mAdapter
.
getFragment
().
circulationPlayer
();
}
}
startBanner
(
0
);
startBanner
(
0
);
...
@@ -279,7 +224,6 @@ public class ImageVideoBanner extends FrameLayout implements ViewPager.OnPageCha
...
@@ -279,7 +224,6 @@ public class ImageVideoBanner extends FrameLayout implements ViewPager.OnPageCha
/**
/**
* 只有1个视频的时候循环播放
* 只有1个视频的时候循环播放
*
* @return
* @return
*/
*/
private
boolean
getLoop
()
{
private
boolean
getLoop
()
{
...
@@ -307,25 +251,21 @@ public class ImageVideoBanner extends FrameLayout implements ViewPager.OnPageCha
...
@@ -307,25 +251,21 @@ public class ImageVideoBanner extends FrameLayout implements ViewPager.OnPageCha
boolean
change
=
compareData
(
listBean
);
boolean
change
=
compareData
(
listBean
);
if
(!
change
)
{
if
(!
change
)
{
notifyDataSetChanged
();
notifyDataSetChanged
();
// startBanner();
return
;
return
;
}
}
if
(
null
!=
listBean
)
{
if
(
listBean
.
size
()
>
0
)
{
mList
.
clear
();
mList
.
clear
();
addData
(
listBean
);
addData
(
listBean
);
//如果第一张是图片 需要开启定时器
if
(
listBean
.
get
(
0
).
getType
()
==
0
)
{
if
(
listBean
.
get
(
0
).
getType
()
==
0
)
{
startBanner
();
startBanner
();
}
}
autoCurrIndex
=
0
;
autoCurrIndex
=
0
;
// startBanner();
}
}
}
}
/**
/**
* 对比数据
* 对比数据
*
* @param listBean
* @param listBean
* @return
* @return
*/
*/
...
@@ -348,6 +288,7 @@ public class ImageVideoBanner extends FrameLayout implements ViewPager.OnPageCha
...
@@ -348,6 +288,7 @@ public class ImageVideoBanner extends FrameLayout implements ViewPager.OnPageCha
public
void
addData
(
List
<
BannerBean
>
listBean
)
{
public
void
addData
(
List
<
BannerBean
>
listBean
)
{
if
(
null
!=
listBean
)
{
if
(
null
!=
listBean
)
{
mList
.
clear
();
mList
.
addAll
(
listBean
);
mList
.
addAll
(
listBean
);
}
}
initBannerBitmap
();
initBannerBitmap
();
...
@@ -357,7 +298,10 @@ public class ImageVideoBanner extends FrameLayout implements ViewPager.OnPageCha
...
@@ -357,7 +298,10 @@ public class ImageVideoBanner extends FrameLayout implements ViewPager.OnPageCha
public
ImageVideoFragment
getFragment
()
{
public
ImageVideoFragment
getFragment
()
{
return
fragment
;
return
fragment
;
}
}
}
public
List
<
BannerBean
>
getBannerInfos
(){
return
mList
;
}
}
}
}
imageVideoBanner/src/main/java/com/widget/imagevideobanner/banner/ImageVideoFragment.java
View file @
d75937a0
This diff is collapsed.
Click to expand it.
imageVideoBanner/src/main/java/com/widget/imagevideobanner/banner/MyVideoView.java
0 → 100644
View file @
d75937a0
package
com
.
widget
.
imagevideobanner
.
banner
;
import
android.content.Context
;
import
android.util.AttributeSet
;
import
android.widget.VideoView
;
public
class
MyVideoView
extends
VideoView
{
private
int
mVideoWidth
;
private
int
mVideoHeight
;
public
MyVideoView
(
Context
context
,
AttributeSet
attrs
)
{
super
(
context
,
attrs
);
}
public
MyVideoView
(
Context
context
,
AttributeSet
attrs
,
int
defStyle
)
{
super
(
context
,
attrs
,
defStyle
);
}
public
MyVideoView
(
Context
context
)
{
super
(
context
);
}
public
void
setVideoSize
(
int
width
,
int
height
)
{
mVideoWidth
=
width
;
mVideoHeight
=
height
;
}
@Override
protected
void
onMeasure
(
int
widthMeasureSpec
,
int
heightMeasureSpec
)
{
// Log.i("@@@", "onMeasure");
int
width
=
getDefaultSize
(
mVideoWidth
,
widthMeasureSpec
);
int
height
=
getDefaultSize
(
mVideoHeight
,
heightMeasureSpec
);
if
(
mVideoWidth
>
0
&&
mVideoHeight
>
0
)
{
if
(
mVideoWidth
*
height
>
width
*
mVideoHeight
)
{
// Log.i("@@@", "image too tall, correcting");
height
=
width
*
mVideoHeight
/
mVideoWidth
;
}
else
if
(
mVideoWidth
*
height
<
width
*
mVideoHeight
)
{
// Log.i("@@@", "image too wide, correcting");
width
=
height
*
mVideoWidth
/
mVideoHeight
;
}
else
{
// Log.i("@@@", "aspect ratio is correct: " +
// width+"/"+height+"="+
// mVideoWidth+"/"+mVideoHeight);
}
}
// Log.i("@@@", "setting size: " + width + 'x' + height);
setMeasuredDimension
(
width
,
height
);
}
}
imageVideoBanner/src/main/java/com/widget/imagevideobanner/bean/BannerBean.java
View file @
d75937a0
...
@@ -5,10 +5,28 @@ import java.io.Serializable;
...
@@ -5,10 +5,28 @@ import java.io.Serializable;
public
class
BannerBean
implements
Serializable
{
public
class
BannerBean
implements
Serializable
{
private
String
url
;
private
String
url
;
private
int
type
;
private
int
type
;
private
byte
[]
loadingImage
;
private
byte
[]
loadingBlurImage
;
private
int
width
;
private
int
width
;
private
int
height
;
private
int
height
;
//视频首帧图片key
private
String
key
;
//视频首帧图片 高斯模糊key
private
String
blurKey
;
public
String
getKey
()
{
return
key
;
}
public
void
setKey
(
String
key
)
{
this
.
key
=
key
;
}
public
String
getBlurKey
()
{
return
blurKey
;
}
public
void
setBlurKey
(
String
blurKey
)
{
this
.
blurKey
=
blurKey
;
}
public
int
getWidth
()
{
public
int
getWidth
()
{
return
width
;
return
width
;
...
@@ -26,21 +44,6 @@ public class BannerBean implements Serializable {
...
@@ -26,21 +44,6 @@ public class BannerBean implements Serializable {
this
.
height
=
height
;
this
.
height
=
height
;
}
}
public
byte
[]
getLoadingBlurImage
()
{
return
loadingBlurImage
;
}
public
void
setLoadingBlurImage
(
byte
[]
loadingBlurImage
)
{
this
.
loadingBlurImage
=
loadingBlurImage
;
}
public
byte
[]
getLoadingImage
()
{
return
loadingImage
;
}
public
void
setLoadingImage
(
byte
[]
loadingImage
)
{
this
.
loadingImage
=
loadingImage
;
}
public
String
getUrl
()
{
public
String
getUrl
()
{
return
url
;
return
url
;
}
}
...
...
imageVideoBanner/src/main/java/com/widget/imagevideobanner/cache/BitmapCache.java
0 → 100644
View file @
d75937a0
package
com
.
widget
.
imagevideobanner
.
cache
;
import
android.content.Context
;
import
android.graphics.Bitmap
;
import
android.graphics.BitmapFactory
;
import
android.os.Build
;
import
android.os.Environment
;
import
android.text.TextUtils
;
import
android.util.Log
;
import
android.util.LruCache
;
import
com.widget.imagevideobanner.BuildConfig
;
import
com.widget.imagevideobanner.cache.disk.DiskLruCache
;
import
com.widget.imagevideobanner.log.BannerLogFileUtils
;
import
com.widget.imagevideobanner.utils.BitmapUtils
;
import
com.widget.imagevideobanner.utils.MD5Util
;
import
com.widget.imagevideobanner.utils.ThrowableUtils
;
import
java.io.File
;
import
java.io.IOException
;
import
java.io.InputStream
;
import
java.io.OutputStream
;
import
java.util.HashMap
;
import
java.util.Map
;
import
java.util.concurrent.ExecutorService
;
import
java.util.concurrent.Executors
;
import
wseemann.media.FFmpegMediaMetadataRetriever
;
/**
* 缓存视频的第一帧图片以及高斯模糊的背景图片
*/
public
class
BitmapCache
{
static
final
String
TAG
=
"BitmapCache####"
;
static
final
int
BYTES_PER_ARGB_8888_PIXEL
=
4
;
//经验值 最多不超过5个视频
static
final
int
MEMORY_CACHE_TARGET_SCREENS
=
10
;
static
final
int
BLUR_RADS
=
40
;
static
final
String
BITMAP_CACHE_DIR
=
Environment
.
getExternalStorageDirectory
().
getPath
()
+
File
.
separator
+
"miyaterminal"
+
File
.
separator
+
"download"
+
File
.
separator
;
private
LruCache
<
String
,
Bitmap
>
memoryCache
;
private
DiskLruCache
diskLruCache
;
private
static
BitmapCache
instance
;
private
ExecutorService
executors
;
private
Map
<
String
,
String
>
keyMaps
=
new
HashMap
<>();
public
static
BitmapCache
getInstance
(
Context
context
)
{
if
(
instance
==
null
)
{
synchronized
(
BitmapCache
.
class
)
{
if
(
instance
==
null
)
{
instance
=
new
BitmapCache
(
context
);
}
}
}
return
instance
;
}
public
BitmapCache
(
Context
context
)
{
int
screenSize
=
context
.
getResources
().
getDisplayMetrics
().
widthPixels
*
context
.
getResources
().
getDisplayMetrics
().
heightPixels
*
BYTES_PER_ARGB_8888_PIXEL
;
//todo 其实应该根据真实的视频个数来比较好
int
targetMemoryCacheSize
=
Math
.
round
(
screenSize
*
MEMORY_CACHE_TARGET_SCREENS
)/
1024
;
// int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
// int cacheSize = maxMemory / 8;
//todo 判断是小于最大可用内存
memoryCache
=
new
LruCache
<
String
,
Bitmap
>(
targetMemoryCacheSize
)
{
@Override
protected
int
sizeOf
(
String
key
,
Bitmap
bitmap
)
{
int
size
=
bitmap
.
getByteCount
()
/
1024
;
if
(
Build
.
VERSION
.
SDK_INT
>=
Build
.
VERSION_CODES
.
KITKAT
)
{
size
=
bitmap
.
getAllocationByteCount
()
/
1024
;
}
Log
.
e
(
TAG
,
"已经加入内存缓存 size ="
+
size
);
BannerLogFileUtils
.
writeLog
(
"已经加入内存缓存 size ="
+
size
);
return
size
;
}
@Override
protected
void
entryRemoved
(
boolean
evicted
,
String
key
,
Bitmap
oldValue
,
Bitmap
newValue
)
{
BannerLogFileUtils
.
writeLog
(
"内存缓存移除 key ="
+
key
);
Log
.
e
(
TAG
,
"内存缓存移除 key ="
+
key
);
//todo bitmap复用
oldValue
.
recycle
();
}
};
try
{
File
cacheFile
=
new
File
(
BITMAP_CACHE_DIR
);
if
(!
cacheFile
.
exists
()
&&
!
cacheFile
.
mkdir
())
return
;
diskLruCache
=
DiskLruCache
.
open
(
cacheFile
,
BuildConfig
.
VERSION_CODE
,
1
,
100
*
1024
*
1024
);
}
catch
(
IOException
e
)
{
e
.
printStackTrace
();
}
}
private
void
addBitmapToMemoryCache
(
String
key
,
Bitmap
bitmap
)
{
if
(
getBitmapFromMemCache
(
key
)
==
null
)
{
memoryCache
.
put
(
key
,
bitmap
);
}
}
public
Bitmap
getBitmap
(
String
key
){
if
(
TextUtils
.
isEmpty
(
key
=
keyMaps
.
get
(
key
))){
return
null
;
}
Bitmap
bitmap
=
getBitmapFromMemCache
(
key
);
if
(
bitmap
!=
null
){
return
bitmap
;
}
return
getBitmapFromDisk
(
key
);
}
/**
* 取高斯模糊的缓存图片
* @param key
* @return
*/
public
Bitmap
getBlurBitmap
(
String
key
){
if
(
TextUtils
.
isEmpty
(
key
=
keyMaps
.
get
(
key
+
"blur"
))){
return
null
;
}
Bitmap
bitmap
=
getBitmapFromMemCache
(
key
);
if
(
bitmap
!=
null
){
return
bitmap
;
}
return
getBitmapFromDisk
(
key
);
}
public
void
addCache
(
final
String
url
){
if
(
TextUtils
.
isEmpty
(
url
))
return
;
if
(
getBitmapFromMemCache
(
url
)
!=
null
){
return
;
}
if
(
null
==
executors
)
executors
=
Executors
.
newFixedThreadPool
(
3
);
executors
.
execute
(
new
Runnable
()
{
@Override
public
void
run
()
{
try
{
String
key
=
MD5Util
.
get32Md5
(
url
);
keyMaps
.
put
(
url
,
key
);
String
blurkey
=
MD5Util
.
get32Md5
(
url
+
"blur"
);
keyMaps
.
put
(
url
+
"blur"
,
blurkey
);
//截取视频首帧 TODO 横竖屏方向调整
FFmpegMediaMetadataRetriever
mmr
=
new
FFmpegMediaMetadataRetriever
();
mmr
.
setDataSource
(
url
);
mmr
.
extractMetadata
(
FFmpegMediaMetadataRetriever
.
METADATA_KEY_ALBUM
);
mmr
.
extractMetadata
(
FFmpegMediaMetadataRetriever
.
METADATA_KEY_ARTIST
);
Bitmap
b
=
mmr
.
getFrameAtTime
();
//加入到内存MemeryLrucache TODO 采样率压缩
addBitmapToMemoryCache
(
key
,
b
);
//加入到DiskLrucache TODO 质量压缩
putBitmap2Disk
(
key
,
b
);
//TODO 如果视频比例刚好相等 可以不用背景图片
Bitmap
blurBitmap
=
BitmapUtils
.
createBlurBitmap
(
b
,
BLUR_RADS
);
addBitmapToMemoryCache
(
blurkey
,
blurBitmap
);
putBitmap2Disk
(
blurkey
,
blurBitmap
);
}
catch
(
Exception
e
){
e
.
printStackTrace
();
BannerLogFileUtils
.
writeLog
(
"截取视频首帧出错 url ="
+
url
+
"出错原因是:"
+
ThrowableUtils
.
getFullStackTrace
(
e
));
}
}
});
}
/**
* 从内存缓存获取bitmap
* @param key
* @return
*/
private
Bitmap
getBitmapFromMemCache
(
String
key
)
{
return
memoryCache
.
get
(
key
);
}
/**
* 从磁盘缓存获取bitmap
* @param key
* @return
*/
private
Bitmap
getBitmapFromDisk
(
String
key
)
{
DiskLruCache
.
Snapshot
snapshot
=
null
;
Bitmap
bitmap
=
null
;
try
{
snapshot
=
diskLruCache
.
get
(
key
);
if
(
snapshot
==
null
)
{
return
null
;
}
InputStream
is
=
snapshot
.
getInputStream
(
0
);
BitmapFactory
.
Options
options
=
new
BitmapFactory
.
Options
();
bitmap
=
BitmapFactory
.
decodeStream
(
is
,
null
,
options
);
if
(
bitmap
!=
null
)
{
memoryCache
.
put
(
key
,
bitmap
);
}
}
catch
(
IOException
e
)
{
e
.
printStackTrace
();
}
finally
{
if
(
snapshot
!=
null
)
{
snapshot
.
close
();
}
}
return
bitmap
;
}
/**
* 放入磁盘缓存
*/
private
void
putBitmap2Disk
(
String
key
,
Bitmap
bitmap
)
{
DiskLruCache
.
Snapshot
snapshot
=
null
;
OutputStream
os
=
null
;
try
{
snapshot
=
diskLruCache
.
get
(
key
);
if
(
snapshot
==
null
)
{
DiskLruCache
.
Editor
edit
=
diskLruCache
.
edit
(
key
);
if
(
edit
!=
null
)
{
os
=
edit
.
newOutputStream
(
0
);
bitmap
.
compress
(
Bitmap
.
CompressFormat
.
JPEG
,
50
,
os
);
edit
.
commit
();
}
}
}
catch
(
IOException
e
)
{
e
.
printStackTrace
();
}
finally
{
if
(
snapshot
!=
null
)
{
snapshot
.
close
();
}
if
(
os
!=
null
)
{
try
{
os
.
close
();
}
catch
(
IOException
e
)
{
e
.
printStackTrace
();
}
}
}
}
}
imageVideoBanner/src/main/java/com/widget/imagevideobanner/cache/disk/DiskLruCache.java
0 → 100755
View file @
d75937a0
This diff is collapsed.
Click to expand it.
imageVideoBanner/src/main/java/com/widget/imagevideobanner/cache/disk/StrictLineReader.java
0 → 100755
View file @
d75937a0
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
com
.
widget
.
imagevideobanner
.
cache
.
disk
;
import
java.io.ByteArrayOutputStream
;
import
java.io.Closeable
;
import
java.io.EOFException
;
import
java.io.IOException
;
import
java.io.InputStream
;
import
java.io.UnsupportedEncodingException
;
import
java.nio.charset.Charset
;
/**
* Buffers input from an {@link InputStream} for reading lines.
*
* <p>This class is used for buffered reading of lines. For purposes of this class, a line ends
* with "\n" or "\r\n". End of input is reported by throwing {@code EOFException}. Unterminated
* line at end of input is invalid and will be ignored, the caller may use {@code
* hasUnterminatedLine()} to detect it after catching the {@code EOFException}.
*
* <p>This class is intended for reading input that strictly consists of lines, such as line-based
* cache entries or cache journal. Unlike the {@link java.io.BufferedReader} which in conjunction
* with {@link java.io.InputStreamReader} provides similar functionality, this class uses different
* end-of-input reporting and a more restrictive definition of a line.
*
* <p>This class supports only charsets that encode '\r' and '\n' as a single byte with value 13
* and 10, respectively, and the representation of no other character contains these values.
* We currently check in constructor that the charset is one of US-ASCII, UTF-8 and ISO-8859-1.
* The default charset is US_ASCII.
*/
class
StrictLineReader
implements
Closeable
{
private
static
final
byte
CR
=
(
byte
)
'\r'
;
private
static
final
byte
LF
=
(
byte
)
'\n'
;
private
final
InputStream
in
;
private
final
Charset
charset
;
/*
* Buffered data is stored in {@code buf}. As long as no exception occurs, 0 <= pos <= end
* and the data in the range [pos, end) is buffered for reading. At end of input, if there is
* an unterminated line, we set end == -1, otherwise end == pos. If the underlying
* {@code InputStream} throws an {@code IOException}, end may remain as either pos or -1.
*/
private
byte
[]
buf
;
private
int
pos
;
private
int
end
;
/**
* Constructs a new {@code LineReader} with the specified charset and the default capacity.
*
* @param in the {@code InputStream} to read data from.
* @param charset the charset used to decode data. Only US-ASCII, UTF-8 and ISO-8859-1 are
* supported.
* @throws NullPointerException if {@code in} or {@code charset} is null.
* @throws IllegalArgumentException if the specified charset is not supported.
*/
public
StrictLineReader
(
InputStream
in
,
Charset
charset
)
{
this
(
in
,
8192
,
charset
);
}
/**
* Constructs a new {@code LineReader} with the specified capacity and charset.
*
* @param in the {@code InputStream} to read data from.
* @param capacity the capacity of the buffer.
* @param charset the charset used to decode data. Only US-ASCII, UTF-8 and ISO-8859-1 are
* supported.
* @throws NullPointerException if {@code in} or {@code charset} is null.
* @throws IllegalArgumentException if {@code capacity} is negative or zero
* or the specified charset is not supported.
*/
public
StrictLineReader
(
InputStream
in
,
int
capacity
,
Charset
charset
)
{
if
(
in
==
null
||
charset
==
null
)
{
throw
new
NullPointerException
();
}
if
(
capacity
<
0
)
{
throw
new
IllegalArgumentException
(
"capacity <= 0"
);
}
if
(!(
charset
.
equals
(
Util
.
US_ASCII
)))
{
throw
new
IllegalArgumentException
(
"Unsupported encoding"
);
}
this
.
in
=
in
;
this
.
charset
=
charset
;
buf
=
new
byte
[
capacity
];
}
/**
* Closes the reader by closing the underlying {@code InputStream} and
* marking this reader as closed.
*
* @throws IOException for errors when closing the underlying {@code InputStream}.
*/
public
void
close
()
throws
IOException
{
synchronized
(
in
)
{
if
(
buf
!=
null
)
{
buf
=
null
;
in
.
close
();
}
}
}
/**
* Reads the next line. A line ends with {@code "\n"} or {@code "\r\n"},
* this end of line marker is not included in the result.
*
* @return the next line from the input.
* @throws IOException for underlying {@code InputStream} errors.
* @throws EOFException for the end of source stream.
*/
public
String
readLine
()
throws
IOException
{
synchronized
(
in
)
{
if
(
buf
==
null
)
{
throw
new
IOException
(
"LineReader is closed"
);
}
// Read more data if we are at the end of the buffered data.
// Though it's an error to read after an exception, we will let {@code fillBuf()}
// throw again if that happens; thus we need to handle end == -1 as well as end == pos.
if
(
pos
>=
end
)
{
fillBuf
();
}
// Try to find LF in the buffered data and return the line if successful.
for
(
int
i
=
pos
;
i
!=
end
;
++
i
)
{
if
(
buf
[
i
]
==
LF
)
{
int
lineEnd
=
(
i
!=
pos
&&
buf
[
i
-
1
]
==
CR
)
?
i
-
1
:
i
;
String
res
=
new
String
(
buf
,
pos
,
lineEnd
-
pos
,
charset
.
name
());
pos
=
i
+
1
;
return
res
;
}
}
// Let's anticipate up to 80 characters on top of those already read.
ByteArrayOutputStream
out
=
new
ByteArrayOutputStream
(
end
-
pos
+
80
)
{
@Override
public
String
toString
()
{
int
length
=
(
count
>
0
&&
buf
[
count
-
1
]
==
CR
)
?
count
-
1
:
count
;
try
{
return
new
String
(
buf
,
0
,
length
,
charset
.
name
());
}
catch
(
UnsupportedEncodingException
e
)
{
throw
new
AssertionError
(
e
);
// Since we control the charset this will never happen.
}
}
};
while
(
true
)
{
out
.
write
(
buf
,
pos
,
end
-
pos
);
// Mark unterminated line in case fillBuf throws EOFException or IOException.
end
=
-
1
;
fillBuf
();
// Try to find LF in the buffered data and return the line if successful.
for
(
int
i
=
pos
;
i
!=
end
;
++
i
)
{
if
(
buf
[
i
]
==
LF
)
{
if
(
i
!=
pos
)
{
out
.
write
(
buf
,
pos
,
i
-
pos
);
}
pos
=
i
+
1
;
return
out
.
toString
();
}
}
}
}
}
public
boolean
hasUnterminatedLine
()
{
return
end
==
-
1
;
}
/**
* Reads new input data into the buffer. Call only with pos == end or end == -1,
* depending on the desired outcome if the function throws.
*/
private
void
fillBuf
()
throws
IOException
{
int
result
=
in
.
read
(
buf
,
0
,
buf
.
length
);
if
(
result
==
-
1
)
{
throw
new
EOFException
();
}
pos
=
0
;
end
=
result
;
}
}
imageVideoBanner/src/main/java/com/widget/imagevideobanner/cache/disk/Util.java
0 → 100755
View file @
d75937a0
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
com
.
widget
.
imagevideobanner
.
cache
.
disk
;
import
java.io.Closeable
;
import
java.io.File
;
import
java.io.IOException
;
import
java.io.Reader
;
import
java.io.StringWriter
;
import
java.nio.charset.Charset
;
/** Junk drawer of utility methods. */
final
class
Util
{
static
final
Charset
US_ASCII
=
Charset
.
forName
(
"US-ASCII"
);
static
final
Charset
UTF_8
=
Charset
.
forName
(
"UTF-8"
);
private
Util
()
{
}
static
String
readFully
(
Reader
reader
)
throws
IOException
{
try
{
StringWriter
writer
=
new
StringWriter
();
char
[]
buffer
=
new
char
[
1024
];
int
count
;
while
((
count
=
reader
.
read
(
buffer
))
!=
-
1
)
{
writer
.
write
(
buffer
,
0
,
count
);
}
return
writer
.
toString
();
}
finally
{
reader
.
close
();
}
}
/**
* Deletes the contents of {@code dir}. Throws an IOException if any file
* could not be deleted, or if {@code dir} is not a readable directory.
*/
static
void
deleteContents
(
File
dir
)
throws
IOException
{
File
[]
files
=
dir
.
listFiles
();
if
(
files
==
null
)
{
throw
new
IOException
(
"not a readable directory: "
+
dir
);
}
for
(
File
file
:
files
)
{
if
(
file
.
isDirectory
())
{
deleteContents
(
file
);
}
if
(!
file
.
delete
())
{
throw
new
IOException
(
"failed to delete file: "
+
file
);
}
}
}
static
void
closeQuietly
(
/*Auto*/
Closeable
closeable
)
{
if
(
closeable
!=
null
)
{
try
{
closeable
.
close
();
}
catch
(
RuntimeException
rethrown
)
{
throw
rethrown
;
}
catch
(
Exception
ignored
)
{
}
}
}
}
imageVideoBanner/src/main/java/com/widget/imagevideobanner/log/BannerLogFileUtils.java
0 → 100644
View file @
d75937a0
package
com
.
widget
.
imagevideobanner
.
log
;
import
android.os.Environment
;
import
android.text.TextUtils
;
import
java.io.File
;
import
java.io.FileOutputStream
;
import
java.io.IOException
;
import
java.io.OutputStreamWriter
;
import
java.nio.charset.Charset
;
import
java.text.SimpleDateFormat
;
import
java.util.Date
;
import
java.util.concurrent.ExecutorService
;
import
java.util.concurrent.Executors
;
public
class
BannerLogFileUtils
{
private
static
ExecutorService
sExecutor
;
private
static
SimpleDateFormat
sdf
=
new
SimpleDateFormat
(
"yyyy-MM-dd HH:mm:ss"
);
private
static
final
String
BANNER_LOG_DIR
=
Environment
.
getExternalStorageDirectory
().
getPath
()
+
File
.
separator
+
"miyaterminal"
+
File
.
separator
+
"log"
+
File
.
separator
+
"bannererr.log"
;
static
{
File
logFiles
=
new
File
(
BANNER_LOG_DIR
);
if
(!
logFiles
.
exists
())
{
logFiles
.
mkdirs
();
}
}
public
static
void
writeLog
(
final
String
input
)
{
if
(
sExecutor
==
null
)
{
sExecutor
=
Executors
.
newSingleThreadExecutor
();
}
sExecutor
.
execute
(
new
Runnable
()
{
@Override
public
void
run
()
{
writeFileLog
(
input
);
}
});
}
private
static
void
writeFileLog
(
String
input
)
{
if
(
TextUtils
.
isEmpty
(
input
))
{
return
;
}
input
=
sdf
.
format
(
new
Date
())
+
input
+
"\n"
;
FileOutputStream
fos
=
null
;
OutputStreamWriter
writer
=
null
;
try
{
File
logFile
=
new
File
(
BANNER_LOG_DIR
);
if
(!
logFile
.
exists
()
&&
!
logFile
.
createNewFile
())
return
;
fos
=
new
FileOutputStream
(
logFile
,
true
);
writer
=
new
OutputStreamWriter
(
fos
,
Charset
.
forName
(
"utf-8"
));
writer
.
write
(
input
);
writer
.
flush
();
writer
.
close
();
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
}
finally
{
if
(
null
!=
fos
)
{
try
{
fos
.
close
();
}
catch
(
IOException
e
)
{
e
.
printStackTrace
();
}
}
if
(
null
!=
writer
)
{
try
{
writer
.
close
();
}
catch
(
IOException
e
)
{
e
.
printStackTrace
();
}
}
}
}
}
imageVideoBanner/src/main/java/com/widget/imagevideobanner/utils/BannerLogFileUtils.java
deleted
100644 → 0
View file @
61e53d36
package
com
.
widget
.
imagevideobanner
.
utils
;
import
android.os.Environment
;
import
android.text.TextUtils
;
import
java.io.File
;
import
java.io.FileNotFoundException
;
import
java.io.FileOutputStream
;
import
java.io.IOException
;
public
class
BannerLogFileUtils
{
/**
* 将播放的错误信息记录到本地
* @param exInfo
* @return
*/
public
static
String
saveInfo2File
(
String
exInfo
)
{
if
(
TextUtils
.
isEmpty
(
exInfo
))
{
return
null
;
}
String
sampleDir
=
"/miyaterminal/log/"
;
String
tmpDir
=
Environment
.
getExternalStorageDirectory
().
toString
()
+
File
.
separator
+
sampleDir
;
File
file
=
new
File
(
tmpDir
);
if
(!
file
.
exists
())
{
file
.
mkdir
();
}
String
logPath
=
tmpDir
+
"bannererr.log"
;
try
{
FileOutputStream
mFileOutputStream
=
new
FileOutputStream
(
logPath
);
mFileOutputStream
.
write
(
exInfo
.
getBytes
());
mFileOutputStream
.
close
();
return
logPath
;
}
catch
(
FileNotFoundException
e
)
{
e
.
printStackTrace
();
}
catch
(
IOException
e
)
{
e
.
printStackTrace
();
}
return
null
;
}
}
imageVideoBanner/src/main/java/com/widget/imagevideobanner/utils/BitmapUtils.java
View file @
d75937a0
...
@@ -17,7 +17,7 @@ public class BitmapUtils {
...
@@ -17,7 +17,7 @@ public class BitmapUtils {
public
static
byte
[]
getBytes
(
Bitmap
bitmap
){
public
static
byte
[]
getBytes
(
Bitmap
bitmap
){
//实例化字节数组输出流
//实例化字节数组输出流
ByteArrayOutputStream
baos
=
new
ByteArrayOutputStream
();
ByteArrayOutputStream
baos
=
new
ByteArrayOutputStream
();
bitmap
.
compress
(
Bitmap
.
CompressFormat
.
PNG
,
0
,
baos
);
//压缩位图
bitmap
.
compress
(
Bitmap
.
CompressFormat
.
PNG
,
10
0
,
baos
);
//压缩位图
return
baos
.
toByteArray
();
//创建分配字节数组
return
baos
.
toByteArray
();
//创建分配字节数组
}
}
...
...
imageVideoBanner/src/main/java/com/widget/imagevideobanner/utils/IMMLeaks.java
0 → 100644
View file @
d75937a0
package
com
.
widget
.
imagevideobanner
.
utils
;
import
android.app.Activity
;
import
android.app.Application
;
import
android.content.Context
;
import
android.content.ContextWrapper
;
import
android.os.Bundle
;
import
android.os.Looper
;
import
android.os.MessageQueue
;
import
android.support.annotation.RequiresApi
;
import
android.util.Log
;
import
android.view.View
;
import
android.view.ViewTreeObserver
;
import
android.view.inputmethod.InputMethodManager
;
import
java.lang.reflect.Field
;
import
java.lang.reflect.InvocationTargetException
;
import
java.lang.reflect.Method
;
import
static
android
.
content
.
Context
.
INPUT_METHOD_SERVICE
;
import
static
android
.
os
.
Build
.
VERSION
.
SDK_INT
;
import
static
android
.
os
.
Build
.
VERSION_CODES
.
KITKAT
;
public
class
IMMLeaks
{
static
class
ReferenceCleaner
implements
MessageQueue
.
IdleHandler
,
View
.
OnAttachStateChangeListener
,
ViewTreeObserver
.
OnGlobalFocusChangeListener
{
private
final
InputMethodManager
inputMethodManager
;
private
final
Field
mHField
;
private
final
Field
mServedViewField
;
private
final
Method
finishInputLockedMethod
;
ReferenceCleaner
(
InputMethodManager
inputMethodManager
,
Field
mHField
,
Field
mServedViewField
,
Method
finishInputLockedMethod
)
{
this
.
inputMethodManager
=
inputMethodManager
;
this
.
mHField
=
mHField
;
this
.
mServedViewField
=
mServedViewField
;
this
.
finishInputLockedMethod
=
finishInputLockedMethod
;
}
@Override
public
void
onGlobalFocusChanged
(
View
oldFocus
,
View
newFocus
)
{
if
(
newFocus
==
null
)
{
return
;
}
if
(
oldFocus
!=
null
)
{
oldFocus
.
removeOnAttachStateChangeListener
(
this
);
}
Looper
.
myQueue
().
removeIdleHandler
(
this
);
newFocus
.
addOnAttachStateChangeListener
(
this
);
}
@Override
public
void
onViewAttachedToWindow
(
View
v
)
{
}
@Override
public
void
onViewDetachedFromWindow
(
View
v
)
{
v
.
removeOnAttachStateChangeListener
(
this
);
Looper
.
myQueue
().
removeIdleHandler
(
this
);
Looper
.
myQueue
().
addIdleHandler
(
this
);
}
@RequiresApi
(
api
=
KITKAT
)
@Override
public
boolean
queueIdle
()
{
clearInputMethodManagerLeak
();
return
false
;
}
@RequiresApi
(
api
=
KITKAT
)
private
void
clearInputMethodManagerLeak
()
{
try
{
Object
lock
=
mHField
.
get
(
inputMethodManager
);
// This is highly dependent on the InputMethodManager implementation.
synchronized
(
lock
)
{
View
servedView
=
(
View
)
mServedViewField
.
get
(
inputMethodManager
);
if
(
servedView
!=
null
)
{
boolean
servedViewAttached
=
servedView
.
getWindowVisibility
()
!=
View
.
GONE
;
if
(
servedViewAttached
)
{
// The view held by the IMM was replaced without a global focus change. Let's make
// sure we get notified when that view detaches.
// Avoid double registration.
servedView
.
removeOnAttachStateChangeListener
(
this
);
servedView
.
addOnAttachStateChangeListener
(
this
);
}
else
{
// servedView is not attached. InputMethodManager is being stupid!
Activity
activity
=
extractActivity
(
servedView
.
getContext
());
if
(
activity
==
null
||
activity
.
getWindow
()
==
null
)
{
// Unlikely case. Let's finish the input anyways.
finishInputLockedMethod
.
invoke
(
inputMethodManager
);
}
else
{
View
decorView
=
activity
.
getWindow
().
peekDecorView
();
boolean
windowAttached
=
decorView
.
getWindowVisibility
()
!=
View
.
GONE
;
if
(!
windowAttached
)
{
finishInputLockedMethod
.
invoke
(
inputMethodManager
);
}
else
{
decorView
.
requestFocusFromTouch
();
}
}
}
}
}
}
catch
(
IllegalAccessException
|
InvocationTargetException
unexpected
)
{
Log
.
e
(
"IMMLeaks"
,
"Unexpected reflection exception"
,
unexpected
);
}
}
private
Activity
extractActivity
(
Context
context
)
{
while
(
true
)
{
if
(
context
instanceof
Application
)
{
return
null
;
}
else
if
(
context
instanceof
Activity
)
{
return
(
Activity
)
context
;
}
else
if
(
context
instanceof
ContextWrapper
)
{
Context
baseContext
=
((
ContextWrapper
)
context
).
getBaseContext
();
// Prevent Stack Overflow.
if
(
baseContext
==
context
)
{
return
null
;
}
context
=
baseContext
;
}
else
{
return
null
;
}
}
}
}
/**
* Fix for https://code.google.com/p/android/issues/detail?id=171190 .
*
* When a view that has focus gets detached, we wait for the main thread to be idle and then
* check if the InputMethodManager is leaking a view. If yes, we tell it that the decor view got
* focus, which is what happens if you press home and come back from recent apps. This replaces
* the reference to the detached view with a reference to the decor view.
* Should be called from {@link Activity#onCreate(android.os.Bundle)} )}.
*/
public
static
void
fixFocusedViewLeak
(
Application
application
)
{
// Don't know about other versions yet.
if
(
SDK_INT
<
KITKAT
||
SDK_INT
>
22
)
{
return
;
}
final
InputMethodManager
inputMethodManager
=
(
InputMethodManager
)
application
.
getSystemService
(
INPUT_METHOD_SERVICE
);
final
Field
mServedViewField
;
final
Field
mHField
;
final
Method
finishInputLockedMethod
;
final
Method
focusInMethod
;
try
{
mServedViewField
=
InputMethodManager
.
class
.
getDeclaredField
(
"mServedView"
);
mServedViewField
.
setAccessible
(
true
);
mHField
=
InputMethodManager
.
class
.
getDeclaredField
(
"mServedView"
);
mHField
.
setAccessible
(
true
);
finishInputLockedMethod
=
InputMethodManager
.
class
.
getDeclaredMethod
(
"finishInputLocked"
);
finishInputLockedMethod
.
setAccessible
(
true
);
focusInMethod
=
InputMethodManager
.
class
.
getDeclaredMethod
(
"focusIn"
,
View
.
class
);
focusInMethod
.
setAccessible
(
true
);
}
catch
(
NoSuchMethodException
|
NoSuchFieldException
unexpected
)
{
Log
.
e
(
"IMMLeaks"
,
"Unexpected reflection exception"
,
unexpected
);
return
;
}
application
.
registerActivityLifecycleCallbacks
(
new
LifecycleCallbacksAdapter
()
{
@Override
public
void
onActivityCreated
(
Activity
activity
,
Bundle
savedInstanceState
)
{
ReferenceCleaner
cleaner
=
new
ReferenceCleaner
(
inputMethodManager
,
mHField
,
mServedViewField
,
finishInputLockedMethod
);
View
rootView
=
activity
.
getWindow
().
getDecorView
().
getRootView
();
ViewTreeObserver
viewTreeObserver
=
rootView
.
getViewTreeObserver
();
viewTreeObserver
.
addOnGlobalFocusChangeListener
(
cleaner
);
}
});
}
}
\ No newline at end of file
imageVideoBanner/src/main/java/com/widget/imagevideobanner/utils/LifecycleCallbacksAdapter.java
0 → 100644
View file @
d75937a0
package
com
.
widget
.
imagevideobanner
.
utils
;
import
android.app.Activity
;
import
android.app.Application
;
import
android.os.Bundle
;
/** Helper to avoid implementing all lifecycle callback methods. */
public
class
LifecycleCallbacksAdapter
implements
Application
.
ActivityLifecycleCallbacks
{
@Override
public
void
onActivityCreated
(
Activity
activity
,
Bundle
savedInstanceState
)
{
}
@Override
public
void
onActivityStarted
(
Activity
activity
)
{
}
@Override
public
void
onActivityResumed
(
Activity
activity
)
{
}
@Override
public
void
onActivityPaused
(
Activity
activity
)
{
}
@Override
public
void
onActivityStopped
(
Activity
activity
)
{
}
@Override
public
void
onActivitySaveInstanceState
(
Activity
activity
,
Bundle
outState
)
{
}
@Override
public
void
onActivityDestroyed
(
Activity
activity
)
{
}
}
\ No newline at end of file
imageVideoBanner/src/main/java/com/widget/imagevideobanner/utils/MD5Util.java
0 → 100755
View file @
d75937a0
package
com
.
widget
.
imagevideobanner
.
utils
;
import
java.io.File
;
import
java.io.FileInputStream
;
import
java.math.BigInteger
;
import
java.security.MessageDigest
;
import
java.security.NoSuchAlgorithmException
;
public
class
MD5Util
{
public
final
static
String
MD5
(
String
s
)
{
byte
[]
btInput
=
s
.
getBytes
();
return
MD5
(
btInput
);
}
public
final
static
String
MD5
(
byte
[]
btInput
)
{
char
hexDigits
[]
=
{
'0'
,
'1'
,
'2'
,
'3'
,
'4'
,
'5'
,
'6'
,
'7'
,
'8'
,
'9'
,
'A'
,
'B'
,
'C'
,
'D'
,
'E'
,
'F'
};
try
{
MessageDigest
mdInst
=
MessageDigest
.
getInstance
(
"MD5"
);
mdInst
.
update
(
btInput
);
byte
[]
md
=
mdInst
.
digest
();
int
j
=
md
.
length
;
char
str
[]
=
new
char
[
j
*
2
];
int
k
=
0
;
for
(
int
i
=
0
;
i
<
j
;
i
++)
{
byte
byte0
=
md
[
i
];
str
[
k
++]
=
hexDigits
[
byte0
>>>
4
&
0xf
];
str
[
k
++]
=
hexDigits
[
byte0
&
0xf
];
}
return
new
String
(
str
);
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
return
null
;
}
}
private
static
final
String
TAG
=
"MD5Utils"
;
/**
* md5加密
*
* @param plainText 待加密字符串
* @return 加密后32位字符串
*/
public
static
String
get32Md5
(
String
plainText
)
{
try
{
MessageDigest
md
=
MessageDigest
.
getInstance
(
"MD5"
);
md
.
update
(
plainText
.
getBytes
());
byte
b
[]
=
md
.
digest
();
int
i
;
StringBuffer
buf
=
new
StringBuffer
(
""
);
for
(
int
offset
=
0
;
offset
<
b
.
length
;
offset
++)
{
i
=
b
[
offset
];
if
(
i
<
0
)
i
+=
256
;
if
(
i
<
16
)
buf
.
append
(
"0"
);
buf
.
append
(
Integer
.
toHexString
(
i
));
}
//32位加密
return
buf
.
toString
();
}
catch
(
NoSuchAlgorithmException
e
)
{
return
null
;
}
}
/**
* @param plainText 待加密字符串
* @return 加密后16位字符串
*/
public
static
String
get16Md5
(
String
plainText
)
{
return
get32Md5
(
plainText
).
substring
(
8
,
24
);
}
/**
* 获得文件的MD5值
*
* 注意:此方法有问题 有可能是31位
*
* @param file
* @return
*/
public
static
String
getFileMD5
(
File
file
)
{
if
(!
file
.
isFile
())
{
return
null
;
}
MessageDigest
digest
=
null
;
FileInputStream
in
=
null
;
byte
buffer
[]
=
new
byte
[
1024
];
int
len
;
try
{
digest
=
MessageDigest
.
getInstance
(
"MD5"
);
in
=
new
FileInputStream
(
file
);
while
((
len
=
in
.
read
(
buffer
,
0
,
1024
))
!=
-
1
)
{
digest
.
update
(
buffer
,
0
,
len
);
}
in
.
close
();
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
return
null
;
}
BigInteger
bigInt
=
new
BigInteger
(
1
,
digest
.
digest
());
return
bigInt
.
toString
(
16
);
}
}
imageVideoBanner/src/main/java/com/widget/imagevideobanner/utils/ThrowableUtils.java
0 → 100644
View file @
d75937a0
package
com
.
widget
.
imagevideobanner
.
utils
;
import
java.io.PrintWriter
;
import
java.io.StringWriter
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.StringTokenizer
;
public
class
ThrowableUtils
{
private
static
final
String
LINE_SEP
=
System
.
getProperty
(
"line.separator"
);
private
ThrowableUtils
()
{
throw
new
UnsupportedOperationException
(
"u can't instantiate me..."
);
}
public
static
String
getFullStackTrace
(
Throwable
throwable
)
{
final
List
<
Throwable
>
throwableList
=
new
ArrayList
<>();
while
(
throwable
!=
null
&&
!
throwableList
.
contains
(
throwable
))
{
throwableList
.
add
(
throwable
);
throwable
=
throwable
.
getCause
();
}
final
int
size
=
throwableList
.
size
();
final
List
<
String
>
frames
=
new
ArrayList
<>();
List
<
String
>
nextTrace
=
getStackFrameList
(
throwableList
.
get
(
size
-
1
));
for
(
int
i
=
size
;
--
i
>=
0
;
)
{
final
List
<
String
>
trace
=
nextTrace
;
if
(
i
!=
0
)
{
nextTrace
=
getStackFrameList
(
throwableList
.
get
(
i
-
1
));
removeCommonFrames
(
trace
,
nextTrace
);
}
if
(
i
==
size
-
1
)
{
frames
.
add
(
throwableList
.
get
(
i
).
toString
());
}
else
{
frames
.
add
(
" Caused by: "
+
throwableList
.
get
(
i
).
toString
());
}
frames
.
addAll
(
trace
);
}
StringBuilder
sb
=
new
StringBuilder
();
for
(
final
String
element
:
frames
)
{
sb
.
append
(
element
).
append
(
LINE_SEP
);
}
return
sb
.
toString
();
}
private
static
List
<
String
>
getStackFrameList
(
final
Throwable
throwable
)
{
final
StringWriter
sw
=
new
StringWriter
();
final
PrintWriter
pw
=
new
PrintWriter
(
sw
,
true
);
throwable
.
printStackTrace
(
pw
);
final
String
stackTrace
=
sw
.
toString
();
final
StringTokenizer
frames
=
new
StringTokenizer
(
stackTrace
,
LINE_SEP
);
final
List
<
String
>
list
=
new
ArrayList
<>();
boolean
traceStarted
=
false
;
while
(
frames
.
hasMoreTokens
())
{
final
String
token
=
frames
.
nextToken
();
// Determine if the line starts with <whitespace>at
final
int
at
=
token
.
indexOf
(
"at"
);
if
(
at
!=
-
1
&&
token
.
substring
(
0
,
at
).
trim
().
isEmpty
())
{
traceStarted
=
true
;
list
.
add
(
token
);
}
else
if
(
traceStarted
)
{
break
;
}
}
return
list
;
}
private
static
void
removeCommonFrames
(
final
List
<
String
>
causeFrames
,
final
List
<
String
>
wrapperFrames
)
{
int
causeFrameIndex
=
causeFrames
.
size
()
-
1
;
int
wrapperFrameIndex
=
wrapperFrames
.
size
()
-
1
;
while
(
causeFrameIndex
>=
0
&&
wrapperFrameIndex
>=
0
)
{
// Remove the frame from the cause trace if it is the same
// as in the wrapper trace
final
String
causeFrame
=
causeFrames
.
get
(
causeFrameIndex
);
final
String
wrapperFrame
=
wrapperFrames
.
get
(
wrapperFrameIndex
);
if
(
causeFrame
.
equals
(
wrapperFrame
))
{
causeFrames
.
remove
(
causeFrameIndex
);
}
causeFrameIndex
--;
wrapperFrameIndex
--;
}
}
}
imageVideoBanner/src/main/res/drawable-mdpi/load_fail.png
0 → 100644
View file @
d75937a0
3.7 KB
imageVideoBanner/src/main/res/layout/item_video_view.xml
View file @
d75937a0
...
@@ -5,7 +5,7 @@
...
@@ -5,7 +5,7 @@
android:layout_height=
"match_parent"
android:layout_height=
"match_parent"
android:id=
"@+id/cVideoView"
>
android:id=
"@+id/cVideoView"
>
<VideoView
<
com.widget.imagevideobanner.banner.My
VideoView
android:id=
"@+id/video_view"
android:id=
"@+id/video_view"
android:layout_width=
"match_parent"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:layout_height=
"match_parent"
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment