簡介
CrossWalk是一款為HTML應(yīng)用提供運行時環(huán)境的開源項目魄眉,從Android開發(fā)的角度講Crosswalk可以用來替代WebView顯示網(wǎng)頁兢哭,在應(yīng)用重度依賴網(wǎng)頁時可以使用Crosswalk解決設(shè)備碎片化問題。直接將Crosswalk的庫包導(dǎo)入到項目中即可像使用WebView一樣使用,這種模式在Crosswalk項目中叫內(nèi)嵌模式,但這樣有一個問題由于Crosswalk庫包太大集成后的應(yīng)用體積太大始赎,為此Crosswalk為Android設(shè)備提供了另外兩種使用方式檬某,共享模式和下載模式。雖然Crosswalk項目
已經(jīng)在17年停止維護枫甲,但它這種動態(tài)加載使用的方式仍值得我學(xué)習(xí)。本文將介紹Crosswalk的三種加載方式。
使用
內(nèi)嵌模式
Crosswalk在它的下載地址提供了很多資源想幻,使用內(nèi)嵌模式只需要下載對應(yīng)的aar包粱栖,官方提供的aar包分為兩種,一種是32位的另一種是64位脏毯,32位的aar中只包含x86和armeabi-v7a兩種so文件闹究,同理64位的包中只包含x86_64和arm64-v8a兩種so文件,由于arm64-v8a平臺能兼容32位的so文件抄沮、x86_64也能兼容32位的x86 so文件跋核,在不考慮性能(暫時未知性能問題)的情況下就可以直接集成32位的aar包岖瑰。需要在build.gradle文件中指定只打包32位的so文件叛买,防止有其他依賴項目有64位的so文件,造成在運行時沒有對應(yīng)的crosswalk包蹋订。
android {
defaultConfig {
ndk {
abiFilters 'armeabi-v7a','x86'
}
}
}
以上是內(nèi)嵌模式下的一種集成方式率挣。由于官方只提供以上2種aar,如果只想集成arm平臺下的so文件或者一定要不同的平臺加載對應(yīng)的so文件露戒,那只有自己新建Android library module了椒功,到官網(wǎng)下載crosswalk.xx.zip和crosswalk.xx-64bit.zip,解壓后如圖把xwalk_core_library\libs目錄下的對應(yīng)so文件復(fù)制到新建的module的jniLibs下、把xwalk_core_library.jar復(fù)制到libs下智什,最后把xwalk_core_library\res目錄下所有資源都復(fù)制到module中
由于module要對外提供xwalk_core_library.jar中的api所以要將build.gradle中依賴方式改為api
dependencies {
api fileTree(dir: 'libs', include: ['*.jar'])
}
兩種內(nèi)嵌集成方式都可行动漾,集成后就可以使用crosswalk了,不做任何額外操作的情況下默認(rèn)是內(nèi)嵌模式。在XWalkView的構(gòu)造方法中會加載so文件荠锭,之后就可以使用了旱眯,和使用WebView一樣XWalkUIClient對應(yīng)WebView的WebChromeClient,XWalkResourceClient對應(yīng)WebViewClient
共享模式
顧名思義共享模式就是和其他應(yīng)用共用一個XWalkRuntime证九,XWalkRuntimeLib并沒有被內(nèi)嵌到自己的應(yīng)用中删豺,而是當(dāng)應(yīng)用要用到XWalkView時去加載包名為org.xwalk.core的應(yīng)用的代碼,如果沒有安裝就去下載XWalkRuntimeLib.APK安裝愧怜,官方提供了兩種下載APK的方式呀页,一種是跳轉(zhuǎn)Google Play Store下載,一種是在manifest文件中自定義下載鏈接的meta-data拥坛,到自己指定的地址去下載APK蓬蝶。第一種方式不可行,在大部分手機中market://details?id=xxx 這種URI跳轉(zhuǎn)的是自家的應(yīng)用商店猜惋,并沒有XWalkRuntimeLib.APK疾党。由于Crosswalk項目已經(jīng)停止更新了,
第二種方式會因為文件權(quán)限問題在Android 7.0之后的手機上拋錯惨奕。雖然這種模式基本被廢了雪位,但這種方式與下載模式大同小異,也值得分析下梨撞。
需要在官網(wǎng)下載crosswalk-shared-xx.aar并將其導(dǎo)入到項目雹洗,讓Activity繼承XWalkActivity并復(fù)寫它的onXWalkReady()方法香罐,在這個方法中使用XWalkView加載網(wǎng)頁。繼承了XWalkActivity的情況下时肿,如果不做任何配置默認(rèn)的是共享模式并且是第一種方式:跳轉(zhuǎn)Google Play Store下載APK庇茫。
import org.xwalk.core.XWalkActivity;
import org.xwalk.core.XWalkView;
public class MainActivity extends XWalkActivity {
XWalkView xWalkView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
xWalkView = findViewById(R.id.xwalk);
}
@Override
protected void onXWalkReady() {
xWalkView.loadUrl("https//:www.baidu.com");
}
}
第二種加載方式與第一種方式一樣需要繼承XWalkActivity,之后在官網(wǎng)下載4個平臺的APK并部署到自己的服務(wù)器上螃成,在manifest文件中配置下載路徑的meta-data旦签。
<meta-data
android:name="xwalk_apk_url"
android:value="http://192.168.1.236:8080/so/XWalkRuntimeLib.apk" />
crosswalk會自動判斷手機的平臺并在下載路徑后添加cpuArch=xxx鍵值對,取值有x86寸宏、x86_64宁炫、armeabi-v7a、arm64-v8a,服務(wù)器根據(jù)不同值返回相應(yīng)的APK就OK了氮凝。
下載模式
和共享模式差不多羔巢,只是共享模式是把APK下載下來當(dāng)成一個應(yīng)用安裝到手機上,而下載模式干脆把APK下載到自己的私有目錄下罩阵,把所有的so文件竿秆、資源解壓出來保存到自己的內(nèi)部私有目錄下只供自己使用。在manifest文件中增加配置xwalk_download_mode為enable稿壁,共享模式秒變下載模式幽钢。
<meta-data
android:name="xwalk_apk_url"
android:value="http://192.168.1.236:8080/video/so/XWalkRuntimeLib.apk" />
<meta-data
android:name="xwalk_download_mode"
android:value="enable" />
除了像共享模式那樣繼承XWalkActivity,下載模式下還可以使用XWalkInitializer傅是,這樣就可以讓Activity更靈活了匪燕,需要注意的是如果在布局中就使用了XWalkView則必須在setContentView()方法之前就要新建mXWalkInitializer對象,如果不這樣做會拋錯落午,原因是setContentView()加載布局時會初始化XWalkView谎懦,在XWalkView的構(gòu)造方法中會反射加載初始化相關(guān)類,可是還沒有下載相關(guān)類啊溃斋,只有拋錯了界拦。而XWalkInitializer的構(gòu)造方法中會調(diào)用preInit(),這個方法就是告訴加載框架我知道還沒有下載相關(guān)的類梗劫,我先在你這里注冊下占個坑享甸,如果下載初始化好了就通知我繼續(xù)初始化XWalkView,共享模式中事先也沒有相關(guān)的類怎么沒這樣的限制呢梳侨?其實在父類XWalkActivity的onCreate()方法中就幫忙注冊了蛉威。下面是使用XWalkInitializer的代碼:
public class MainActivity extends AppCompatActivity implements XWalkInitializer.XWalkInitListener,
XWalkUpdater.XWalkBackgroundUpdateListener {
private static final String TAG = "SplashActivity";
XWalkInitializer mXWalkInitializer;
XWalkUpdater mXWalkUpdater;
XWalkView mXWalkView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mXWalkInitializer = new XWalkInitializer(this, this);
mXWalkInitializer.initAsync();
setContentView(R.layout.activity_splash);
}
@Override
public void onXWalkInitStarted() {
}
@Override
public void onXWalkInitCancelled() {
finish();
}
@Override
public void onXWalkInitFailed() {
// Initialization failed. Trigger the Crosswalk runtime download
if (mXWalkUpdater == null) {
mXWalkUpdater = new XWalkUpdater(this, this);
}
mXWalkUpdater.updateXWalkRuntime();
}
@Override
public void onXWalkInitCompleted() {
// Initialization successfully, ready to invoke any XWalk embedded API
Log.d(TAG,"onXWalkInitCompleted");
mXWalkView = findViewById(R.id.xwalk);
mXWalkView.loadUrl("https://www.baidu.com");
}
@Override
public void onXWalkUpdateStarted() {
}
@Override
public void onXWalkUpdateProgress(int percentage) {
Log.d(TAG, "XWalkUpdate progress: " + percentage);
}
@Override
public void onXWalkUpdateCancelled() {
finish();
}
@Override
public void onXWalkUpdateFailed() {
finish();
}
@Override
public void onXWalkUpdateCompleted() {
// Crosswalk Runtime update finished, re-init again.
mXWalkInitializer.initAsync();
}
}
Crosswalk加載代碼淺析
Crosswalk的全部加載代碼在其Github項目地址的runtime/android/core/路徑下,如圖所示
代碼不多,XWalkActivity暴露出來用于繼承走哺;XWalkActivityDelegate聚合在XWalkActivity中蚯嫌,將加載的邏輯和Activity的生命周期結(jié)合起來;XWalkCoreWrapper負(fù)責(zé)加載初始化so文件;官網(wǎng)還提供壓縮版的APK择示,XWalkDecompressor負(fù)責(zé)解壓壓縮的so文件束凑、資源;XWalkDialogManager在共享模式下為用戶提供提示栅盲、選擇彈窗汪诉;XWalkEnvironment提供運行環(huán)境信息,比如cpuArch谈秫、manifest中配置的參數(shù)扒寄;XWalkLibraryLoader是最主要的類,管理協(xié)調(diào)整個加載過程拟烫。而實際加載so文件该编、調(diào)用native方法的類在/runtime/android/core_internal/目錄下,這也就是上文提到XWalk初始化相關(guān)類构灸,這些類與so文件上渴、資源一起出現(xiàn)岸梨,比如為內(nèi)嵌模式提供的jar喜颁、aar中有internal包,而為共享曹阔、下載模式提供的包中就沒有半开。
內(nèi)嵌模式
在XWalkView的構(gòu)造方法中會調(diào)用reflectionInit()方法,顧名思義也就是要去使用反射初始化類了赃份,方法中調(diào)用XWalkCoreWrapper.initEmbeddedMode(),這個方法中會新建一個XWalkCoreWrapper對象寂拆,進而調(diào)用實例的findEmbeddedCore(),其中分別調(diào)用checkCoreVersion()抓韩、checkCoreArchitecture()確認(rèn)內(nèi)核的版本纠永、so文件是否與cpuArch匹配,在checkCoreVersion()方法中調(diào)用getBridgeClass("XWalkCoreVersion")去反射加org.xwalk.core.internal.XWalkCoreVersion,前面提到了internal包與內(nèi)核同時出現(xiàn)谒拴,如果加載失敗了就證明“XWalk core not found”報錯尝江。XWalkCoreVersion保存的是內(nèi)核版本信息、XWalkAppVersion保存的是集成的aar或jar包的版本信息英上,在內(nèi)嵌模式中這兩個沒區(qū)別的炭序。在checkCoreArchitecture()方法中加載org.xwalk.core.internal.XWalkViewDelegate的loadXWalkLibrary()方法,這個方法中才會去真正的加載so文件苍日,由于是內(nèi)嵌模式so文件的位置由系統(tǒng)決定惭聂,放在了data/app/package name/lib/目錄下,直接調(diào)用的是 System.loadLibrary(lib)加載相恃,最后加載成功后在XWalkView中反射初始化XWalkViewBridge以及確立XWalkView中的方法與XWalkViewBridge反射方法對應(yīng)關(guān)系辜纲,比如XWalkView.loadUrl()對應(yīng)的XWalkViewBridge中的loadUrlSuper()方法,完畢后就可以愉快的使用了。
public XWalkView(Context context, AttributeSet attrs) {
super(context, attrs);
......
this.reflectionInit();
}
void reflectionInit() {
XWalkCoreWrapper.initEmbeddedMode();
}
//XWalkCoreWrapper中
public static void initEmbeddedMode() {
if (sInstance != null || !sReservedActivities.isEmpty()) return;
Log.d(TAG, "Init embedded mode");
XWalkCoreWrapper provisionalInstance = new XWalkCoreWrapper(null, -1);
//查找so文件
if (!provisionalInstance.findEmbeddedCore()) {
throw new RuntimeException(
"Please have your activity extend XWalkActivity for shared mode");
}
sInstance = provisionalInstance;
sInstance.initCoreBridge();
}
private boolean findEmbeddedCore() {
mBridgeContext = null;
mBridgeLoader = XWalkCoreWrapper.class.getClassLoader();
//分別確認(rèn)版本和so文件的abi
if (!checkCoreVersion() || !checkCoreArchitecture()) {
mBridgeLoader = null;
return false;
}
Log.d(TAG, "Running in embedded mode");
mCoreStatus = XWalkLibraryInterface.STATUS_MATCH;
return true;
}
private boolean checkCoreVersion() {
Log.d(TAG, "[Environment] SDK:" + Build.VERSION.SDK_INT);
Log.d(TAG, "[App Version] build:" + XWalkAppVersion.XWALK_BUILD_VERSION
+ ", api:" + mApiVersion + ", min_api:" + mMinApiVersion);
try {
Class<?> clazz = getBridgeClass("XWalkCoreVersion");
String buildVersion = "";
try {
buildVersion = (String) new ReflectField(clazz, "XWALK_BUILD_VERSION").get();
} catch (RuntimeException e) {
}
int libVersion = (int) new ReflectField(clazz, "API_VERSION").get();
int minLibVersion = (int) new ReflectField(clazz, "MIN_API_VERSION").get();
Log.d(TAG, "[Lib Version] build:" + buildVersion
+ ", api:" + libVersion + ", min_api:" + minLibVersion);
if (XWalkEnvironment.isDownloadMode() && XWalkEnvironment.isDownloadModeUpdate()
&& !buildVersion.isEmpty()
&& !buildVersion.equals(XWalkAppVersion.XWALK_BUILD_VERSION)) {
mCoreStatus = XWalkLibraryInterface.STATUS_RUNTIME_MISMATCH;
return false;
}
if (mMinApiVersion > libVersion) {
mCoreStatus = XWalkLibraryInterface.STATUS_OLDER_VERSION;
return false;
} else if (mApiVersion < minLibVersion) {
mCoreStatus = XWalkLibraryInterface.STATUS_NEWER_VERSION;
return false;
}
} catch (RuntimeException e) {
Log.d(TAG, "XWalk core not found");
mCoreStatus = XWalkLibraryInterface.STATUS_NOT_FOUND;
return false;
}
Log.d(TAG, "XWalk core version matched");
return true;
}
private boolean checkCoreArchitecture() {
try {
Class<?> clazz = getBridgeClass("XWalkViewDelegate");
ReflectMethod method = new ReflectMethod(clazz, "loadXWalkLibrary",
Context.class, String.class);
boolean architectureMatched = false;
String libDir = null;
if (mBridgeContext != null) {
.......
} else {//下載模式mBridgeContext為null
try {
architectureMatched = (boolean) method.invoke(mBridgeContext, libDir);
} catch (RuntimeException ex) {
Log.d(TAG, ex.getLocalizedMessage());
}
if (!architectureMatched && mWrapperContext != null) {
libDir = XWalkEnvironment.getPrivateDataDir();
architectureMatched = (boolean) method.invoke(mBridgeContext, libDir);
}
}
if (!architectureMatched) {
Log.d(TAG, "Mismatch of CPU architecture");
mCoreStatus = XWalkLibraryInterface.STATUS_ARCHITECTURE_MISMATCH;
return false;
}
} catch (RuntimeException e) {
Log.d(TAG, e.getLocalizedMessage());
if (e.getCause() instanceof UnsatisfiedLinkError) {
mCoreStatus = XWalkLibraryInterface.STATUS_ARCHITECTURE_MISMATCH;
return false;
}
mCoreStatus = XWalkLibraryInterface.STATUS_INCOMPLETE_LIBRARY;
return false;
}
Log.d(TAG, "XWalk core architecture matched");
return true;
}
內(nèi)嵌模式的加載就這些耕腾,一句話:在第一次新建XWalkView對象時就去通過反射加載so文件屋摇。同一個包中為什么不直接調(diào)用,而用反射去加載幽邓?這就是為了和接下來要說的共享炮温、下載模式統(tǒng)一。
共享模式
1牵舵、跳轉(zhuǎn)Google Play 下載
共享模式需要繼承XWalkActivity柒啤,在onCreate()方法中會新建XWalkActivityDelegate對象
,加載的起端就在XWalkActivityDelegate的構(gòu)造方法中傻谁,它會調(diào)用XWalkLibraryLoader.prepareToInit(mActivity)钳榨,這就是前面說的這個預(yù)先初始化,過程如下:
public abstract class XWalkActivity extends Activity {
private XWalkActivityDelegate mActivityDelegate;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mActivityDelegate = new XWalkActivityDelegate(this, cancelCommand, completeCommand);
}
}
//XWalkActivityDelegate的構(gòu)造方法
public XWalkActivityDelegate(Activity activity,
Runnable cancelCommand, Runnable completeCommand) {
mActivity = activity;
XWalkLibraryLoader.prepareToInit(mActivity);
}
//XWalkLibraryLoader的prepareToInit方法
public static void prepareToInit(Context context) {
XWalkEnvironment.init(context);
XWalkCoreWrapper.handlePreInit(context.getClass().getName());
}
//XWalkCoreWrapper中handlePreInit方法换棚,將反射方法保存到sReservedActions集合没炒,類名保存到sReservedActivities集合
public static void handlePreInit(String tag) {
if (sInstance != null) return;
Log.d(TAG, "Pre init xwalk core in " + tag);
if (sReservedActions.containsKey(tag)) {
sReservedActions.remove(tag);
} else {
sReservedActivities.add(tag);
}
sReservedActions.put(tag, new LinkedList<ReservedAction>());
}
//sReservedActions是一個map涛癌,鍵:類名、值:ReservedAction集合(之后需要執(zhí)行的初始化方法的反射)
private static HashMap<String, LinkedList<ReservedAction> > sReservedActions =
new HashMap<String, LinkedList<ReservedAction> >();
//sReservedActivities 保存類名
private static LinkedList<String> sReservedActivities = new LinkedList<String>();
這樣preInit添加到sReservedActions后送火,setContentView中去初始化XWalkView時就不會報錯了拳话,為什么不報錯了?看下面初始化XWalkView的具體過程:
public XWalkView(Context context, AttributeSet attrs) {
super(context, attrs);
......
this.reflectionInit();
}
//XWalkView的構(gòu)造方法會調(diào)用reflectionInit方法种吸,這時雖然調(diào)用了
initEmbeddedMode弃衍,但sReservedActivities不為空直接返回了;雖然調(diào)用了
若干XWalkCoreWrapper靜態(tài)方法坚俗,但是設(shè)計的原則是開始初始化才會新建私有的
XWalkCoreWrapper對象镜盯、初始化完才會暴露這個私有對象,這時并沒有初始化內(nèi)核
XWalkCoreWrapper.getInstance()為空猖败,
不會繼續(xù)初始化XWalkView了速缆,XWalkCoreWrapper.reserveReflectObject(this)注冊占坑,等待下載內(nèi)核恩闻。
void reflectionInit() {
XWalkCoreWrapper.initEmbeddedMode();
this.coreWrapper = XWalkCoreWrapper.getInstance();
if (this.coreWrapper == null) {
//添加到sReservedActions中
XWalkCoreWrapper.reserveReflectObject(this);
} else {
......
}
}
//XWalkCoreWrapper中initEmbeddedMode方法
public static void initEmbeddedMode() {
if (sInstance != null || !sReservedActivities.isEmpty()) return;
}
//XWalkCoreWrapper中reserveReflectObject從sReservedActivities拿最近的
tag艺糜,為什么取最近的呢?是因為默認(rèn)繼承XWalkActivity的Activity中的onCreate方法
中調(diào)用了super()之后立即就setContentView了判呕,線程執(zhí)行的最小粒度就是方法了倦踢,
不可能在這時會有同一線程的其他方法向sReservedActivities添加tag。之后拿到tag后向sReservedActions添加反射類侠草,用于加載完內(nèi)核后執(zhí)行
public static void reserveReflectObject(Object object) {
String tag = (String)sReservedActivities.getLast();
Log.d("XWalkLib", "Reserve object " + object.getClass() + " to " + tag);
((LinkedList)sReservedActions.get(tag)).add(new XWalkCoreWrapper.ReservedAction(object));
}
上面實際上是初始化XWalkView未遂的過程辱挥,雖然XWalkView對象有了,但它只是一個沒有靈魂的殼,不能立即使用边涕,還得等待下載內(nèi)核晤碘,下載是在XWalkActivityDelegate的onResume()方法中開始的褂微,下面是XWalkActivityDelegate的onResume()方法:
//XWalkActivityDelegate的onResume()
public void onResume() {
if (mIsXWalkReady) return;
if (XWalkLibraryLoader.isInitializing() || XWalkLibraryLoader.isDownloading()) {
Log.d(TAG, "Other initialization or download is proceeding");
return;
}
Log.d(TAG, "Initialize by XWalkActivity");
XWalkLibraryLoader.startDecompress(this);
}
//XWalkLibraryLoader中
public static void startDecompress(DecompressListener listener) {
new DecompressTask(listener).execute();
}
DecompressTask之后會調(diào)用ActivateTask,內(nèi)核都沒下載這些都是徒勞园爷,最后會得一個
STATUS_NOT_FOUND的結(jié)論宠蚂,調(diào)用 mListener.onActivateFailed(),而這個mListener就是
XWalkActivityDelegate對象童社,所以看它的這個方法
@Override
public void onActivateFailed() {
if (mXWalkUpdater == null) {
if (XWalkEnvironment.isDownloadMode()) {//下載模式
mXWalkUpdater = new XWalkUpdater(
new XWalkBackgroundUpdateListener() {
@Override
public void onXWalkUpdateStarted() {
}
@Override
public void onXWalkUpdateProgress(int percentage) {
}
@Override
public void onXWalkUpdateCancelled() {
mCancelCommand.run();
}
@Override
public void onXWalkUpdateFailed() {
mCancelCommand.run();
}
@Override
public void onXWalkUpdateCompleted() {
XWalkLibraryLoader.startActivate(XWalkActivityDelegate.this);
}
},
mActivity);
} else {//共享模式
mXWalkUpdater = new XWalkUpdater(
new XWalkUpdateListener() {
@Override
public void onXWalkUpdateCancelled() {
mCancelCommand.run();
}
},
mActivity, mDialogManager);
}
}
if (mXWalkUpdater.updateXWalkRuntime() && !XWalkEnvironment.isDownloadMode()) {
求厕。。扰楼。呀癣。。弦赖。
}
}
}
這是共享模式项栏,新建XWalkUpdater對象調(diào)用它的updateXWalkRuntime()下載,共享模式會彈一個窗告訴用戶去下載蹬竖,ok后調(diào)用downloadXWalkApk()沼沈。下面是XWalkUpdater的updateXWalkRuntime()
public boolean updateXWalkRuntime() {
if (mUpdateListener != null) {
mDownloadCommand = new Runnable() {
@Override
public void run() {
downloadXWalkApk();
}
};
mCancelCommand = new Runnable() {
@Override
public void run() {
Log.d(TAG, "XWalkUpdater cancelled");
mUpdateListener.onXWalkUpdateCancelled();
}
};
mDialogManager.showInitializationError(status, mCancelCommand, mDownloadCommand);
} else if (mBackgroundUpdateListener != null) {
String url = XWalkEnvironment.getXWalkApkUrl();
XWalkLibraryLoader.startHttpDownload(new BackgroundListener(), mContext, url);
} else {
throw new IllegalArgumentException("Update listener is null");
}
return true;
}
下載APK,如果在meta-data中配置了下載地址就使用這個地址下載APK币厕,否則
開啟應(yīng)用市場下載,這里沒有配置下載URL列另,使用應(yīng)用市場下載,過程如下:
private void downloadXWalkApk() {
String url = XWalkEnvironment.getXWalkApkUrl();
if (!url.isEmpty()) {
XWalkLibraryLoader.startDownloadManager(new ForegroundListener(), mContext, url);
return;
}
//拼接intent,查看是否有應(yīng)用商店
String packageName = "org.xwalk.core";
final Intent intent = new Intent("android.intent.action.VIEW");
intent.setData(Uri.parse("market://details?id=" + packageName));
List<ResolveInfo> infos = mContext.getPackageManager().queryIntentActivities(
intent, PackageManager.MATCH_ALL);
StringBuilder supportedStores = new StringBuilder();
boolean hasGooglePlay = false;
......
//IA 是 Intel Architecture的縮寫,IA架構(gòu)的Android也就只有平板或者模擬器了
如果是IA架構(gòu)包名也得改
if (!hasGooglePlay && XWalkEnvironment.isIaDevice()) {
//64位的
if (XWalkEnvironment.is64bitApp()) {
packageName = "org.xwalk.core64.ia";
} else {//32位的
packageName = "org.xwalk.core.ia";
}
} else if (XWalkEnvironment.is64bitApp()) {//除此之外就是ARM架構(gòu)的了
packageName = "org.xwalk.core64";//64位的
} else {
packageName = "org.xwalk.core";//32位的
}
Log.d(TAG, "Package name of Crosswalk to download: " + packageName);
//之后跳轉(zhuǎn)下載google play下載安裝
......
}
這里判斷架構(gòu)劈榨、位數(shù)的代碼值得借鑒访递。分為兩個方法晦嵌,一個是getRuntimeAbi()
獲取運行時的ABI同辣,一個是getDeviceAbi() 獲取設(shè)備的ABI,為什么要分兩個惭载?
大概是因為有時候在64位的機器上會運行32位應(yīng)用旱函,防止去下載64位的APK,
具體實現(xiàn)如下描滔,細節(jié)不太懂
public static String getRuntimeAbi() {
if (sRuntimeAbi == null) {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
throw new NoSuchFieldError();
}
//5.0之前的手機直接獲取
String abi = Build.CPU_ABI.toLowerCase();
switch (abi) {
case "armeabi":
case "armeabi-v7a":
sRuntimeAbi = "armeabi-v7a";
break;
case "arm64-v8a":
sRuntimeAbi = "arm64-v8a";
break;
case "x86":
sRuntimeAbi = "x86";
break;
case "x86_64":
sRuntimeAbi = "x86_64";
break;
default:
throw new RuntimeException("Unexpected CPU_ABI: " + abi);
}
} catch (NoSuchFieldError e) {
//5.0之后的手機
//相當(dāng)于lunix cmd輸入uname -m
String arch = System.getProperty("os.arch").toLowerCase();
switch (arch) {
case "x86":
case "i686":
case "i386":
case "ia32":
sRuntimeAbi = "x86";
break;
case "x64":
case "x86_64":
if (is64bitDevice()) {
sRuntimeAbi = "x86_64";
} else {
sRuntimeAbi = "x86";
}
break;
case "armv7l":
case "armeabi":
case "armeabi-v7a":
sRuntimeAbi = "armeabi-v7a";
break;
case "aarch64":
case "armv8":
case "armv8l":
case "arm64":
if (is64bitDevice()) {
sRuntimeAbi = "arm64-v8a";
} else {
sRuntimeAbi = "armeabi-v7a";
}
break;
default:
throw new RuntimeException("Unexpected os.arch: " + arch);
}
}
// The value may be ARM on Houdini devices.
if (sRuntimeAbi.equals("armeabi-v7a")) {
if (isIaDevice()) {
sRuntimeAbi = "x86";
}
} else if (sRuntimeAbi.equals("arm64-v8a")) {
if (isIaDevice()) {
sRuntimeAbi = "x86_64";
}
}
Log.d(TAG, "Runtime ABI: " + sRuntimeAbi);
}
return sRuntimeAbi;
}
public static String getDeviceAbi() {
if (sDeviceAbi == null) {
try {
sDeviceAbi = Build.SUPPORTED_ABIS[0].toLowerCase();
} catch (NoSuchFieldError e) {
try {
Process process = Runtime.getRuntime().exec("getprop ro.product.cpu.abi");
InputStreamReader ir = new InputStreamReader(process.getInputStream());
BufferedReader input = new BufferedReader(ir);
sDeviceAbi = input.readLine().toLowerCase();
input.close();
ir.close();
} catch (IOException ex) {
throw new RuntimeException("Can not detect device's ABI");
}
}
Log.d(TAG, "Device ABI: " + sDeviceAbi);
}
return sDeviceAbi;
}
2棒妨、自定義下載鏈接
流程與跳轉(zhuǎn)商店下載是一樣的,只是到了downloadXWalkApk()方法時url不為空了含长,
XWalkLibraryLoader啟動DownloadManagerTask下載券腔,下載完后調(diào)用安裝,這時7.0以上手機
會因為文件權(quán)限問題崩潰
安裝成功后拘泞,手機上就會有一個包名為org.xwalk.core的應(yīng)用了纷纫,這個應(yīng)用沒有提供入口,桌面沒圖標(biāo)陪腌。這時重新打開自己的應(yīng)用還是會走上面的流程辱魁,只是到了ActiveTask時會執(zhí)行XWalkCoreWrapper.attachXWalkCore()烟瞧,過程如下:
public static int attachXWalkCore() {
//判斷不是內(nèi)嵌模式、不是下載模式染簇,那就是共享模式了
......
sProvisionalInstance = new XWalkCoreWrapper(XWalkEnvironment.getApplicationContext(), 1);
if (XWalkEnvironment.is64bitDevice()) {
if (!sProvisionalInstance.findSharedCore("org.xwalk.core") && !sProvisionalInstance.findSharedCore("org.xwalk.core64") && XWalkEnvironment.isIaDevice()) {
sProvisionalInstance.findSharedCore("org.xwalk.core64.ia");
}
} else if (!sProvisionalInstance.findSharedCore("org.xwalk.core") && XWalkEnvironment.isIaDevice()) {
sProvisionalInstance.findSharedCore("org.xwalk.core.ia");
}
return sProvisionalInstance.mCoreStatus;
}
//findSharedCore查找包名為org.xwalk.core的應(yīng)用
private boolean findSharedCore(String packageName) {
if (!checkCorePackage(packageName)) return false;
//賦值mBridgeLoader
mBridgeLoader = mBridgeContext.getClassLoader();
if (!checkCoreVersion() || !checkCoreArchitecture()) {
mBridgeContext = null;
mBridgeLoader = null;
return false;
}
Log.d(TAG, "Running in shared mode");
mCoreStatus = XWalkLibraryInterface.STATUS_MATCH;
return true;
}
private boolean checkCorePackage(String packageName) {
//如果在manifest中配置要驗證APK
if (XWalkAppVersion.VERIFY_XWALK_APK) {
try {
PackageInfo packageInfo = mWrapperContext.getPackageManager().getPackageInfo(
packageName, PackageManager.GET_SIGNATURES);
if (!verifyPackageInfo(packageInfo,
XWalkAppVersion.XWALK_APK_HASH_ALGORITHM,
XWalkAppVersion.XWALK_APK_HASH_CODE)) {
Log.d(TAG, packageName + " signature verification failed");
mCoreStatus = XWalkLibraryInterface.STATUS_SIGNATURE_CHECK_ERROR;
return false;
}
} catch (NameNotFoundException e) {
Log.d(TAG, packageName + " not found");
return false;
}
}
//加載這個包的Conetxt并賦值給全局變量mBridgeContext
try {
mBridgeContext = mWrapperContext.createPackageContext(packageName,
Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
} catch (NameNotFoundException e) {
Log.d(TAG, packageName + " not found");
return false;
}
Log.d(TAG, "Created package context for " + packageName);
return true;
}
之后調(diào)用checkCoreVersion()参滴、checkCoreArchitecture()確認(rèn)版本、加載so文件锻弓,完畢后回到ActiveTask砾赔,激活成功了調(diào)用XWalkLibraryLoader.finishInit(this.mActivity)
,這里就是在填剛才留下的坑青灼,這個方法會再次調(diào)用XWalkView的reflectionInit()重新init一次过蹂,這時就會走else的流程了,建立方法對應(yīng)關(guān)系聚至。
public static void finishInit(Context context) {
XWalkCoreWrapper.handlePostInit(context.getClass().getName());
}
//XWalkCoreWrapper中
public static void handlePostInit(String tag) {
Log.d(TAG, "Post init xwalk core in " + tag);
if (!sReservedActions.containsKey(tag)) {
return;
}
LinkedList<ReservedAction> reservedActions = sReservedActions.get(tag);
for (ReservedAction action : reservedActions) {
if (action.mObject != null) {
Log.d(TAG, "Init reserved object: " + action.mObject.getClass());
new ReflectMethod(action.mObject, "reflectionInit").invoke();
}
...
action.mMethod.invoke(args);
}
}
sReservedActions.remove(tag);
sReservedActivities.remove(tag);
}
看了這個流程大概明白了為什么代碼要混淆了酷勺,如果不混淆在知道詳細代碼的情況下就可以反射調(diào)用代碼。
下載模式
1扳躬、繼承XWalkActivity
這種方式與共享模式是一樣的脆诉,onActivateFailed()方法中會判斷是否是下載模式,依據(jù)就是manifest文件中xwalk_download_mode=enable贷币,是下載模式就不彈窗了直下載击胜,最后調(diào)用HttpDownloadTask將APK下載到內(nèi)部私有目錄xwalk_download下,下載完畢后把APK中的classes.dex役纹、資源使用XWalkDecompressor對象解壓到內(nèi)部私有目錄extracted_xwalkcore下,解壓完畢繼續(xù)走ActiveTask的流程,和上面一樣了,只是ClassLoader不通過context創(chuàng)建,而是新建DexClassLoader
private boolean findDownloadedCore() {
String libDir = XWalkEnvironment.getExtractedCoreDir();
String dexPath = libDir + File.separator + "classes.dex";
String dexOutputPath = XWalkEnvironment.getOptimizedDexDir();
ClassLoader localClassLoader = ClassLoader.getSystemClassLoader();
this.mBridgeLoader = new DexClassLoader(dexPath, dexOutputPath, libDir, localClassLoader);
if (this.checkCoreVersion() && this.checkCoreArchitecture()) {
Log.d("XWalkLib", "Running in downloaded mode");
this.mCoreStatus = 1;
return true;
} else {
this.mBridgeLoader = null;
return false;
}
}
2偶摔、使用XWalkInitializer
XWalkInitializer與XWalkActivityDelegate的作用是一樣的,只是后者把所有的邏輯
都封裝了,并與Activity生命周期綁定,而XWalkInitializer由于不與Activity綁定,需要自己去寫XWalkUpdater以及一些回調(diào).
Crosswalk的學(xué)習(xí)筆記完,如有不正確的地方促脉,歡迎多多指教辰斋!