Crosswalk 学习笔记

2018-12-30  本文已影响136人  msisuzney

简介

CrossWalk是一款为HTML应用提供运行时环境的开源项目,从Android开发的角度讲Crosswalk可以用来替代WebView显示网页,在应用重度依赖网页时可以使用Crosswalk解决设备碎片化问题。直接将Crosswalk的库包导入到项目中即可像使用WebView一样使用,这种模式在Crosswalk项目中叫内嵌模式,但这样有一个问题由于Crosswalk库包太大集成后的应用体积太大,为此Crosswalk为Android设备提供了另外两种使用方式,共享模式下载模式。虽然Crosswalk项目
已经在17年停止维护,但它这种动态加载使用的方式仍值得我学习。本文将介绍Crosswalk的三种加载方式。

使用

内嵌模式

Crosswalk在它的下载地址提供了很多资源,使用内嵌模式只需要下载对应的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文件,造成在运行时没有对应的crosswalk包。

android {
defaultConfig {
    ndk {
        abiFilters 'armeabi-v7a','x86'
    }
}
}

以上是内嵌模式下的一种集成方式。由于官方只提供以上2种aar,如果只想集成arm平台下的so文件或者一定要不同的平台加载对应的so文件,那只有自己新建Android library module了,到官网下载crosswalk.xx.zip和crosswalk.xx-64bit.zip,解压后如图把xwalk_core_library\libs目录下的对应so文件复制到新建的module的jniLibs下、把xwalk_core_library.jar复制到libs下,最后把xwalk_core_library\res目录下所有资源都复制到module中


xwalk_core_library\libs目录下 复制到module后

由于module要对外提供xwalk_core_library.jar中的api所以要将build.gradle中依赖方式改为api

dependencies {
    api fileTree(dir: 'libs', include: ['*.jar'])
}

两种内嵌集成方式都可行,集成后就可以使用crosswalk了,不做任何额外操作的情况下默认是内嵌模式。在XWalkView的构造方法中会加载so文件,之后就可以使用了,和使用WebView一样XWalkUIClient对应WebView的WebChromeClient,XWalkResourceClient对应WebViewClient

共享模式

顾名思义共享模式就是和其他应用共用一个XWalkRuntime,XWalkRuntimeLib并没有被内嵌到自己的应用中,而是当应用要用到XWalkView时去加载包名为org.xwalk.core的应用的代码,如果没有安装就去下载XWalkRuntimeLib.APK安装,官方提供了两种下载APK的方式,一种是跳转Google Play Store下载,一种是在manifest文件中自定义下载链接的meta-data,到自己指定的地址去下载APK。第一种方式不可行,在大部分手机中market://details?id=xxx 这种URI跳转的是自家的应用商店,并没有XWalkRuntimeLib.APK。由于Crosswalk项目已经停止更新了,
第二种方式会因为文件权限问题在Android 7.0之后的手机上抛错。虽然这种模式基本被废了,但这种方式与下载模式大同小异,也值得分析下。

需要在官网下载crosswalk-shared-xx.aar并将其导入到项目,让Activity继承XWalkActivity并复写它的onXWalkReady()方法,在这个方法中使用XWalkView加载网页。继承了XWalkActivity的情况下,如果不做任何配置默认的是共享模式并且是第一种方式:跳转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,之后在官网下载4个平台的APK并部署到自己的服务器上,在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,服务器根据不同值返回相应的APK就OK了。

下载模式

和共享模式差不多,只是共享模式是把APK下载下来当成一个应用安装到手机上,而下载模式干脆把APK下载到自己的私有目录下,把所有的so文件、资源解压出来保存到自己的内部私有目录下只供自己使用。在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的构造方法中会反射加载初始化相关类,可是还没有下载相关类啊,只有抛错了。而XWalkInitializer的构造方法中会调用preInit(),这个方法就是告诉加载框架我知道还没有下载相关的类,我先在你这里注册下占个坑,如果下载初始化好了就通知我继续初始化XWalkView,共享模式中事先也没有相关的类怎么没这样的限制呢?其实在父类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的生命周期结合起来;XWalkCoreWrapper负责加载初始化so文件;官网还提供压缩版的APK,XWalkDecompressor负责解压压缩的so文件、资源;XWalkDialogManager在共享模式下为用户提供提示、选择弹窗;XWalkEnvironment提供运行环境信息,比如cpuArch、manifest中配置的参数;XWalkLibraryLoader是最主要的类,管理协调整个加载过程。而实际加载so文件、调用native方法的类在/runtime/android/core_internal/目录下,这也就是上文提到XWalk初始化相关类,这些类与so文件、资源一起出现,比如为内嵌模式提供的jar、aar中有internal包,而为共享、下载模式提供的包中就没有。

内嵌模式

在XWalkView的构造方法中会调用reflectionInit()方法,顾名思义也就是要去使用反射初始化类了,方法中调用XWalkCoreWrapper.initEmbeddedMode(),这个方法中会新建一个XWalkCoreWrapper对象,进而调用实例的findEmbeddedCore(),其中分别调用checkCoreVersion()、checkCoreArchitecture()确认内核的版本、so文件是否与cpuArch匹配,在checkCoreVersion()方法中调用getBridgeClass("XWalkCoreVersion")去反射加org.xwalk.core.internal.XWalkCoreVersion,前面提到了internal包与内核同时出现,如果加载失败了就证明“XWalk core not found”报错。XWalkCoreVersion保存的是内核版本信息、XWalkAppVersion保存的是集成的aar或jar包的版本信息,在内嵌模式中这两个没区别的。在checkCoreArchitecture()方法中加载org.xwalk.core.internal.XWalkViewDelegate的loadXWalkLibrary()方法,这个方法中才会去真正的加载so文件,由于是内嵌模式so文件的位置由系统决定,放在了data/app/package name/lib/目录下,直接调用的是 System.loadLibrary(lib)加载,最后加载成功后在XWalkView中反射初始化XWalkViewBridge以及确立XWalkView中的方法与XWalkViewBridge反射方法对应关系,比如XWalkView.loadUrl()对应的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();
        //分别确认版本和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;
    }

内嵌模式的加载就这些,一句话:在第一次新建XWalkView对象时就去通过反射加载so文件。同一个包中为什么不直接调用,而用反射去加载?这就是为了和接下来要说的共享、下载模式统一。

共享模式

1、跳转Google Play 下载

共享模式需要继承XWalkActivity,在onCreate()方法中会新建XWalkActivityDelegate对象
,加载的起端就在XWalkActivityDelegate的构造方法中,它会调用XWalkLibraryLoader.prepareToInit(mActivity),这就是前面说的这个预先初始化,过程如下:

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的构造方法
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集合(之后需要执行的初始化方法的反射)
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的构造方法会调用reflectionInit方法,这时虽然调用了
initEmbeddedMode,但sReservedActivities不为空直接返回了;虽然调用了
若干XWalkCoreWrapper静态方法,但是设计的原则是开始初始化才会新建私有的
XWalkCoreWrapper对象、初始化完才会暴露这个私有对象,这时并没有初始化内核
XWalkCoreWrapper.getInstance()为空,
不会继续初始化XWalkView了,XWalkCoreWrapper.reserveReflectObject(this)注册占坑,等待下载内核。
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,为什么取最近的呢?是因为默认继承XWalkActivity的Activity中的onCreate方法
中调用了super()之后立即就setContentView了,线程执行的最小粒度就是方法了,
不可能在这时会有同一线程的其他方法向sReservedActivities添加tag。之后拿到tag后向sReservedActions添加反射类,用于加载完内核后执行
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对象有了,但它只是一个没有灵魂的壳,不能立即使用,还得等待下载内核,下载是在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之后会调用ActivateTask,内核都没下载这些都是徒劳,最后会得一个
STATUS_NOT_FOUND的结论,调用 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对象调用它的updateXWalkRuntime()下载,共享模式会弹一个窗告诉用户去下载,ok后调用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,否则
开启应用市场下载,这里没有配置下载URL,使用应用市场下载,过程如下:

private void downloadXWalkApk() {
    String url = XWalkEnvironment.getXWalkApkUrl();
    if (!url.isEmpty()) {
        XWalkLibraryLoader.startDownloadManager(new ForegroundListener(), mContext, url);
        return;
    }
    //拼接intent,查看是否有应用商店
    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架构的Android也就只有平板或者模拟器了
    如果是IA架构包名也得改
    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架构的了
        packageName = "org.xwalk.core64";//64位的
    } else {
        packageName = "org.xwalk.core";//32位的
    }
    Log.d(TAG, "Package name of Crosswalk to download: " + packageName);

    //之后跳转下载google play下载安装
    ......
}

这里判断架构、位数的代码值得借鉴。分为两个方法,一个是getRuntimeAbi()
获取运行时的ABI,一个是getDeviceAbi() 获取设备的ABI,为什么要分两个?
大概是因为有时候在64位的机器上会运行32位应用,防止去下载64位的APK,
具体实现如下,细节不太懂

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之后的手机
          //相当于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、自定义下载链接

流程与跳转商店下载是一样的,只是到了downloadXWalkApk()方法时url不为空了,
XWalkLibraryLoader启动DownloadManagerTask下载,下载完后调用安装,这时7.0以上手机
会因为文件权限问题崩溃

安装成功后,手机上就会有一个包名为org.xwalk.core的应用了,这个应用没有提供入口,桌面没图标。这时重新打开自己的应用还是会走上面的流程,只是到了ActiveTask时会执行XWalkCoreWrapper.attachXWalkCore(),过程如下:

public static int attachXWalkCore() {
    //判断不是内嵌模式、不是下载模式,那就是共享模式了
    ......
    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的应用
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;
}

之后调用checkCoreVersion()、checkCoreArchitecture()确认版本、加载so文件,完毕后回到ActiveTask,激活成功了调用XWalkLibraryLoader.finishInit(this.mActivity)
,这里就是在填刚才留下的坑,这个方法会再次调用XWalkView的reflectionInit()重新init一次,这时就会走else的流程了,建立方法对应关系。

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);
}

看了这个流程大概明白了为什么代码要混淆了,如果不混淆在知道详细代码的情况下就可以反射调用代码。

下载模式

1、继承XWalkActivity

这种方式与共享模式是一样的,onActivateFailed()方法中会判断是否是下载模式,依据就是manifest文件中xwalk_download_mode=enable,是下载模式就不弹窗了直下载,最后调用HttpDownloadTask将APK下载到内部私有目录xwalk_download下,下载完毕后把APK中的classes.dex、资源使用XWalkDecompressor对象解压到内部私有目录extracted_xwalkcore下,解压完毕继续走ActiveTask的流程,和上面一样了,只是ClassLoader不通过context创建,而是新建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;
        }
    }
解压后的extracted_xwalkcore
2、使用XWalkInitializer

XWalkInitializer与XWalkActivityDelegate的作用是一样的,只是后者把所有的逻辑
都封装了,并与Activity生命周期绑定,而XWalkInitializer由于不与Activity绑定,需要自己去写XWalkUpdater以及一些回调.

Crosswalk的学习笔记完,如有不正确的地方,欢迎多多指教!

上一篇下一篇

猜你喜欢

热点阅读