android原生项目webView与js交互实例

一、我项目中选择DSBridge作为H5页面与Native进行通信

1、原生的webView对js的支持比较生硬,选择DSBridge进行混合开发它支持跨平台,双向调用,仅支持同步调用(符合我的项目需求),简单易用;

2.添加jitpack,在项build.gradle

maven { url "https://jitpack.io" }

3.添加依赖app的build.gradle

implementation 'com.github.wendux:DSBridge-Android:master-SNAPSHOT'

二、接下来就是使用了

1、创建JsApi用来接收H5页面js方法进行参数交互,为了安全起见每个方法前添加 @JavascriptInterface;绑定activity后该页面加载的h5页面js方法名和JsApi中的同名即可实现相互通信;以下代码实例日期选择并上传H5页,自定义js方法参考showApprovalContacts(Object arg, final CompletionHandler handler) ;

public class JsApi {
    AppContainerActivity activity;
    public JsApi(AppContainerActivity activity) {
        this.activity = activity;
    }
    @JavascriptInterface
    public void selectDate(Object msg, final CompletionHandler<String> handler) {
        final Calendar calendar = Calendar.getInstance();
        new DatePickerDialog(activity,
                // 绑定监听器(How the parent is notified that the date is set.)
                new DatePickerDialog.OnDateSetListener() {
                    @Override
                    public void onDateSet(DatePicker view, final int year,
                                          final int monthOfYear, final int dayOfMonth) {
                        new TimePickerDialog(activity, new TimePickerDialog.OnTimeSetListener() {
                            @Override
                            public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
                                calendar.set(year, monthOfYear, dayOfMonth, hourOfDay, minute);
                                JSONObject jsonObject = new JSONObject();
                                try {
                                    jsonObject.put("result", "success");
                                    jsonObject.put("message", "");
                                    jsonObject.put("data", String.valueOf(calendar.getTimeInMillis()));
                                } catch (JSONException e) {
                                    e.printStackTrace();
                                }
                                handler.complete(jsonObject.toString());
                            }
                        }, 0, 0, true).show();
                    }
                }
                // 设置初始日期
                , calendar.get(Calendar.YEAR)
                , calendar.get(Calendar.MONTH)
                , calendar.get(Calendar.DAY_OF_MONTH)).show();
    }

   
    @JavascriptInterface
    public void testNoArgAsyn(Object arg, CompletionHandler<String> handler) {
        handler.complete("testNoArgAsyn   called [ asyn call]");
    }


    //@JavascriptInterface
    //without @JavascriptInterface annotation can't be called
    public String testNever(Object arg) throws JSONException {
        JSONObject jsonObject = (JSONObject) arg;
        return jsonObject.getString("msg") + "[ never call]";
    }

    @JavascriptInterface
    public void callProgress(Object args, final CompletionHandler<Integer> handler) {
        new CountDownTimer(11000, 1000) {
            int i = 10;
            @Override
            public void onTick(long millisUntilFinished) {
                //setProgressData can be called many times util complete be called.
                handler.setProgressData((i--));
            }
            @Override
            public void onFinish() {
                //complete the js invocation with data; handler will be invalid when complete is called
                handler.complete(0);
            }
        }.start();
    }
    //自定义JS Api
    @JavascriptInterface
    public void getHost(Object arg, final CompletionHandler<String> handler) {
        String i = "{\"result\":\"success\",\"message\":\"\",\"data\":\"" + App.getInstance().getIP().substring(8) + "\"}";
        LogUtils.e(i);
        handler.complete(i);
    }

    @JavascriptInterface
    public void closeCurPage(Object arg, final CompletionHandler<String> handler) {
        AppManager.getInstance().finishTopActivity();
    }


    @JavascriptInterface
    public void getExternalStorageState(Object arg, final CompletionHandler<String> handler) {
        boolean mExternalStorageAvailable = false;
        boolean mExternalStorageWriteable = false;
        String state = Environment.getExternalStorageState();
        if (Environment.MEDIA_MOUNTED.equals(state)) {
            // We can read and write the media
            mExternalStorageAvailable = mExternalStorageWriteable = true;
        } else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
            // We can only read the media
            mExternalStorageAvailable = true;
            mExternalStorageWriteable = false;
        } else {
            // Something else is wrong. It may be one of many other states, but all we need
            //  to know is we can neither read nor write
            mExternalStorageAvailable = mExternalStorageWriteable = false;
        }
        handler.complete("{\"result\":\"success\",\"message\":{\"externalStorageAvailable\":" + mExternalStorageAvailable + ",\"externalStorageWriteable\":" + mExternalStorageWriteable + "},\"data\":\"\"}");
    }

    //外部存储接口,访问公共路径,删除app后会保留
    @JavascriptInterface
    public File writePublicStorage(Object arg, final CompletionHandler<String> handler) {
        // Get the directory for the user's public pictures directory.
        LogUtils.wtf(arg);
        File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), "fileName");
        LogUtils.wtf(file.getAbsolutePath());
        if (!file.mkdirs()) {
            LogUtils.wtf("Directory not created");
        }
        return file;
    }

    //外部存储接口,访问程序目录下路径,删除app后不会保留
    @JavascriptInterface
    public File writePrivateStorage(Object arg, final CompletionHandler<String> handler) {
        // Get the directory for the app's private pictures directory.
        File file = new File(App.getInstance().getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS), "fileName");
        LogUtils.wtf(file.getAbsolutePath());
        if (!file.mkdirs()) {
            LogUtils.wtf("Directory not created");
        }
        return file;
    }

    @JavascriptInterface
    public void save(Object arg, final CompletionHandler<String> handler) {
                handler.complete("{\"result\":\"success\",\"message\":\"\",\"data\":\"\"}");
    }
    @JavascriptInterface
    public void showApprovalContacts(Object arg, final CompletionHandler<String> handler) {
   //接收js传参后操作
        activity.selectPerson(this, handler);
    }

2、布局文件使用wendu.dsbridge.DWebView

<FrameLayout
      android:layout_width="match_parent"
      android:layout_height="0dp"
      android:layout_weight="1" >
      <wendu.dsbridge.DWebView
          android:id="@+id/webView"
          android:layout_width="match_parent"
          android:layout_height="match_parent" />
  </FrameLayout>

3、java代码使用,以下代码支持相册选择,文件上传下载;

public class AppContainerActivity extends BaseActivity {
    private static final int SET_TITLE = 1;
    boolean isShowClose = false;
    private static final String TAG = AppContainerActivity.class.getSimpleName();
    private static final int TAKE_PHOTO = 1;
    private static final int CROP_PHOTO = 11;
    private static final int CHOOSE_PHOTO = 2;
    private static final int SELECT_PERSON = 3;
    private static final int SELECT_FILE = 4;
    private DWebView webView;
    String appUrl;
    private String appName;
    private int REQUEST_PERMISSION = 1;
    private static final int REQUEST_PERMISSION_FINGER = 2;

    Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case SET_TITLE:
                    break;
            }
        }
    };
    @Override
    public void widgetClick(View v) {

    }
    @Override
    public void initParms(Bundle parms) {//页面传参接收器
    }

    @Override
    public View bindView() {
        return null;
    }

    @Override
    public int bindLayout() {
        return R.layout.activity_app_container;
    }

    private ValueCallback<Uri> mUploadMessage;
    private ValueCallback<Uri[]> mUploadCallbackAboveL;

    @Override
    public void initView(View view) {
        isShowClose = getIntent().getBooleanExtra("isShowClose", false);
        if (isShowClose)
            close.setVisibility(View.VISIBLE);
        else
            close.setVisibility(View.GONE);

      
        webView = findViewById(R.id.webView);


        WaitDialog.show(this, "正在加载").setCanCancel(true);
        //使webview 支持 上传文件
        webView.setWebChromeClient(new WebChromeClient() {
            // For Android < 3.0
            public void openFileChooser(ValueCallback<Uri> valueCallback) {
                mUploadMessage = valueCallback;
                openFileChooserActivity();
            }

            // For Android  >= 3.0
            public void openFileChooser(ValueCallback valueCallback, String acceptType) {
                mUploadMessage = valueCallback;
                openFileChooserActivity();
            }

            //For Android  >= 4.1
            public void openFileChooser(ValueCallback<Uri> valueCallback, String acceptType, String capture) {
                mUploadMessage = valueCallback;
                openFileChooserActivity();
            }

            // For Android >= 5.0
            @Override
            public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) {
                mUploadCallbackAboveL = filePathCallback;
                openFileChooserActivity();
                return true;
            }
        });

        webView.setWebViewClient(new WebViewClient() {
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                view.loadUrl(url);
                return true;
            }

            @Override
            public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
                // handler.cancel();// Android默认的处理方式
                handler.proceed();// 接受所有网站的证书
                // handleMessage(Message msg);// 进行其他处理
            }

            @Override
            public void onPageFinished(WebView view, String url) {
                super.onPageFinished(view, url);
                WaitDialog.dismiss();
            }

        });

        //webview支持下载
        webView.setDownloadListener(new DownloadListener() {
            @Override
            public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) {
                Intent intent = new Intent(Intent.ACTION_VIEW);
                intent.addCategory(Intent.CATEGORY_BROWSABLE);
                intent.setData(Uri.parse(url));
                startActivity(intent);
            }
        });

        webView.getSettings().setJavaScriptEnabled(true);
        webView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);

        jsApi = new JsApi(AppContainerActivity.this);
        webView.addJavascriptObject(jsApi, null);
        webView.addJavascriptObject(new HardWareApi(), "hardware");
        if (!TextUtils.isEmpty(appUrl)) {
              webView.loadUrl(appUrl);
        } else {
            SharedPreferences sharedPreferences = getSharedPreferences(Config.SP_CONFIGURATION, Context.MODE_PRIVATE);
            String url = sharedPreferences.getString("app_container_url", "");
            if (TextUtils.isEmpty(url)) {
                TipDialog.show(AppContainerActivity.this, "提供给第三方的api demo页面", TipDialog.TYPE_WARNING);
                webView.loadUrl("file:///android_asset/wx.html");
            } else {
                webView.loadUrl(url);
                TipDialog.show(AppContainerActivity.this, "当前网址是:" + url, TipDialog.TYPE_WARNING);
            }
        }
    }

    private void openFileChooserActivity() {
        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
        i.addCategory(Intent.CATEGORY_OPENABLE);
        i.setType("*/*");
        try {
            startActivityForResult(Intent.createChooser(i, "File Chooser"), SELECT_FILE);
        } catch (android.content.ActivityNotFoundException ex) {
            Toast.makeText(this, "请安装文件管理器", Toast.LENGTH_SHORT).show();
        }
    }

    @Override
    public void setListener() {
    }
    @Override
    public void doBusiness(Context mContext) {

    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        EventBus.getDefault().register(this);
        StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
        StrictMode.setVmPolicy(builder.build());
        builder.detectFileUriExposure();
    }
    @Override
    protected void onStop() {
        super.onStop();
        clearWebViewCache();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        EventBus.getDefault().unregister(this);
    }

    /**
     * 清除WebView缓存
     */
    public void clearWebViewCache() {
        webView.clearFormData();
        webView.clearHistory();
        webView.clearMatches();
        webView.clearSslPreferences();
    }

    Uri imageUri;
    JsApi jsApi;
    HardWareApi hardWareApi;

    CompletionHandler fingerHandler;

    public void fingerAuth(HardWareApi hardWareApi, CompletionHandler handler) {
        this.hardWareApi = hardWareApi;
        this.fingerHandler = handler;
        Fingerprint fingerprint = new Fingerprint(AppContainerActivity.this, hardWareApi, fingerHandler);
    }

    public void takePhoto(HardWareApi hardWareApi) {
        this.hardWareApi = hardWareApi;
        File outputImage = new File(Environment.getExternalStorageDirectory(),
                "tempImage" + ".jpg");
        try {
            if (outputImage.exists()) {
                outputImage.delete();
            }
            outputImage.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
        }
        imageUri = Uri.fromFile(outputImage);
        ActivityCompat.requestPermissions(this,
                new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE},
                REQUEST_PERMISSION);
    }


    public void openAlbum() {
        Intent intent = new Intent("android.intent.action.GET_CONTENT");
        intent.setType("image/*");
        startActivityForResult(intent, CHOOSE_PHOTO);
    }


    @TargetApi(19)
    private void handleImageOnKitKat(Intent data) {
        String imagePath = null;
        Uri uri = data.getData();
        if ("content".equalsIgnoreCase(uri.getScheme())) {
            //如果是content类型的Uri,则使用普通方法处理
            imagePath = getImagePath(uri, null);
            Log.i(imagePath, "相册选择3");
        } else if ("file".equalsIgnoreCase(uri.getScheme())) {
            //如果是file类型的Uri,直接获取图片路径即可
            imagePath = uri.getPath();
            Log.i(imagePath, "相册选择4");
        } else if (DocumentsContract.isDocumentUri(this, uri)) {
            //如果是document类型的Uri,则通过document id处理
            String docId = DocumentsContract.getDocumentId(uri);
            if ("com.android.providers.media.document".equals(uri.getAuthority())) {
                String id = docId.split(":")[1];//解析出数字格式的id
                String selection = MediaStore.Images.Media._ID + "=" + id;
                imagePath = getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                        selection);
                Log.i(imagePath, "相册选择1");
            } else if ("com.android.providers.downloads.documents".equals
                    (uri.getAuthority())) {
                Uri contentUri = ContentUris.withAppendedId(Uri.parse(
                        "content://downloads/public_downloads"), Long.valueOf(docId));
                imagePath = getImagePath(contentUri, null);
                Log.i(imagePath, "相册选择2");
            }
        }
        displayImage(imagePath);
    }

    private void handleImageBeforeKitKat(Intent data) {
        Uri uri = data.getData();
        String imagePath = getImagePath(uri, null);
        Log.i(imagePath, "相册选择5");
        displayImage(imagePath);
    }

    private String getImagePath(Uri uri, String selection) {
        String path = null;
        //通过uri和selection来获取真实的图片路径
        Cursor cursor = getContentResolver().query(uri, null, selection, null, null);
        if (cursor != null) {
            if (cursor.moveToFirst()) {
                path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.
                        Media.DATA));
            }
            cursor.close();
        }
        Log.i(path, "相册选择");
        return path;
    }

    private void displayImage(String imagePath) {
        LogUtils.wtf(imagePath);
        if (imagePath != null) {
            Bitmap bitmap = BitmapFactory.decodeFile(imagePath);
//            picture.setImageBitmap(bitmap);
        } else {
            Toast.makeText(this, "failed to get image", Toast.LENGTH_LONG).show();
        }
    }


    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (resultCode == RESULT_OK)
            switch (requestCode) {
                case TAKE_PHOTO:
                    Intent intent = new Intent("com.android.camera.action.CROP");
                    intent.setDataAndType(imageUri, "image/*");
                    intent.putExtra("scale", true);
                    intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
                    startActivityForResult(intent, CROP_PHOTO); // 启动裁剪程序
                    break;
                case CROP_PHOTO:
                    try {
                        final Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver()
                                .openInputStream(imageUri));
                        hardWareApi.uploadImg(bitmap);
                    } catch (FileNotFoundException e) {
                        e.printStackTrace();
                    }
                    break;
                case CHOOSE_PHOTO:
                    //判断手机系统版本号
                    if (Build.VERSION.SDK_INT >= 19) {
                        //4.4及以上系统使用该方法处理图片
                        handleImageOnKitKat(data);
                    } else {
                        //4.4以下系统使用该方法处理图片
                        handleImageBeforeKitKat(data);
                    }
                    break;
                case SELECT_PERSON:
               
                    break;
                case SELECT_FILE:
                    if (null == mUploadMessage && null == mUploadCallbackAboveL) return;
                    Uri result = data == null || resultCode != RESULT_OK ? null : data.getData();
                    if (mUploadCallbackAboveL != null) {
                        onActivityResultAboveL(requestCode, resultCode, data);
                    } else if (mUploadMessage != null) {
                        mUploadMessage.onReceiveValue(result);
                        mUploadMessage = null;
                    }
                    break;
            }
        else {
            switch (requestCode) {
                case SELECT_FILE:
                    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
                        Uri result = null;
                        mUploadMessage.onReceiveValue(result);
                    } else {
                        Uri[] results = null;
                        mUploadCallbackAboveL.onReceiveValue(results);
                    }
                    mUploadMessage = null;
                    mUploadCallbackAboveL = null;
                    break;
            }
        }
    }

    // 4. 选择内容回调到Html页面
    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    private void onActivityResultAboveL(int requestCode, int resultCode, Intent intent) {
        if (requestCode != SELECT_FILE || mUploadCallbackAboveL == null)
            return;
        Uri[] results = null;
        if (resultCode == Activity.RESULT_OK) {
            if (intent != null) {
                String dataString = intent.getDataString();
                ClipData clipData = intent.getClipData();
                if (clipData != null) {
                    results = new Uri[clipData.getItemCount()];
                    for (int i = 0; i < clipData.getItemCount(); i++) {
                        ClipData.Item item = clipData.getItemAt(i);
                        results[i] = item.getUri();
                    }
                }
                if (dataString != null)
                    results = new Uri[]{Uri.parse(dataString)};
            }
        }
        mUploadCallbackAboveL.onReceiveValue(results);
        mUploadCallbackAboveL = null;
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                           @NonNull int[] grantResults) {
        if (requestCode == REQUEST_PERMISSION) {
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
                intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
                startActivityForResult(intent, TAKE_PHOTO);
            } else
                Toast.makeText(AppContainerActivity.this, "请开启拍照权限", Toast.LENGTH_LONG).show();
   
     
    }

    @Override
    public void onBackPressed() {
        if (webView.canGoBack())
            webView.goBack();
        else
            super.onBackPressed();
    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        Bundle bundle = intent.getExtras();
        if (bundle != null) {
            appUrl = bundle.getString("appUrl", "");
        }
        webView.loadUrl(appUrl);
    }

    CompletionHandler<String> selectHandler;
    public void selectPerson(JsApi jsApi, CompletionHandler<String> handler) {
        this.selectHandler = handler;
    }
}

4、h5页面调用Java API

(1)初始化dsBridge

 var dsBridge=require("dsBridge");

(2)调用API

 dsBridge.call(str,{msg:"str"})

你可能感兴趣的:(android,h5)