Android設備廣告投放解決方案——大量網絡圖片、多個網絡視頻的輪播、緩存與更新


    轉載請注明原文地址:http://www.cnblogs.com/ygj0930/p/7742996.html

    

    一:業務場景

    基於Android系統的設備上投放廣告,諸如:地鐵廣告屏、自助服務機器上的廣告位等。

 

    二:業務難點

    廣告投放的主要矛盾集中於:廣告的本地緩存與及時更新。

    廣告本地緩存的必要性:圖片、視頻都是比較吃流量的內容,在不停輪播過程中,如果每展示一張圖片、播放一個視頻,都實時從服務器拉取,那么廣告播多久,流量就消耗多久,這樣明顯是不合算的。

    廣告更新的時效性:廣告不是一成不變的,往大了說,可以按日期跨度來規划;小了說,可以按每天的時段來規划。這就要求我們播放的廣告與服務器進行同步。

 

    三:難點解決思路

    1:本地緩存的實現

    圖片緩存:圖片的緩存很容易實現,android有很多圖片加載框架,這些框架本身就自帶緩存機制。我是采用Glide這個框架,其自帶磁盤緩存、內存緩存兩級緩存機制,我們無需關心它是怎么緩存圖片的。其對緩存內容的訪問機制是通過“鍵值對”的方式——圖片url是key,圖片內容是value。也就是說:第一次加載時,glide會根據url訪問到圖片並且緩存到本地,之后再通過該url進行加載時,glide會直接從本地緩存中把圖片加載出來。

 

    視頻緩存:android的視頻播放控件VideoView自帶單個視頻緩存功能,如果需要循環播放的廣告視頻只有一個的話,只需用videoview的setLooping(true)即可實現,這樣只會在第一次加載視頻url時拉取視頻內容,之后就不再發生網絡請求了。    

    問題在於,現實中不會全天候循環播放單個視頻的,最起碼也會根據廣告投放的區域、級別,輪播好幾個視頻,這樣的話,videoview的循環播放就不起作用了,每當播放一個新url時都會拉取數據,即使這個視頻它不久前還播放過。

    有一種笨辦法:就是先把要播放的視頻下載到sd卡,然后只需輪播下載好的本地視頻即可。    這種方案解決了輪播視頻時的流量消耗痛點,但是不能滿足廣告時效性的要求:它需要定期查詢服務器,檢查本地視頻是否最新,如果服務器的廣告內容發生了變化,又要手動下載新視頻,同時還要處理舊視頻,否則手機容量會被不停下載的視頻文件擠爆。

    最優雅的辦法是:使用視頻緩存框架,我推薦使用:danikula大神開源的videocache框架。其緩存內容的訪問機制也是“鍵值對”——如果url曾經加載過,則從本地緩存中加載視頻。至於緩存內容的管理,框架已經自動幫我們完成——使用LRU算法定期清理。

 

    2:時效性的保證

    廣告需要定時更新,很多人第一反應就是——使用android的Alarm機制,定時更新內容,這種方案雖然可行,但是太麻煩啦~

    上面提到的圖片緩存框架、視頻緩存框架,都設計一個重要、核心的設計理念——以url為鍵,以內容為值。

    基於這個理念,我們可以通過動態url來達到實時更新緩存內容的目的,至於更新的頻率,就看你怎么拼接url了。

    按天更新:如果是按日期來更新廣告,可以在圖片、視頻的url后面加上“年月日”,這樣的話,就保證了url每日一變,而緩存框架只會在當天第一次加載時拉取數據,后面就直接從本地緩存加載數據了。而之前緩存的內容則會被自動清理掉。

    按時段更新:如果是按照一天當中的不同時段來更換播放的廣告,則應該先從服務器拉取有什么時段,然后根據當前時間處於那個時段之間,在url后拼接 時段的開始或結束時間 即可。

    按日期區間更新:如果是按照日期跨度來更新,比如說2017/01/01~2017/02/03號播放某幾個視頻。其實這只不過是大概念的時段播放而已,同理,我們先從服務器查詢出當前日期處於哪些視頻的播放時段之間,然后在url后拼接 起始或終止日期  即可。

    按日期+時段更新:綜合上面的日期區間、一天當中的時間區間來播放不同廣告:拼接 終止日期+時段的終止時間 即可。

    實時更新:如果要保證每次播放都是新的,可以拼接隨機數。

    

    四:實戰舉例

    0:工具類准備

public class Utils {
    //獲取當天年月日,作為動態后綴,每天變化一次
    public static String getTimeStamp(){
        Calendar now = Calendar.getInstance();
        String timeStamp = ""+now.get(Calendar.YEAR)+now.get(Calendar.MONTH)+now.get(Calendar.DAY_OF_MONTH);
        return timeStamp;
    }
}

 

  1:圖片的輪播與按日期更新

    輪播控件:使用convenientbanner。

    圖片緩存:使用glide。

    1)添加依賴

compile 'com.github.bumptech.glide:glide:3.7.0'
compile 'com.bigkoo:convenientbanner:2.0.5'

    2)編寫網絡圖片加載Holder

import android.content.Context;
import android.view.View;
import android.widget.ImageView;
import com.bigkoo.convenientbanner.holder.Holder;
import com.bumptech.glide.Glide;


/**
 * Created by yeguojian on 2017/10/24.
 */

public class NetworkImageHolderView implements Holder<String> {

    private ImageView imageView;
    @Override
    public View createView(Context context) {
        //你可以通過layout文件來inflate一個輪播的頁面。這里我輪播的頁面只有圖片,所以直接在代碼中創建了
        imageView = new ImageView(context);
        return imageView;
    }

    @Override
    public void UpdateUI(Context context, final int position, String data) {
        Glide.with(context).load(data).placeholder(備用圖片:網絡圖片加載失敗時顯示).into(imageView); 
    }
}

    3)編寫輪播頁面,這里我是用Fragment實現的

/**
 * Created by yeguojian on 2017/9/26.
 */

public class AdvertFragment extends Fragment {
    private FrameLayout videoLayout;
    private ConvenientBanner convenientBanner;
    private List<String> networkImages;
    private String[] images;
    protected ImageLoader imageLoader;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View advert = inflater.inflate(R.layout.advert_fragment,container,false);
        images=new String[]{
              圖片url+"&date="+Utils.getTimeStamp(),......};//這里保存向服務器請求圖片的url地址們,在后面拼接時間戳參數來達到每天從服務器拉取一次的目的。
        convenientBanner = advert.findViewById(R.id.convenientBanner);
        imageLoader = ImageLoader.getInstance();
        imageLoader.init(ImageLoaderConfiguration.createDefault(getActivity()));

        //網絡加載圖片
        networkImages = Arrays.asList(images);
        convenientBanner.setPages(new CBViewHolderCreator<NetworkImageHolderView>() {
            @Override
            public NetworkImageHolderView createHolder() {
                return new NetworkImageHolderView();
            }
        },networkImages)
                //設置自動切換(同時設置切換時間間隔)
                .startTurning(2000)
                //設置是否手動影響(設置了該項無法手動切換)
                .setManualPageable(false);
        return advert;
    }
}

     4)圖片輪播碎片的布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_gravity="center"
    android:background="#000000"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
        <com.bigkoo.convenientbanner.ConvenientBanner
            xmlns:app="http://schemas.android.com/apk/res-auto"
            android:id="@+id/convenientBanner"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:canLoop="true"/>
</LinearLayout>

 

 

    2:多個視頻的輪播緩存與按日更新

    1)添加依賴:使用androidvideocache音/視頻緩存框架

compile 'com.danikula:videocache:2.7.0'

    2)視頻播放控件使用videoview,具體布局就因項目而異了,這個不影響緩存的實現

    3)videoview輪播並緩存網絡視頻的實現

/**
 * Created by yeguojian on 2017/9/26.
 */

public class VendingFragment extends Fragment {

    private VideoView videoView;
    private HttpProxyCacheServer proxy; //視頻緩存代理

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View vending = inflater.inflate(R.layout.vending_fragment,container,false);

       //創建緩存代理
        proxy = new HttpProxyCacheServer.Builder(getActivity())
                .maxCacheSize(1024 * 1024 * 1024) //1Gb 緩存
                .maxCacheFilesCount(5)//最大緩存5個視頻
                .build();

        videoView = (VideoView) vending.findViewById(R.id.vending_videoView);
     
        videoView.setOnErrorListener(new MediaPlayer.OnErrorListener() {
            @Override
            public boolean onError(MediaPlayer mp, int what, int extra) {
                videoView.stopPlayback(); //播放異常,則停止播放,防止彈窗使界面阻塞
                return true;
            }
        });

        playVideoOne();//播放第一個視頻
   
        return vending;
    }

    public void playVideoOne(){
        String proxyUrl = proxy.getProxyUrl(videoOneUrl+"&date="+Utils.getTimeStamp()); //視頻url拼接日期,實現按日更新
        videoView.setVideoPath(proxyUrl); //為videoview設置播放路徑,而不是設置播放url
        videoView.start();
        videoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
            @Override
            public void onCompletion(MediaPlayer mPlayer) {
                playVideoTwo(); //監聽視頻一的播放完成事件,播放完畢就播放視頻二
            }
        });
    }

    public void playVideoTwo(){
        String proxyUrl = proxy.getProxyUrl(videoTwoUrl+"&date="+Utils.getTimeStamp());
        videoView.setVideoPath(proxyUrl);
        videoView.start();
        videoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
            @Override
            public void onCompletion(MediaPlayer mPlayer) {
                playVideoThree();
            }
        });
    }
    public void playVideoThree(){
        String proxyUrl = proxy.getProxyUrl(videoThreeUrl+"&date="+Utils.getTimeStamp());
        videoView.setVideoPath(proxyUrl);
        videoView.start();
        videoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
            @Override
            public void onCompletion(MediaPlayer mPlayer) {
                playVideoOne();//視頻三播放完后播放視頻一,從而實現輪播
            }
        });
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM