1. 程式人生 > >android 學習之路h5混合開發專案經歷

android 學習之路h5混合開發專案經歷

 

首先需求是要做一個h5呼叫原生去傳送udp mqtt 儲存 tcp 相機之類的功能

第一步在mainActivity 註冊監聽

private void initWebLoad(String url) {
        Util.synCookies(getApplicationContext(), url, SharePreferenceUtils.getPrefString(getApplicationContext(), SharePreferenceUtils.WEB_VIEW_COOKIE, ""));
        //第一個引數為使用者名稱,第二個引數為密碼
        final String basic = Credentials.basic("zhangsan", "123456");
        Map<String, String> heads=new HashMap<>();
        heads.put("Authorization",basic);
        bridgeWebView2.loadUrl(url,heads);

        ShakeService shakeService=ARouter.getInstance().navigation(ShakeService.class);
        if(shakeService!=null){
            shakeService.handleShake(this);
            //除錯
            if(Build.VERSION.SDK_INT> Build.VERSION_CODES.KITKAT) {
                bridgeWebView2.setWebContentsDebuggingEnabled(true);
            }
        }

        getPresenter().registerH5CallNative(bridgeWebView2);
    }

第二步 在MainPresenter p層做registerH5CallNative方法的實現

/**
     * H5呼叫原生
     * @param bridgeWebView
     */
    public void registerH5CallNative(final WVJBWebView bridgeWebView) {
        bridgeWebView.registerHandler(REGISTER_HANDLE_NAME, new WVJBWebView.WVJBHandler() {
            @Override
            public void handler(Object dataobj, WVJBWebView.WVJBResponseCallback function) {

                String data=dataobj.toString();
                functionMap.put(function.toString(),function);
                try {
                    Timber.e(TAG, "##H5CallNative 收到: " + data);
                    String service = Util.getService(data);
                    Timber.e(TAG, service);
                    String action = Util.getAction(data);
                    Timber.e(TAG,action);
                    LinkedTreeMap messageTreeMap = GsonUtils.getJsonLinkedTree(data);
                    String message = "";
                    if (messageTreeMap != null) {
                        message = GsonUtils.getJsonString(messageTreeMap.get("data"));
                    }
                    Timber.e(TAG,message);
                    switch (service){
                        case SERVICE_HTTP:
                        case SERVICE_FILE:
                            if(httpService==null) {
                                httpService = ARouter.getInstance().navigation(HttpService.class);
                            }
                            if(httpService!=null){
                                httpService.handleData(function.toString(),getActivity(),action,message);
                            }
                            break;
                        case SERVICE_UDP:
                            if(udpService==null) {
                                udpService = ARouter.getInstance().navigation(UdpService.class);
                            }
                            if(udpService!=null){
                                udpService.handleData(action,message);
                            }
                            break;
                        case SERVICE_MQTT:
                            if(mqttService==null){
                                mqttService=ARouter.getInstance().navigation(MqttService.class);
                            }
                            if(mqttService!=null){
                                mqttService.handleData(function.toString(),action,message);
                            }
                            break;
                            //資料儲存
                        case SERVICE_DATA:
                        case SERVICE_DATA_BASE:
                            if(databaseService==null){
                                databaseService=ARouter.getInstance().navigation(DatabaseService.class);
                            }
                            if(databaseService!=null){
                                databaseService.handleData(function.toString(),action,message);
                            }
                            break;
                        case SERVICE_SYSTEM:
                            if(systemService==null){
                                systemService=ARouter.getInstance().navigation(SystemService.class);
                            }
                            if(systemService!=null){
                                systemService.handleData(function.toString(),getActivity(),action,message);
                            }
                            break;
                        case SERVICE_SMARTLINK:
                            if(smartLinkService==null){
                                smartLinkService=ARouter.getInstance().navigation(SmartLinkService.class);
                            }
                            if(smartLinkService!=null){
                                smartLinkService.handleData(function.toString(),action,message);
                            }
                            break;
                        case SERVICE_TCP:
                            if(tcpService==null){
                                tcpService=ARouter.getInstance().navigation(TcpService.class);
                            }
                            if(tcpService!=null){
                                tcpService.handleData(function.toString(),action,message);
                            }
                            break;
                        case SERVICE_CAMERA:
                            if(cameraService==null){
                                cameraService=ARouter.getInstance().navigation(CameraService.class);
                            }
                            if(cameraService!=null){
                                cameraService.handleData(function.toString(),getActivity(),action,message);
                            }
                            break;
                        case SERVICE_APPUPGRADE:
                            if(upgradeService==null){
                                upgradeService=ARouter.getInstance().navigation(UpgradeService.class);
                            }
                            if(upgradeService!=null){
                                upgradeService.handleData(getActivity(),function.toString(),action,message);
                            }
                            break;
                        case SERVICE_MAP:
                            if(mapService==null){
                                mapService=ARouter.getInstance().navigation(MapService.class);
                            }
                            if(mapService!=null){
                                mapService.handleData(function.toString(),getActivity(),action,message);
                            }
                            break;
                        case SERVICE_BLE:
                            if(bleService==null){
                                bleService=ARouter.getInstance().navigation(BleService.class);
                            }
                            if(bleService!=null){
                                bleService.handleData(function.toString(),action,message);
                            }
                            break;
                        case SERVICE_CLIENT:
                            if(ipcService==null){
                                ipcService=ARouter.getInstance().navigation(IpcService.class);
                            }
                            if(ipcService!=null){
                                if(ACTION_TO_START.equals(action) ){
                                    if(getView()!=null){
                                        getView().addVideoFragment(message);
                                    }
                                }
                                ipcService.handleData(getActivity(),function.toString(),service,action,message);
                            }
                            break;
                        case SERVICESD_CARD:
                            if(ipcService==null){
                                ipcService=ARouter.getInstance().navigation(IpcService.class);
                            }
                            if(ipcService!=null){
                                ipcService.handleData(getActivity(),function.toString(),service,action,message);
                            }
                            break;
                        case SERVICE_STATUSBAR:
                            final LinkedTreeMap linkedTreeMap =GsonUtils.getJsonLinkedTree(message);
                            switch (action){
                                case ACTION_ROTATE:
                                    //1豎屏,0橫屏
                                    int orientation = -1;
                                    if (linkedTreeMap != null) {
                                        if (linkedTreeMap.containsKey("orientation")) {
                                            orientation = (int) Double.parseDouble(linkedTreeMap.get("orientation").toString());
                                            if(orientation==0){
                                                getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);//橫屏
                                                callH5Callback(function.toString(),"{\"code\":200}");
                                            }else if(orientation==1){
                                                getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);//豎屏
                                                callH5Callback(function.toString(),"{\"code\":200}");
                                            }
                                        }
                                    }
                                    break;
                                case ACTION_STATE:
                                    //1顯示,0隱藏
                                    int isVisible = -1;
                                    if (linkedTreeMap != null) {
                                        if (linkedTreeMap.containsKey("isVisible")) {
                                            isVisible = (int) Double.parseDouble(linkedTreeMap.get("isVisible").toString());
                                            if(isVisible==0){
                                                getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); //隱藏狀態列
                                                callH5Callback(function.toString(),"{\"code\":200}");
                                            }else if(isVisible==1){
                                                getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); //顯示狀態列
                                                callH5Callback(function.toString(),"{\"code\":200}");
                                            }
                                        }
                                    }
                                    break;
                                case ACTION_STYLE:
                                    //1黑色,0白色
                                    int setStyle = -1;
                                    if (linkedTreeMap != null) {
                                        if (linkedTreeMap.containsKey("setStyle")) {
                                            setStyle = (int) Double.parseDouble(linkedTreeMap.get("setStyle").toString());
                                            if(setStyle==0){
                                                StatusBarUtil.setLightMode(getActivity());
                                                callH5Callback(function.toString(),"{\"code\":200}");
                                            }else if(setStyle==1){
                                                StatusBarUtil.setDarkMode(getActivity());
                                                callH5Callback(function.toString(),"{\"code\":200}");
                                            }
                                        }
                                    }
                                    break;
                            }
                            break;
                        case SERVICE_EXTERNAL:
                            if(externalService==null){
                                externalService=ARouter.getInstance().navigation(ExternalService.class);
                            }
                            if(externalService!=null){
                                externalService.handleData(getActivity(),function.toString(),action,message);
                            }
                            break;
                    }
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        });

    }

第三步 在對應的service serviceImpl中實現其方法,舉例說udpservice udpserviceImpl

public interface UdpService extends IProvider {
    void handleData(String action, String data);
}

udpserviceImpl去實現handleData方法

public class UdpServiceImpl implements UdpService {
    private static final String TAG = "UdpServiceImpl";
    private Context context;
    private static final String ACTION_SEND="send";

    public static final String ACTION_LISTEN="listen";
    public static final String ACTION_CLOSE="close";
    private static final String H5_MODULE_UDP="UDP";
    private static final String H5_ACTION_PUSH_MESSAGE = "pushMessage";
    @Override
    public void handleData( String action, String data) {
//        Log.e(TAG,action+"------data:"+data);
        switch (action){
            case ACTION_SEND:
                final LinkedTreeMap linkedTreeMap = GsonUtils.getJsonLinkedTree(data);
                String ip = "";
                int port = 0;
                String msg = "";
                if (linkedTreeMap != null) {
                    ip = linkedTreeMap.get("ip").toString();
                    if(linkedTreeMap.containsKey("port"))
                        port = (int) Double.parseDouble(linkedTreeMap.get("port").toString());
                    msg = GsonUtils.getJsonString(linkedTreeMap.get("message"));

                    LinkedTreeMap t = GsonUtils.getJsonLinkedTree(msg);
                    String method = t.get("method").toString();
                    if (method.equals("devDiscoveryReq")) {
                        UDPManager.getInstance().ScanDevice(context, port, msg, new UdpResponseListener() {
                            @Override
                            public void onResponse(final String m) {
                                EventBus.getDefault().post(new JsCallH5ByNativeEvent(H5_MODULE_UDP,H5_ACTION_PUSH_MESSAGE,m));
                            }

                            @Override
                            public void onTimeOut() {

                            }

                            @Override
                            public void onError(Exception e) {

                            }
                        });
                    } else {
                        int timeout;
                        if (method.equals("wifiListReq")) {
                            timeout=20000;
                        }else{
                            timeout=-1;
                        }
                        UDPManager.getInstance().sendAsyn(context, ip, port,timeout, msg, new UdpResponseListener() {
                            @Override
                            public void onResponse(final String m) {
                                EventBus.getDefault().post(new JsCallH5ByNativeEvent(H5_MODULE_UDP,H5_ACTION_PUSH_MESSAGE,m));
                            }

                            @Override
                            public void onTimeOut() {

                            }

                            @Override
                            public void onError(Exception e) {

                            }
                        });
                    }
                }
                break;
            case ACTION_LISTEN:
                final LinkedTreeMap linkedTreeMap2 = GsonUtils.getJsonLinkedTree(data);
                int port2 = 0;
                if (linkedTreeMap2 != null) {
                    port2 = (int) Double.parseDouble(linkedTreeMap2.get("port").toString());
                    //listenAsyn
                    UDPManager.getInstance().nioListen( port2, new UdpResponseListener() {
                        @Override
                        public void onResponse(String message) {
                            EventBus.getDefault().post(new JsCallH5ByNativeEvent(H5_MODULE_UDP, H5_ACTION_PUSH_MESSAGE, message));
                        }

                        @Override
                        public void onTimeOut() {

                        }

                        @Override
                        public void onError(Exception e) {

                        }
                    });
                }
                break;
            case ACTION_CLOSE:
//                UDPManager.getInstance().StopListen();
                UDPManager.getInstance().stopNioListen();

                break;
        }
    }

    @Override
    public void init(Context context) {
        this.context=context;
//        Log.e(TAG, "init: " );
    }
}

將h5傳過來的json轉換成map 通過對應key取出來

  final LinkedTreeMap linkedTreeMap = GsonUtils.getJsonLinkedTree(data);
  public static LinkedTreeMap getJsonLinkedTree(String jsonData) {
        if (TextUtils.isEmpty(jsonData)) {
            return null;
        }
        Gson gson = new GsonBuilder()
                .disableHtmlEscaping().create();
        LinkedTreeMap map = gson.fromJson(jsonData, LinkedTreeMap.class);
        return map;
    }

根據不同的引數呼叫udp的不同方法 這邊舉例講send

 UDPManager.getInstance().sendAsyn(context, ip, port,timeout, msg, new UdpResponseListener() {
                            @Override
                            public void onResponse(final String m) {
                                EventBus.getDefault().post(new JsCallH5ByNativeEvent(H5_MODULE_UDP,H5_ACTION_PUSH_MESSAGE,m));
                            }

                            @Override
                            public void onTimeOut() {

                            }

                            @Override
                            public void onError(Exception e) {

                            }
                        }

第四步 在UDPManager中去實現send方法 因為防止執行緒阻塞 非同步傳送 

 /**
     * 非同步傳送
     */
    public void sendAsyn(final Context context, final String ip, final int port, final int timeout, final String message, final UdpResponseListener responseListener) {
        this.context=context;
        thread = null;
        thread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    send(ip, port,timeout, message, responseListener);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
        thread.start();
    }

具體實現udp傳送資料及收到資料方法 new ds一個DatagramSocket物件 用來發送udp new 兩個DatagramPacket 一個dp_send用來儲藏傳送資料,一個dp_receive用來接收資料,

  private void send(String ip, int port, int timeout, String message, UdpResponseListener responseListener) throws IOException {
//        Log.e(TAG, "send 傳送udp資料: " );
        byte[] buf = new byte[10240];
        DatagramSocket ds = new DatagramSocket();
        InetAddress loc = InetAddress.getLocalHost();
        try {
            if (TextUtils.isEmpty(ip)) {
                loc = InetAddress.getByName("255.255.255.255");
            } else {
                loc = getAddressByString(ip);
            }
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
        //定義用來發送資料的DatagramPacket例項
        DatagramPacket dp_send = new DatagramPacket(message.getBytes(), message.length(), loc, port);
        //定義用來接收資料的DatagramPacket例項
        DatagramPacket dp_receive = new DatagramPacket(buf, 10240);
        //資料發向本地36877埠
        if(timeout<0) {
            ds.setSoTimeout(TIMEOUT);              //設定接收資料時阻塞的最長時間
        }else{
            ds.setSoTimeout(timeout);
        }
        int tries = 0;                         //重發資料的次數
        boolean receivedResponse = false;     //是否接收到資料的標誌位
        //直到接收到資料,或者重發次數達到預定值,則退出迴圈
        while (!receivedResponse && tries < MAXNUM) {
            //傳送資料
            ds.send(dp_send);
            try {
                //接收從服務端傳送回來的資料
                ds.receive(dp_receive);
                String strMsg=new String(dp_receive.getData()).trim();
//                Log.e("##########", dp_receive.getAddress()
//                        .getHostAddress().toString()+dp_receive.getPort()
//                        + ":" +strMsg );
                //如果接收到的資料不是來自目標地址,則丟擲異常
//                if (!"/255.255.255.255".equals(loc.toString()) && !dp_receive.getAddress().equals(loc)) {
//                    throw new IOException("Received packet from an umknown source" + loc.toString() + "  receiver Address:" + dp_receive.getAddress());
//                }
                //如果接收到資料。則將receivedResponse標誌位改為true,從而退出迴圈
                receivedResponse = true;
            } catch (InterruptedIOException e) {
                e.printStackTrace();
                //如果接收資料時阻塞超時,重發並減少一次重發的次數
                tries += 1;
//                Log.e("duyucheng", "Time out," + (MAXNUM - tries) + " more tries...");
            }
        }
        if (receivedResponse) {
            //如果收到資料,則打印出來
//            Log.e("duyucheng", "client received data from server:");
            String str_receive = new String(dp_receive.getData(), 0, dp_receive.getLength());
            String device_ip=dp_receive.getAddress().getHostAddress().toString();
            int rec_port=dp_receive.getPort();
            try {
                JSONObject jsonObject1=new JSONObject(str_receive);
                JSONObject paloadObject=jsonObject1.getJSONObject("payload");
                paloadObject.put("ip",device_ip);
                paloadObject.put("port",rec_port);
                str_receive =jsonObject1.toString();
            }catch (Exception e){
                e.printStackTrace();
            }
            //由於dp_receive在接收了資料之後,其內部訊息長度值會變為實際接收的訊息的位元組數,
            //所以這裡要將dp_receive的內部訊息長度重新置為1024
            dp_receive.setLength(1024);
            if (responseListener != null) {
                responseListener.onResponse(str_receive);
            }
        } else {
            //如果重發MAXNUM次資料後,仍未獲得伺服器傳送回來的資料,則列印如下資訊
//            Log.e("duyucheng", "No response -- give up.");
            if (responseListener != null) {
                responseListener.onError(null);
            }
        }
        ds.close();
    }

第五步  在第三步的send方法 成功用eventbus 將資料傳送出來,自己先new一個事件類JsCallH5ByNativeEvent,並在mainActivity中註冊 EventBus.getDefault().register(this); 

 UDPManager.getInstance().sendAsyn(context, ip, port,timeout, msg, new UdpResponseListener() {
                            @Override
                            public void onResponse(final String m) {
                                EventBus.getDefault().post(new JsCallH5ByNativeEvent(H5_MODULE_UDP,H5_ACTION_PUSH_MESSAGE,m));
                            }

                            @Override
                            public void onTimeOut() {

                            }

                            @Override
                            public void onError(Exception e) {

                            }
                        }
public class JsCallH5ByNativeEvent {
    private String service;
    private String action;
    private String data;
    public JsCallH5ByNativeEvent(String service,String action,String data){
        this.service=service;
        this.action=action;
        this.data=data;
    }

    public String getService() {
        return service;
    }

    public void setService(String service) {
        this.service = service;
    }

    public String getAction() {
        return action;
    }

    public void setAction(String action) {
        this.action = action;
    }

    public String getData() {
        return data;
    }

    public void setData(String data) {
        this.data = data;
    }
}
EventBus.getDefault().register(this);

第六步 在mainActivity中監聽eventBus傳送的資料

@Subscribe(threadMode=ThreadMode.MAIN)
    public void onJsCallH5ByNative(JsCallH5ByNativeEvent event){
        if (event != null&&getPresenter()!=null) {
            getPresenter().callH5ByNavite(bridgeWebView2,event.getService(),event.getAction(),event.getData());
        }
    }

第七步 將資料轉換成json傳送給h5端

 /**
     * 原生髮資料到H5
     * @param service
     * @param action
     * @param data
     */

    public void callH5ByNavite(final WVJBWebView bridgeWebView, String service, String action, String data) {
        JsonObject jsonBean = null;
        if (!TextUtils.isEmpty(data)) {
            try {
                jsonBean = new JsonParser().parse(data).getAsJsonObject();
            }catch (Exception e){
                e.printStackTrace();
            }
        }

        H5NativeBean h5NativeBean = new H5NativeBean();
        h5NativeBean.setService(service);
        h5NativeBean.setAction(action);
        h5NativeBean.setData(jsonBean);
        final String message = GsonUtils.getJsonString(h5NativeBean);
        Timber.e("%%callH5ByNavite 發到H5: " + message);
        if(getActivity()!=null&&bridgeWebView!=null){
            bridgeWebView.callHandler(HANDLER_CALL_NATIVE, message);
        }
    }

搞定 !!!  

涉及到的知識點有

1 service介面+serviceImpl實現類 "高擴充套件性”。

2 mvp模式 

Activity 和Fragment 視為View層,負責處理 UI。

Presenter 為業務處理層,既能呼叫UI邏輯,又能請求資料,該層為純Java類,不涉及任何Android API。

Model 層中包含著具體的資料請求,資料來源。

3 Handler

4 廣播

5 非同步

6 udp

7 eventBus