您的位置:首页 > 产品设计 > UI/UE

android Dalvikvm GC 多次运行 引起UI主线程卡顿问题

2017-04-13 13:02 351 查看
问题背景:

我这边android客户端需要在每一次启动的时候加载开机广告,具体流程,请求广告服务器拿回来图片的url,然后去下载图片,再然后就是popwindow去展示。

代码如下:

package com.iptvclient.android.baseclient.startupad;

import android.content.Context;
import android.graphics.Bitmap;
import android.os.Handler;
import android.text.TextUtils;
import android.view.Display;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.PopupWindow;
import com.iptvclient.android.R;
import com.iptvclient.android.androidsdk.common.LogEx;
import com.iptvclient.android.androidsdk.common.PreferenceHelper;
import com.iptvclient.android.androidsdk.common.StringUtil;
import com.iptvclient.android.androidsdk.common.TimerMgr;
import com.iptvclient.android.androidsdk.common.TimerMgr.ITimerMgr;
import com.iptvclient.android.androidsdk.ui.gifview.GifImageView;
import com.iptvclient.android.baseclient.operation.play.AdVodBannerRspXMLParser;
import com.iptvclient.android.baseclient.operation.play.AdVodPlayRspXMLParser;
import com.iptvclient.android.baseclient.operation.play.NewAdRequestHelper;
import com.iptvclient.android.baseclient.startupad.ImgFileUtil.ImgDownloadListener;
import java.io.InputStream;
import java.net.URLDecoder;
/**
*
* @ClassName:StartUpAdUtil
* @Description: 查询展示开机广告的工具类
* @author: 10191204
* @date: 2015年4月8日
*
*/
public class StartUpAdUtil
{
private static String LOG_TAG = "StartUpAdUtil";
public final String AD_LOCAL_KEY = "AdLocalKey";
private Context mContext;
/** 主Activity的Handler */
private Handler mHandler;
/** 图片popupwindow打开的时候需要使用的viewroot */
private View mViewRoot;
/** 开机图片广告的界面 */
private PopupWindow mPopAD;

/** 开机广告图片的本地地址 */
private String mStartUpAdLocalUrl = "";

/** 解析广告地址的loader */
private GetStartUpAdLoader getAdLoader;
/** 广告图片下载工具*/
private ImgFileUtil imgDownloader;
/** 保存文本辅助类,用于存取用户名和密码相关的全局数据 */
private PreferenceHelper mPreferenceHelper;

/** 是否正在请求开机广告的标记位 */
private boolean isRequesting = false;
/** 查询开机广告的超时时间,单位秒*/
private int getStartUpAdDelayTime = 10;

/** GIF图片展示控件 */
private GifImageView imgGIF;
/** 普通图片展示控件 */
private ImageView imgNormal;
/** 启动广告地址 */
private String mstrBannerStartUrl = "";
/** 启动广告展示时间 */
private int miDurationStartAd;
/** 广告图片请求对象 */
private NewAdRequestHelper mNewAdRequestHelper;

/**
*  启动广告  构造方法  初始化数据
* @param context
* @param viewRoot
* @param handler
*/
public StartUpAdUtil(Context context, View viewRoot, Handler handler){
mContext = context;
mHandler = handler;
this.mViewRoot = viewRoot;
initData(context);
}

/**
*  展示广告 初始化定时器  初始化 请求对象
*/
public void showAd()
{
if (isRequesting)
{
return;
}
//发起请求
LogEx.d(LOG_TAG, "begin to request start ad");
isRequesting = true;
mNewAdRequestHelper.requestBannerAdbyPositionId(
mNewAdRequestHelper.Banner_Advertise_Adplaceid_Start);
LogEx.d(LOG_TAG, "timer begin, time:"+getStartUpAdDelayTime +"s");
}

/**
* 初始化数据
* 初始化 定时器 10s 超时
* @param context
*/
private void initData(Context context)
{
mPreferenceHelper = new PreferenceHelper(context, "startUpAd");
//创建超时监听
mHandler.postDelayed(new Runnable() {
@Override
public void run() {

if(isRequesting)
{
showAdToUser();
}
}
}, getStartUpAdDelayTime * 1000);

readAdFromLocal();

mNewAdRequestHelper = new NewAdRequestHelper(new NewAdRequestHelper.VodAdReturnListener() {

@Override
public void BannerReturn(AdVodBannerRspXMLParser.BannerPic bannerPic) {

mstrBannerStartUrl = bannerPic.getUrl();
LogEx.d(LOG_TAG, "bannerPic.getDuration()=" + bannerPic.getDuration());
if(TextUtils.isEmpty(bannerPic.getDuration()))
{
return;
}
miDurationStartAd = Integer.parseInt(bannerPic.getDuration());
LogEx.d(LOG_TAG, "miDurationStartAd=" + miDurationStartAd);
if (!isRequesting) {
LogEx.d(LOG_TAG, "get ad url, but not requesting ,return");
return;
}

if (!TextUtils.isEmpty(mstrBannerStartUrl)) {
try {
mstrBannerStartUrl = URLDecoder.decode(mstrBannerStartUrl, "UTF-8");

} catch (Exception e) {
e.printStackTrace();
showAdToUser();
return;
}
}
else
{
showAdToUser();
return;
}

LogEx.d(LOG_TAG, "get ad url, will download the new img ,wait");
//下载新图片
imgDownloader = new ImgFileUtil(mContext, new ImgDownloadListener() {

@Override
public void onImgDownloaded(String filename) {

LogEx.d(LOG_TAG, "download ad img success");
// mPreferenceHelper.putString(AD_START_URL_KEY, mstrBannerStartUrl);

mStartUpAdLocalUrl = filename;
writeAdToLocal();

if (!isRequesting) {
LogEx.d(LOG_TAG, "overtime, return");
return;
}

showAdToUser();
}
@Override
public void onImgDownloadFail() {
LogEx.i(LOG_TAG, "download ad img failed, use local img");
if (!isRequesting) {
LogEx.w(LOG_TAG, "overtime, return");
return;
}
showAdToUser();
}
});
//下载 图片的 请求
imgDownloader.stratDownloadImg(mstrBannerStartUrl);
}

@Override
public void VodPlayAdReturn(AdVodPlayRspXMLParser parser) {

}
});

}

/**
* 写进本地缓存
*/
private void writeAdToLocal()
{
LogEx.d(LOG_TAG, "write new img to local");
if (!TextUtils.isEmpty(mStartUpAdLocalUrl))
{
mPreferenceHelper.putString(AD_LOCAL_KEY, mStartUpAdLocalUrl);
}
}
/**
* 读本地缓存
*/
private void readAdFromLocal(){
LogEx.d(LOG_TAG, "load local img");
String temAdLocalUrl = mPreferenceHelper.getString(AD_LOCAL_KEY, "");
if (!TextUtils.isEmpty(temAdLocalUrl))
{
mStartUpAdLocalUrl = temAdLocalUrl;
}
else
{
mStartUpAdLocalUrl = "";
}
}

/**
*  展示 广告图片
*/
private void showAdToUser(){
LogEx.d(LOG_TAG, "enter showAdToUser");
isRequesting = false;
if (StringUtil.isEmptyString(mStartUpAdLocalUrl))
{
LogEx.w(LOG_TAG, "at showAdToUser,but mStartUpAdLocalUrl is empty, return");
return;
}
//创建PopupWindow
View popView = ((LayoutInflater) mContext.getSystemService
(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.start_ad_layout, null);
mPopAD = new PopupWindow(popView, FrameLayout.LayoutParams.FILL_PARENT,
FrameLayout.LayoutParams.FILL_PARENT,true);
//展示广告
mHandler.post(new Runnable()
{

@Override
public void run()
{
mPopAD.showAtLocation(mViewRoot, Gravity.CENTER, 0, getStatusBarHeight());
}
});

if (mStartUpAdLocalUrl.endsWith("gif"))
{
imgGIF = (GifImageView) popView.findViewById(R.id.start_ad_gif);
InputStream in = ImgFileUtil.readDataFile(mContext, mStartUpAdLocalUrl);
imgGIF.setImageStream(in);
WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
int width = display.getWidth();
int height = display.getHeight();
imgGIF.setWidthAndHeight(width,height);

imgGIF.setVisibility(View.VISIBLE);
}
else
{
imgNormal = (ImageView) popView.findViewById(R.id.start_ad_img);
Bitmap imgSrc = ImgFileUtil.File2Bitmap(mContext,mStartUpAdLocalUrl);
if(imgSrc == null)
{
return;
}
imgNormal.setImageBitmap(imgSrc);
imgNormal.setVisibility(View.VISIBLE);
}

LogEx.d(LOG_TAG, "start ad is showing, show time = "+miDurationStartAd+"s");
//启动定时器
mHandler.postDelayed(new Runnable()
{

@Override
public void run()
{
LogEx.d(LOG_TAG, "start ad show time end, dismiss");
mPopAD.dismiss();
if (null != imgGIF)
{
imgGIF.destroyDrawingCache();
}

}
},miDurationStartAd * 1000);
}

public int getStatusBarHeight() {
int result = 0;
int resourceId = mContext.getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0)
{
result = mContext.getResources().getDimensionPixelSize(resourceId);
}
return result;
}
}

 NewAdRequest Hepler 是请求广告服务器拿回来图片的url,preference 虽然有保存图片名称,但是没有使用到。

但是有一个问题,就是应用多次启动之后,就会出现UI页面卡死,也没有弹出来开机广告,点击页面任何控件都没有相应。抓日志如下:

  L[122][showAd]              timer begin, time:10s
Line 47484: 04-13 10:55:55.144 D/StartUpAdUtil(23947): 2017年4月13日 上午10:55:55   F[StartUpAdUtil]       L[122][showAd]              timer begin, time:10s
Line 53237: 04-13 10:55:58.304 D/StartUpAdUtil(23947): 2017年4月13日 上午10:55:58   F[StartUpAdUtil]       L[155][BannerReturn]        bannerPic.getDuration()=10
Line 53237: 04-13 10:55:58.304 D/StartUpAdUtil(23947): 2017年4月13日 上午10:55:58   F[StartUpAdUtil]       L[155][BannerReturn]        bannerPic.getDuration()=10
Line 53239: 04-13 10:55:58.304 D/StartUpAdUtil(23947): 2017年4月13日 上午10:55:58   F[StartUpAdUtil]       L[161][BannerReturn]        miDurationStartAd=10
Line 53239: 04-13 10:55:58.304 D/StartUpAdUtil(23947): 2017年4月13日 上午10:55:58   F[StartUpAdUtil]       L[161][BannerReturn]        miDurationStartAd=10
Line 53241: 04-13 10:55:58.304 D/StartUpAdUtil(23947): 2017年4月13日 上午10:55:58   F[StartUpAdUtil]       L[183][BannerReturn]        get ad url, will download the new img ,wait
Line 53241: 04-13 10:55:58.304 D/StartUpAdUtil(23947): 2017年4月13日 上午10:55:58   F[StartUpAdUtil]       L[183][BannerReturn]        get ad url, will download the new img ,wait
Line 53243: 04-13 10:55:58.304 I/StartUpAdUtil(23947): 2017年4月13日 上午10:55:58   F[ImgFileUtil]         L[75] [getFilename]         img url is http://80.80.46.159:8888/mad_interface/pictureservlet?1492052139907&userid=123&contentcode=170105001&terminaltype=2&ordercode=prom9999010020170105001&adid=111&appcode=000001&srcflag=0&materialcode=00000001001000000111&file=1483584761358.bmp Line 53245: 04-13 10:55:58.304 I/StartUpAdUtil(23947): 2017年4月13日 上午10:55:58   F[ImgFileUtil]         L[89] [getFilename]         filename = startupad.bmp
Line 53299: 04-13 10:55:58.364 D/StartUpAdUtil(23947): 2017年4月13日 上午10:55:58   F[ImgFileUtil]         L[128][onData]               datarsp=com.zte.androidsdk.http.bean.HttpResponse@42c1de20
Line 53301: 04-13 10:55:58.364 D/StartUpAdUtil(23947): 2017年4月13日 上午10:55:58   F[ImgFileUtil]         L[130][onData]              file size = 701B
Line 53307: 04-13 10:55:58.614 I/StartUpAdUtil(23947): 2017年4月13日 上午10:55:58   F[ImgFileUtil]         L[213][reportSaveFileRet]   IStartAdFileListener is notified
Line 53309: 04-13 10:55:58.614 D/StartUpAdUtil(23947): 2017年4月13日 上午10:55:58   F[StartUpAdUtil]       L[190][onImgDownloaded]     download ad img success
Line 53309: 04-13 10:55:58.614 D/StartUpAdUtil(23947): 2017年4月13日 上午10:55:58   F[StartUpAdUtil]       L[190][onImgDownloaded]     download ad img success
Line 53311: 04-13 10:55:58.614 D/StartUpAdUtil(23947): 2017年4月13日 上午10:55:58   F[StartUpAdUtil]       L[232][writeAdToLocal]      write new img to local
Line 53311: 04-13 10:55:58.614 D/StartUpAdUtil(23947): 2017年4月13日 上午10:55:58   F[StartUpAdUtil]       L[232][writeAdToLocal]      write new img to local
Line 53313: 04-13 10:55:58.624 D/StartUpAdUtil(23947): 2017年4月13日 上午10:55:58   F[StartUpAdUtil]       L[258][showAdToUser]        enter showAdToUser
Line 53313: 04-13 10:55:58.624 D/StartUpAdUtil(23947): 2017年4月13日 上午10:55:58   F[StartUpAdUtil]       L[258][showAdToUser]        enter showAdToUser                 04-13 10:55:58.694 D/dalvikvm(23947): GC_FOR_ALLOC freed
744K, 13% free 27740K/31828K, paused 29ms, total 29ms

04-13 10:55:58.694 I/dalvikvm-heap(23947): Grow heap (frag case) to 30.013MB for 955564-byte allocation

04-13 10:55:58.694 I/ThermalEngine(  233): Sensor:batt_temp:29700 mC

04-13 10:55:58.704 D/OpenGLRenderer(23947): GL error from OpenGLRenderer: 0x502           

04-13 10:56:00.104 D/SizeAdaptiveLayout( 4228): com.android.internal.widget.SizeAdaptiveLayout{43053ab8 V.E..... ......ID 0,0-621,130 #7f0800bd app:id/adaptive}child view android.widget.FrameLayout{43a025a0
V.E..... ......ID 0,0-621,129 #7f080001 app:id/status_bar_latest_event_content} measured out of bounds at 129px clamped to 130px

04-13 10:56:00.134 D/dalvikvm( 4228): GC_CONCURRENT freed 4781K, 39% free 23567K/38260K, paused 3ms+6ms, total 46ms        

发现日志不继续往下走去展示,紧接着就是dalvikvm GC的日志。

分析:每一次下载在内存中创建流对象:

HttpRequest req = new HttpRequest(HttpRequest.METHOD_GET, downloadUrl);
// 网络请求
DataAttribute attr = new DataAttribute();
attr.setMergeMode(DataDownload.FLAG_MERGE_MODE_LAST);
attr.setUniqueKey(downloadUrl);
HttpRequestParams params = new HttpRequestParams(attr, req, new IHttpDownloadListener()
{
@Override
public void onError(Exception arg0)
{
reportSaveFileRet();
}
@Override
public void onData(HttpRequest datareq, HttpResponse datarsp)
{
do
{
if (null == datarsp)
{
break;
}
LogEx.d(LOG_TAG, " datarsp=" + datarsp);

LogEx.d(LOG_TAG, "file size = " + datarsp.getContentLength()
/ 1024 + "B");
try
{
InputStream in = datarsp.getInputStream();

int ret = saveFile(in);
if (ret ==0)
{
saveSuccFlag = true;
}
in.close();
}
catch (Exception e)
{
e.printStackTrace();
LogEx.w(LOG_TAG, "" + e);
break;
}
}
while (false);
reportSaveFileRet();
}
@Override
public void onCancel(HttpRequest arg0, HttpResponse arg1)
{
reportSaveFileRet();

}
});
DataDownload.getInstance().sendHttpRequest(params);
}
private int saveFile(InputStream in){
if (in == null)
{
LogEx.w(LOG_TAG, "input null.");
return -1;
}

FileOutputStream output = null;
try
{
output = new FileOutputStream(path);
byte[] buffer = new byte[1024];
int len = 0;
while (-1 != (len = in.read(buffer)))
{
output.write(buffer, 0, len);
}
output.flush();
}
catch (Exception e)
{
e.printStackTrace();
LogEx.w(LOG_TAG, "save file Exception " + e);
return -1;
}
finally
{
try
{
output.close();
}
catch (Exception e)
{
e.printStackTrace();
LogEx.w(LOG_TAG, "save file Exception " + e);
return -1;
}
}
return 0;

}


导致内存的大量消耗!!!

优化:使用preference缓存上次请求的url ,如果和这一次一样,那么说明图片是一样的,那么不再次下载

private void initData(Context context)
{
mPreferenceHelper = new PreferenceHelper(context, "startUpAd");
//创建超时监听
mHandler.postDelayed(new Runnable() {
@Override
public void run() {

if(isRequesting)
{
showAdToUser();
}

}
}, getStartUpAdDelayTime * 1000);

readAdFromLocal();

mNewAdRequestHelper = new NewAdRequestHelper(new NewAdRequestHelper.VodAdReturnListener() {

@Override
public void BannerReturn(AdVodBannerRspXMLParser.BannerPic bannerPic) {

mstrBannerStartUrl = bannerPic.getUrl();
LogEx.d(LOG_TAG, "bannerPic.getDuration()=" + bannerPic.getDuration());
if(TextUtils.isEmpty(bannerPic.getDuration()))
{
return;
}
miDurationStartAd = Integer.parseInt(bannerPic.getDuration());
LogEx.d(LOG_TAG, "miDurationStartAd=" + miDurationStartAd);
if (!isRequesting) {
LogEx.d(LOG_TAG, "get ad url, but not requesting ,return");
return;
}

if (!TextUtils.isEmpty(mstrBannerStartUrl)) {
try {
mstrBannerStartUrl = URLDecoder.decode(mstrBannerStartUrl, "UTF-8");

} catch (Exception e) {
e.printStackTrace();
showAdToUser();
return;
}
 //读取缓存的online url
mStartUpAdOnlineUrl = mPreferenceHelper.getString(AD_ONLINE_KEY, "");
if(TextUtils.isEmpty(mStartUpAdOnlineUrl)
|| !mstrBannerStartUrl.equals(mStartUpAdOnlineUrl))
{
mPreferenceHelper.putString(AD_ONLINE_KEY, mstrBannerStartUrl);
}

//相同的url 且读到缓存的图片名称
if(mstrBannerStartUrl.equals(mStartUpAdOnlineUrl) &&
!TextUtils.isEmpty(mStartUpAdLocalUrl))
{
showAdToUser();
return;
}
}
else
{
showAdToUser();
return;
}
LogEx.d(LOG_TAG, "get ad url, will download the new img ,wait");
//下载新图片
imgDownloader = new ImgFileUtil(mContext, new ImgDownloadListener() {

@Override
public void onImgDownloaded(String filename) {

LogEx.d(LOG_TAG, "download ad img success");
// mPreferenceHelper.putString(AD_START_URL_KEY, mstrBannerStartUrl);

mStartUpAdLocalUrl = filename;
writeAdToLocal();

if (!isRequesting) {
LogEx.d(LOG_TAG, "overtime, return");
return;
}

showAdToUser();
}
@Override
public void onImgDownloadFail() {
LogEx.i(LOG_TAG, "download ad img failed, use local img");
if (!isRequesting) {
LogEx.w(LOG_TAG, "overtime, return");
return;
}
showAdToUser();
}
});
//下载 图片的 请求
imgDownloader.stratDownloadImg(mstrBannerStartUrl);
}
@Override
public void VodPlayAdReturn(AdVodPlayRspXMLParser parser) {

}
});
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: