weex devtool集成到Android项目

        我们使用weex开发Android项目时,会遇到难以真机调试的问题,开发起来会很头疼,难以跟踪bug。weex提供了一个工具来调试weex开发的代码,weex devtool与weex的一个app应用playground。可是这样调试不仅麻烦费时费力,而且并不是真的真机调试,与用户实际使用的情况不一样。因此,我们的项目将playground应用中的扫码调试功能,集成到了我们的项目中,这样就可以直接使用我们的项目二维码扫码连接weex devtool,进行调试。官网文档有集成方式:http://weex.apache.org/cn/guide/integrate-devtool-to-android.html。可是这里有很多坑,所以下面我就从这篇文档的基础上进行完善。

        官网文档中,有两种接入方式,一种是直接在代码上修改,写入weex devtool的url进行连接,一种是进行扫码动态连接,个人推荐扫码动态连接,原因在介绍完第一种方式后解释。

一、接入坑        

1)、直接在代码上修改,接入weex devtool

            1、通过在 XXXApplication 中设置开关打开调试模式            

public class MyApplication extends Application {
  public void onCreate() {
  super.onCreate();
  initDebugEnvironment(true, "xxx.xxx.xxx.xxx"/*"DEBUG_SERVER_HOST"*/);//initDebugEnvironment(boolean enable, String host) enable是否开启debug模式,weex devtool的url
  }
}initDebugEnvironment(boolean enable, String host) enable是否开启debug模式,weex devtool的url
  }
}
 
private void initDebugEnvironment(boolean enable, String host) {
  WXEnvironment.sRemoteDebugMode = enable;
  WXEnvironment.sRemoteDebugProxyUrl = "ws://" + host + ":8088/debugProxy/native/"+"这里填你打开的weex debuge时产生的weex id";//这里是官网的一个坑,没有告诉你要接一个weex devtool的id
}

 

Weex SDK 的 WXEnvironment 类里有一对静态变量标记了 Weex 当前的调试模式是否开启分别是:

 

public static boolean sRemoteDebugMode; // 是否开启 debug 模式,默认关闭
public static String sRemoteDebugProxyUrl; // DebugServer的websocket地址

无论在App 中无论以何种方式设置 Debug 模式,都必须设置 WXEnvironment.sRemoteDebugMode 和 WXEnvironment.sRemoteDebugProxyUrl。

   2、修改 sRemoteDebugMode 后一定要调用WXSDKEngine.reload()。

        一般來說,在修改了 WXEnvironment.sRemoteDebugMode 以后调用了 WXSDKEngine.reload() 方法才能够使 Debug模式生效。WXSDKEngine.reload() 用来重置 Weex 的运行环境上下文,在切换调试模式时需要调用此方法来创建新的 Weex 运行时和 DebugBridge 并将所有的 JS 调用桥接到调试服务器执行。

        可以把这句放到mainActivity的初始过程中,比如onResume()回调方法中。

        3、通过响应 ACTION_DEBUG_INSTANCE_REFRESH 广播及时刷新。(一般不用写,weex中的AbsWeexActivity已经写好了,只要加载weex的界面是用WXPageActivity加载的就可以)

广播 ACTION_DEBUG_INSTANCE_REFRESH 在调试模式切换和 Chrome 调试页面刷新时发出,主要用来通知当前的 Weex容器以 Debug 模式重新加载当前页。在 playground 中的处理过程如下:

public class RefreshBroadcastReceiver extends BroadcastReceiver {
  @Override
  public void onReceive(Context context, Intent intent) {
    if (IWXDebugProxy.ACTION_DEBUG_INSTANCE_REFRESH.equals(intent.getAction())) {
      if (mUri != null) {
        if (TextUtils.equals(mUri.getScheme(), "http") || TextUtils.equals(mUri.getScheme(), "https")) {
          loadWXfromService(mUri.toString());
        } else {
          loadWXfromLocal(true);
        }
      }
    }
  }
}

如果接入方的容器未对该广播做处理,那么将不支持刷新和调试过程中编辑代码时的 watch 功能。

 

      第一种接入方式已介绍完成,我们可以看到,在接入过程中,我们将weex debug的调试网址用代码的形式写入了app,从而实现联调,这就导致每次启动一次weex debug,都要改一次代码。因此我推荐第二种方式,动态接入。

        2)、二维码扫描,动态接入weex devtool

第二种接入方式与第一种只在第一步有差别,第二与第三步一样,原理是写一个二维码扫描,获取每次启动weex debug的新url,然后赋值给weexSDK,交给他去联调即可。代码如下:

      先写一个全局悬浮按钮,以便启动二维码扫描在每个界面都支持debug调试,点击事件启动扫描:

public class FloatingBtnService  extends Service {
    protected static final String TAG = "ShowFloatingBtnService";
    public static String STATAG = "stop";
    private LinearLayout view;

    // 获取到手机的窗体管理器
    private WindowManager wm;
    private WindowManager.LayoutParams params;

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    /**
     *把一个view对象显示到手机窗体上
     */
    public void showAddress() {
        LayoutInflater inflater = LayoutInflater.from(getApplication());
        view = (LinearLayout) inflater.inflate(R.layout.toast_location, null);
        // 给窗体上的view对象注册点击事件
        view.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //二维码扫描
                IntentIntegrator integrator = new IntentIntegrator(getCurrentActivity());
                integrator.setDesiredBarcodeFormats(IntentIntegrator.QR_CODE_TYPES);
                integrator.setPrompt("Scan a barcode");
                //integrator.setCameraId(0);  // Use a specific camera of the device
                integrator.setBeepEnabled(true);
                integrator.setOrientationLocked(false);
                integrator.setBarcodeImageEnabled(true);
                integrator.initiateScan();
            }
        });

        // 给窗体上的view对象注册触摸事件
        view.setOnTouchListener(new View.OnTouchListener() {
            int startX;
            int startY;
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                        Log.i(TAG, "呼叫界面,更改位置+摸到");
                        startX = (int) event.getRawX();
                        startY = (int) event.getRawY();
                        break;
                    case MotionEvent.ACTION_MOVE:
                        Log.i(TAG, "呼叫界面,更改位置+移动");
                        int newX = (int) event.getRawX();
                        int newY = (int) event.getRawY();
                        int dx = newX - startX;
                        int dy = newY - startY;
                        // 更改view对象在窗体上显示的位置.
                        params.x += dx;
                        params.y += dy;

                        if (params.x < 0) {
                            params.x = 0;
                        }
                        if (params.y < 0) {
                            params.y = 0;
                        }
                        if (params.x > wm.getDefaultDisplay
                                ().getWidth()) {
                            params.x = wm.getDefaultDisplay
                                    ().getWidth();
                        }
                        if (params.y > wm.getDefaultDisplay
                                ().getHeight()) {
                            params.y = wm.getDefaultDisplay
                                    ().getHeight();
                        }
                        wm.updateViewLayout(view, params);
                        // 重新初始化手指的位置
                        startX = (int) event.getRawX();
                        startY = (int) event.getRawY();
                        break;
                    case MotionEvent.ACTION_UP:
                        int lastx = params.x;
                        int lasty = params.y;
                        SharedPreferences sp = getSharedPreferences("config", Context.MODE_PRIVATE);
                        SharedPreferences.Editor editor = sp.edit();
                        editor.putInt("lastx", lastx);
                        editor.putInt("lasty", lasty);
                        editor.commit();
                        break;
                }
                return false;
            }
        });
//         tv = (ImageView) view.findViewById
//                (R.id.tv_toast_address);
//        tv.setText(address);
        SharedPreferences sp = getSharedPreferences("config",
                MODE_PRIVATE);
        params = new WindowManager.LayoutParams();
        params.gravity = Gravity.TOP | Gravity.LEFT;
        int lastx = sp.getInt("lastx", 0);
        int lasty = sp.getInt("lasty", 0);
        params.x = lastx;
        params.y = lasty;
        params.height = WindowManager.LayoutParams.WRAP_CONTENT;
        params.width = WindowManager.LayoutParams.WRAP_CONTENT;
        // 定义控件 可以触摸 删除一个flag
        params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
        params.format = PixelFormat.TRANSLUCENT;
        // 定义窗体的类型 TYPE_PRIORITY_PHONE
        params.type = WindowManager.LayoutParams.TYPE_PRIORITY_PHONE;
        wm.addView(view, params);
    }

    @Override
    public void onCreate() {
        wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
        showAddress();
        STATAG = "start";
        super.onCreate();
    }

    @Override
    public void onDestroy() {
        if (view != null) {
            wm.removeView(view);
            view = null;
        }
        STATAG = "stop";
        super.onDestroy();
    }
}

然后,在每个Activity都要继承的基Activity类里重写 onActivityResult(int requestCode, int resultCode, Intent data),接收weex debug的url,并处理,启动app weex debug模式,打开新的WeexActivity,并加载:

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == IntentIntegrator.REQUEST_CODE) {
            IntentResult result = IntentIntegrator.parseActivityResult(requestCode, resultCode, data);
            if (result != null) {
                handleDecodeInternally(result.getContents());
            }
        }
        super.onActivityResult(requestCode, resultCode, data);
    }


/**
     * 处理devtool返回的DebugProxyUrl,WX启动devtool模式 by leon.wen
     *
     * @param code
     */
    // Put up our own UI for how to handle the decoded contents.
    private void handleDecodeInternally(String code) {
        if (!TextUtils.isEmpty(code)) {
            Uri uri = Uri.parse(code);
            if (uri.getQueryParameterNames().contains("bundle")) {
                WXEnvironment.sDynamicMode = uri.getBooleanQueryParameter("debug", false);
                WXEnvironment.sDynamicUrl = uri.getQueryParameter("bundle");
                String tip = WXEnvironment.sDynamicMode ? "Has switched to Dynamic Mode" : "Has switched to Normal Mode";
                Toast.makeText(this, tip, Toast.LENGTH_SHORT).show();
                finish();
                return;
            } else if (uri.getQueryParameterNames().contains("_wx_devtool")) {
                WXEnvironment.sRemoteDebugProxyUrl = uri.getQueryParameter("_wx_devtool");
                WXEnvironment.sDebugServerConnectable = true;
                WXSDKEngine.reload();
                Toast.makeText(this, "devtool", Toast.LENGTH_SHORT).show();
                return;
            } else if (code.contains("_wx_debug")) {
                uri = Uri.parse(code);
                String debug_url = uri.getQueryParameter("_wx_debug");
                // todo 新的weexCore没有这个方法,可删除
                switchDebugModel(true, debug_url);
                finish();
            } else {
                refreshWeexUrl(code);
            }
        }
    }

/**
     * 处理weexdebug 模式下,扫描单个weex.vue时的刷新界面
     *
     * @param code vue的路径
     */
    public void refreshWeexUrl(String code) {
        Toast.makeText(this, code, Toast.LENGTH_SHORT)
                .show();
        Intent intent = new Intent(BaseActivity.this, WXPageActivity.class);
        intent.setPackage(getPackageName());
        intent.setData(Uri.parse(code));
        startActivity(intent);
    }

二、SDK版本坑

     当你写好代码以后就以为万事大吉了么!no!!!后面的坑很大!等你的app跑起来你会发现,无法联调,app甚至闪退!为什么呢?经过查看weex sdk与weex_inspector两个sdk源码有冲突!这他妈阿里坑爹啊!没有说清楚相对应的版本库。weex sdk是weex加载界面的核心库,weex_inspector是负责app和浏览器联调的sdk,js的发送依赖他。经过我一个个版本的比较,得出以下结论:

推荐

weex_sdk 0.19.0.4 对应 weex_inspector 0.18.68

weex_sdk 0.18.0 对应 weex_inspector 0.13.4

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值