Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
407 views
in Technique[技术] by (71.8m points)

css - Android WebView Hardware Rendering Weird Artifact Issue

On some devices like the Nexus 7 or Samsung Galaxy Nexus (possibly others), I noticed this issue (See picture). This is running on Hardware Acceleration Mode in a WebView. However, when I turn it to Software Rendering Mode, it displays fine but slows down performance a little. I would love to learn how to fix this issue and only use Hardware Acceleration on the WebViews and not Software Mode.

Bad rendering (running on Samsung Galaxy Nexus): Image showing weird rending issue.

Correct rendering (running on Motorola Bionic): How it should render.

Android Manifest:

    <uses-sdk
        android:minSdkVersion="10"
        android:targetSdkVersion="17" />

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

    <supports-screens
       android:resizeable="true"
       android:smallScreens="true" 
       android:normalScreens="true" 
       android:largeScreens="true"
       android:xlargeScreens="true"
       android:anyDensity="true" />

    <application
        android:allowBackup="true"
        android:hardwareAccelerated="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        ....
    </application>
</manifest>

Java code:

....
@TargetApi(VERSION_CODES.HONEYCOMB)
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        try{

            db = new DatabaseHandler(this);
            tts = new TextToSpeech(this, this);
            handler = new Handler();

            frame = new FrameLayout(this);
            frame.setBackgroundColor(Color.GRAY);

            wv = new WebView(this);
            wv.setScrollBarStyle(WebView.SCROLLBARS_INSIDE_OVERLAY);

            frame.addView(wv);

            wv.setVisibility(WebView.INVISIBLE);

            iv = new ImageView(this);
            Drawable d = Drawable.createFromStream(getAssets().open("www/img/loading.jpg"), null);
            iv.setImageDrawable(d);

            FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,
                    FrameLayout.LayoutParams.MATCH_PARENT);
            iv.setLayoutParams(params);

            frame.addView(iv);

            WebSettings ws = wv.getSettings();
            ws.setRenderPriority(RenderPriority.HIGH);
            ws.setJavaScriptEnabled(true);
            ws.setAllowFileAccess(true);
            if (Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) 
                  ws.setAllowUniversalAccessFromFileURLs(true);
            ws.setCacheMode(WebSettings.LOAD_NO_CACHE);
            SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
            if(Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB && !pref.getBoolean("prefHardware", true)){
                wv.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
            }
            wv.setWebChromeClient(new WebChromeClient(){
                //int id = 0;
                @Override
                public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
                    // TODO Auto-generated method stub
//                  Log.d("JS Console", consoleMessage.message() + " at " + consoleMessage.sourceId()  + ":" + consoleMessage.lineNumber());
                    //Toast.makeText(TaskRunner.this, consoleMessage.message() + " at " + consoleMessage.sourceId()  + ":" + consoleMessage.lineNumber(), Toast.LENGTH_LONG).show();
//                  NotificationCompat.Builder mBuilder =
//                          new NotificationCompat.Builder(TaskRunner.this)
//                          .setSmallIcon(R.drawable.ic_launcher)
//                          .setContentTitle("Console Message")
//                          .setContentText(consoleMessage.message() + " at " + consoleMessage.sourceId()  + ":" + consoleMessage.lineNumber());
////                    mBuilder.setContentIntent(null);
//                  NotificationManager mNotificationManager =
//                      (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
//                  // mId allows you to update the notification later on.
//                  mNotificationManager.notify(id++, mBuilder.build());
                    return super.onConsoleMessage(consoleMessage);
                }
            });

            wv.setWebViewClient(new WebViewClient(){
                @Override
                public void onPageFinished(WebView view, String url) {
                    // TODO Auto-generated method stub
                    //Log.i("TEST", "TEST");
                    //hideLoading();
                    super.onPageFinished(view, url);
                }

                @Override
                public void onPageStarted(WebView view, String url,
                        Bitmap favicon) {
                    // TODO Auto-generated method stub
                    if(frame.getChildAt(frame.getChildCount()-1) != iv){
                        frame.addView(iv);
                        wv.setVisibility(WebView.INVISIBLE);
                    }
                    super.onPageStarted(view, url, favicon);
                }

                @Override
                public void onReceivedError(WebView view, int errorCode,
                        String description, String failingUrl) {
                    // TODO Auto-generated method stub
                    Log.e("ERROR", description);
                    super.onReceivedError(view, errorCode, description, failingUrl);
                }
            });
            MyJavascriptInterface ji = new MyJavascriptInterface();
            wv.addJavascriptInterface(ji, "ji");

            setOrientation();

            wv.loadUrl("file:///android_asset/www/index.html");

            setContentView(frame);

        }
        catch(Exception e){

        }

    }
    ....

HTML file:

<!DOCTYPE html>
<html>
<head>
    <title>Task Player</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <meta name="format-detection" content="telephone=no" />
    ......
</head>
<body>
    <div id="gamespacer">
        <div id="instructions-block">
            <div id="explaination">
            </div>
            <div id="instructions-close">
                <br /><br />
                <div class="btn btn-large btn-info" id="speak-tts">Speak</div>
                <br /><br />
                <input type='checkbox' id='auto-speak' value="yes" checked="checked" />Auto speak
            </div>

            <div id="drag-container">
                <div id='drag'>
                    <span id='au'><img src="img/arrow-up.png" alt="" width="45" /></span>
                    <span id='ad'><img src="img/arrow-down.png" alt="" width="45"/></span>
                </div>
            </div>
        </div>
    </div>
    <div id="play-container">
            <canvas>
            </canvas>
    </div>
    <div class="centerMe" id="rotate" style="display: none;"><p style="font-size: 24px; margin-top: 20px">Rotate the device to play the game.</p></div>
    <div id="winScreen">
        <div id="points">
            You Got<br/> 
            <div id="mypoints">0</div> 
            Points
        </div>
        <a href="#" id='done' class="btn btn-large btn-inverse">Done</a>
    </div>
....
</body>
</html>

css:

#winScreen{
  display: none;
  width: 100%;
  height: 100%;
  text-align: center;
  background-color: green;
  font-size: 24px;
  margin: 0 auto;
  position: absolute;
  left: 0px;
  top: 0px;
}
  #points {
    padding-top: 110px;
    color: yellow;
    margin-bottom: 10px;
  }

  #mypoints{
    margin: 10px;
  }

  .button{
    margin: 0 auto;
    margin-top: 50px;
  }

#play-container{  
  width: 100%;
  height: 100%;
  margin: 0 auto;
  text-align: center;
}

canvas {
    background-color: white;
    position: absolute;
    top: 0px;
    left: 0px;
}

#gamespacer {
}

      #instructions-block {

            position: absolute;
            left: 0px;
            top: -144px;
            width: 100%;
            background-color: white;
            z-index: 1;
        }

        #instructions-content {
          margin-top: 5px;
        }

        #instructions-close {
          text-align: center;
          margin-bottom: 10px;
        }

        #explaination {
          margin: 5px;
          font-size: 24px;
          line-height: 1;
          position: relative;
          width: 100%
          height: 100%
        }


        #drag-container{
            width: 100%;
        }

        #drag {
            position: absolute;
            left: 92%;
            margin-top: 5px;
        }

#au{
    display: none;
    pointer-events: none;
}    

#ad {
    pointer-events: none;
}

body{

   -webkit-user-select: none;
  user-select: none;


  -webkit-touch-callout: none;
  touch-callout: none;

  -webkit-tap-highlight-color: rgba(0,0,0,0);
  tap-highlight-color: rgba(0,0,0,0); 

}

UPDATE 1

So I tried the following code and that did not work as well.

wv.setLayerType(View.LAYER_TYPE_HARDWARE, null);

UPDATE 2

It looks like so far to my knowledge that this only happens on Google made devices (like the Nexus). So, could there be a bug in their kernel or video driver? Or, it could be something wrong with Android 4.2.2.

If you have any ideas on how to fix this, please let me know.

UPDATE 3

I have made a test project for you to try. Play around with the zoom level and moving the window around. Let me know if it does anything weird. It so far does this with Android 4.2.2

http://jsbin.com/equtoq/2

Thanks!

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

After a number of false starts, I could only find the hackish solution of switching between hardware rendering and software rendering depending on the current scaling.

Basically the idea is that rendering artifacts would only show up after a certain scaling, so we can safely use hardware rendering before this threshold is crossed and switch to slower-but-correct software rendering when larger scaling is used. The scaling threshold is > 1.67 on N7 though you may have to check on Galaxy Nexus to see if this value is universal. In my experience there are quite a number of issues with WebView across every version of Android, so this may be the best solution currently until everyone updated to 4.3...

 webView.setWebViewClient(new WebViewClient() {

                @Override
                public void onScaleChanged(WebView view, float oldScale,
                        float newScale) {
                //4.3 SDK required, or change Build.VERSION_CODES.JELLY_BEAN_MR2 to 18
                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
                    if (newScale > 1.7f) {
                        if (webView.getLayerType() != View.LAYER_TYPE_SOFTWARE) {
                            webView.setLayerType(View.LAYER_TYPE_SOFTWARE,
                                    null);
                        }
                    } else {
                        if (webView.getLayerType() != View.LAYER_TYPE_HARDWARE) {
                            webView.setLayerType(View.LAYER_TYPE_HARDWARE,
                                    null);
                        }
                    }
                }
                }
});

Note that onScaleChanged() would be called multiple times, sometimes crossing the threshold with a single zoom (using zoom in/out button), so you should add some logic to reduce the number of webView.setLayerType() calls to one-per-zoom event to reduce hardware/software rendering artifacts that would be displayed for a brief interval


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...