美文网首页
Crosswalk 学习笔记

Crosswalk 学习笔记

作者: msisuzney | 来源:发表于2018-12-30 19:49 被阅读136次

    简介

    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的学习笔记完,如有不正确的地方,欢迎多多指教!

    相关文章

      网友评论

          本文标题:Crosswalk 学习笔记

          本文链接:https://www.haomeiwen.com/subject/mxnvlqtx.html