weex旨在“一次撰寫,多端使用”,意思是,用weex寫的頁面,不論是Android還是iOS甚至web端都可以使用,這樣的話就可以極大的降低開發成本, weex其實就是寫的一個類似於h5的頁面(js編寫),寫完之後將vue文件編譯、部署到Nginx伺服器上(Nginx伺服器後面會講到),這時候在... ...
最近為了項目需要(實際上是為了年底KPI),領導要求用3天時間,學習並使用weex開發一個頁面,說實話,壓力山大。在這之前壓根兒就沒聽說過啊,一臉懵逼
無奈之餘只能Google了,驚喜的發現weex的官網上面是什麼都有啊,環境搭建、weex集成開發、js教程等等還有好多,雙手奉上官網地址:http://weex.apache.org/cn/
既然官網講的這麼全面,我就只說一下我的操作流程;
1、weex環境搭建
首先下載安裝node.js,node.js是weex編譯、打包用的基礎工具,javascript和node.js關係,類似於java與jvm,nodejs去官方網站下載最新穩定版6.xx就可以
安裝好了之後,用以下命令檢查是否安裝成功
node -v
npm -v
出現上圖就說明node.js安裝成功了
npm是nodejs的包管理工具,相當於android中的gradle或者iOS中的cocoapods,但是npm在國內訪問速度較慢,建議大家安裝cnpm,是淘寶的一個npm國內鏡像
安裝完cnpm後執行cnpm install -g weex-toolkit安裝weex-toolkit
$ npm install -g cnpm --registry=https://registry.npm.taobao.org $ cnpm install -g weex-toolkit
命令行敲weex,出現上圖就說明weex-toolkit安裝成功了。
至此weex的安裝就告一段落了,官網上的環境搭建到這裡之後還有幾步,不過我都沒有用上,這裡先記一下,後續需要用到了再補充!!!!!!
2、集成到 Android
接下來就是將weex項目集成到Android項目中去,這塊真是沒有什麼好講的,這塊就和集成第三方sdk一毛一樣的
直接上官網的集成方式:http://weex.apache.org/cn/guide/integrate-to-your-app.html
2-1、個人對weex開發的理解
說一下我對在Android中集成weex的理解吧,可能理解的很淺顯,不喜勿噴!
weex旨在“一次撰寫,多端使用”,意思是,用weex寫的頁面,不論是Android還是iOS甚至web端都可以使用,這樣的話就可以極大的降低開發成本,
weex其實就是寫的一個類似於h5的頁面(js編寫),寫完之後將vue文件編譯、部署到Nginx伺服器上(Nginx伺服器後面會講到),這時候在你的Nginx伺服器下的html文件夾內會生成一個.js的文件,這個.js文件就是你要嵌入到Android項目內的文件,如果一個項目內有很多個weex頁面,就需要編譯部署很多個vue文件,也就是說會在html這個文件夾下生成很多個.js文件,然後你可能會問,那我們要怎麼管理和維護這麼多個.js文件呢?這就需要我們將這些個.js壓縮成一個zip包,把這個壓縮後的zip包個後臺,後臺會生成一個json文件,將這個.json放在Nginx伺服器的html文件夾內,我們需要做的就只是管理和維護這個.json文件就可以了,最後在你的Android項目內,訪問這個Nginx伺服器(伺服器的地址就是你電腦的ip地址),獲取到伺服器上的weex頁面(其實這也能做到預載入的作用)然後進行各種處理和操作;
生成的.json文件就是這麼個東西,在後文要講的獲取頁面,完全靠這裡面的url,非常的重要;
貼一些util代碼吧:
//在mainActivity或者啟動頁獲取weex相關json文件 WeexUtils.getPages(); public static void getPages() { new Thread(new Runnable() { @Override public void run() { String url = http://ip地址/後臺生成的.json文件; okhttp3.Request request = new okhttp3.Request.Builder() .url(url) .build(); try { okhttp3.Response response = AlbatrossApplication.getInstance().getClient().newCall(request).execute(); String data = response.body().string();
//okhttp請求.json數據,最後將請求到的數據通過sharedprefer存儲到本地 SharedPreferences pages = AlbatrossApplication.getInstance().getSharedPreferences("pages", MODE_PRIVATE); pages.edit().putString("pages", data).commit(); downloadBundle(); } catch (IOException e) { e.printStackTrace(); } } }).start(); }
下載weex bundle:
/** * 下載weex bundle */ public static void downloadBundle() { new Thread(new Runnable() { @Override public void run() { try { SharedPreferences pages = AlbatrossApplication.getInstance().getSharedPreferences("pages", MODE_PRIVATE); String pagesData = pages.getString("pages", ""); com.alibaba.fastjson.JSONObject jsonObject = JSON.parseObject(pagesData); String url = jsonObject.getJSONObject("zip").getString("url"); String path = AlbatrossApplication.getInstance().getCacheDir() + File.separator + "weex"; if (!new File(path).exists()) { new File(path).mkdirs(); } File downloadedFile = new File(path, "js_bundle.zip"); String localMd5 = WeexUtils.calculateMD5(downloadedFile); if (jsonObject.getJSONObject("zip").getString("md5").equals(localMd5)) { //本地zip和伺服器zip一致,return return; } okhttp3.Request request = new okhttp3.Request.Builder() .url(url) .build(); okhttp3.Response response = AlbatrossApplication.getInstance().getClient().newCall(request).execute(); BufferedSink sink = Okio.buffer(Okio.sink(downloadedFile)); sink.writeAll(response.body().source()); sink.close(); Log.e("bobo", downloadedFile.length() + " " + downloadedFile.getAbsolutePath()); WeexUtils.unzip(downloadedFile, new File(path)); Log.e("bobo 當前目錄文件數", new File(path).listFiles().length + ""); // downloadPatch();//下載patch } catch (Exception e) { e.printStackTrace(); } } }).start(); }
以上的操作就可以獲取到weex頁面了,獲取到了之後就可以開始操作了,這裡我就簡單的舉一個點擊跳轉的例子好了,上代碼
View view = findviewById(R.id.tv_consist); view.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { WXSDKInstance mWXSDKInstance = new WXSDKInstance(getActivity()); Intent intent = new Intent(getActivity(), WeexActivity.class); intent.putExtra("WX_URL", "/html文件夾下的js文件.js"); mWXSDKInstance.getContext().startActivity(intent); } });
跳轉到WeexActivity後,在WeexActivity進行的處理就是將前文存儲在本地的數據解析,根據解析後的數據獲取到指定.js文件的路徑,最後載入這個路徑的頁面,這塊挺亂的,我說的只是一個大概方向,具體的還要看實際情況;
public class WeexActivity extends BaseMainActivity implements View.OnClickListener, IWXRenderListener { private String TEST_URL = "http://172.16.36.232/dist/jump.js"; private WXSDKInstance mWXSDKInstance; private FrameLayout mContainer; private TextView mTv_title; private HashMap<String, String> mLocalBundleBean; private TextView mTv_right; private boolean isFinish = true; private View mTv_left; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); // KYApplication.getInstance().addActivity(this); setContentView(R.layout.activity_weex); initView(); WXSDKEngine.setActivityNavBarSetter(new NavigatorAdapter()); mContainer = (FrameLayout) findViewById(R.id.container); mWXSDKInstance = new WXSDKInstance(this); mWXSDKInstance.registerRenderListener(this); /** * pageName:自定義,一個標示符號。 * url:遠程bundle JS的下載地址 * options:初始化時傳入WEEX的參數,比如 bundle JS地址 * flag:渲染策略。WXRenderStrategy.APPEND_ASYNC:非同步策略先返回外層View,其他View渲染完成後調用onRenderSuccess。WXRenderStrategy.APPEND_ONCE 所有控制項渲染完後後一次性返回。 */ Map<String, Object> options = new HashMap<>(); String wx_url = getIntent().getStringExtra("WX_URL"); String title = getIntent().getStringExtra("title"); // mTv_title.setText(title); TEST_URL = wx_url.substring(wx_url.lastIndexOf("/")+1); options.put(WXSDKInstance.BUNDLE_URL, TEST_URL); //獲取集合,維護文件md5值以及文件對應路徑 //從json文件里,取出md5對應的文件,有則載入文件,無則載入url SharedPreferences pages = getSharedPreferences("pages", MODE_PRIVATE); String pageData = pages.getString("pages", ""); JSONObject jsonObject = JSON.parseObject(pageData); JSONArray jsonArray = jsonObject.getJSONArray("pages"); HashMap<String, PageBean> pageMap = new HashMap<>();//存儲伺服器最新頁面信息 for (int i = 0; i < jsonArray.size(); i++) { PageBean pageBean = JSON.parseObject(jsonArray.getString(i), PageBean.class); pageMap.put(pageBean.getPage(), pageBean); } mLocalBundleBean = new HashMap<>(); try { long time = System.currentTimeMillis(); getAllFiles(new File(Constant.WEEX_URL));//本地zip包解壓文件md5 Log.e("bobo", "計算md5總耗時" + (System.currentTimeMillis() - time)); } catch (Exception e) { e.printStackTrace(); } PageBean pageBean = pageMap.get(TEST_URL);//獲取login.js對應的md5、url if(pageBean==null){ Toast.makeText(this,"頁面不存在", Toast.LENGTH_SHORT).show(); return; } String localUrl = mLocalBundleBean.get(pageBean.getMd5()); TEST_URL=pageBean.getUrl(); if (null != localUrl) { mWXSDKInstance.render(TEST_URL, WXFileUtils.loadFileOrAsset(localUrl, this), null, null, WXRenderStrategy.APPEND_ASYNC); Log.e("bobo","從本地載入"); } else { mWXSDKInstance.renderByUrl(TEST_URL, TEST_URL, options, null, WXRenderStrategy.APPEND_ONCE); Log.e("bobo","從網路載入"); } // mWXSDKInstance.renderByUrl("WXSample", TEST_URL, options, null, WXRenderStrategy.APPEND_ONCE); } public void initView() { mTv_left = findViewById(R.id.tv_left); mTv_title = (TextView) findViewById(R.id.tv_title); mTv_right = (TextView) findViewById(R.id.tv_right); mTv_left.setOnClickListener(this); mTv_title.setOnClickListener(this); mTv_right.setOnClickListener(this); } @Override public void onClick(View view) { if (view.getId() == R.id.tv_left) { if(isFinish){ super.onBackPressed(); }else { mWXSDKInstance.fireGlobalEventCallback("left_click", null); } } else if (view.getId() == R.id.tv_title) { // Toast.makeText(this, "標題點擊了", Toast.LENGTH_SHORT).show(); } else if (view.getId() == R.id.tv_right) { mWXSDKInstance.fireGlobalEventCallback("clickrightitem", null); } } @Override protected void onStart() { super.onStart(); if (mWXSDKInstance != null) { mWXSDKInstance.onActivityStart(); } } @Override protected void onResume() { super.onResume(); WXStorageModule wxStorageModule = new WXStorageModule(); wxStorageModule.setItem("currentPage", WXUtils.getNameFromUrl(TEST_URL),null); if (mWXSDKInstance != null) { mWXSDKInstance.onActivityResume(); //發送頁面出現廣播 Map<String, Object> params = new HashMap<>(); params.put("page", WXUtils.getNameFromUrl(TEST_URL)); mWXSDKInstance.fireGlobalEventCallback("appear", params); } } @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(); // KYApplication.getInstance().deleteActivity(this); if (mWXSDKInstance != null) { mWXSDKInstance.onActivityDestroy(); } } @Override public void onViewCreated(WXSDKInstance instance, View view) { if (view.getParent() != null) { ((ViewGroup) view.getParent()).removeView(view); } mContainer.addView(view); //發送頁面出現廣播 Map<String, Object> params = new HashMap<>(); params.put("page", WXUtils.getNameFromUrl(TEST_URL)); mWXSDKInstance.fireGlobalEventCallback("appear", params); } @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) { } private class NavigatorAdapter implements IActivityNavBarSetter { @Override public boolean push(String param) { return false; } @Override public boolean pop(String param) { return false; } @Override public boolean setNavBarRightItem(String param) { String title = JSON.parseObject(param).getString("title"); if(!TextUtils.isEmpty(title) && ("添加報價".equals(title) || "返迴首頁".equals(title))){ mTv_right.setTextColor(getResources().getColor(R.color.color_5E8BF8)); } mTv_right.setText(title); return false; } @Override public boolean clearNavBarRightItem(String param) { return false; } @Override public boolean setNavBarLeftItem(String param) { return false; } @Override public boolean clearNavBarLeftItem(String param) { return false; } @Override public boolean setNavBarMoreItem(String param) { return false; } @Override public boolean clearNavBarMoreItem(String param) { return false; } @Override public boolean setNavBarTitle(String param) { String title = JSON.parseObject(param).getString("title"); mTv_title.setText(title); return false; } } public void getAllFiles(File file) throws Exception { File[] files = file.listFiles(); // long time = System.currentTimeMillis(); for (int i = 0; i < files.length; i++) { if (files[i].isDirectory()) { getAllFiles(files[i]); } else { Log.e("bobo", "name" + files[i].getName() + " md5" + WeexUtils.calculateMD5(files[i])); mLocalBundleBean.put(WeexUtils.calculateMD5(files[i]), files[i].getAbsolutePath()); } } } /** * @return get mWXSDKInstance */ public WXSDKInstance getWXSDKInstance() { return mWXSDKInstance; } @Override public void onBackPressed() { if(isFinish){ super.onBackPressed(); }else { mWXSDKInstance.fireGlobalEventCallback("back_click", null); } } /** * Sets the isFinish 設置是否直接返回上個頁面 * You can use getFinish() to get the value of isFinish */ public void setFinish(boolean finish) { isFinish = finish; } public void hideBackButton(){ mTv_left.setVisibility(View.GONE); } }View Code
3、Nginx伺服器
終於到最後一步了,Nginx伺服器下載地址:http://nginx.org/en/download.html
我是下載的穩定版
下載完之後解壓到文件夾下,雙擊nginx.exe啟動服務,打開conf這個文件夾,打開nginx.conf這個文件,修改location下的root
3-1、編譯、部署.vue文件
哦。對了,大家應該都是到用webstorm寫vue頁面吧??哈哈哈哈
(1)首次創建項目或者是首次導入項目的時候,需要在控制台執行:cnpm install,這個就類似於Android studio中的sync now。
我們需要通過這個命令將package.json中的所有依賴下載下來;
(2)同樣的,控制台執行:weex compile src/vue文件名 D:\nginx\nginx-1.12.2\html
意思是將制定的vue文件編譯到nginx伺服器下的指定文件夾內,這時候就會生成一個前文所說到的.js文件。
終於全都結束了,這是我今天一天的調研學習的成果,可能有一些地方理解的有偏差或者不對的地方,非常歡迎大家批評指正
如果有任何疑問,歡迎留言~~~~
2017/12/6 21:05 晚安各位