Android中利用動態(tài)加載實現(xiàn)手機淘寶的節(jié)日特效
相信去年圣誕節(jié)打開過手機淘寶的童鞋都會對當(dāng)時的特效記憶猶新吧:全屏飄雪,旁邊還有個小雪人來控制八音盒背景音樂的播放,讓人有種身臨其境的感覺,甚至忍不住想狠狠購物了呢(誤),大概就是下面這個樣子滴:

嗯,確實很炫,那么我們一步步去分析是如何實現(xiàn)的:
一、實現(xiàn)下雪的 View
首先,最上面一層的全屏雪花極有可能是一個頂層的View,而這個View是通過動態(tài)加載去控制顯示的(不更新淘寶也能看到這個效果)。那么我們先得實現(xiàn)雪花效果的 View,人生苦短,拿來就用。打開 gank.io,搜索"雪花":

看樣子第7個庫就是我們想要的了,點進源碼,直接 download 不解釋,記得 star 一個支持作者。那么現(xiàn)在我們的項目中就有一個完整的下雪效果 View 了。
二、實現(xiàn)雪人播放器 View
這個一張雪人圖片+一個按鈕即可實現(xiàn),就不多解釋了。接下來需要一段圣誕節(jié)音頻,直接進行在線音頻播放無疑是節(jié)省空間的好方案?!何业幕逍缓嫱谐龅募拍鹈鄣姆諊鸁o疑是最適合圣誕節(jié)的,因此我們得到了『神曲』URL 一枚:
http://cdn.ifancc.com/TomaToDo/bgms/my_hbx.mp3
接下來要找一個小雪人的圖片當(dāng)作播放器的背景,那么阿姆斯特朗...不對,是這個:

嗯,相當(dāng)可愛喜慶。那么播放器核心代碼如下:
package com.kot32.christmasview.player;
import android.content.Context;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.util.AttributeSet;
import android.view.View;
import android.widget.Toast;
import com.kot32.christmasview.R;
import java.io.IOException;
/**
* Created by kot32 on 16/12/8.
*/
public class MyPlayer extends View {
public MediaPlayer mediaPlayer;
public MyPlayer(Context context) {
super(context);
init();
}
public MyPlayer(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
setBackgroundResource(R.drawable.pig);
mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
playUrl("http://172.20.248.106/IXC5b415fcacfc3c439e25a3e74533d2239/TomaToDo/bgms/my_hbx.mp3");
Toast.makeText(getContext(), "開始播放", Toast.LENGTH_SHORT).show();
setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (!mediaPlayer.isPlaying()) {
mediaPlayer.start();
Toast.makeText(getContext(), "繼續(xù)播放", Toast.LENGTH_SHORT).show();
} else {
mediaPlayer.pause();
Toast.makeText(getContext(), "暫停播放", Toast.LENGTH_SHORT).show();
}
}
});
}
public void playUrl(String videoUrl) {
try {
mediaPlayer.reset();
mediaPlayer.setDataSource(videoUrl);
mediaPlayer.prepare();//prepare之后自動播放
mediaPlayer.start();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
try {
mediaPlayer.stop();
mediaPlayer.release();
}catch (Exception e){
e.printStackTrace();
}
}
}
三、動態(tài)加載思路
上面基本實現(xiàn)了在本地的雪花以及播放音樂效果,那么在不更新主程序的情況下,如何將這兩個View動態(tài)加載到主程序當(dāng)中去呢?
首先我們明白,Android 的DexClassloader 是擁有加載任意APK 中任意類的能力的,只是有以下限制:
加載出的Activity 由于不在宿主 Manifest 文件中聲明,因此框架無法找到并初始化這個Activity。
加載出的Activity 不具備生命周期,理由同上。
加載出的類的Resource 文件id 會和主程序混淆在一起。
由于我們只是加載View,并不是加載整個Activity,所以前兩個問題并不會遇到,而第三個問題可以想辦法解決掉。
在主程序中我們也要做這三件事:
把能夠裝載View的ViewGroup 的空位留出來
去獲取更新的patch包
把View 從apk包中加載出來之后,放進留好的ViewGroup 中。
這樣一來,不僅是圣誕節(jié),在之后的各種活動上都可以在線去加載活動的View。
四、開始加載
在加載View 之前,首先要意識到這個View 是引用了圖片資源的(小豬圖片),因此我們要解決資源問題:
private void initResource() {
Resources resources = getContext().getResources();
try {
AssetManager newManager = AssetManager.class.newInstance();
Method addAssetPath = newManager.getClass().getMethod("addAssetPath", String.class);
addAssetPath.invoke(newManager, DynamicViewManager.getInstance().getUpdateFileFullPath());
Resources newResources = new Resources(newManager,
resources.getDisplayMetrics(), resources.getConfiguration());
Reflect.onObject(getContext()).set("mResources", newResources);
} catch (Exception e) {
e.printStackTrace();
}
}
上面代碼的作用是:把添加了外部更新包路徑的資源管理器賦值給了App原來的資源管理器,也就是說現(xiàn)在可以在宿主中訪問插件資源了。
核心加載代碼如下:
DexClassLoader classLoader = new DexClassLoader(apkFile.getAbsolutePath()
, "dex_out_put_dir"
, null
, getClass().getClassLoader());
Class newViewClazz = classLoader.loadClass("view's package name");
Constructor con = newViewClazz.getConstructor(Context.class);
//first use Activity's Resource lie to View
if (dynamicView == null) {
dynamicView = (View) con.newInstance(getContext());
}
//Replace the View's mResources and recovery the Activity's avoid disorder of Resources
Reflect.onObject(getContext()).set("mResources", null);
getContext().getResources();
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(DisplayUtil.dip2px(getContext(), viewInfo.layoutParams.width),
DisplayUtil.dip2px(getContext(), viewInfo.layoutParams.height));
layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE);
addView(dynamicView, layoutParams);
中間對 mResources 的操作的作用是:將宿主的Activity 的mResources 重置,避免在Activity 中使用資源時和插件沖突。
然而機智的我已經(jīng)把更新包下載、版本管理、動態(tài)加載都封裝好了,所以正確的加載方式是:
引用它:https://github.com/kot32go/dynamic-load-view
然后:
1.宿主聲明:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/tb_bg" > <com.kot32.dynamicloadviewlibrary.core.DynamicViewGroup android:layout_width="match_parent" android:layout_height="match_parent" app:uuid="activity_frame"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="原始頁面" /> </com.kot32.dynamicloadviewlibrary.core.DynamicViewGroup> <com.kot32.dynamicloadviewlibrary.core.DynamicViewGroup android:layout_width="60dp" android:layout_height="60dp" android:layout_alignParentRight="true" android:layout_centerVertical="true" app:uuid="activity_player"> </com.kot32.dynamicloadviewlibrary.core.DynamicViewGroup> </RelativeLayout>
以上聲明了主界面的布局,當(dāng)然,在動態(tài)加載之前除了原有的"原始頁面"TextView,是不會有任何其他東西的,也就是圣誕節(jié)來臨之前的程序。注意:uuid 會和在線包相匹配。
2.打插件包
其實就是把之前包含了我們所寫的兩個View(雪花和雪人)的程序打包成apk??梢圆缓灻?/p>
3.把插件包放到服務(wù)器
在服務(wù)器返回的JSON中聲明插件包地址和動態(tài)View 的一些參數(shù),這里的演示程序請求地址為:
http://tomatodo.ifancc.com/php/dynamicView.php
返回值為:
{
"version": 54,
"downLoadPath": "http://obfgb7oet.bkt.clouddn.com/patch106.apk",
"fileName": "patch106.apk",
"viewInfo": [
{
"packageName": "com.kot32.testdynamicviewproject.snow.widgets.SnowingView",
"uuid": "activity_frame",
"layoutParams": {
"width": -1,
"height": -1
}
},
{
"packageName": "com.kot32.testdynamicviewproject.player.MyPlayer",
"uuid": "activity_player",
"layoutParams": {
"width": -1,
"height": -1
}
}
]
}
我們聲明了這次在線包的版本,每個View 的包名和布局參數(shù), 以及最重要的 和宿主程序中聲明對齊的uuid。
以上所述是小編給大家介紹的Android中利用動態(tài)加載實現(xiàn)手機淘寶的節(jié)日特效,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
相關(guān)文章
Android開發(fā)仿bilibili刷新按鈕的實現(xiàn)代碼
這篇文章主要介紹了Android 仿bilibili刷新按鈕的實現(xiàn)代碼,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2017-10-10
Android Studio中使用lambda表達(dá)式的方法
這篇文章主要介紹了Android Studio中使用lambda表達(dá)式的方法,需要的朋友可以參考下2017-06-06
WorkManager解決應(yīng)用退出后繼續(xù)運行后臺任務(wù)
這篇文章主要為大家介紹了WorkManager解決應(yīng)用退出后繼續(xù)運行后臺任務(wù)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-07-07

