Weex基于Android Studio从开发环境搭建到运行

Weex之路——引路篇

Weex的迭代目前来说已经经过了很多版本,官方的文档已经过时,而且文档本身写的也很简陋,按官方文档很难把APP跑起来。经过多次趟坑后,决定把自己的经历写下来,给当前准备着手Weex开发的人一点参考:

  • Android Studio版本 3.1
  • Node.js版本 v8.9.3
  • Npm版本 5.5.1
  • JDK版本 1.7
  • Android SDK Compile Sdk Version 23
  • Android SDK Build Tools Version 25.0.0
  • Gradle version 4.1
  • Android Plugin version 2.3.3

环境安装及配置

  • JDK、Android SDK的安装及环境变量配置不赘述
  • Node.js安装:
    安装完成后检查
    Weex基于Android Studio从开发环境搭建到运行_第1张图片
  • 安装 weex-toolkit。
npm install weex-toolkit -g

创建Weex项目

找一个目录作为Weex项目的工作空间(workspace),在命令行执行如下代码,创建weex项目:

weex create awesome-app

项目名就是awesome-app
Weex基于Android Studio从开发环境搭建到运行_第2张图片

如果有小白不知道命令行执行是什么意思的,看下图:
Weex基于Android Studio从开发环境搭建到运行_第3张图片

运行Weex项目

在命令行执行:

cd awesome-app
npm install
npm start

上面三个命令的意思分别是:进入awesome-app目录,安装依赖,启动。
所以后续需要运行weex工程,只需要在awesome-app目录下的命令行模式,运行npm start就能启动项目。
如图,我对代码做了一点修改
Weex基于Android Studio从开发环境搭建到运行_第4张图片
启动完成后会自动在浏览器中打开index界面,效果如下
Weex基于Android Studio从开发环境搭建到运行_第5张图片

至此,weex项目在web环境的搭建及运行就完成了。

下面开始Android开发所需的配置。

创建Android项目

怎么将Weex最终做出APP能运行起来最主要的工作就是这里了
Step 1
在Android Studio中新建一个空白的Android项目,配置包名。我的应用包名是:cn.com.acoe.weexapp.app
Step2
配置build.gradle
apply plugin: 'com.android.application'

android {
    compileSdkVersion 23
    defaultConfig {
        applicationId "cn.com.acoe.weexapp.app"
        minSdkVersion 15
        targetSdkVersion 19
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    buildToolsVersion '25.0.0'
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_7
        targetCompatibility JavaVersion.VERSION_1_7
    }
    productFlavors {
    }
}

dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    compile 'com.android.support:appcompat-v7:23.1.1'
    compile 'com.android.support:recyclerview-v7:23.1.1'
    compile 'com.alibaba:fastjson:1.1.46.android'
    compile 'com.taobao.android:weex_sdk:0.18.0@aar'

    compile 'com.squareup.okhttp:okhttp:2.3.0'
    compile 'com.squareup.okhttp:okhttp-ws:2.3.0'
    compile 'com.squareup.okio:okio:1.0.1'
    compile 'com.github.bumptech.glide:glide:3.7.0'


}

因为示例中有图片的显示,所以除了官方文档给出的例子中依赖的几个包以外,我还引入了glide用于图片加载。okhttp是用来做网络请求的,所以我也引入了相关依赖。

Step 3
创建图片加载的Adapter
package com.app.weexapp.util;

import android.widget.ImageView;

import com.bumptech.glide.Glide;
import com.taobao.weex.adapter.IWXImgLoaderAdapter;
import com.taobao.weex.common.WXImageStrategy;
import com.taobao.weex.dom.WXImageQuality;

public class ImageAdapter implements IWXImgLoaderAdapter {
    @Override
    public void setImage(String url, ImageView imageView, WXImageQuality wxImageQuality, WXImageStrategy wxImageStrategy) {
        Glide.with(imageView.getContext()).load(url).into(imageView);
    }
}
Step 4
创建Application,每个Android项目都少不了的步骤。
package com.app.weexapp.app;

import android.app.Application;
import android.util.Log;

import com.app.weexapp.util.ImageAdapter;
import com.taobao.weex.InitConfig;
import com.taobao.weex.WXSDKEngine;

/**
 * Application
 * Created by Acoe on 2018/4/4.
 */

public class WeexApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        InitConfig.Builder builder = new InitConfig.Builder();
        builder.setImgAdapter(new ImageAdapter());
        InitConfig config = builder.build();
        WXSDKEngine.initialize(this, config);
        Log.i("Application", "WXSDKEngine.isInitialized: " + WXSDKEngine.isInitialized());
    }
}
Step 5
创建Activity,来显示weex项目中编写的界面
package com.app.weexapp.ui;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.FrameLayout;

import com.taobao.weex.IWXRenderListener;
import com.taobao.weex.WXSDKEngine;
import com.taobao.weex.WXSDKInstance;
import com.taobao.weex.common.WXRenderStrategy;
import com.taobao.weex.utils.WXFileUtils;

import cn.com.acoe.weexapp.app.R;


/**
 * Created by Acoe on 2018/4/4.
 */

public class MainActivity extends AppCompatActivity {
    private WXSDKInstance mWXSDKInstance;
    private FrameLayout mContainer;
    private Handler mHandler;


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mContainer = (FrameLayout) findViewById(R.id.container);

        mWXSDKInstance = new WXSDKInstance(this);
        mWXSDKInstance.onActivityCreate();
        mWXSDKInstance.registerRenderListener(new IWXRenderListener() {
            @Override
            public void onViewCreated(WXSDKInstance wxsdkInstance, View view) {
                if (mContainer != null) {
                    mContainer.addView(view);
                }
            }

            @Override
            public void onRenderSuccess(WXSDKInstance wxsdkInstance, int i, int i1) {
            }

            @Override
            public void onRefreshSuccess(WXSDKInstance wxsdkInstance, int i, int i1) {
            }

            @Override
            public void onException(WXSDKInstance wxsdkInstance, String s, String s1) {
            }
        });


        // 在渲染前先要保证WXSDKEngine已经完成了初始化
        if (WXSDKEngine.isInitialized()) {
          startRender();
        } else {
            mHandler = new Handler() {
                @Override
                public void handleMessage(Message msg) {
                    if (msg.what == 0) {
                        Log.i("Main", "WXSDKEngine.isInitialized: " + WXSDKEngine.isInitialized());
                        if (WXSDKEngine.isInitialized()) {
                            startRender();
                        } else {
                            msg = mHandler.obtainMessage(0);
                            mHandler.sendMessageDelayed(msg, 100);
                        }
                    }
                }
            };
            Message msg = mHandler.obtainMessage(0);
            mHandler.sendMessageDelayed(msg, 100);
        }
        Log.i("Main", "onCreate over");

    }

    /**
     * 开始执行渲染
     */
    private void startRender() {
        mWXSDKInstance.render("Main", WXFileUtils.loadAsset("app.js", this),
                null, null, WXRenderStrategy.APPEND_ASYNC);
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (mWXSDKInstance != null) {
            mWXSDKInstance.onActivityResume();
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        if(mWXSDKInstance != null) {
            mWXSDKInstance.onActivityPause();
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        if(mWXSDKInstance != null) {
            mWXSDKInstance.onActivityStop();
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if(mWXSDKInstance != null) {
            mWXSDKInstance.onActivityDestroy();
        }
    }
}

布局文件的代码,activity_main.xml


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <FrameLayout
        android:id="@+id/container"
        android:layout_width="match_parent"
        android:layout_height="match_parent">FrameLayout>

LinearLayout>

如果有看过其他的weex介绍的文章,或者看过官网的文章,可以发现我的Activity代码有一点不一样的地方,就是在WXSDKInstance.render()执行weex界面渲染之前判断了WXSDKEngine是否有初始化完成。
因为我在没有加这个判断及相应的处理之前,weex在浏览器里显示的那个界面一直渲染不出来,但是在debug一步一步找原因的过程中,发现在渲染之前断点多停留一会儿,界面最终就渲染出来了。
而且日志中也出现多条JsFramework没有初始化的提示,于是就怀疑是在MainActivity进行界面渲染的时候WXSDKEngine应该还没有初始化成功,导致渲染失败。
最终加上了确保WXSDKEngine初始化成功后再进行渲染,解决了问题。
这个坑网上真是找不到一丁点资料,weex的相关文章还是太少,有深入了解的人也应该还是不多。

Step 5
AndroidMainfest.xml中配置好Application和Activity
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="cn.com.acoe.weexapp.app">

    <uses-permission android:name="android.permission.INTERNET" />

    <application
        android:name="com.app.weexapp.app.WeexApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme">
        <activity
            android:name="com.app.weexapp.ui.MainActivity"
            android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            intent-filter>
        activity>
    application>
manifest>
Step 6
将weex项目编译成js文件,拷贝到Android工程的assets目录下
在之前创建的awesome-project目录,进入命令行模式,执行weex build android可以在当前目录的dist文件夹中编译出js文件。其实在启动weex项目的时候js文件也会生成好。
拷贝好后(我这里是重命名成了app.js),我的Android主要目录结构如下

Weex基于Android Studio从开发环境搭建到运行_第6张图片

然后执行运行即可,下面是运行结果。
Weex基于Android Studio从开发环境搭建到运行_第7张图片

至此从搭建环境到运行,整个流程就结束了。有什么不祥尽的地方敬请留言我会进行补充的。

最终感受

虽然Weex官方提出的“一次编写,处处运行”可以说是做到了,但是原生的工作量确实还是不少。其实如果阿里把Weex专门运营成一个API产品,前景应该还是非常好的,类似于国内DCloud、ACloud做的那样,底层封装好,开发者只关注于前端怎么实现,与底层的交互全是API厂商封装好的那样。但是阿里似乎不太可能有这样子的做法,但是给混合开发开创了一种高体验的途径,这点相信会给很多人带来启示。反正就目前而言,个人觉得不是一个很好的混合开发解决方案,仍然还有很多路要走。

github地址:https://github.com/Acoee/WeexApp.git

参考文档
[1]: 官方手册 http://weex.apache.org/cn/guide/

你可能感兴趣的:(Weex)