Android Package WebAPP with webView

  • 2021-08-17 01:11:41
  • OfStack

Preface Android webView compatibility experience is really poor to the extreme! !

A while ago, the boss wanted to put WebAPP into Android and iOS, and because I have done Android before, I will package this aspect. The principle is very simple, that is, when opening APP, load the website address with webView, so that the server can update WeChat version, iOS version and Android version once;

First of all, I want to say that if you have files uploaded in your WebAPP and want to be fully compatible, then don't use the native WebAPP. Later, I will write a blog about crossWalk, but before that, I will first record some pits I have experienced. My tool uses Android studio;;

Create a project, I won't say this, there are many tutorials on the Internet;

First add permissions in app/src/main/AndroidManifest. xml:

Note that "..." in the code in this article represents omitted code


<manifest ...>
  <uses-permission android:name="android.permission.INTERNET" />
  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
  <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
  <application
      ...
  </application>
</manifest>
The first is to allow access to network connections; The second is to allow programs to write to external storage, such as writing files on SD cards; The third is to allow applications to read from external storage;

Then add app/src/main/res/layout/activity_main. xml:


<WebView
    android:id="@+id/local_webview"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:visibility="gone" />

MainActivety.java:


private WebView webview;
//...
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
    WebView.setWebContentsDebuggingEnabled(true);
  }

  webview = findViewById(R.id.local_webview);

  WebSettings settings = webview.getSettings();

  loading = findViewById(R.id.loadView);

  settings.setJavaScriptEnabled(true);// Must 

  settings.setCacheMode(WebSettings.LOAD_DEFAULT);// Shut down webview Cache in 
  settings.setRenderPriority(WebSettings.RenderPriority.HIGH);// Increase the priority of rendering 
  settings.setUseWideViewPort(true);//WebView Whether to support HTML " viewport "Label or use wide viewport . 
  settings.setAllowContentAccess(true);// Is it allowed in the WebView Access content in URL
  settings.setBuiltInZoomControls(true);// Whether to use its built-in zoom mechanism 
  settings.setJavaScriptCanOpenWindowsAutomatically(true);// Whether to allow pop-up window to open automatically 
  settings.setDomStorageEnabled(true);// Whether to turn it on DOM Storage API Authority 

  webview.loadUrl("http://www.baidu.com");

  webview.setWebChromeClient(new WebChromeClient() {
    @Override
    public void onProgressChanged(WebView view, int newProgress) {
      Log.d(" Loading ", "on page progress changed and progress is " + newProgress);
      //...
    }

  }
  
  webview.setWebViewClient(new WebViewClient() {
    @Override
    public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
      super.onReceivedError(view, errorCode, description, failingUrl);
          //  Handling when loading a web page fails   Such as: 
      view.loadDataWithBaseURL(null,
        "<span> Page Load Failed , Please confirm that the network is connected </span>",
        "text/html",
        "utf-8",
        null);
    }

    @Override
    public void onPageFinished(WebView view, String url) {
      if (!webview.getSettings().getLoadsImagesAutomatically()) {
        webview.getSettings().setLoadsImagesAutomatically(true);
      }
      Log.d(" Loading ", "end ");
    }

  });
}

This is a relatively simple example of webView, and there are several points to be said here:

For WebSettings:

1.1 This setting is required for Web pages that need to run js: setJavaScriptEnabled

1.2 For setCacheMode, try not to set the value of LOAD_CACHE_ONLY. Setting this value will cause Provisional headers are shown problems when webkit browsers access ajax in a short time;

1.3 As for the default value of AllowFileAccess 1, there will be safety problems after opening;

1.4 WebSettings has a lot of settings. If you want to see more, you can search;

1.5 No other problems have been found yet, to be determined;

setWebChromeClient and setWebViewClient:

2.1 These two are configuration attributes of webView, but they are functionally different:

WebViewClient helps WebView handle various notifications, request events

WebChromeClient is to assist WebView processing Javascript dialog box, website icon, website title, loading progress, etc.;

js inside the use of alert and confirm need to be modified in WebChromeClient, provide a dialog;

2.2 For onPageFinished:

If your route is loaded asynchronously, such as resolve = > require (['./routers/XXX'], resolve), then it should be noted that this function will be triggered after entering the asynchronously loaded page, so if you need to execute the code only once after the page is loaded, put it in onProgressChanged of setWebChromeClient to judge whether the progress is 100 before executing;

webview.loadUrl():

3.1 There are two loading addresses here, one is webview.loadUrl ("file:///android_asset/index. html"); Access to local files, 2 is webview. loadUrl ("http://www. baidu. com"); Access network files;

Each has its own advantages: if you access network files and update server contents, you can use the latest functions; If you visit local resources, the loading speed will be 1 point faster, and you can see the default things even if you are disconnected from the network;

Just now, I mentioned the speed of entering APP. Here I called a loaded animation to complete it:

I choose this animation here: Click to view it

In Android studio, the way to call plug-ins is 10 points simple:

Open build. gradle in the root directory and add in repositories of allprojects:


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

Then open app/build. gradle and add:

compile 'com.github.zzz40500:android-shapeLoadingView:1.0.3.2'

At this time, first build project, and then add code in src/main/res/layout/activity_main. xml:


<android.support.constraint.ConstraintLayout >

  <com.mingle.widget.LoadingView
    android:id="@+id/loadView"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" />

  ...

</android.support.constraint.ConstraintLayout>

This is OK, so loading animation is added, and it only needs to be displayed and hidden in Java code;

The most critical html: input [type= "file"] problem, this is the biggest problem, let's talk about it first
If your webApp doesn't need to upload files or you don't care about Android version 4.2-4.4, you can use this method
MainActivity.java:
Create variables first


public static final int INPUT_FILE_REQUEST_CODE = 1;
  private ValueCallback<Uri> mUploadMessage;
  private final static int FILECHOOSER_RESULTCODE = 2;
  private ValueCallback<Uri[]> mFilePathCallback;
  private String mCameraPhotoPath;

Add code in setWebChromeClient:


public void openFileChooser(ValueCallback uploadMsg, String acceptType) {
        Log.d(" Select ", "3.0+");
        mUploadMessage = uploadMsg;
        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
        i.addCategory(Intent.CATEGORY_OPENABLE);
        i.setType("image/*");
        MainActivity.this.startActivityForResult(
            Intent.createChooser(i, "Image Chooser"),
            FILECHOOSER_RESULTCODE);
      }


      //Android 5.0
      public boolean onShowFileChooser(
          WebView webView, ValueCallback<Uri[]> filePathCallback,
          WebChromeClient.FileChooserParams fileChooserParams) {
        Log.d(" Select ", "5.0+");
        if (mFilePathCallback != null) {
          mFilePathCallback.onReceiveValue(null);
        }


        mFilePathCallback = filePathCallback;


        Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
          // Create the File where the photo should go
          File photoFile = null;
          try {
            // Settings MediaStore.EXTRA_OUTPUT Path , Full path written by camera 
            photoFile = createImageFile();
            takePictureIntent.putExtra("PhotoPath", mCameraPhotoPath);
          } catch (Exception ex) {
            // Error occurred while creating the File
            Log.e("WebViewSetting", "Unable to create Image File", ex);
          }

          // Continue only if the File was successfully created
          if (photoFile != null) {
            mCameraPhotoPath = "file:" + photoFile.getAbsolutePath();
            takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,
                Uri.fromFile(photoFile));
            System.out.println(mCameraPhotoPath);
          } else {
            takePictureIntent = null;
          }
        }

        Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
        contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
        contentSelectionIntent.setType("image/*");
        Intent[] intentArray;
        if (takePictureIntent != null) {
          intentArray = new Intent[]{takePictureIntent};
          System.out.println(takePictureIntent);
        } else {
          intentArray = new Intent[0];
        }

        Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
        chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
        chooserIntent.putExtra(Intent.EXTRA_TITLE, "Image Chooser");
        chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);

        startActivityForResult(chooserIntent, INPUT_FILE_REQUEST_CODE);

        return true;
      }
      // For Android 3.0+
      public void openFileChooser(ValueCallback<Uri> uploadMsg) {
        Log.d(" Select ", "3.0+");

        mUploadMessage = uploadMsg;
        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
        i.addCategory(Intent.CATEGORY_OPENABLE);
        i.setType("image/*");
        MainActivity.this.startActivityForResult(Intent.createChooser(i, "Image Chooser"), FILECHOOSER_RESULTCODE);

      }

      //For Android 4.1
      public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
        Log.d(" Select ", "4+");
        mUploadMessage = uploadMsg;
        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
        i.addCategory(Intent.CATEGORY_OPENABLE);
        i.setType("image/*");
        MainActivity.this.startActivityForResult(Intent.createChooser(i, "Image Chooser"), MainActivity.FILECHOOSER_RESULTCODE);

      }

In the main class, add:


@SuppressLint("SdCardPath")
  private File createImageFile() {
    File file=new File(Environment.getExternalStorageDirectory()+"/","tmp.png");
    mCameraPhotoPath=file.getAbsolutePath();
    if(!file.exists())
    {
      try {
        file.createNewFile();
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
    return file;
  }
   @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
      Log.d("result", "show");
      if (requestCode == FILECHOOSER_RESULTCODE) {
        if (null == mUploadMessage) return;
        Uri result = data == null || resultCode != RESULT_OK ? null
            : data.getData();
        if (result != null) {
          String imagePath = ImageFilePath.getPath(this, result);
          if (!TextUtils.isEmpty(imagePath)) {
            result = Uri.parse("file:///" + imagePath);
          }
        }
        mUploadMessage.onReceiveValue(result);
        mUploadMessage = null;
      } else if (requestCode == INPUT_FILE_REQUEST_CODE && mFilePathCallback != null) {
        // 5.0 Callback of 
        Uri[] results = null;
  
        // Check that the response is a good one
        if (resultCode == Activity.RESULT_OK) {
          if (data == null && !TextUtils.isEmpty(data.getDataString())) {
            // If there is not data, then we may have taken a photo
            if (mCameraPhotoPath != null) {
              results = new Uri[]{Uri.parse(mCameraPhotoPath)};
            }
          } else {
            String dataString = data.getDataString();
            if (dataString != null) {
              results = new Uri[]{Uri.parse(dataString)};
            }
          }
        }
  
  
        mFilePathCallback.onReceiveValue(results);
        mFilePathCallback = null;
      } else {
        super.onActivityResult(requestCode, resultCode, data);
        return;
      }
    }

I also need an ImageFilePath class file here. I put it in GitHub. I will attach a link later:

As for Android 4.2-4.4, there will be problems because of this: click to view

ps: FQ is required

If you are an native developer, it is easier to solve it, that is, you can call Java directly with js when clicking. If not, 1 generally needs the support of other frameworks or plug-ins;

These are the basic problems I have encountered. If there are mistakes and omissions, please point out them, thank you;

GitHub:https://github.com/Grewer/android-casing-webapp


Related articles: